DeprecationWarning

A notice that something will go away. The default filters hide it unless the code that triggered it lives in __main__ — so library deprecations stay invisible until you run with -W default, -X dev, or a test runner.

InheritsBaseException›Exception›Warning›DeprecationWarning
Warning categoryPython 3 (all)Live demo
DeprecationWarning(*args)
Raised by
warnings.warn(msg, DeprecationWarning, stacklevel=2), @warnings.deprecated (3.13)
Message
app.py:7: DeprecationWarning: old_api() is deprecated
Quick fix
see them: python -W default::DeprecationWarning (or -X dev)
Watch out
hidden by default outside __main__ — silence is not "no deprecations"

Demo

Live evaluation
Issue a DeprecationWarning as if from a module of your choice, under the default filters. catch_warnings(record=True) counts what would be printed.
Try:
Inputs
modulestrmodule name the warning comes from
Code
import warnings
with warnings.catch_warnings(record=True) as caught:
    warnings.warn_explicit('f() is deprecated', DeprecationWarning, 'lib.py', 1, module='__main__')
len(caught)
Result
1

Only the exact module name __main__ gets a 1: the first default filter is default::DeprecationWarning:__main__, and its module field must equal the module name exactly. Any library module — even __main__.cli — falls through to ignore::DeprecationWarning and records nothing. In Handle mode, error turns the call into an exception (0 recorded), always lets it return 1 and records the warning.

Constructor

NameTypeRequiredDescription
*argsobjectnoThe message; warnings.warn(text, DeprecationWarning) builds it for you.

Attributes

AttributeTypeMeaning
argstupleThe constructor arguments; args[0] is the message text.
__deprecated__strNot on the warning — set by @warnings.deprecated(msg) (3.13) on the decorated function or class, holding msg.

Common patterns

Deprecate a function (3.13+)
The decorator warns on every call (and type checkers flag uses statically).
from warnings import deprecated

@deprecated('Use load_config() instead')
def read_config(path):
    return load_config(path)
Deprecate by hand (any version)
stacklevel=2 attributes the warning to the caller, whose code has to change — and makes it visible when that caller is __main__.
import warnings

def read_config(path):
    warnings.warn('read_config() is deprecated; use load_config()', DeprecationWarning, stacklevel=2)
    return load_config(path)
Fail tests on deprecations
Catch them before the next upgrade removes the feature. pytest shows them in its summary by default; -W error::DeprecationWarning makes them fail.
# pytest.ini
# [pytest]
# filterwarnings =
#     error::DeprecationWarning
import warnings
warnings.simplefilter('error', DeprecationWarning)

Examples

1. Shown when triggered in __main__
import warnings with warnings.catch_warnings(record=True) as caught: warnings.warn('going away', DeprecationWarning) len(caught)
Returns
1
2. Ignored when a library triggers it
import warnings with warnings.catch_warnings(record=True) as caught: warnings.warn_explicit('f() is deprecated', DeprecationWarning, 'lib.py', 1, module='mylib') len(caught)
Returns
0
3. The default filters behind that
import warnings [f for f in warnings.filters if f[2] in (DeprecationWarning, PendingDeprecationWarning)]
Returns
[('default', None, <class 'DeprecationWarning'>, '__main__', 0), ('ignore', None, <class 'DeprecationWarning'>, None, 0), ('ignore', None, <class 'PendingDeprecationWarning'>, None, 0)]
4. PendingDeprecationWarning hidden, FutureWarning shown
import warnings with warnings.catch_warnings(record=True) as caught: warnings.warn('later', PendingDeprecationWarning) warnings.warn('users see this', FutureWarning) [w.category.__name__ for w in caught]
Returns
['FutureWarning']
5. The three are separate classes
issubclass(FutureWarning, DeprecationWarning), issubclass(PendingDeprecationWarning, DeprecationWarning)
Returns
(False, False)
6. @warnings.deprecated (3.13)
import warnings @warnings.deprecated('use new_api() instead') def old_api(): return 1 with warnings.catch_warnings(record=True) as caught: warnings.simplefilter('always') old_api() [(w.category.__name__, str(w.message)) for w in caught]
Returns
[('DeprecationWarning', 'use new_api() instead')]
7. The decorator stores the message
import warnings @warnings.deprecated('use new_api() instead') def old_api(): return 1 old_api.__deprecated__
Returns
'use new_api() instead'
8. Raised under an error filter
import warnings with warnings.catch_warnings(): warnings.simplefilter('error', DeprecationWarning) warnings.warn('going away', DeprecationWarning)
Returns
DeprecationWarning: going away

Pitfalls

1. Without stacklevel the warning blames the library line
The default stacklevel=1 points at the warn() call inside your function, which the user cannot change. stacklevel=2 points at the caller.
stacklevel=1
import warnings
def old(x):
    warnings.warn('old() is deprecated', DeprecationWarning)
    return x
with warnings.catch_warnings(record=True) as caught:
    warnings.simplefilter('always')
    old(1)
[w.lineno for w in caught]
[3]
stacklevel=2
import warnings
def old(x):
    warnings.warn('old() is deprecated', DeprecationWarning, stacklevel=2)
    return x
with warnings.catch_warnings(record=True) as caught:
    warnings.simplefilter('always')
    old(1)
[w.lineno for w in caught]
[7]
2. Using DeprecationWarning for end users of an application
End users of an app never see DeprecationWarning from library code. For behavior changes they must notice (e.g. a config option going away), use FutureWarning, which the default filters show.
DeprecationWarning
import warnings
with warnings.catch_warnings(record=True) as caught:
    warnings.warn_explicit('option x is going away', DeprecationWarning, 'lib.py', 1, module='mylib')
len(caught)
0
FutureWarning
import warnings
with warnings.catch_warnings(record=True) as caught:
    warnings.warn_explicit('option x is going away', FutureWarning, 'lib.py', 1, module='mylib')
len(caught)
1

When to use

Use it
  • DeprecationWarning: an API other developers call is going away
  • PendingDeprecationWarning: it will be deprecated later (rarely used; hidden by default even in __main__)
  • FutureWarning: users of an application must see it — the default filters do not hide it
Reach for something else
  • Messages for end users → FutureWarning, or plain output/logging
  • The feature is already gone → raise a real exception (AttributeError, TypeError …)

Notes

CPython impl
The default filter list is built in Python/_warnings.c; the module field "__main__" is compared to the module name exactly
Debug builds
The default filter list is empty — everything is shown
Dev mode
python -X dev shows DeprecationWarning, PendingDeprecationWarning, ImportWarning and ResourceWarning
PEP
PEP 565 (show in __main__), PEP 702 (@warnings.deprecated)

FAQ

Release builds ignore DeprecationWarning unless it is attributed to code in __main__ (the script you ran). Deprecations triggered inside libraries — including your own imported modules — are hidden. Show them with python -W default::DeprecationWarning, python -X dev, PYTHONWARNINGS=default, or warnings.simplefilter('default', DeprecationWarning) at startup. Test runners such as pytest show them in their summary.

History

3.2
DeprecationWarning is ignored by default, in addition to PendingDeprecationWarning.
3.7
DeprecationWarning is again shown by default when triggered directly by code in __main__.
3.13
Added the @warnings.deprecated decorator (PEP 702).