Skip to content

Changelog🔗

[0.8.0] — 2026-05-21 — Default TargetMode enum & CLI audit tools🔗

Added🔗

  • TargetMode enum exported from deprecate. TargetMode.NOTIFY replaces target=None and TargetMode.ARGS_REMAP replaces target=True. Both are public API. (#150)
  • args_extra parameter for deprecated_class() and deprecated_instance(). Injects fixed keyword arguments into forwarded calls after args_mapping has been applied, matching the same semantics as @deprecated(args_extra=...). Ignored (with a construction-time UserWarning) when target is TargetMode.NOTIFY. (#150)
  • template_mgs parameter for deprecated_class() and deprecated_instance(). Overrides the built-in warning message template with a %-style format string, matching the same semantics as @deprecated(template_mgs=...). Available placeholders: %(source_name)s, %(deprecated_in)s, %(remove_in)s, %(target_name)s (callable target only), %(target_path)s (callable target only), %(argument_map)s (args_mapping warnings only). (#150)
  • DeprecationConfig.misconfigured field. Boolean field on the shared metadata dataclass; True when an invalid raw target sentinel (False) was passed at decoration time. Audit tools surface this via DeprecationWrapperInfo.misconfigured_target. (#150)
  • Multi-page topic documentation site. Replaced the monolithic README-copy home page with a curated 7-page MkDocs Material site: Home, Getting Started, User Guide (Use Cases / void() Helper / Audit Tools), Troubleshooting, and demo links. Switched theme to Material, added Open Graph tags, JSON-LD structured data (SoftwareApplication / FAQPage / TechArticle per page), spec-compliant llms.txt, and git-revision-date-localized plugin. README is unchanged (still the PyPI cover page). (#146)
  • pydeprecate CLI command. Run pydeprecate <subcommand> path/to/your/package to scan any package or module for misconfigured @deprecated wrappers — reports invalid argument mappings, identity mappings, and no-effect wrappers with rich-formatted output when rich is available. Also available as python -m deprecate. (#76)
  • Four CLI subcommands: check, expiry, chains, all. check validates wrapper configuration; expiry reports wrappers past their remove_in deadline (requires pip install 'pyDeprecate[audit]'); chains detects deprecated-to-deprecated forwarding chains; all runs all three in a single scan pass. Flags: --norecursive, --skip_errors. (#149)
  • New DeprecationWrapperInfo.empty_deprecated_in field. True when deprecated_in is absent on a wrapper; intended for CI pipeline introspection. dataclasses.asdict() output and repr() now include this field. (#166)
  • template_mgs validated at decoration time. Malformed %-style format strings now raise ValueError immediately at decoration time — not silently at call time. Applies to @deprecated and the deprecated_class()/deprecated_instance() proxy factories. (#169)
  • Stacked-callable-target guard. Applying @deprecated(target=fn_a) on a callable whose target is itself a callable-target @deprecated wrapper now emits UserWarning at decoration time instead of crashing with TypeError at call time. (#169)

Deprecated🔗

  • target=None sentinel — use TargetMode.NOTIFY. Passing target=None now emits a FutureWarning at decoration time. The sentinel remains accepted but will be removed in v1.0. Migrate to target=TargetMode.NOTIFY. (#150)
  • target=True sentinel — use TargetMode.ARGS_REMAP. Passing target=True now emits a FutureWarning at decoration time. The sentinel remains accepted but will be removed in v1.0. Migrate to target=TargetMode.ARGS_REMAP. (#150)
  • DeprecationWrapperInfo attributes empty_mappingempty_args_mapping and identity_mappingidentity_args_mapping. Old names are kept as deprecated @property aliases that emit DeprecationWarning on access and will be removed in v1.0. (#166)

Changed🔗

  • Misconfigured TargetMode combinations now warn at construction time. TargetMode.ARGS_REMAP without args_mapping, TargetMode.NOTIFY with args_mapping, and TargetMode.NOTIFY with args_extra all surface a UserWarning immediately. (#150)
  • DeprecationConfig.target always stores a normalised TargetMode or callable. Legacy boolean sentinels (True / False) are now normalised at decoration time and are never stored verbatim in DeprecationConfig.target. Code that inspects __deprecated__.target must compare against TargetMode.NOTIFY, TargetMode.ARGS_REMAP, a callable, or None — never against True or False. (#150)
  • deprecated_class() with target=TargetMode.NOTIFY now emits UserWarning at decoration time when args_mapping or args_extra is supplied. These parameters are ignored in NOTIFY mode; passing them has always been a misconfiguration. The warning will become TypeError in v1.0. (#150)
  • Docs site URL layout is now versioned. Content is published under https://borda.github.io/pyDeprecate/stable/ (for the stable alias) and https://borda.github.io/pyDeprecate/<tag>/ (for release tags). The root URL (https://borda.github.io/pyDeprecate/) redirects to stable/. External bookmarks to flat paths like .../pyDeprecate/troubleshooting.html will break on first deploy — update them to .../pyDeprecate/stable/troubleshooting.html. (#148)
  • target parameter of @deprecated now defaults to TargetMode.NOTIFY. Callers can omit target entirely for warn-only deprecation: @deprecated(deprecated_in="1.0", remove_in="2.0") is now the canonical form. Passing target=TargetMode.NOTIFY explicitly remains valid. This default is permanent and will not change in future releases. (#162)
  • Decoration-time UserWarning when @deprecated omits deprecated_in. When deprecated_in is absent a UserWarning is emitted immediately at decoration time (not at call time) regardless of target shape (TargetMode.NOTIFY, callable, or ARGS_REMAP), even if remove_in is set. Applies to functions and methods (not classes). Suppressed when stream=None or when a custom template_mgs is provided. (#162)
  • CLI chains reporting split. check subcommand reports deprecated-to-deprecated chains as warnings (exit 0); chains and all subcommands report chains as errors (exit 1). (#149)

Fixed🔗

  • PEP 702 compatibility crash fixed. When @deprecated was stacked under a PEP 702 @typing.deprecated decorator, wrapped_fn attempted to look up __deprecated__ on the outer wrapper and raised AttributeError. Fixed by capturing dep_meta as a closure variable at decoration time instead of re-reading it from the wrapper. (#169)
  • Double FutureWarning emission on deprecated_class() in NOTIFY mode fixed. Using deprecated_class() with target=TargetMode.NOTIFY triggered two FutureWarning emissions per construction call. (#162)
  • Cross-class guard false positives resolved; TypeError semantics preserved. Two previously documented "irresolvable" false-positive scenarios are now handled: (1) targets with metaclass/dynamic-class qualnames (e.g. type("Name", bases, ns) or manual fn.__qualname__ = "FakeOwner.method") — guard now skips silently when the named class is absent from the target's module globals; (2) pre-applied decorators that rewrite the source's __qualname__ — guard reads the true enclosing class from the Python class-body frame, which cannot be mutated by user decorators. The guard continues to raise TypeError at decoration time for genuine cross-class forwarding. (#169)
  • args_mapping rename no longer clobbers source default when both old and new parameter names are present. Previously, calling a deprecated wrapper with the old argument name while the source also accepted the new name could silently overwrite the new-name value. The remapping now correctly renames old=X to new=X without discarding a separately supplied new value. (#150)
  • target=False sentinel now emits UserWarning at decoration time. target=False was never a valid configuration; previously the behavior was undefined. The sentinel now surfaces a UserWarning immediately and will raise TypeError in v1.0. (#150)

[0.7.0] — 2026-03-31 — Docstring Tooling🔗

Added🔗

  • MkDocs admonition output. @deprecated now accepts docstring_style="mkdocs" (alias: "markdown"). When update_docstring=True, the deprecation notice is injected as a !!! warning "Deprecated in X" admonition instead of a Sphinx .. deprecated:: directive. Use docstring_style="auto" to detect style automatically from existing docstring content. (#134)
  • Google / NumPy section-aware docstring injection. update_docstring=True now inserts the deprecation notice before the first section (Args:, Returns:, Parameters, …) rather than appending it at the end. (#134)
  • Inline arg deprecation in docstrings. When args_mapping is set and update_docstring=True, each renamed or removed argument is annotated directly in the Args: / :param section of the docstring. (#136)
  • Griffe extension for mkdocstrings (deprecate.docstring.griffe_ext, beta) and Sphinx autodoc extension for deprecated classes (deprecate.docstring.sphinx_ext, beta). (#134)
  • Live demo documentation published to GitHub Pages — MkDocs demo, Sphinx demo, and portal landing page. (#134, #137)

Fixed🔗

  • Fixed getattr/setattr string-literal calls (B009/B010) replaced with direct attribute access. (#139)
  • Fixed proxy swap skipped correctly when super().import_object() returns False in the Griffe extension; empty _proxy_doc now delegates to super().get_doc() in the Sphinx extension. (#139)

[0.6.0.post0] — 2026-03-14 — Deprecation Proxy for class/instances🔗

Changed🔗

  • Softer class-deprecation fallback. @deprecated applied directly to a class (Enum, dataclass, or plain class) now emits a UserWarning at decoration time and delegates to deprecated_class() internally, instead of raising TypeError. Code using the old pattern continues to work; the warning points to the recommended API. (#132)

[0.6.0] — 2026-03-13🔗

Added🔗

  • deprecated_class() and deprecated_instance() — full proxy support. Enum, dataclass, and built-in types can now be wrapped in a transparent proxy. Attribute access, item access, method calls, and class behaviour all forward to the underlying type with a FutureWarning emitted on first access. (#114)
  • Correct isinstance() / issubclass() semantics on proxy classes. isinstance(x, proxy) and issubclass(Sub, proxy) now work as expected — previously raised TypeError. Type checks do not consume the warning budget. (#126)

Changed🔗

  • @deprecated on a class raises TypeError. Applying @deprecated directly to a class now raises TypeError at decoration time instead of silently misbehaving. Superseded in v0.6.0.post0 by a UserWarning + delegation to deprecated_class(). Use @deprecated_class() for class-level deprecation. (#120)

Deprecated🔗

  • Audit API renamed for consistency. Old names remain as @deprecated shims until v1.0. (#125)
Old name New name
find_deprecated_callables find_deprecation_wrappers
validate_deprecated_callable validate_deprecation_wrapper
DeprecatedCallableInfo DeprecationWrapperInfo
  • no_warning_call renamed to assert_no_warnings. The new name mirrors assertWarns / assertRaises from the standard library, making test intent immediately obvious. Old name kept as a deprecated alias until v1.0. (#131)

Fixed🔗

  • Cross-class method forwarding now fails at decoration time. Passing a class as target on a non-__init__ method previously silently forwarded self of the wrong type — always a runtime bug, never a valid pattern. The guard now raises TypeError at decoration time so the misconfiguration is caught immediately. (#121)
  • find_deprecation_wrappers() no longer reports false invalid_args for proxy objects. The proxy __call__ catch-all signature previously caused all args_mapping keys to be flagged as invalid; signature validation is now skipped for proxy objects. (#124)

[0.5.0] — 2026-02-23 — Deprecation Lifecycle Management🔗

Added🔗

  • deprecate.audit module — deprecation lifecycle management. A dedicated module grouping all inspection and enforcement utilities, designed to be called from pytest or CI scripts. Requires the optional [audit] extra: pip install pyDeprecate[audit]. (#111)
  • find_deprecated_callables() / validate_deprecated_callable() — zero-impact wrapper detection. Scans a module or package for @deprecated wrappers that have no real effect: invalid args_mapping keys, identity mappings, self-referencing targets, or missing version fields. Returns DeprecatedCallableInfo dataclasses. (#72)
  • validate_deprecation_expiry() — enforce removal deadlines in CI. Scans a module or package and returns all wrappers whose remove_in version has been reached or passed. Auto-detects the installed package version. Integrate as a pytest fixture or CI step to prevent zombie code from shipping past its scheduled removal. (#89)
  • validate_deprecation_chains() — detect deprecated-to-deprecated forwarding. Identifies wrappers whose target is itself a deprecated callable, forming chains that users traverse unnecessarily. Reports two chain kinds via the ChainType enum: TARGET (forwarding chain) and STACKED (composed argument mappings). (#90)

Fixed🔗

  • @deprecated wrappers now correctly handle var-positional Enum signatures. A subtle edge case where callables with var-positional parameters in their Enum signature caused incorrect argument forwarding is now resolved. (#104)

[0.4.0] — 2025-12-03 — Enhanced Documentation & Modernization🔗

Added🔗

  • update_docstring parameter — automatic Sphinx deprecation notices. Set update_docstring=True on @deprecated to automatically append a .. deprecated:: reStructuredText block to the function's docstring. IDE tooltips and Sphinx-generated API docs show the notice without any manual edits. (#31)

Changed🔗

  • Deprecation warnings now use FutureWarning instead of DeprecationWarning. DeprecationWarning is silenced by Python's default warning filters outside of test contexts, making it invisible to most end-users. FutureWarning is shown by default, ensuring callers actually see the migration message. (#16)
  • Minimum Python version raised to 3.9. Python 3.8 reached end-of-life in October 2024. (#73)
  • License changed from MIT to Apache-2.0.
  • Error messages now include the originating class or function name for easier debugging when a mapping fails. (#11)

[0.3.2] — 2021-06-11 — Support containing kwargs in target function🔗

Added🔗

  • target functions using **kwargs are now supported. Previously, forwarding to a target that accepted **kwargs and accessed them via kwargs.get(...) raised TypeError for unrecognised argument names. Extra arguments from the deprecated call are now forwarded correctly. (#6)

[0.3.1] — 2021-05-31 — Fixed void typing🔗

Fixed🔗

  • void() type annotation corrected to satisfy mypy. The return type of void() is now properly annotated — IDE and type checker warnings about unused parameters in deprecated function bodies are suppressed correctly.

[0.3.0] — 2021-04-21 — Conditional skip🔗

Added🔗

  • skip_if parameter — conditional deprecation. Pass a bool or a zero-argument callable returning bool to skip the warning and forwarding when a runtime condition is true. Useful for gating deprecation behaviour on package version checks or feature flags. (#4)

[0.2.0] — 2021-03-29 — Improved self arg deprecations🔗

Added🔗

  • target=True — self-deprecation mode. Deprecate and remap arguments within the same function without forwarding to a separate callable. Use with args_mapping to rename a parameter while keeping the function body intact. (#3)
  • void() helper. Accepts any arguments and returns None. Silences IDE "unused parameter" warnings in deprecated function bodies where the body is never reached.
  • no_warning_call() context manager. Assert that a block of code raises no deprecation warning — useful for verifying that new API paths are clean in tests. Renamed to assert_no_warnings() in v0.6.0. (#2)
  • Stacked @deprecated decorators. Multiple @deprecated(True, ...) decorators can be stacked on the same function for multi-hop argument migrations across versions, each with independent warning counts and version metadata.

[0.1.1] — 2021-03-21 — Allow infinite warning🔗

Added🔗

  • num_warns=-1 — always-on warnings. Setting num_warns to -1 causes the deprecation warning to fire on every call rather than stopping after N times.
  • target=None — warn-only mode. The original function body still executes; @deprecated adds only a warning with no call forwarding. Useful when you want to signal deprecation without changing any call behaviour.

[0.1.0] — 2021-03-20 — Initial release🔗

Added🔗

  • @deprecated(target=callable) decorator. Marks a function as deprecated and automatically forwards all calls — including argument mapping — to a replacement function. The deprecated function body is never executed when target is a callable.
  • Automatic argument mapping. Positional arguments are resolved to keyword arguments and forwarded to the target's signature. args_mapping renames ({"old": "new"}) or drops ({"old": None}) individual arguments during forwarding.
  • args_extra — inject additional kwargs into the target call. Pass a dict of extra keyword arguments to merge into every forwarded call. Useful for providing default values or adapter arguments that the deprecated API never accepted.
  • Configurable warning count (num_warns). Warnings fire once per function by default; set to any positive integer to limit the total count per function lifetime.
  • Custom warning message template (template_mgs). Format string with %(source_name)s, %(target_path)s, %(deprecated_in)s, %(remove_in)s, and %(argument_map)s placeholders.
  • Custom warning stream (stream). Route warnings to logging.warning, warnings.warn, or any callable.