Home› Tutorials› Standard Library
Standard LibraryPython's warnings Module: Deprecation Notices Without Raising Exceptions
warnings.warn()flags a problem without stopping execution, unlike an exception, and without being silently invisible, unlike a bareprint().- Built-in categories like
DeprecationWarningandUserWarninglet callers filter by the kind of issue, not just by message text. - By default, Python only shows most warnings once per location, and hides
DeprecationWarningentirely unless the code triggering it is__main__. warnings.filterwarnings()andcatch_warnings()give scripts and tests explicit control over what shows and what's captured.
Why warn instead of raising or printing
Three ways exist to flag a problem: raise an exception, print a message, or issue a warning — and they serve genuinely different purposes. Raising stops execution, which is wrong for something the caller can safely ignore or work around, like a deprecated argument that still works. Printing is easy to miss in noisy output and gives the caller no structured way to detect or suppress it. A warning sits between the two: visible by default, filterable by category and location, and non-fatal unless the caller explicitly asks for that.
warnings.warn() basics and categories
The basic call takes a message and, optionally, a warning category class:
import warnings
def old_function():
warnings.warn("old_function() is slow, use new_function() instead", UserWarning)
return 42
old_function()
# script.py:5: UserWarning: old_function() is slow, use new_function() instead
# warnings.warn(...)
UserWarning is the default category if none is given. The output includes the file and line number where warn() was called, which by default points inside the function itself rather than the caller's line — usually not what you want, since the caller is who needs to change their code. Passing stacklevel=2 fixes this by attributing the warning to the caller's frame instead of the function's own.
DeprecationWarning for library authors
DeprecationWarning is the standard category for signalling that a function, argument, or module will be removed in a future version:
def old_function():
warnings.warn(
"old_function() is deprecated, use new_function() instead",
DeprecationWarning,
stacklevel=2,
)
return 42
By default, Python's warning filters hide DeprecationWarning unless the code that triggered it is running as __main__ — the reasoning being that end users of an application generally shouldn't see warnings meant for the developers calling a deprecated API directly. This is precisely why deprecation warnings are easy to miss during normal use and why running a test suite with warnings elevated to errors, or reading a library's changelog directly, catches them before they become a surprise at the next major version bump.
Controlling visibility with filterwarnings
warnings.filterwarnings() changes what's shown, by category, message pattern, or module:
warnings.filterwarnings("error", category=DeprecationWarning) # turn into exceptions
warnings.filterwarnings("ignore", message=".*old_function.*") # silence a specific one
warnings.filterwarnings("always") # show every occurrence, not just the first
Turning warnings into errors during development or in CI is a genuinely useful habit — it surfaces deprecated usage as a test failure immediately, rather than as a warning easily lost in scrollback, which connects to the same testing discipline covered in the unit testing guide. Filters apply in the order they're added and the most recently added matching filter wins, so order matters when combining several.
Asserting warnings in tests
Testing that a function actually issues the warning it's supposed to needs a way to capture warnings instead of letting them print to the console. warnings.catch_warnings() as a context manager does this:
def test_old_function_warns():
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
old_function()
assert len(caught) == 1
assert issubclass(caught[0].category, DeprecationWarning)
assert "new_function" in str(caught[0].message)
record=True collects triggered warnings into a list instead of printing them, and simplefilter("always") inside the block ensures the warning fires even if it was already triggered once earlier in the test run and would otherwise be suppressed by the default once-per-location filter. This pattern confirms both that a warning happened and that it carries the right category and message, which a plain print()-based approach has no equivalent way to check. See the warnings module documentation for the complete filter action list and the full set of built-in categories beyond UserWarning and DeprecationWarning.