Specify your configuration as a dataclass. Then, parse your CLI with the parse_cli
function.
import dataclasses
from farconf import parse_cli
@dataclasses.dataclass
class Optimizer:
name: str
lr: float
@dataclasses.dataclass
class Algorithm:
optimizer: Optimizer
n_steps: int
alg: Algorithm = parse_cli(["n_steps=2", '--set-json=optimizer={"name": "adam", "lr": 0.1}'], Algorithm)
assert alg == Algorithm(Optimizer("adam", 0.1), 2)
Values can be fetched from YAML and Python files, and specified in the command line. Arguments are applied from left to
right onto the same dict
object, and then parsed with farconf.from_dict
.
--set=path.to.key=VALUE
just sets attributespath
,to
andkey
(for nested subclasses) to the JSON-parsed valueVALUE
.- If parsing the value as JSON fails, and the value does not contain any of the characters
{}[]"
, then it will be passed as a string. - Equivalently you can use
path.to.key=VALUE
as an argument
- If parsing the value as JSON fails, and the value does not contain any of the characters
--set-json=path.to.key="JSON_VALUE"
. Same as above but if JSON parsing fails, parsing the command line errors.--from-file=/path/to/file.yaml
. Incorporate the values specified infile.yaml
into the main dict.--set-from-file=path.to.key=PATH_TO_YAML_FILE
. Same as above, but for a sub-path.--from-py-fn=package.module:function_name
. Points to a Python file which defines functionfunction_name
which, when called with empty arguments, will return a dataclass or dict which can be merged into the main dict.- The intended way is to return typed config objects with this
--set-from-py-fn=path.to.key=package.module:function_name
. Same as above but setspath.to.key
in the main dict.
Sometimes you have different fields for different types of objects, e.g. optimizers. FARConf supports this by using
dataclasses which inherit from abc.ABC
.
import dataclasses
from farconf import parse_cli, from_dict
import abc
@dataclasses.dataclass
class LRSchedule(abc.ABC):
lr: float
@dataclasses.dataclass
class FlatLRSchedule(LRSchedule):
pass
@dataclasses.dataclass
class ExpDecayLRSchedule(LRSchedule):
discount: float = 0.999
assert from_dict(dict(_type_="__main__:FlatLRSchedule", lr=0.2), LRSchedule) == FlatLRSchedule(0.2)
assert parse_cli(["_type_=__main__:ExpDecayLRSchedule", "lr=0.2"], abc.ABC) == ExpDecayLRSchedule(0.2, discount=0.999)
- Support typechecked dataclass-configurations
- Support modular, inheritance-based configurations. For example, you want to specify an optimizer, but not which optimizer, and different optimizers have different attributes
- Make it easy to specify hyperparameter searches
- Ingest arguments from Python files, YAML and JSON files, YAML and JSON from the command line.
- Be easy to maintain
- Generate help text for configuration. Usually it'll be too long and nobody will read it.