google-deepmind/fancyflags

Python

Open original ↗

Captured source

source ↗
published Feb 5, 2021seen 15hcaptured 2hhttp 200method plain

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

!PyPI version

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.