feat(config): centralize env var deprecation handling in the registry#18175
Draft
bm1549 wants to merge 18 commits into
Draft
feat(config): centralize env var deprecation handling in the registry#18175bm1549 wants to merge 18 commits into
bm1549 wants to merge 18 commits into
Conversation
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Added in #17886 but not registered in supported-configurations.json, which was causing the supported_configurations --check job to fail. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ATIONS shape Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…env registry Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Wrap long DEPRECATED_CONFIGURATIONS entries in the generator so regenerated output matches ruff format expectations. - Blank-line cleanups in env.py, test files, supported_configurations.py. - Drop `# noqa: I100` on a test import (flake8-import-order rule that ruff doesn't recognize; was emitting a warning). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- env.py: add warn_deprecated_set_vars() that fires DDTraceDeprecationWarning for every deprecated env var the user has set, regardless of whether any code path reads it. Removes dead second branch in _maybe_warn_deprecated_read. - _config.py: call warn_deprecated_set_vars() at Config init so registry-only deprecations (e.g. DD_PYTEST_USE_NEW_PLUGIN_BETA, whose explicit deprecate() call was removed in this branch) still surface a warning. - test_env.py: add coverage for the sweep, simplify the fixture, and extend the __contains__ test to cover the set-but-deprecated case. - release note: split DD_ASGI_TRACE_WEBSOCKET into its own deprecations entry, add tracing scope prefix. - remove the implementation plan doc — it was working notes, not project docs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Drop _maybe_warn_deprecated_read wrapper — it was a single-line forwarder to _emit_deprecation_warning, which already early-returns for non-deprecated keys. Inline at the two call sites in EnvConfig.__getitem__. - Merge _format_alias_entry and _format_deprecation into a single _format_entry helper parameterized by open/close chars and pre-rendered items. - Drop two narrative comments in tests that just restated the test names. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codeowners resolved as |
Contributor
|
- env.py: drop unused 'from __future__ import annotations'; replace debtcollector.deprecate with stdlib warnings.warn (eliminates the lazy import and the # type: ignore — the circular chain via ddtrace.vendor is no longer triggered). Fix TOCTOU on _deprecation_warned by switching to a dict + setdefault-with-fresh-sentinel pattern (atomic under the GIL). - _config.py: hoist warn_deprecated_set_vars import to module top. - supported-configurations.json: fill in removal_version=5.0.0 for the three long-standing deprecated entries (DATADOG_TAGS, DATADOG_TRACE_AGENT_HOSTNAME, DD_COMPILE_DEBUG) and for DD_ASGI_TRACE_WEBSOCKET. - generator: require deprecation.removal_version whenever deprecated: true is set (and whenever an alias appears under deprecation.aliases). - tests: replace the marker-only-empty-dict test with one asserting every deprecation entry has a removal_version (consequence of the new generator rule). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bm1549
commented
May 19, 2026
Comment on lines
-442
to
-447
| deprecate( | ||
| "the DD_PYTEST_USE_NEW_PLUGIN_BETA environment variable is deprecated", | ||
| message="the new pytest plugin is now the default version. No additional configurations are required.", | ||
| removal_version="3.0.0", | ||
| category=DDTraceDeprecationWarning, | ||
| ) |
Contributor
Author
There was a problem hiding this comment.
This was missed from the 3.x release, but to avoid potentially breaking changes on a minor version, I was leaning to drop this in 5.x with the rest of the configs
The generator now requires removal_version on every deprecated entry, so the existence check inside _emit_deprecation_warning is unreachable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
BenchmarksBenchmark execution time: 2026-05-19 20:44:16 Comparing candidate commit 1ff3ba4 in PR branch Found 0 performance improvements and 9 performance regressions! Performance is the same for 599 metrics, 10 unstable metrics. scenario:httppropagationinject-ids_only
scenario:iastaspects-stringio_aspect
scenario:iastaspectsospath-ospathbasename_aspect
scenario:iastaspectssplit-rsplit_aspect
scenario:iastaspectssplit-split_aspect
scenario:iastaspectssplit-splitlines_aspect
scenario:span-start
scenario:telemetryaddmetric-1-count-metric-1-times
scenario:tracer-small
|
The _deprecation_warned set was an extra dedup layer on top of the warnings
module's existing per-location dedup. It also broke the existing contract
that tests/tracer/test_span.py::test_128bit_trace_id_config_deprecation_warning
relies on (Config() inside catch_warnings fires the warning when the var is set):
once the bootstrap Config() registered the key in _deprecation_warned, the
test's second Config() became a silent no-op.
Drop the gate. Let Python's default filter handle dedup (one emission per
(message, category, module, lineno) per process). The test in test_span.py
asserts >= 1 warning, which our two warning sites (the sweep + the env read
during _get_config) easily satisfy with simplefilter("always"); production
users with the default filter still see exactly one warning per location.
Also drop test_deprecation_warning_fires_only_once_per_key — it was testing
the gate, not user-facing behavior. The exactly-once test for Config() loses
its "exactly" qualifier and becomes a presence assertion to match
test_span.py's pattern.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adding a new env-var deprecation should be a JSON edit, not a
deprecate(...)call placed at the right line. This PR moves deprecation metadata into a nesteddeprecation: {...}block insupported-configurations.jsonand hasConfig.__init__sweep the registry for set deprecated vars, firing oneDDTraceDeprecationWarningper process viawarnings.warn.The four scattered manual call sites (
_config.py×2,asgi/middleware.py,pytest/_plugin_v2.py) are removed; their metadata now lives in the JSON.Schema
deprecated: trueremains the cross-tracer-compatible signal. The newdeprecation: {...}block carries Python-tracer metadata.removal_versionis required for any deprecated entry; the generator rejects entries without it.Alias-only deprecation uses
deprecation.aliases.<NAME>: {removal_version, replaced_by, extra_message}on the canonical entry, withoutdeprecated: trueat the top level. Used here forDD_TRACE_INFERRED_SPANS_ENABLED→DD_TRACE_INFERRED_PROXY_SERVICES_ENABLED.Migrated entries
DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED(removal 5.0.0)DD_TRACE_INFERRED_SPANS_ENABLED→DD_TRACE_INFERRED_PROXY_SERVICES_ENABLED(5.0.0)DD_ASGI_TRACE_WEBSOCKET→DD_TRACE_WEBSOCKET_MESSAGES_ENABLED(5.0.0; was previously only alog.warning)DD_PYTEST_USE_NEW_PLUGIN_BETA(3.0.0)DATADOG_TAGS,DATADOG_TRACE_AGENT_HOSTNAME,DD_COMPILE_DEBUG) getremoval_version: 5.0.0to satisfy the new schema requirement.Drive-by
Registers
DD_PROFILING_MEMORY_MEM_DOMAIN_ENABLED(added in #17886 without a registry entry, was breakingsupported_configurations --check).Testing
tests/internal/test_supported_configurations_deprecations.py(new): asserts theDEPRECATED_CONFIGURATIONSdict shape, alias-only-deprecation handling, and that every entry has aremoval_version.tests/internal/test_env.py(extended): set var fires, alias fires, once-only (atomic viasetdefault+sentinel),__contains__silent (set and unset), unset var silent,warn_deprecated_set_varssweep, single warning acrossConfig()construction.python scripts/supported_configurations.py --checkpasses;scripts/lint fmt && scripts/lint typingclean.Risks
DD_ASGI_TRACE_WEBSOCKETpreviously emitted only alog.warning; now firesDDTraceDeprecationWarningthrough the standard Python warnings pipeline. Called out in the release note.Config.__init__now runs a sweep overDEPRECATED_CONFIGURATIONS(~8 entries) at bootstrap. Negligible on a one-shot startup path.