{"id":26192958,"url":"https://github.com/jeankossaifi/zencfg","last_synced_at":"2026-02-27T01:23:55.703Z","repository":{"id":274525431,"uuid":"922708918","full_name":"JeanKossaifi/zencfg","owner":"JeanKossaifi","description":"A Zen approach to configuring your Python project","archived":false,"fork":false,"pushed_at":"2026-02-03T19:34:01.000Z","size":3763,"stargazers_count":15,"open_issues_count":2,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-04T08:42:19.369Z","etag":null,"topics":["config-management","configuration","deep-learning","machine-learning","python","type-hints"],"latest_commit_sha":null,"homepage":"http://jeankossaifi.com/zencfg/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JeanKossaifi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-01-26T22:19:37.000Z","updated_at":"2026-02-03T19:34:33.000Z","dependencies_parsed_at":"2025-01-27T21:21:23.891Z","dependency_job_id":"53b3fede-c917-4988-9b5c-42d5c721497b","html_url":"https://github.com/JeanKossaifi/zencfg","commit_stats":null,"previous_names":["jeankossaifi/zencfg"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/JeanKossaifi/zencfg","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JeanKossaifi%2Fzencfg","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JeanKossaifi%2Fzencfg/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JeanKossaifi%2Fzencfg/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JeanKossaifi%2Fzencfg/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JeanKossaifi","download_url":"https://codeload.github.com/JeanKossaifi/zencfg/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JeanKossaifi%2Fzencfg/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29880426,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-26T23:51:21.483Z","status":"ssl_error","status_checked_at":"2026-02-26T23:50:46.793Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["config-management","configuration","deep-learning","machine-learning","python","type-hints"],"created_at":"2025-03-12T01:25:28.058Z","updated_at":"2026-02-27T01:23:55.688Z","avatar_url":"https://github.com/JeanKossaifi.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ZenCFG\n\n![ZenCFG Logo](docs/_static/ZenCFG_image.png)\n\nA Zen way to configure your Python packages while keeping your sanity.\n\n## ⚡ Quick Start\n\nFirst install the library:\n\n```bash\npip install zencfg\n```\n\n```python\nfrom zencfg import ConfigBase, make_config_from_cli\n\n# Define base categories\nclass ModelConfig(ConfigBase):\n    pass\n\nclass OptimizerConfig(ConfigBase):\n    pass\n\n# Define model architectures\nclass TransformerConfig(ModelConfig):\n    layers: int = 12\n    n_heads: int = 8\n\nclass CNNConfig(ModelConfig):\n    channels: list[int] = [64, 128, 256]\n    kernel_size: int = 3\n\n# Define optimizers  \nclass AdamConfig(OptimizerConfig):\n    lr: float = 1e-4\n    weight_decay: float = 0.01\n\nclass SGDConfig(OptimizerConfig):\n    lr: float = 1e-3\n    momentum: float = 0.9\n\n# Compose your experiment\nclass ExperimentConfig(ConfigBase):\n    model: ModelConfig = TransformerConfig()\n    optimizer: OptimizerConfig = AdamConfig()\n    batch_size: int = 32\n\n# Get config with command-line overrides\nconfig = make_config_from_cli(ExperimentConfig)\n```\n\nSwitch between architectures and tune their specific parameters:\n```bash\n# Switch to CNN with specific CNN parameters\npython train.py --model cnnconfig --model.channels \"[32,64,128]\" --model.kernel_size 5\n\n# Try SGD with momentum\npython train.py --optimizer sgdconfig --optimizer.momentum 0.95 --batch_size 128\n\n# Mix and match everything\npython train.py --model transformerconfig --model.n_heads 16 --optimizer adamconfig --optimizer.weight_decay 0.001\n```\n\n## Why ZenCFG\n\nZenCFG (for *Zen ConFiGuration*), is the result of many iterations of trying pretty much every existing approach to Python configuration management systems and being satisfied by none of them. \n\nThe key advantages of ZenCFG are:\n\n### **1. Native Python Tooling**\nWork with configs like any other Python code—inheritance, composition, and type hints provide familiar development patterns.\nThis also means full IDE support with autocomplete, refactoring safety, and type checking.\n\n```python\nclass ModelConfig(ConfigBase):\n    layers: int = 12  # IDE autocomplete and type checking\n    learning_rate: float = 1e-4  # Runtime validation through type hints\n```\n\n### **2. Reduced Debugging Time** \nCatch configuration errors at startup with type safety and runtime validation, not hours into expensive training runs.\n\n```python\nconfig = ModelConfig(layers=\"invalid\")  # ValidationError immediately!\n# No more failed experiments due to config typos\n```\n\n### **3. Quick and Flexible Experimentation**\nOverride any nested parameter through intuitive command-line syntax without file editing. Switch between model architectures, optimizers, and their specific parameters in a single command.\n\n```bash\n# Switch architectures and tune their specific parameters\npython train.py --model ditconfig --model.n_heads 16 --optimizer sgdconfig --optimizer.momentum 0.9\n```\n\n### **4. Zero Boilerplate**\nPure Python classes with no frameworks, no special syntax, and no additional dependencies. If you know Python, you know ZenCFG.\n\n```python\nfrom zencfg import make_config_from_cli\nconfig = make_config_from_cli(MyConfig)  # That's it!\n```\n\nIt was built originally to configure and manage scripts for Deep Learning experiments, but you can use it for any Python project.\nThe examples I use are Deep Learning inspired.\n\n## Install\n\nJust clone the repository and install it, here in editable mode:\n\n```bash\ngit clone https://github.com/JeanKossaifi/zencfg\ncd zencfg\npython -m pip install -e .\n```\n\n## Defining configurations\n\nThere are two main type of configurations: core configuration categories, and subcategories.\n\n### Core configurations categories\n\nCore categories are defined by inheriting **directly** from ConfigBase:\n\n```python\n# We define a Model core config\nclass ModelConfig(ConfigBase):\n    version: str = \"0.1.0\"\n\n# Another base class: optimizer configurations\nclass OptimizerConfig(ConfigBase):\n    lr: float = 0.001\n```\n\n### SubCategories\n\nNow that you have core categories, you can optionally instantiate this as subcategories. \nFor instance, the different type of models you have, optimizers, etc.\n\nTo do this, simply inherit from your core category:\n```python\nclass DiT(ModelConfig):\n    layers: Union[int, List[int]] = 16\n\nclass Unet(ModelConfig):\n    conv: str = \"DISCO\"\n\n# Nested config.\nclass CompositeModel(ModelConfig):\n    submodel: ModelConfig\n    num_heads: int = 4\n\nclass AdamW(OptimizerConfig):\n    weight_decay: float = 0.01\n```\n\n### Composing categories\nYou can have configuration objects as parameters in your config: \nfor instance, our main configuration will contain a model and an optimizer.\n\n```python\n# Our main config is also a core category, and encapsulates a model and an optimizer\nclass Config(ConfigBase):\n    model: ModelConfig\n    opt: OptimizerConfig = OptimizerConfig(_config_name='adamw')\n```\n\nNote the `_config_name=\"adamw\"`: this indicates that the default will be the AdamW class. \nYou can create a subcategory by passing to the main category class the class name of the subcategory you want to create, \nthrough `_config_name`. \n\nThe above is equivalent to explicitly creating an ADAMW optimizer:\n\n```python\nclass Config(ConfigBase):\n    model: ModelConfig\n    opt: OptimizerConfig = AdamW(_config_name='adamw')\n```\n\n### Instantiating configurations\n\nYour configurations are Python object: you can instantiate them:\n\n```python\nconfig = Config(model = ModelConfig(_config_name='dit', layers=24))\n```\n\n## Instantiating a configuration with optional values from the command line\n\nThe library also lets you override parameters from the configuration through the command line, \nusing `make_config_from_cli`.\n\nFor instance, you can create a script `main.py` containing:\n```python\nfrom zencfg import make_config_from_cli\n\nfrom YOUR_CONFIG_FILE import Config\n\nconfig = make_config_from_cli(Config, strict=True)\n```\n\nOr load configs from files:\n```python\nfrom zencfg import load_config_from_file, make_config_from_cli\n\n# Load config class from file\nConfig = load_config_from_file(\n    config_path=\"configs/\",\n    config_file=\"experiment.py\", \n    config_name=\"ExperimentConfig\"\n)\nconfig = make_config_from_cli(Config)\n```\n\nYou can then call your script via the command line. \nIn that case, we simply use `.` to indicate nesting.\n\nFor instance, to instantiate the same config as above, you could simply do:\n```bash\npython main.py --model dit --model.layers 24\n```\n\nOr, equivalently, but more verbose (but perhaps also more explicitly):\n```bash\npython main.py --model._config_name dit --model.layers 24\n```\n\nYou can switch between different config types and override their specific parameters:\n```bash\n# Switch optimizers with their specific parameters\npython main.py --opt adamw --opt.weight_decay 0.001\npython main.py --opt sgd --opt.momentum 0.9\n\n# Mix model and optimizer changes\npython main.py --model unet --model.conv \"new_conv\" --opt adamw --opt.weight_decay 0.01\n```\n\n## Export your configuration to dictionary\n\nWhile you can directly use the configuration, you can also get a Python dictionary from a configuration instance, by simply using the `to_dict` method:\n\n```python\nconfig_dict = config.to_dict()\n\nmodel_cfg = config_dict['model']\n\n# You can access values as attributes too\noptimizer_cfg = config_dict.opt\n```\n\n## Examples\n\nFor concrete examples, check the [`examples`](examples/) folder.\nYou can try running [`test_config`](examples/test_config.py) script.\n\n## Gotchas\n\nNote that we handle ConfigBase types differently. Consider the following scenario:\n```python\nclass ModelConfig(ConfigBase):\n    in_channels: int = 3\n    out_channels: int = 1\n\nclass UNet(ModelConfig):\n    layers: int = 10\n    kernel: Tuple[int] = (3, 3)\n\nclass DiT(ModelConfig):\n    layers: int = 10\n    n_heads: int = 12\n\nclass Config(ConfigBase):\n    some_param: str = 'whatever'\n    model: ModelConfig = DiT(layers=4)\n```\n\nNow, if a user wants to override the number of layers through the command line to 6, they'd want to write:\n```bash\npython script.py --model.layers 6\n```\n\nWe allow this and it will give you a DiT model with 6 layers. \n\nThis is where the gotcha comes from: if you just instantiate the default type with layers=6, \nyou would be instantiating a `ModelConfig`, **not** a DiT (which would also cause an error since ModelConfig does not have layers).\n\nTo fix this, we treat ConfigBase parameters differently: we first take the default value (here, DiT(layers=4)). \nThen, if the user passes a new `_config_name` (e.g. 'unet'), we discard those and use only users defaults.\n\nOtherwise, if the user does **not** pass a `_config_name` (i.e. they want to use the default), then we use \nthe same defaults (`DiT(layers=4)`), which is turned into a dict: `{'_config_name': 'dit', 'layers': 4}` and we update it \nwith the values passed by the user. \n\nThis causes the least surprises in general but you may want to be aware of this.\nFor example, back to our example, this will allow the users to get back a config that matches what they'd expect: \n `{'_config_name': 'dit', 'layers': 6}`\n\n## Questions or issues\nThis is very much a project in development that I wrote for myself and decided to share so others could easily reuse it for multiple projects, while knowing it is tested and actively developed!\n\nIf you have any questions or find any bugs, please open an issue, or better yet, a pull-request!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeankossaifi%2Fzencfg","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjeankossaifi%2Fzencfg","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeankossaifi%2Fzencfg/lists"}