Skip to main content

Dataclass companion for config metadata and parsing, with a ConfigParser that generates and loads configs from files.

Project description

classconf

Dataclass companion for config metadata and parsing, with a ConfigParser that generates and loads typed configs from files.

Core ideas

  • Use @configclass to attach config metadata to dataclasses.
  • Provide config classes to ConfigParser.
  • Parse config files into dataclass instances.

Basic usage

from dataclasses import dataclass
from pathlib import Path

from classconf import ConfigParser, configclass
from classconf.format import JSONFormat


@configclass
@dataclass
class PathsConfig:
    output_dir: Path = Path("./out")


@configclass(top_level=True)
@dataclass
class AppConfig:
    name: str = "demo"
    paths: PathsConfig = field(default_factory=PathsConfig)


parser = ConfigParser(
    "config.json",
    AppConfig,
    format=JSONFormat(),
    create_noexist=True,
)

config = parser.get(AppConfig)

Generated config

{
  "name": "demo",
  "paths": {
    "output_dir": "out"
  }
}

Formats

  • TOMLFormat (default if format is None)
  • JSONFormat
from classconf.format import TOMLFormat

parser = ConfigParser("config.toml", AppConfig, PathsConfig, format=TOMLFormat())

TOMLFormat accepts none_value to control how None is written. Use none_value=None to omit None fields entirely.

Custom formats

To add a new format, implement ConfigFormat with read and write methods. read should return None when the file does not exist.

from pathlib import Path
from typing import Any

from classconf.format import ConfigFormat


class YAMLFormat(ConfigFormat):
    def read(self, path: Path) -> dict[str, Any] | None:
        ...

    def write(self, path: Path, data: dict[str, Any]) -> None:
        ...

Field mappings, serializers, deserializers

from dataclasses import dataclass
from classconf import ConfigParser, configclass
from classconf.format import JSONFormat


def deserialize_num(value: str, **_) -> int:
    return int(value.rstrip("x"))


def serialize_num(value: int) -> str:
    return f"{value}x"


@configclass(
    name="metrics",
    field_deserialzers={"count": deserialize_num},
    field_serializers={"count": serialize_num},
    field_name_mappings={"count": "count_value"},
)
@dataclass
class MetricsConfig:
    count: int = 3


parser = ConfigParser(
    "config.json",
    MetricsConfig,
    format=JSONFormat(),
    create_noexist=True,
)

metrics = parser.get(MetricsConfig)

Generated config file

{
  "metrics": {
    "count_value": "3x"
  }
}

Deserializers can also accept a parser to resolve other configs. This is useful when a field is typed as a protocol/ABC and a string selects which config section to load.

from dataclasses import dataclass, field
from typing import Protocol, runtime_checkable

from classconf import ConfigParser, configclass
from classconf.format import JSONFormat


@runtime_checkable
class DatabaseConfig(Protocol):
    driver: str


@configclass(name="sqlite")
@dataclass
class SQLiteConfig:
    driver: str = "sqlite"
    path: str = "app.db"


@configclass(name="postgres")
@dataclass
class PostgresConfig:
    driver: str = "postgres"
    host: str = "localhost"
    port: int = 5432


def resolve_database(name: str, parser: ConfigParser) -> DatabaseConfig:
    return parser.get(SQLiteConfig if name == "sqlite" else PostgresConfig)


def serialize_database(db: DatabaseConfig) -> str:
    return db.driver


@configclass(
    top_level=True,
    field_deserialzers={"database": resolve_database},
    field_serializers={"database": serialize_database},
)
@dataclass
class AppConfig:
    database: DatabaseConfig = field(default_factory=SQLiteConfig)


parser = ConfigParser(
    "config.json",
    AppConfig,
    SQLiteConfig,
    PostgresConfig,
    format=JSONFormat(),
    create_noexist=True,
)

config = parser.get(AppConfig)
print(config.database.driver)

Generated config

database = "sqlite"

[postgres]
driver = "postgres"
host = "localhost"
port = 5432

[sqlite]
driver = "sqlite"
path = "app.db"

Adding configs later

parser.add(OtherConfig)
other = parser.get(OtherConfig)

Quirks and constraints

  • Only one top-level config class is allowed per parser.
  • create_noexist=False requires the file to exist.
  • Missing config keys raise KeyError during parsing.
  • get() raises if the class was not provided to the parser.
  • With JSON/TOML, fields without defaults are written as null/None placeholders.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

classconf-0.1.0.tar.gz (22.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

classconf-0.1.0-py3-none-any.whl (8.7 kB view details)

Uploaded Python 3

File details

Details for the file classconf-0.1.0.tar.gz.

File metadata

  • Download URL: classconf-0.1.0.tar.gz
  • Upload date:
  • Size: 22.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.1 {"installer":{"name":"uv","version":"0.10.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for classconf-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c3601832b6dff092ede91e178467d08577ecf4991ef2f945e162e96560dfb1b4
MD5 f8277bb748adb7f1521e12d2bed9bbc5
BLAKE2b-256 e952ec64f366670cca79cb4158386780df01ef375bb45134f2700a79531f2e6f

See more details on using hashes here.

File details

Details for the file classconf-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: classconf-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 8.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.1 {"installer":{"name":"uv","version":"0.10.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for classconf-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 19c3004592644f1adc59095a727fff663a9ffb675c7b862ba4895671f31198af
MD5 f277671898894d83d3e239c559d24919
BLAKE2b-256 fe6b6b0c14572fb91259a213c8c777d0fa370f6c02caf3468cd25fe3b669fec9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page