Skip to content

feat(config): centralize env var deprecation handling in the registry#18175

Draft
bm1549 wants to merge 18 commits into
mainfrom
brian.marks/registry-deprecations
Draft

feat(config): centralize env var deprecation handling in the registry#18175
bm1549 wants to merge 18 commits into
mainfrom
brian.marks/registry-deprecations

Conversation

@bm1549
Copy link
Copy Markdown
Contributor

@bm1549 bm1549 commented May 19, 2026

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 nested deprecation: {...} block in supported-configurations.json and has Config.__init__ sweep the registry for set deprecated vars, firing one DDTraceDeprecationWarning per process via warnings.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: true remains the cross-tracer-compatible signal. The new deprecation: {...} block carries Python-tracer metadata. removal_version is required for any deprecated entry; the generator rejects entries without it.

"DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED": [
  {
    "implementation": "A", "type": "boolean", "default": "true",
    "deprecated": true,
    "deprecation": {
      "removal_version": "5.0.0",
      "extra_message": "128-bit trace ID generation will become mandatory in version 5.0.0."
    }
  }
]

Alias-only deprecation uses deprecation.aliases.<NAME>: {removal_version, replaced_by, extra_message} on the canonical entry, without deprecated: true at the top level. Used here for DD_TRACE_INFERRED_SPANS_ENABLEDDD_TRACE_INFERRED_PROXY_SERVICES_ENABLED.

Migrated entries

  • DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED (removal 5.0.0)
  • DD_TRACE_INFERRED_SPANS_ENABLEDDD_TRACE_INFERRED_PROXY_SERVICES_ENABLED (5.0.0)
  • DD_ASGI_TRACE_WEBSOCKETDD_TRACE_WEBSOCKET_MESSAGES_ENABLED (5.0.0; was previously only a log.warning)
  • DD_PYTEST_USE_NEW_PLUGIN_BETA (3.0.0)
  • Pre-existing deprecated entries (DATADOG_TAGS, DATADOG_TRACE_AGENT_HOSTNAME, DD_COMPILE_DEBUG) get removal_version: 5.0.0 to satisfy the new schema requirement.

Drive-by

Registers DD_PROFILING_MEMORY_MEM_DOMAIN_ENABLED (added in #17886 without a registry entry, was breaking supported_configurations --check).

Testing

  • tests/internal/test_supported_configurations_deprecations.py (new): asserts the DEPRECATED_CONFIGURATIONS dict shape, alias-only-deprecation handling, and that every entry has a removal_version.
  • tests/internal/test_env.py (extended): set var fires, alias fires, once-only (atomic via setdefault+sentinel), __contains__ silent (set and unset), unset var silent, warn_deprecated_set_vars sweep, single warning across Config() construction.
  • python scripts/supported_configurations.py --check passes; scripts/lint fmt && scripts/lint typing clean.

Risks

  • DD_ASGI_TRACE_WEBSOCKET previously emitted only a log.warning; now fires DDTraceDeprecationWarning through the standard Python warnings pipeline. Called out in the release note.
  • Config.__init__ now runs a sweep over DEPRECATED_CONFIGURATIONS (~8 entries) at bootstrap. Negligible on a one-shot startup path.

bm1549 and others added 15 commits May 19, 2026 11:20
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>
@bm1549 bm1549 added the AI Generated Largely based on code generated by an AI or LLM. This label is the same across all dd-trace-* repos label May 19, 2026
@cit-pr-commenter-54b7da
Copy link
Copy Markdown

cit-pr-commenter-54b7da Bot commented May 19, 2026

Codeowners resolved as

ddtrace/internal/settings/env.py                                        @DataDog/apm-sdk-capabilities-python
tests/internal/test_env.py                                              @DataDog/apm-sdk-capabilities-python

@datadog-prod-us1-5
Copy link
Copy Markdown
Contributor

datadog-prod-us1-5 Bot commented May 19, 2026

Pipelines  Tests

Fix all issues with BitsAI

⚠️ Warnings

🚦 1 Pipeline job failed

DataDog/apm-reliability/dd-trace-py | download win_arm64 wheels   View in Datadog   GitLab

🛟 This job is unlikely to succeed on retry. Please review your pipeline configuration. RUN_ID not found. Check if the GitHub build jobs were successfully triggered on your PR.

ℹ️ Info

No other issues found (see more)

🧪 All tests passed
❄️ No new flaky tests detected

🚧 81 tests that failed were ignored due to quarantine View in Datadog

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 1ff3ba4 | Docs | Datadog PR Page | Give us feedback!

- 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>
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,
)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@pr-commenter
Copy link
Copy Markdown

pr-commenter Bot commented May 19, 2026

Benchmarks

Benchmark execution time: 2026-05-19 20:44:16

Comparing candidate commit 1ff3ba4 in PR branch brian.marks/registry-deprecations with baseline commit a6282d1 in branch main.

Found 0 performance improvements and 9 performance regressions! Performance is the same for 599 metrics, 10 unstable metrics.

scenario:httppropagationinject-ids_only

  • 🟥 execution_time [+1.569µs; +1.735µs] or [+7.764%; +8.583%]

scenario:iastaspects-stringio_aspect

  • 🟥 execution_time [+631.336µs; +665.538µs] or [+16.482%; +17.375%]

scenario:iastaspectsospath-ospathbasename_aspect

  • 🟥 execution_time [+105.100µs; +112.577µs] or [+24.749%; +26.510%]

scenario:iastaspectssplit-rsplit_aspect

  • 🟥 execution_time [+12.062µs; +16.548µs] or [+8.142%; +11.170%]

scenario:iastaspectssplit-split_aspect

  • 🟥 execution_time [+10.713µs; +14.656µs] or [+7.382%; +10.099%]

scenario:iastaspectssplit-splitlines_aspect

  • 🟥 execution_time [+10.934µs; +13.962µs] or [+7.702%; +9.835%]

scenario:span-start

  • 🟥 execution_time [+1.495ms; +1.642ms] or [+9.626%; +10.573%]

scenario:telemetryaddmetric-1-count-metric-1-times

  • 🟥 execution_time [+220.398ns; +271.490ns] or [+10.552%; +12.998%]

scenario:tracer-small

  • 🟥 execution_time [+24.948µs; +26.945µs] or [+7.399%; +7.992%]

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI Generated Largely based on code generated by an AI or LLM. This label is the same across all dd-trace-* repos

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant