Skip to main content

cot.config.ingest

Declare configuration once, as annotated classes, and ingest it from CLI arguments, environment variables and config files — with the merge order, the name mapping and the provenance all falling out of the declaration.

Experimental. This is an early experiment that may or may not work out. The API moves between releases, and there is no deprecation policy yet.

0.1.0 patched pytest on install. That release registered a pytest11 entry point that monkeypatched Parser and Config. The next release patches nothing; see pytest integration.

The problem

pytest declares every logging option twice — once as an ini option, once as a CLI option — and reconciles the two by hand at read time:

def pytest_addoption(parser: Parser) -> None:
    group = parser.getgroup("logging")

    def add_option_ini(option, dest, default=None, type=None, **kwargs):
        parser.addini(dest, default=default, type=type,
                      help="Default value for " + option)
        group.addoption(option, dest=dest, **kwargs)

    add_option_ini("--log-level", dest="log_level", default=None, metavar="LEVEL",
                   help="Level of messages to catch/display.")
    add_option_ini("--log-format", dest="log_format", default=DEFAULT_LOG_FORMAT,
                   help="Log format used by the logging module")
    # ... 11 more, plus a bare parser.addini for log_cli, plus a bare
    # group.addoption for --log-disable

Then, at use:

log_cli_format = get_option_ini(config, "log_cli_format", "log_format")

Thirteen options, ~90 lines, a local helper, and a fallback chain spelled out by hand at every read site. Adding pyproject.toml support on top of that is daunting, which is the itch this library exists to scratch.

The same thing, declared once

This is the real declaration from testing/test_pytest_logging.py, the project's acceptance test — all 13 ini options plus the CLI-only --log-disable, reachable by their real pytest names from ini, TOML, env and CLI:

from typing import Annotated

from cot.config import ConfigPart, from_parent, help, named, no_cli


class LogOutputConfig(ConfigPart):
    """Settings shared by every log output.

    `from_parent` is what makes `log_cli_level` fall back to `log_level`.
    """

    level: Annotated[str | None, from_parent, help("level of messages to catch")] = None
    format: Annotated[str, from_parent, help("log format")] = DEFAULT_LOG_FORMAT
    date_format: Annotated[str, from_parent, help("log date format")] = (
        DEFAULT_LOG_DATE_FORMAT
    )


class LogCliConfig(LogOutputConfig):
    # pytest calls this `log_cli`, and it is ini-only: live logging is switched
    # on from the command line with --log-cli-level instead.
    enabled: Annotated[bool, named("log_cli"), no_cli, help("enable live logs")] = False


class LogFileConfig(LogOutputConfig):
    # Structurally `file.path`, but pytest calls it `log_file`.
    path: Annotated[str | None, named("log_file"), help("path to log file")] = None
    mode: Annotated[str, named("log_file_mode"), help("log file open mode")] = "w"


class LoggingConfig(
    LogOutputConfig, ConfigPart, prefix="pytest", name_prefix="log", from_env=True
):
    auto_indent: Annotated[str | None, help("auto-indent multiline messages")] = None
    logger_disable: Annotated[
        list[str], named("log_disable"), help("disable a logger by name")
    ] = []

    cli: LogCliConfig
    file: LogFileConfig

The fallback pytest hand-rolls with get_option_ini(config, "log_cli_format", "log_format") is the from_parent cascade. Reading a value is attribute access:

config.cli.format      # falls back to config.format when unset
config.file.path       # the --log-file / log_file value

Using it

The usage walkthrough, names, precedence, provenance and help live in the documentation; it is executed by testing/test_docs_index.py the same way this README is by testing/test_readme.py. In short:

import sys

from cot.config import CLISource, ConfigManager, EnvSource

manager = ConfigManager(sources=[EnvSource(), CLISource(sys.argv[1:])])
manager.declare(LoggingConfig)
config = manager.get(LoggingConfig)

declare() registers a type, get() resolves every declared type once and returns the built instance, and manager.explain(LoggingConfig) says where each value came from.

pytest integration

cot.config.pytest_binding has two typed functions, and nothing is patched or auto-enabled: a plugin that does not import them is untouched.

from cot.config.pytest_binding import add_config, explain_config, get_config

def pytest_addoption(parser):
    add_config(parser, LoggingConfig)        # declare and register options

def pytest_configure(config):
    log = get_config(config, LoggingConfig)  # resolve + get, typed
    explain_config(config, LoggingConfig)    # provenance table

pytest keeps argument parsing and ini reading; the binding translates each field's spec into addoption/addini calls and reads the values back. An ini key pytest already declares (log_level) is adopted rather than clobbered, and a colliding CLI option raises ConfigCollisionError naming the field. See docs/design/pytest/index.md.

Installing

pip install cot-config

Requires Python 3.10+.

Where the code and the design differ

docs/design/ is normative. The code has been rebuilt to it (D20) through step 9 of the build order; conformance and YAML remain. Plugin discovery, list merge semantics, YAML and hot reload are design intent with no code behind them either way.

Development

uv run pytest -q
uv run mypy src
pre-commit run -a

License

MPL-2.0. See LICENSE.

Metadata

Release files for cot-config 0.2.0

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

Source distribution (sdist)

Source distribution for cot-config 0.2.0
File Size Uploaded Provenance Actions
cot_config-0.2.0.tar.gz 240.1 kB With provenance Details for cot_config-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for cot-config 0.2.0
File Interpreter ABI Platform Actions
cot_config-0.2.0-py3-none-any.whl Python 3 none any Details for cot_config-0.2.0-py3-none-any.whl

Total release size: 306.9 kB

Release files / cot_config-0.2.0.tar.gz

Download URL cot_config-0.2.0.tar.gz
Size 240.1 kB
Tags Source
SHA-256 checksum
How to use checksums
5fca321b0f69054d89be4d108c8abb406f8b8737bf1a2a141893ce9fff046479
BLAKE2b-256 checksum
How to use checksums
b6a7ea38ee1e22ca4faed17cb59e2143e7a7022bc7c3728c37044a8e00d8ebbb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release files / cot_config-0.2.0-py3-none-any.whl

Download URL cot_config-0.2.0-py3-none-any.whl
Size 66.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1aeae94ea81f8aa6f90f1cee0361ede56c74034f2ab6100f314a1a06fc647549
BLAKE2b-256 checksum
How to use checksums
5d6af2a3753c0b76b6cabcedf9b49ac594ce93dbba46d5ad91a0457f2229b313
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Security for cot-config 0.2.0

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.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for cot_config-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

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.2.0 This release

2 release files Full 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