Skip to content

Commit b1d726d

Browse files
ashishenoypcopybara-github
authored andcommitted
Add docstrings for the new flags API and update the flags codelab documentation.
PiperOrigin-RevId: 555528764
1 parent 5c74ca2 commit b1d726d

File tree

2 files changed

+177
-22
lines changed

2 files changed

+177
-22
lines changed

docs/flags_code_lab.md

Lines changed: 120 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,40 @@
44

55
You can easily fiddle with your configuration using command line flags, thanks
66
to Fiddle's absl_flags integration! This code lab will describe how it all
7-
works. See also the [complete working example][example].
7+
works. Configuration of fiddle config objects via command line arguments is
8+
supported using 2 APIs:
89

9-
[example]: http://github.com/google/fiddle/tree/main/fiddle/absl_flags/example
10+
| API | Purpose |
11+
| ---------- | --------------------------------------------------------------- |
12+
| New API | Defines custom configurations per binary using the API |
13+
: : `DEFINE_fiddle_config()` which returns a built config object :
14+
: : handle after applying all the command line overrides in order. :
15+
: : The usage of this API is more intuitive than the legacy API, :
16+
: : and it provides the ability to define custom overrides per :
17+
: : binary. Additionally, the overrides are applied in order, which :
18+
: : is a more intuitive user experience than the current order :
19+
: : followed by the legacy API :
20+
| Legacy API | Invoked via `create_buildable_from_flags()` that returns a |
21+
: : built config object. Command line overrides are NOT applied in :
22+
: : order; all fiddlers are applied first, followed by all tags, :
23+
: : followed by all overrides. :
24+
25+
> NOTE: New usages of the legacy flags API are discouraged and users should
26+
> migrate their legacy usage to the new API.
27+
28+
See some working examples of the APIs below.
29+
30+
<section class="tabs" markdown=1>
31+
32+
### New API {.new-tab}
33+
34+
[example](http://github.com/google/fiddle/tree/main/fiddle/_src/absl_flags/sample_test_binary.py)
35+
36+
### Legacy API {.new-tab}
37+
38+
[example](http://github.com/google/fiddle/tree/main/fiddle/absl_flags/example)
39+
40+
</section>
1041

1142
[TOC]
1243

@@ -36,6 +67,30 @@ Fiddle pattern includes:
3667

3768
When our example program is executed, the following steps occur:
3869

70+
<section class="tabs" markdown=1>
71+
72+
### New API {.new-tab}
73+
74+
1. **Launch**: We run our program on the command line:
75+
76+
```sh
77+
python3 -m fiddle._src.absl_flags.sample_test_binary \
78+
--sample_config config:base_experiment \ --sample_config
79+
fiddler:'set_dtypes(dtype="float64")' \ --sample_config
80+
set:decoder.mlp.use_bias=True \ --sample_config
81+
set:decoder.mlp.dtype='"float16"'
82+
```
83+
84+
2. **Flag Parsing**: The custom Fiddle flag parser parses Fiddle-related flags
85+
from the command line, applying any overrides in the order specified in the
86+
command line, and returns a built object `_SAMPLE_FLAG.value` that has all
87+
the overrides applied.
88+
89+
3. **Business logic**: `main` calls arbitrary functions on the built objects to
90+
carry out whatever task your application has been designed to do.
91+
92+
### Legacy API {.new-tab}
93+
3994
1. **Launch**: We run our program on the command line:
4095

4196
```sh
@@ -84,30 +139,75 @@ When our example program is executed, the following steps occur:
84139
case of the example, `main` calls `runner.run()` to (pretend to) train a
85140
neural network.
86141
142+
</section>
143+
87144
## Flag Syntax
88145
89146
The Fiddle flag integration supports the following flag syntax:
90147
91-
- **Base Config**: The base configuration function is specified with the flag
92-
`--fdl_config`. Example: `--fdl_config=base`. Alternatively, a
93-
JSON-serialized configuration can be read from a file via with the flag
94-
`--fdl_config_file`. Example: `--fdl_config_file='/some/path/config.json'`.
148+
- **Base Config**: The base configuration function is specified with the flag:
149+
150+
<section class="tabs" markdown=1>
151+
152+
### New API {.new-tab}
153+
154+
that was set as the `name` argument for `DEFINE_fiddle_config` and the command
155+
`config`. For example, if the flag object was instantiated as
156+
`DEFINE_fiddle_config(name="my_flag", ...)`, then the base config is specified
157+
by using `--my_flag
158+
config:some_function_returning_fiddle_config_to_be_overridden()`.
159+
160+
### Legacy API {.new-tab}
161+
162+
`--fdl_config`. Example: `--fdl_config=base`. Alternatively, a JSON-serialized
163+
configuration can be read from a file via with the flag `--fdl_config_file`.
164+
Example: `--fdl_config_file='/some/path/config.json'`.
165+
166+
</section>
95167
96168
- **Fiddlers**: Fiddlers are specified on the command line with the
97-
`--fiddler=` flag. This flag can be specified multiple times. Example:
98-
`--fiddler=swap_weight_and_biases --fiddler=other_fiddler`.
169+
170+
<section class="tabs" markdown=1>
171+
172+
### New API {.new-tab}
173+
174+
command `fiddler` after the `name` argument for `DEFINE_fiddle_config`. For
175+
example, if the flag object was instantiated as
176+
`DEFINE_fiddle_config(name="my_flag", ...)` then the fiddlers would be invoked
177+
like `--my_flag fiddler:name_of_fiddler(value="new_value")`.
178+
179+
### Legacy API {.new-tab}
180+
181+
`--fiddler=` flag. This flag can be specified multiple times. Example:
182+
`--fiddler=swap_weight_and_biases --fiddler=other_fiddler`.
183+
184+
</section>
99185
100186
- **Specific Overrides**: Specific overrides allow you to specify specific
101187
values to arbitrary fields on the command line. The syntax is
102-
`--fdl.dotted.path.of.fields=3`, where everything after the `fdl.` prefix
103-
corresponds to the name of a field in the Fiddle configuration object
104-
corresponding to exactly the same Python code. For example, if (in Python)
105-
we wanted to set the value of a nested field to 15, I might write:
106-
`cfg.model.dense1.parameters = 15`. The corresponding syntax on the command
107-
line would be: `--fdl.model.dense1.parameters=15`. Due to shell escaping, to
108-
specify a string value, you often need to use two nested quotes, or escape
109-
the outer quotes (depending on your shell). For example:
110-
`--fdl.data.filename='"other.txt"'` (or equivalently:
111-
`--fdl.data.filename=\"other.txt\"`). Only "literal" values may be specified
112-
on the command line like this; if you'd like to set a complex value, please
113-
write a fiddler and invoke it with the previous Fiddlers syntax.
188+
189+
<section class="tabs" markdown=1>
190+
191+
### New API {.new-tab}
192+
193+
the command `set` after the `name` argument for `DEFINE_fiddle_config`. For
194+
example, if the flag object was instantiated as
195+
`DEFINE_fiddle_config(name="my_flag", ...)`, then the specific overrides are
196+
specified using `--my_flag set:some_attr.some_sub_attr=some_value`.
197+
198+
### Legacy API {.new-tab}
199+
200+
`--fdl.dotted.path.of.fields=3`, where everything after the `fdl.` prefix
201+
corresponds to the name of a field in the Fiddle configuration object
202+
corresponding to exactly the same Python code. For example, if (in Python) we
203+
wanted to set the value of a nested field to 15, I might write:
204+
`cfg.model.dense1.parameters = 15`. The corresponding syntax on the command line
205+
would be: `--fdl.model.dense1.parameters=15`. Due to shell escaping, to specify
206+
a string value, you often need to use two nested quotes, or escape the outer
207+
quotes (depending on your shell). For example:
208+
`--fdl.data.filename='"other.txt"'` (or equivalently:
209+
`--fdl.data.filename=\"other.txt\"`). Only "literal" values may be specified on
210+
the command line like this; if you'd like to set a complex value, please write a
211+
fiddler and invoke it with the previous Fiddlers syntax.
212+
213+
</section>

fiddle/_src/absl_flags/flags.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
_F = TypeVar("_F")
4040

4141

42-
class FiddleFlag(flags.MultiFlag):
42+
class _FiddleFlag(flags.MultiFlag):
4343
"""ABSL flag class for a Fiddle config flag."""
4444

4545
def __init__(
@@ -127,8 +127,63 @@ def DEFINE_fiddle_config( # pylint: disable=invalid-name
127127
default_module: Optional[types.ModuleType] = None,
128128
flag_values: flags.FlagValues = flags.FLAGS,
129129
) -> flags.FlagHolder[Any]:
130+
r"""Declare and define a fiddle command line flag object.
131+
132+
When used in a python binary, after the flags have been parsed from the
133+
command line, this command line flag object will have the `fdl.Config` object
134+
built.
135+
136+
Example usage in a python binary:
137+
```
138+
.... other imports ...
139+
from fiddle import absl_flags as fdl_flags
140+
141+
_MY_FLAG = fdl_flags.DEFINE_fiddle_config(
142+
"my_config",
143+
help_string="My binary's fiddle config handle",
144+
default_module=sys.modules[__name__],
145+
)
146+
147+
def base_config() -> fdl.Config:
148+
return some_import.fixture.as_buildable()
149+
150+
def set_attributes(config, value: str):
151+
config.some_attr = value
152+
return config
153+
154+
def main(argv) -> None:
155+
if len(argv) > 1:
156+
raise app.UsageError("Too many command-line arguments.")
157+
# _MY_FLAG.value contains the config object, built from `base_config()`
158+
# with any command line flags and overrides applied in the order passed in
159+
# the command line.
160+
some_import.do_something(_MY_FLAG.value.some_attr)
161+
162+
if __name__ == "__main__":
163+
app.run(main)
164+
```
165+
166+
Invoking the above binary with:
167+
python3 -m path.to.my.binary --my_config config:base_config \
168+
--my_config fiddler:'set_attributes(value="float64")' --my_config \
169+
set:some_other_attr.enable=True
170+
171+
results in the `_MY_FLAG.value` set to the built config object with all the
172+
command line flags applied in the order they were passed in.
173+
174+
Args:
175+
name: name of the command line flag.
176+
default: default value of the flag.
177+
help_string: help string describing what the flag does.
178+
default_module: the python module where this flag is defined.
179+
flag_values: the ``FlagValues`` instance with which the flag will be
180+
registered. This should almost never need to be overridden.
181+
182+
Returns:
183+
A handle to defined flag.
184+
"""
130185
return flags.DEFINE_flag(
131-
FiddleFlag(
186+
_FiddleFlag(
132187
name=name,
133188
default_module=default_module,
134189
default=default,

0 commit comments

Comments
 (0)