python/cpython · error · TypeError
@deprecated decorator with non-None category must be applied
Error message
@deprecated decorator with non-None category must be applied to a class or callable, not {arg!r} What it means
warnings.deprecated(category=None) can wrap anything (it is a no-op passthrough used for static typing), but with a non-None category it must install a runtime wrapper that emits a warning on call or instantiation. That is only possible for classes and callables, so decorating any other object (an int, module, instance, property object) raises TypeError at decoration time.
Source
Thrown at Lib/_py_warnings.py:867
arg.__deprecated__ = __new__.__deprecated__ = msg
__init_subclass__.__deprecated__ = msg
return arg
elif callable(arg):
import functools
import inspect
@functools.wraps(arg)
def wrapper(*args, **kwargs):
_wm.warn(msg, category=category, stacklevel=stacklevel + 1)
return arg(*args, **kwargs)
if inspect.iscoroutinefunction(arg):
wrapper = inspect.markcoroutinefunction(wrapper)
arg.__deprecated__ = wrapper.__deprecated__ = msg
return wrapper
else:
raise TypeError(
"@deprecated decorator with non-None category must be applied to "
f"a class or callable, not {arg!r}"
)
_DEPRECATED_MSG = "{name!r} is deprecated and slated for removal in Python {remove}"
def _deprecated(name, message=_DEPRECATED_MSG, *, remove, _version=sys.version_info):
"""Warn that *name* is deprecated or should be removed.
RuntimeError is raised if *remove* specifies a major/minor tuple older than
the current Python version or the same version but past the alpha.
The *message* argument is formatted with *name* and *remove* as a Python
version tuple (e.g. (3, 11)).
"""View on GitHub (pinned to bc6749cc3b)
Solutions
- Deprecate a getter function that returns the constant, and warn there
- If the target is a module, deprecate its public functions/classes individually, or emit warnings.warn at module import in a wrapper module
- If you only need static-analysis marking with no runtime warning, call deprecated with category=None on a suitable target, or set __deprecated__ manually on the object
Example fix
// before
LIMIT = 1000
LIMIT = warnings.deprecated('use LIMIT_V2')(LIMIT) # TypeError
# after
def get_limit():
warnings.warn('LIMIT is deprecated; use LIMIT_V2', DeprecationWarning, stacklevel=2)
return 1000 Defensive patterns
Strategy: type-guard
Validate before calling
import typing
def deprecated_or_identity(obj):
return warnings.deprecated('use something else')(obj) if callable(obj) else obj Type guard
def is_decoratable(obj: object) -> bool:
import inspect
return inspect.isclass(obj) or callable(obj) Prevention
- Only decorate functions, methods, and classes with category set
- To deprecate a constant, wrap access in a deprecated getter function
- Run the test suite after adding decorators: misapplication fails at import
When it happens
Trigger: @warnings.deprecated('msg') applied to a module-level variable (e.g. an int constant), an imported module object, a dataclass field/property, or the result of another decorator that returned a non-callable; assigning the decorator to an instance attribute.
Common situations: Trying to deprecate a constant or module rather than its accessor function; decorating a property getter inside a class body; stacking decorators in the wrong order so deprecated receives a non-callable intermediate value.
Related errors
- Expected an object of type str for 'message', not {type(mess
- {cls.__name__}() takes no arguments
- {name!r} was slated for removal after Python {remove_formatt
- Cannot exit %r without entering first
- 'import warnings' failed; traceback:\n
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/821253c48a12bc0a.
Report an issue: GitHub.