Skip to content

Add proper deprecation warnings for deprecated module attributes - #974

Draft
Carreau wants to merge 1 commit into
mainfrom
claude/deprecation-warnings-audit-36wunh
Draft

Add proper deprecation warnings for deprecated module attributes#974
Carreau wants to merge 1 commit into
mainfrom
claude/deprecation-warnings-audit-36wunh

Conversation

@Carreau

@Carreau Carreau commented Aug 9, 2026

Copy link
Copy Markdown
Member

Summary

This PR improves deprecation handling across the traitlets codebase by implementing proper DeprecationWarning emissions for deprecated APIs instead of silent aliases. This follows PEP 562 for module-level __getattr__ and ensures users are properly notified when using deprecated features.

Key Changes

  • traitlets/traitlets.py: Replaced silent alias NoDefaultSpecified = Undefined with a module-level __getattr__ implementation that emits a DeprecationWarning when the deprecated name is accessed. Added __dir__() to keep deprecated attributes discoverable.

  • traitlets/config/configurable.py: Added explicit DeprecationWarning to Configurable._get_log_handler() method with updated docstring indicating the deprecation version and recommended alternative.

  • traitlets/config/loader.py: Converted Config.has_key from a simple alias to a proper method that emits a DeprecationWarning before delegating to __contains__().

  • tests/test_traitlets.py: Added test_no_default_specified_deprecated() to verify the deprecation warning is emitted and that the deprecated name remains discoverable via dir().

  • tests/config/test_loader.py: Added test_has_key_deprecated() to verify Config.has_key() emits appropriate deprecation warnings.

  • tests/config/test_configurable.py: Added test_get_log_handler_deprecated() to verify _get_log_handler() emits a deprecation warning.

Implementation Details

The changes use Python's standard warnings.warn() with DeprecationWarning and stacklevel=2 to ensure warnings point to the caller's code. The module-level __getattr__ approach (PEP 562) allows deprecated names to be accessed with warnings while keeping the module namespace clean and maintaining discoverability through __dir__().

https://claude.ai/code/session_01AEeaWWBdmyp9x2DaCombXD

@codecov

codecov Bot commented Aug 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.19%. Comparing base (1dae189) to head (7f791be).

Components Coverage Δ
traitlets 85.60% <100.00%> (+0.10%) ⬆️
tests 99.07% <100.00%> (+<0.01%) ⬆️

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Three APIs were documented as deprecated but never warned. Deprecation
versions traced through git history:

- `traitlets.traitlets.NoDefaultSpecified`, an alias for `Undefined`
  since 8261449 (2015-06-17, released in 4.0). Turned into a
  module-level `__getattr__` (PEP 562) so accessing it warns;
  `__dir__` keeps it discoverable.
- `Config.has_key`, a Python-2-era alias for `__contains__` that
  arrived already marked deprecated in the IPython.config ->
  traitlets.config split, c34e315 (2015-03-28, released in 4.0). Now a
  real method that warns before delegating.
- `Configurable._get_log_handler`, marked deprecated in its docstring
  by b5b5085 (2022-02-07, released in 5.2).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AEeaWWBdmyp9x2DaCombXD
@Carreau
Carreau force-pushed the claude/deprecation-warnings-audit-36wunh branch from 76dd927 to 7f791be Compare August 9, 2026 13:26

Carreau commented Aug 9, 2026

Copy link
Copy Markdown
Member Author

The two red downstream checks (downstream / jupyter_server and the downstream_check gate that aggregates it) are not caused by this PR, and can't be fixed from this repo.

Single failure, in jupyter_server's own suite:

FAILED tests/test_utils.py::test_check_version - packaging.version.InvalidVersion: Invalid version: 1.0
1 failed, 1121 passed, 32 skipped

Root cause is a packaging behaviour change. test_check_version deliberately passes a float:

assert check_version(1.0, "1.0.1")  # type:ignore[arg-type]

and jupyter_server's check_version only catches TypeError:

try:
    return bool(Version(v) >= Version(check))
except TypeError:
    return True

Older packaging raised TypeError from the regex on a non-str; packaging >= 26 raises InvalidVersion instead, which isn't caught. Reproduced locally against unmodified traitlets:

packaging Version(1.0) test
24.0 TypeError (caught) passes
26.3 InvalidVersion: Invalid version: 1.0 fails

Nothing in this PR touches version parsing — the diff only adds DeprecationWarnings to three already-deprecated APIs. The last downstream run on main was 2026-08-03, before packaging 26 landed, which is why main isn't showing this yet; it should go red on its next run too.

The fix belongs in jupyter_server (catch InvalidVersion as well, or stop passing a float). Every other check on this PR is green, including Test Lint.


Generated by Claude Code

@Carreau

Carreau commented Aug 9, 2026

Copy link
Copy Markdown
Member Author

Failures will be fixed by jupyter-server/jupyter_server#1688

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants