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
pytest11entry point that monkeypatchedParserandConfig. 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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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 logRelease 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 logSecurity 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.
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 logA 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.