{"id":28753515,"url":"https://github.com/google-deepmind/fancyflags","last_synced_at":"2025-06-17T00:40:46.694Z","repository":{"id":45523199,"uuid":"336220996","full_name":"google-deepmind/fancyflags","owner":"google-deepmind","description":"A Python library for defining flat or nested dictionary flags.","archived":false,"fork":false,"pushed_at":"2023-07-04T14:50:11.000Z","size":220,"stargazers_count":28,"open_issues_count":0,"forks_count":7,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-04-16T04:53:38.939Z","etag":null,"topics":["configuration","flags","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/google-deepmind.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2021-02-05T09:14:53.000Z","updated_at":"2024-04-01T00:07:39.000Z","dependencies_parsed_at":"2023-09-07T20:34:48.329Z","dependency_job_id":"35e1855e-1360-4ea3-82ee-547c1b2e6117","html_url":"https://github.com/google-deepmind/fancyflags","commit_stats":null,"previous_names":["google-deepmind/fancyflags"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/google-deepmind/fancyflags","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/google-deepmind%2Ffancyflags","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/google-deepmind%2Ffancyflags/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/google-deepmind%2Ffancyflags/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/google-deepmind%2Ffancyflags/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/google-deepmind","download_url":"https://codeload.github.com/google-deepmind/fancyflags/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/google-deepmind%2Ffancyflags/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260268635,"owners_count":22983601,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["configuration","flags","python"],"created_at":"2025-06-17T00:40:35.217Z","updated_at":"2025-06-17T00:40:46.684Z","avatar_url":"https://github.com/google-deepmind.png","language":"Python","readme":"# fancyflags\n\n\u003c!--* freshness: { owner: 'ydoron' reviewed: '2022-07-18' } *--\u003e\n\n![PyPI Python version](https://img.shields.io/pypi/pyversions/fancyflags)\n![PyPI version](https://badge.fury.io/py/fancyflags.svg)\n\n## Introduction\n\n`fancyflags` is a Python library that extends\n[`absl.flags`](https://github.com/abseil/abseil-py) with additional structured\nflag types.\n\n`fancyflags` provides flags corresponding to structures such as\n[dicts](#dict-flags), [dataclasses, and (somewhat) arbitrary callables](#auto).\n\nThese flags are typed and validated like regular `absl` flags, catching errors\nbefore they propagate into your program. To override values, users can access\nfamiliar \"dotted\" flag names.\n\nTIP: Already a `fancyflags` user? Check out our [usage tips](#tips)!\n\n## A short note on design philosophy:\n\n`fancyflags` promotes mixing with regular `absl` flags. In many cases a few\nregular `absl` flags are all you need!\n\n`fancyflags` does not require you to modify library code: it should only be used\nin your \"main\" file\n\n`fancyflags` is not a dependency injection framework, and avoids\nprogramming-language-like power features. We prefer that users write regular\nPython for wiring up their code, because it's explicit, simple to understand,\nand allows static analysis tools to identify problems.\n\n## Installation\n\n`fancyflags` can be installed from PyPI using `pip`:\n\n```shell\npip install fancyflags\n```\n\nIt can also be installed directly from our GitHub repository:\n\n```shell\npip install git+git://github.com/deepmind/fancyflags.git\n```\n\nor alternatively by checking out a local copy of our repository and running:\n\n```shell\npip install /path/to/local/fancyflags/\n```\n\n## Dict flags\n\nIf we have a class `Replay`, with arguments `capacity`, `priority_exponent` and\nothers, we can define a corresponding dict flag in our main script\n\n```python\nimport fancyflags as ff\n\n_REPLAY_FLAG = ff.DEFINE_dict(\n    \"replay\",\n    capacity=ff.Integer(int(1e6)),\n    priority_exponent=ff.Float(0.6),\n    importance_sampling_exponent=ff.Float(0.4),\n    removal_strategy=ff.Enum(\"fifo\", [\"rand\", \"fifo\", \"max_value\"])\n)\n```\n\nand `**unpack` the values directly into the `Replay` constructor\n\n```python\nreplay_lib.Replay(**_REPLAY_FLAG.value)\n```\n\n`ff.DEFINE_dict` creates a flag named `replay`, with a default value of\n\n```python\n{\n    \"capacity\": 1000000,\n    \"priority_exponent\": 0.6,\n    \"importance_sampling_exponent\": 0.4,\n    \"removal_strategy\": \"fifo\",\n}\n```\n\nFor each item in the dict, `ff.DEFINE_dict` also generates a dot-delimited\n\"item\" flag that can be overridden from the command line. In this example the\nitem flags would be\n\n```\nreplay.capacity\nreplay.priority_exponent\nreplay.importance_sampling_exponent\nreplay.removal_strategy\n```\n\nOverriding an item flag from the command line updates the corresponding entry in\nthe dict flag. The value of the dict flag can be accessed by the return value\nof `ff.DEFINE_dict` (`_REPLAY_FLAG.value` in the example above), or via the\n`FLAGS.replay` attribute of the `absl.flags` module. For example, the override\n\n```shell\npython script_name.py -- --replay.capacity=2000 --replay.removal_strategy=max_value\n```\n\nsets `_REPLAY_FLAG.value` to\n\n```python\n{\n    \"capacity\": 2000,  # \u003c-- Overridden\n    \"priority_exponent\": 0.6,\n    \"importance_sampling_exponent\": 0.4,\n    \"removal_strategy\": \"max_value\",  # \u003c-- Overridden\n}\n```\n\n## Nested dicts\n\nfancyflags also supports nested dictionaries:\n\n```python\n_NESTED_REPLAY_FLAG = ff.DEFINE_dict(\n    \"replay\",\n    capacity=ff.Integer(int(1e6)),\n    exponents=dict(\n        priority=ff.Float(0.6),\n        importance_sampling=ff.Float(0.4),\n    )\n)\n```\n\nIn this example, `_NESTED_REPLAY_FLAG.value` would be\n\n```python\n{\n    \"capacity\": 1000000,\n    \"exponents\" : {\n      \"priority\": 0.6,\n      \"importance_sampling\": 0.4,\n    }\n}\n```\n\nand the generated flags would be\n\n```\nreplay.capacity\nreplay.exponents.priority\nreplay.exponents.importance_sampling\n```\n\n### Help strings\n\nfancyflags uses the item flag's name as the default help string, however this\ncan also be set manually:\n\n```python\n_NESTED_REPLAY_FLAG = ff.DEFINE_dict(\n    \"replay\",\n    capacity=ff.Integer(int(1e6), \"Maximum size of replay buffer.\"),\n    exponents=dict(\n        priority=ff.Float(0.6),  # Help string: \"replay.exponents.priority\"\n        importance_sampling=ff.Float(0.4, \"Importance sampling coefficient.\"),\n    )\n)\n```\n\n## \"Auto\" flags for functions and other structures {#auto}\n\n`fancyflags` also provides `ff.DEFINE_auto` which automatically generates a flag\ndeclaration corresponding to the signature of a given callable. The return value\nwill also carry the correct type information.\n\nFor example the callable could be a constructor\n\n```python\n_REPLAY = ff.DEFINE_auto('replay', replay_lib.Replay)\n```\n\nor it could be a container type, such as a `dataclass`\n\n```python\n@dataclasses.dataclass\nclass DataSettings:\n  dataset_name: str = 'mnist'\n  split: str = 'train'\n  batch_size: int = 128\n\n# In main script.\n# Exposes flags: --data.dataset_name --data.split and --data.batch_size.\n_DATA_SETTINGS = ff.DEFINE_auto('data', datasets.DataSettings)\n\ndef main(argv):\n  # del argv  # Unused.\n  dataset = datasets.load(_DATA_SETTINGS.value())\n  # ...\n```\n\nor any other callable that satisfies the `ff.auto` requirements. It's also\npossible to override keyword arguments in the call to `.value()`, e.g.\n\n```python\ntest_settings = _DATA_SETTINGS.value(split='test')\n```\n\n## Defining a dict flag from a function or constructor.\n\nThe function `ff.auto` returns a dictionary of `ff.Items` given a function or\nconstructor. This is used to build `ff.DEFINE_dict` and is also exposed in the\ntop-level API.\n\n`ff.auto` can be used with `ff.DEFINE_dict` as follows:\n\n```python\n_WRITER_KWARGS = ff.DEFINE_dict('writer', **ff.auto(logging.Writer))\n```\n`ff.auto` may be useful for creating kwarg dictionaries in situations where\n`ff.DEFINE_auto` is not suitable, for example to pass kwargs into nested\nfunction calls.\n\n## Auto requirements\n\n`ff.DEFINE_auto` and `ff.auto` will work if:\n\n1.  The function or class constructor has type annotations.\n1.  Each argument has a default value.\n1.  The types of the arguments are relatively simple types (`int`, `str`,\n    `bool`, `float`, or sequences thereof).\n\n## Notes on using `flagsaver`\n\nabseil-py's [flagsaver](https://github.com/abseil/abseil-py/blob/master/absl/testing/flagsaver.py)\nmodule is useful for safely overriding flag values in test code. Here's how to\nmake it work well with fancyflags.\n\n### Making dotted names work with `flagsaver` keyword arguments\n\nSince `flagsaver` relies on keyword arguments, overriding a flag with a dot in\nits name will result in a `SyntaxError`:\n\n```python\n# Invalid Python syntax.\nflagsaver.flagsaver(replay.capacity=100, replay.priority_exponent=0.5)\n```\n\nTo work around this, first create a dictionary and then `**` unpack it:\n\n```python\n# Valid Python syntax.\nflagsaver.flagsaver(**{'replay.capacity': 100, 'replay.priority_exponent': 0.5})\n```\n\n### Be careful when setting flag values inside a `flagsaver` context\n\nIf possible we recommend that you avoid setting the flag values inside the\ncontext altogether, and instead pass the override values directly to the\n`flagsaver` function as shown above. However, if you _do_ need to set values\ninside the context, be aware of this gotcha:\n\nThis syntax does not work properly:\n\n```python\nwith flagsaver.flagsaver():\n  FLAGS.replay[\"capacity\"] = 100\n# The original value will not be restored correctly.\n```\n\nThis syntax _does_ work properly:\n\n```python\nwith flagsaver.flagsaver():\n  FLAGS[\"replay.capacity\"].value = 100\n# The original value *will* be restored correctly.\n```\n\n## fancyflags in more detail\n\n### What is an `ff.Float` or `ff.Integer`?\n\n`ff.Float` and `ff.Integer` are both `ff.Item`s. An `ff.Item` is essentially a\nmapping from a default value and a help string, to a specific type of flag.\n\nThe `ff.DEFINE_dict` function traverses its keyword arguments (and any nested\ndicts) to determine the name of each flag. It calls the `.define()` method of\neach `ff.Item`, passing it the name information, and the `ff.Item` then defines\nthe appropriate dot-delimited flag.\n\n### What `ff.Item`s are available?\n\nff.Item             | Corresponding Flag\n:------------------ | :------------------------------\n`ff.Boolean`        | `flags.DEFINE_boolean`\n`ff.Integer`        | `flags.DEFINE_integer`\n`ff.Enum`           | `flags.DEFINE_enum`\n`ff.EnumClass`      | `flags.DEFINE_enum_class`\n`ff.Float`          | `flags.DEFINE_float`\n`ff.Sequence`       | `ff.DEFINE_sequence`\n`ff.String`         | `flags.DEFINE_string`\n`ff.StringList`     | `flags.DEFINE_list`\n`ff.MultiEnum`      | `ff.DEFINE_multi_enum`\n`ff.MultiEnumClass` | `flags.DEFINE_multi_enum_class`\n`ff.MultiString`    | `flags.DEFINE_multi_string`\n`ff.DateTime`       | -\n\n### Defining a new `ff.Item`\n\nGiven a `flags.ArgumentParser`, we can define an `ff.Item` in a few lines of\ncode.\n\nFor example, if we wanted to define an `ff.Item` that corresponded to\n`flags.DEFINE_spaceseplist`, we would look for the parser that this definition\nuses, and write:\n\n```python\nclass SpaceSepList(ff.Item):\n\n  def __init__(self, default, help_string)\n    parser = flags.WhitespaceSeparatedListParser()\n    super(SpaceSepList, self).__init__(default, help_string, parser)\n\n```\n\nNote that custom `ff.Item` definitions do not _need_ to be added to the\nfancyflags library to work.\n\n### Defining `Item` flags only\n\nWe also expose a `define_flags` function, which defines flags from a flat or\nnested dictionary that maps names to `ff.Item`s. This function is used as part\nof `ff.DEFINE_dict` and `ff.DEFINE_auto`, and may be useful for writing\nextensions on top of `fancyflags`.\n\n```python\n_writer_items = dict(\n    path=ff.String('/path/to/logdir', \"Output directory.\"),\n    log_every_n=ff.Integer(100, \"Number of calls between writes to disk.\"),\n)\n\n_WRITER_KWARGS = ff.define_flags(\"writer\", _writer_items)\n```\n\nThis example defines the flags `replay.capacity` and `replay.priority_exponent`\nonly: does _not_ define a dict-flag. The return value (`REPLAY`) is a\ndictionary that contains the default values. Any overrides to the individual\nflags will also update the corresponding item in this dictionary.\n\n### Tips\n\nAny direct access, e.g. `_DICT_FLAG.value['item']` is an indication that you\nmay want to change your flag structure:\n\n*   Try to align dict flags with constructors or functions, so that you always\n    `**unpack` the items into their corresponding constructor or function.\n*   If you need to access an item in a dict directly, e.g. because its value is\n    used in multiple places, consider moving that item to its own plain flag.\n*   Check to see if you should have `**unpacked` somewhere up the call-stack,\n    and convert function \"config\" args to individual items if needed.\n*   Don't group things under a dict flag just because they're thematically\n    related, and don't have one catch-all dict flag. Instead, define individual\n    dict flags to match the constructor or function calls as needed.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgoogle-deepmind%2Ffancyflags","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgoogle-deepmind%2Ffancyflags","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgoogle-deepmind%2Ffancyflags/lists"}