google-deepmind/fancyflags
Python
Captured source
source ↗google-deepmind/fancyflags
Description: A Python library for defining structured command-line flags.
Language: Python
License: Apache-2.0
Stars: 35
Forks: 7
Open issues: 1
Created: 2021-02-05T09:14:53Z
Pushed: 2026-06-10T22:18:46Z
Default branch: master
Fork: no
Archived: no
README:
fancyflags
Introduction
fancyflags is a Python library that extends `absl.flags` with additional structured flag types.
fancyflags provides flags corresponding to structures such as [dicts](#dict-flags), [dataclasses, and (somewhat) arbitrary callables](#auto).
These flags are typed and validated like regular absl flags, catching errors before they propagate into your program. To override values, users can access familiar "dotted" flag names.
TIP: Already a fancyflags user? Check out our [usage tips](#tips)!
A short note on design philosophy:
fancyflags promotes mixing with regular absl flags. In many cases a few regular absl flags are all you need!
fancyflags does not require you to modify library code: it should only be used in your "main" file
fancyflags is not a dependency injection framework, and avoids programming-language-like power features. We prefer that users write regular Python for wiring up their code, because it's explicit, simple to understand, and allows static analysis tools to identify problems.
Installation
fancyflags can be installed from PyPI using pip:
pip install fancyflags
It can also be installed directly from our GitHub repository:
pip install git+git://github.com/deepmind/fancyflags.git
or alternatively by checking out a local copy of our repository and running:
pip install /path/to/local/fancyflags/
Dict flags
If we have a class Replay, with arguments capacity, priority_exponent and others, we can define a corresponding dict flag in our main script
import fancyflags as ff
_REPLAY_FLAG = ff.DEFINE_dict(
"replay",
capacity=ff.Integer(int(1e6)),
priority_exponent=ff.Float(0.6),
importance_sampling_exponent=ff.Float(0.4),
removal_strategy=ff.Enum("fifo", ["rand", "fifo", "max_value"])
)and **unpack the values directly into the Replay constructor
replay_lib.Replay(**_REPLAY_FLAG.value)
ff.DEFINE_dict creates a flag named replay, with a default value of
{
"capacity": 1000000,
"priority_exponent": 0.6,
"importance_sampling_exponent": 0.4,
"removal_strategy": "fifo",
}For each item in the dict, ff.DEFINE_dict also generates a dot-delimited "item" flag that can be overridden from the command line. In this example the item flags would be
replay.capacity replay.priority_exponent replay.importance_sampling_exponent replay.removal_strategy
Overriding an item flag from the command line updates the corresponding entry in the dict flag. The value of the dict flag can be accessed by the return value of ff.DEFINE_dict (_REPLAY_FLAG.value in the example above), or via the FLAGS.replay attribute of the absl.flags module. For example, the override
python script_name.py -- --replay.capacity=2000 --replay.removal_strategy=max_value
sets _REPLAY_FLAG.value to
{
"capacity": 2000, # <-- Overridden
"priority_exponent": 0.6,
"importance_sampling_exponent": 0.4,
"removal_strategy": "max_value", # <-- Overridden
}Nested dicts
fancyflags also supports nested dictionaries:
_NESTED_REPLAY_FLAG = ff.DEFINE_dict( "replay", capacity=ff.Integer(int(1e6)), exponents=dict( priority=ff.Float(0.6), importance_sampling=ff.Float(0.4), ) )
In this example, _NESTED_REPLAY_FLAG.value would be
{
"capacity": 1000000,
"exponents" : {
"priority": 0.6,
"importance_sampling": 0.4,
}
}and the generated flags would be
replay.capacity replay.exponents.priority replay.exponents.importance_sampling
Help strings
fancyflags uses the item flag's name as the default help string, however this can also be set manually:
_NESTED_REPLAY_FLAG = ff.DEFINE_dict( "replay", capacity=ff.Integer(int(1e6), "Maximum size of replay buffer."), exponents=dict( priority=ff.Float(0.6), # Help string: "replay.exponents.priority" importance_sampling=ff.Float(0.4, "Importance sampling coefficient."), ) )
"Auto" flags for functions and other structures {#auto}
fancyflags provides several "auto" functions for generating flags corresponding to callables, dataclasses and mappings. In these functions, the type information and default values are inferred from a provided callable or example structure.
This is more concise than spelling out individual items like ff.Float(...), and provides stronger static analysis support via typed containers such as dataclasses.
DEFINE_auto
ff.DEFINE_auto automatically generates a flag declaration corresponding to the _signature_ of a given callable. The return value will also carry the correct type information.
For example the callable could be a constructor
_REPLAY = ff.DEFINE_auto('replay', replay_lib.Replay)or it could be a container type, such as a dataclass
@dataclasses.dataclass
class DataSettings:
dataset_name: str = 'mnist'
split: str = 'train'
batch_size: int = 128
# In main script.
# Exposes flags: --data.dataset_name --data.split and --data.batch_size.
_DATA_SETTINGS = ff.DEFINE_auto('data', datasets.DataSettings)
def main(argv):
# del argv # Unused.
dataset = datasets.load(_DATA_SETTINGS.value())
# ...or any other callable that satisfies the ff.auto requirements. It's also possible to override keyword arguments in the call to .value(), e.g.
test_settings = _DATA_SETTINGS.value(split='test')
Parser overrides
Both ff.DEFINE_auto and ff.DEFINE_from_instance accept an optional parser_overrides argument, which is a mapping from a parameter name to a ff.Item or ff.MultiItem instance. This is useful when the default flag parsing isn't desired. For example, to use ff.TypedList for a sequence instead of the default parser:
@dataclasses.dataclass
class DataSettings:
dataset_name: str = 'mnist'
batch_size: int = 128
augmentations: Sequence[str] = ('flip', 'crop')
_DATA_SETTINGS = ff.DEFINE_auto(
'data',
DataSettings,
parser_overrides={
'augmentations': ff.TypedList(str, ('flip', 'crop'), 'augmentations'),
},
)This allows...
Excerpt shown — open the source for the full document.