Env vars override the YAML config file, as documented (#19) - #63
Conversation
from_yaml() passed the file's values as constructor kwargs, which pydantic-settings ranks above environment variables, so any key present in the YAML file silently ignored its MLDP_* variable. The values now reach pydantic through a ContextVar-backed settings source ranked below env and above defaults (plan/tickets/19/plan.md, D1). - load_config(config_object=) returns the object as-is (level 1), replacing a nine-field rebuild whose comment and log claimed env overrides applied. - Real-file precedence tests with ambient MLDP_* cleared replace the test that mocked from_yaml away; one end-to-end MldpClient case. - Cookbook callout removed; CLAUDE.md records the invariant; NEXT.md records the silent behavior change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PsRVNfPSn4SG7UiU7m7hQq
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
Existing YAML-focused tests must also isolate ambient MLDP_* environment variables.
Review effort: Lite
Findings: None
What changed in this PR
Fixes configuration precedence so MLDP_* environment variables override YAML values while preserving explicit configuration objects.
Changes:
- Adds a ContextVar-backed YAML settings source.
- Updates loader behavior and expands precedence tests.
- Updates documentation and release notes.
| File | Summary |
|---|---|
tests/unit/test_mldp_client.py |
Adds end-to-end environment override coverage. |
tests/unit/test_config.py |
Adds comprehensive precedence and isolation tests. |
src/dp_python_lib/config/loader.py |
Preserves explicit configuration objects. |
src/dp_python_lib/config/config.py |
Implements corrected settings-source precedence. |
doc/release-notes/NEXT.md |
Documents the behavior change. |
doc/cookbook/connecting.md |
Documents corrected configuration precedence. |
CLAUDE.md |
Records the configuration invariant. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- env_ignore_empty=True: once env outranks the YAML file, an empty MLDP_*
variable (a compose ${VAR} whose source is unset) would otherwise beat a
working file with a blank host, or fail to parse as a port.
- Move mldp_env() to tests/unit/mldp_env.py and add isolate_mldp_env(),
applied in setUp to TestMldpConfig, TestConfigLoader, and
TestMldpClientConfigIntegration. The fix exposed test_from_yaml_valid,
which the file used to shield, so an exported MLDP_* now broke 9 tests
(8 on main); all pass with MLDP_* exported.
- Cookbook, NEXT.md, CLAUDE.md, and a dated scope note in the plan.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PsRVNfPSn4SG7UiU7m7hQq
|
Re the Copilot overview ("Existing YAML-focused tests must also isolate ambient With
🤖 Generated with Claude Code |
Closes #19. Implements
plan/tickets/19/plan.md(merged in #62), tasks 1–8.The bug
MldpConfig.from_yaml()didcls(**flat_data). pydantic-settings ranks init kwargs above env vars, so any key present in the YAML file silently ignored itsMLDP_*variable — levels 2 and 3 of the documented priority were inverted on everyMldpClient()that found a config file (including the tracked repo-rootmldp-config.yaml).Changes
config/config.py— a private_YamlValuesSourcereads the flattened YAML from a module-levelContextVar;settings_customise_sourcesranks it last (init, env, dotenv, secrets, yaml).from_yaml()sets the var, callscls(), and resets infinally. Error handling and the missing-file fallback are unchanged. (D1)config/loader.py—if config_object is not None: return config_object. Same values as the old nine-field rebuild; the misleading "with environment variable overrides" comment and log go with it. (D3)TestConfigPrecedenceintest_config.pydrives real files throughload_config()andfrom_yaml()with ambientMLDP_*cleared: env > YAML, YAML > default, absent key → env/default, int/bool coercion, lower-case env names,MLDP_CONFIG_FILE+ override, explicit object built inside the patched env plusassertIs, ContextVar reset after a failed and a successful load, and D2 (overridden invalid value loads). The mockedtest_load_config_from_yamlis removed. One end-to-endMldpClientcase intest_mldp_client.py. 8 of the new tests fail againstmain; the two reset tests pass there trivially (nothing to leak).doc/cookbook/connecting.md— known-bug callout and ToC pointer removed; per-key and D4 (explicit object) sentences added; the auto-discovery paragraph rewritten.CLAUDE.md— records the invariant: YAML never goes in as init kwargs.doc/release-notes/NEXT.md— new section, written as a silent behavior change for Q3 at the cut.Checks
pytest tests/unit/(737 passed),ruff check .,ruff format --check .,mypy src/(Success), cookbook snippet checker, release-notes checker — all clean locally.🤖 Generated with Claude Code
https://claude.ai/code/session_01PsRVNfPSn4SG7UiU7m7hQq