Getting Started🔗
You renamed a function (or retired an argument) and now you need the old name to keep working, callers to see a clear deprecation notice, and a firm removal date you can enforce. pyDeprecate does all of that with a single decorator so you can focus on the new API instead of the plumbing.
Why pyDeprecate instead of warnings.warn?🔗
warnings.warn tells callers that something is deprecated — but they still have to update their code manually and the old code path keeps running. pyDeprecate adds three things on top:
- Automatic call forwarding — every call to the old function is transparently redirected to the replacement; no stale code runs.
- Argument mapping — rename or drop arguments across the API boundary with
args_mapping={"old": "new"}to help callers migrate to the new signature. - CI deadline enforcement —
validate_deprecation_expiry()(viapip install 'pyDeprecate[audit]') returns a message for every wrapper whose removal version has been reached, andpydeprecate expiryexits1on any, so CI fails and deprecated code cannot quietly outlive its deadline.
If you only need a one-line notice with no forwarding and no deadline tracking, warnings.warn is sufficient. Choose pyDeprecate when you need the old name to keep working while callers migrate.
Installation🔗
pyDeprecate requires Python 3.9 or later and has zero runtime dependencies. Python 3.9 is past its upstream end of life but is still listed as supported in the package metadata.
Choose the install that matches the workflow you need:
| Workflow | Command | Includes |
|---|---|---|
| Runtime deprecation wrappers | pip install pyDeprecate |
@deprecated, @deprecated_class, deprecated_instance, docstring helpers, and most audit metadata helpers |
| CI deadline checks | pip install 'pyDeprecate[audit]' |
Adds packaging for PEP 440 version comparison in validate_deprecation_expiry() and validate_deprecation_policy() |
| Command-line audit workflows | pip install 'pyDeprecate[audit,cli]' |
Adds packaging version support for pydeprecate expiry, pydeprecate policy, and pydeprecate all; the CLI itself needs no dependency (cli is an empty extra kept for compatibility) |
Base install from PyPI:
To install directly from source (for pre-release or development versions):
The audit extra adds packaging for version comparison, needed only by validate_deprecation_expiry and validate_deprecation_policy. Install it when you want to enforce removal deadlines or a deprecation policy in CI:
For command-line audit workflows:
Quick Start🔗
Here is the most common scenario: you renamed a function and need the old name to keep working during the transition. The decorator handles call forwarding automatically, so the deprecated wrapper needs no implementation.
from deprecate import deprecated
# NEW/FUTURE API — renamed to be more explicit about what it computes
def compute_sum(a: int = 0, b: int = 3) -> int:
return a + b
# DEPRECATED API — `addition` was the original name before the rename
@deprecated(target=compute_sum, deprecated_in="1.0", remove_in="2.0")
def addition(a: int, b: int = 5) -> int:
pass # body is not needed — calls are forwarded to compute_sum
# Using the original name still works but emits a deprecation notice
print(addition(1, 2))
All calls to addition() are automatically forwarded to compute_sum() with a FutureWarning. The old function's body is never executed.
Customizing deprecation messages
To customize the message template or redirect deprecation output to a logger, see Customization.
API at a Glance🔗
Not sure which API to reach for? This table maps common scenarios to the right tool. For worked examples of each, see Use Cases.
Pick the right decorator:
| Scenario | API to use |
|---|---|
| Renaming a function or method | @deprecated(target=new_func) |
| Renaming an argument within the same function | @deprecated(target=TargetMode.ARGS_REMAP, args_mapping={"old": "new"}) |
| Notice only — original body still runs | @deprecated(target=TargetMode.NOTIFY) |
| Deprecating a class, Enum, or dataclass name | @deprecated_class(target=NewClass) |
| Deprecating a module-level constant or object | deprecated_instance(obj, ...) |
All @deprecated parameters:
| Param | Default | Purpose |
|---|---|---|
target |
TargetMode.AUTO |
Callable to forward to · TargetMode.ARGS_REMAP to remap args · TargetMode.NOTIFY warn-only · TargetMode.AUTO (default) infers the mode from the other arguments |
deprecated_in |
"" |
Version when deprecated (e.g. "1.0") |
remove_in |
"" |
Version when removed (e.g. "2.0") |
stream |
deprecation_warning |
Output sink callable (set None to silence deprecation messages) |
num_warns |
1 |
0 never · 1 once · -1 always · N exactly N times |
args_mapping |
None |
{"old": "new"} rename · {"old": None} drop |
message_template |
None |
Custom deprecation message template (%-style placeholders) |
args_extra |
None |
Fixed kwargs injected into the target call |
skip_if |
False |
bool or Callable → bool; deactivate the deprecation machinery when true |
as_exception |
None |
Raise DeprecatedError instead of warning; None follows the global deprecate.AS_EXCEPTIONS |
update_docstring |
False |
Append Sphinx .. deprecated:: notice to docstring |
docstring_style |
"auto" |
Style of the injected notice: "auto", "rst", "mkdocs", "markdown" |
Prefer passing an explicit target (TargetMode.NOTIFY, TargetMode.ARGS_REMAP, or a callable) so the intent is visible at the call site — TargetMode.AUTO is only the front-door default that resolves an omitted target, and you never write it yourself.
All three decorators (@deprecated, deprecated_callable(), deprecated_class()) share every parameter above. The only differences: deprecated_class() adds the class-only attrs_mapping (attribute-name remapping — TypeError on the other two), and a class source is dispatched to deprecated_class by @deprecated but rejected with TypeError by deprecated_callable(). TargetMode.AUTO is front-door-only: deprecated_callable() defaults target to TargetMode.NOTIFY, deprecated_class() leaves it unset, and both raise TypeError when handed TargetMode.AUTO explicitly.
deprecated_instance() shares deprecated_in, remove_in, num_warns, stream, args_extra, message_template, skip_if, and as_exception; it requires obj and adds name (display name) and read_only.
Parameter matrix across the API🔗
Every parameter each factory accepts, with its default. 🚫 marks a parameter the factory does not accept — passing it raises TypeError. required marks a parameter with no default.
| Parameter | @deprecated |
deprecated_callable |
deprecated_class |
deprecated_instance |
deprecated_module |
|---|---|---|---|---|---|
obj |
🚫 | 🚫 | 🚫 | required | 🚫 |
name |
🚫 | 🚫 | 🚫 | "" |
None |
target |
TargetMode.AUTO |
TargetMode.NOTIFY |
None |
🚫 | None |
args_mapping |
None |
None |
None |
🚫 | 🚫 |
attrs_mapping |
🚫 | 🚫 | None |
🚫 | None |
args_extra |
None |
None |
None |
None |
🚫 |
deprecated_in |
"" |
"" |
"" |
"" |
"" |
remove_in |
"" |
"" |
"" |
"" |
"" |
num_warns |
1 |
1 |
1 |
1 |
-1 |
stream |
deprecation_warning |
deprecation_warning |
deprecation_warning |
deprecation_warning |
<warnings.warn> |
message_template |
None |
None |
None |
None |
None |
skip_if |
False |
False |
False |
False |
🚫 |
read_only |
🚫 | 🚫 | 🚫 | False |
🚫 |
as_exception |
None |
None |
None |
None |
None |
update_docstring |
False |
False |
False |
🚫 | 🚫 |
docstring_style |
"auto" |
"auto" |
"auto" |
🚫 | 🚫 |
Notes: deprecation_warning is the default FutureWarning sink (stream=None silences it). deprecated_module is the exception: omit stream to warn through warnings.warn; an explicit stream=None still warns there and emits a FutureWarning at the call, because it silences the module warning only from v1.0. name is a display label for deprecated_instance and the module's __name__ for deprecated_module (auto-detected from the caller when omitted). message_template is the custom-notice override on every factory; template_mgs is its deprecated alias (renamed in v0.12; emits a FutureWarning, removed in v1.0) — pass message_template instead. The front door @deprecated exposes only the parameters common to deprecated_callable and deprecated_class; the class-only attrs_mapping is 🚫 there — use deprecated_class(attrs_mapping=...) directly.
Enforce and automate🔗
- Fatal deprecations —
as_exception=True(ordeprecate.as_exceptions()/DEPRECATE_AS_EXCEPTIONS=1) raisesDeprecatedErrorinstead of warning, so a test run can refuse deprecated calls. See Fatal deprecations. - CLI —
pydeprecate check,expiry,policy,chainsandallaudit a package from the shell with CI-friendly exit codes. See the CLI Reference. - pre-commit and GitHub Action — run the same checks as a pre-commit hook or a GitHub Action step.
Next: Use Cases — overview of all deprecation patterns with links to each topic. See also: Customization · Audit Tools · Troubleshooting