Skip to main content

cfgable

cfgable

English | 简体中文

Python License: MIT Built on pydantic

A small, reusable configuration framework for Python, built on pydantic. It lets every component declare a single config object and be constructed uniformly — from Python, from a YAML file, or from Hydra — with validation happening once at the boundary.

The core depends only on pydantic; the Hydra bridge is optional.

Requirements

  • Python 3.8 or newer
  • pydantic 2.x
  • Hydra support requires the optional hydra extra

Install

pip install cfgable
pip install "cfgable[hydra]"

From a local checkout, use pip install . for the core package or pip install ".[hydra]" for the optional Hydra bridge.

The idea

  1. A component's settings are a pydantic model (*Config), documented with attribute docstrings.
  2. Every class takes one config parameter and reads fields off it.
  3. Inheriting ConfigurableBasis lets the same class be built from an explicit config, a plain mapping, or flat keyword arguments — the shape Hydra's instantiate passes — because the InitConfigMeta metaclass assembles and validates the config for you, then calls config_post_init().
from typing import Optional
from pydantic import BaseModel, ConfigDict, PositiveInt
from cfgable import ConfigurableBasis


class CameraConfig(BaseModel, frozen=True):
    """Configuration for a camera."""

    model_config = ConfigDict(use_attribute_docstrings=True, extra="forbid")

    device: str = "/dev/video0"
    """Video device path."""
    fps: PositiveInt = 30
    """Capture rate in frames per second."""
    serial: Optional[str] = None
    """Optional camera serial number for disambiguation."""


class Camera(ConfigurableBasis):
    """A camera built from a single validated config."""

    def __init__(self, config: CameraConfig):
        self.config = config

    def config_post_init(self):
        super().config_post_init()
        self._opened = False

    def on_configure(self) -> bool:
        self._opened = True
        return True


# All three build the same object:
cam = Camera(CameraConfig(fps=60))            # explicit config
cam = Camera({"fps": 60})                     # mapping
cam = Camera(fps=60)                          # flat kwargs (Hydra-style)

Use ConfigurableBasis when the component has a configure lifecycle. For plain objects that only need config assembly, inherit from InitConfigMixin.

With Hydra

Point _target_ at the class and list the config fields as siblings; the metaclass turns them into the config model:

# camera.yaml
_target_: my_pkg.Camera
fps: 60
device: /dev/video2
from cfgable.hydra_utils import init_hydra_config, hydra_instance

cam = hydra_instance(init_hydra_config("camera.yaml"))

For plain classes you can also nest the config under its own _target_ so standard hydra.utils.instantiate builds Camera(config=CameraConfig(...)).

Round-tripping

Because a component keeps its whole config, it can serialize back to the config that would rebuild it:

cam.dump()                 # -> dict of fields + a "_target_" pointing at the class
cam.save_config("cam.yaml")

The saved YAML can be loaded again by passing the path back to the component:

cam = Camera("cam.yaml")

What's in the box

  • ConfigurableBasis, InitConfigMixin, InitConfigABCMixin — base classes for the single-config construction protocol.
  • InitConfigMeta / InitConfigABCMeta — the metaclass that assembles configs.
  • NoConfig — placeholder for components that need no settings.
  • StrEnum, ReprEnum — a string-enum backport (use this StrEnum, not enum.StrEnum, for consistent behavior across Python 3.8–3.13).
  • ForceSetAttr / force_set_attr — controlled mutation of otherwise-frozen configs.
  • import_string, get_fully_qualified_class_name, dump_or_repr, fetch_config.
  • cfgable.hydra_utils — init_hydra_config, hydra_instance, hydra_instance_from_dict, hydra_instance_from_config_path (needs [hydra]).

import cfgable never imports Hydra — the bridge is loaded only when you import cfgable.hydra_utils.

Development

python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest

Contributing

Issues and pull requests are welcome — see CONTRIBUTING.md for the development setup and conventions, and please follow the Code of Conduct. To report a security issue, see SECURITY.md. Release notes are tracked in CHANGELOG.md, and support options are listed in SUPPORT.md.

License

Released under the MIT License.

Metadata

Release files for cfgable 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cfgable 0.1.1
File Size Uploaded Provenance
cfgable-0.1.1.tar.gz 486.3 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for cfgable 0.1.1
File Interpreter ABI Platform
cfgable-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 500.5 kB

Release files / cfgable-0.1.1.tar.gz

Download URL cfgable-0.1.1.tar.gz
Size 486.3 kB
Tags Source
SHA-256 checksum
How to use checksums
dab5a6bcaaff4b81cee3e38290a81e41da353c1ad0d12529524d813eee5a6010
BLAKE2b-256 checksum
How to use checksums
c06724736737f6c8b83b46d367dab12bc2a534abd26fa52369a72404137e2767
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.8.18

Release files / cfgable-0.1.1-py3-none-any.whl

Download URL cfgable-0.1.1-py3-none-any.whl
Size 14.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
86589dbdead48aaedb4ba671eea41f07eec864958e2326577ca0ac0b5b5ccba5
BLAKE2b-256 checksum
How to use checksums
9cf9a071252729e5ea6a830ab21b1bafb5956348869dde74b3b26cbf47050f25
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.8.18

Security for cfgable 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page