Skip to content

Compare Python Deprecation Tools🔗

Python has several ways to signal deprecation. The right choice depends on whether you need only a warning, static type checker visibility, or full runtime compatibility during an API migration.

Quick comparison🔗

Tool Best for Runtime forwarding Generator / async Argument rename Argument deprecation Class/object alias Testing helpers CI audit Static checker signal
warnings.warn One-off internal warnings No No No No No No No No
warnings.deprecated Python 3.13+ static-checker visibility No No No No No No No Yes
deprecation Simple decorator warnings No No No No No @fail_if_not_removed No No
Deprecated Simple decorator warnings No No No No No No No No
pyDeprecate (this library) Full API migration compatibility Yes Yes Yes Yes Yes Yes Yes Via stacking

Use warnings.warn when🔗

Use raw warnings for small internal messages where callers do not need a compatibility shim.

import warnings


def internal_hook() -> None:
    warnings.warn("internal_hook is deprecated", DeprecationWarning, stacklevel=2)

Use warnings.deprecated when🔗

Use warnings.deprecated/typing_extensions.deprecated for static-checker-only visibility when runtime behavior does not need to change.

from typing_extensions import deprecated


@deprecated("Use new_api instead.")
def old_api() -> None:
    pass

Use pyDeprecate when🔗

Use pyDeprecate when old callers must keep working while receiving a deprecation warning.

from deprecate import deprecated


def new_api(value: int) -> int:
    return value + 1


@deprecated(
    target=new_api,
    deprecated_in="1.2",
    remove_in="2.0",
    # FutureWarning: "The `old_api` was deprecated since v1.2 in favor of `new_api`. It will be removed in v2.0."
)
def old_api(value: int) -> int:
    raise RuntimeError("Forwarded by pyDeprecate.")


print(old_api(1))
Output: old_api(1)
2

Migrating from warnings.warn🔗

Replace manual forwarding boilerplate with a single decorator.

Before: manual warning and forwarding

import warnings


def new_api(value: int) -> int:
    return value + 1


def old_api(value: int) -> int:
    warnings.warn(
        "old_api is deprecated; use new_api instead.",
        DeprecationWarning,
        stacklevel=2,
    )
    return new_api(value)


print(old_api(1))
Output: old_api(1)
2

After: pyDeprecate owns the migration contract

from deprecate import deprecated


def new_api(value: int) -> int:
    return value + 1


@deprecated(
    target=new_api,
    deprecated_in="1.2",
    remove_in="2.0",
    # FutureWarning: "The `old_api` was deprecated since v1.2 in favor of `new_api`. It will be removed in v2.0."
)
def old_api(value: int) -> int:
    raise RuntimeError("Forwarded by pyDeprecate; this body is not used.")


print(old_api(1))
Output: old_api(1)
2

Decision guide🔗

  • Need only an internal warning: use warnings.warn.
  • Need static checker visibility only: use warnings.deprecated on Python 3.13+.
  • Need public runtime compatibility: use pyDeprecate.
  • Need to deprecate async def functions, generator functions, or async generators: use pyDeprecate.
  • Need argument rename or dropped argument compatibility: use pyDeprecate with TargetMode.ARGS_REMAP.
  • Need class, constant, or object alias compatibility: use deprecated_class or deprecated_instance.
  • Need removal deadline checks in CI: use pyDeprecate audit tools.
  • One-off internal warning with no forwarding or CI audit: warnings.warn is sufficient.

Fair strengths in alternative tools worth considering🔗

To keep this comparison fair, here are capabilities where alternatives can be the better fit:

  • warnings.deprecated: native static diagnostics in mypy/pyright/IDEs without adding runtime wrappers; adds no per-call overhead when the warning category is suppressed.
  • deprecation: includes the @fail_if_not_removed test decorator for direct test-failure enforcement when removal deadlines are reached.
  • Deprecated: deprecated.sphinx includes @versionadded and @versionchanged decorators that inject Sphinx directives into docstrings for lifecycle annotation in Sphinx-built API docs.
  • warnings.warn: no dependency and minimal surface area for quick internal notices where migration behavior is not needed.