python/cpython · error · TypeError
@deprecated decorator with non-None category must be…
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.
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
- () takes no arguments
- was slated for removal after Python alpha
- 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.
Appendix: 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)