The warnings Module
Reviewed & published by Brayan K
The warnings module lets your code raise non-fatal alerts — like deprecation notices — that surface a message to the caller without stopping the program.
Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
It's how libraries say "this still works, but you should change it." You'll learn to emit warnings, choose categories, filter them, and even turn them into errors when you need to.
What You'll Learn in This Lesson
- Emitting a warning with warnings.warn
- Categories: UserWarning, DeprecationWarning
- Capturing warnings with catch_warnings
- Filtering with simplefilter & filterwarnings
- Turning warnings into errors
- Warnings vs exceptions: when to use each
1 Emitting Warnings & Categories
warnings.warn(message, category) raises a warning and keeps running. To make the demos here produce deterministic output, we capture warnings with catch_warnings(record=True) instead of letting them print to stderr:
import warnings
def old_function():
warnings.warn("old_function is deprecated; use new_function",
DeprecationWarning)
return 42
# Capture warnings so we can inspect them, not just print them
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always") # don't suppress anything
result = old_function()
print("result:", result) # result: 42
print("warnings:", len(caught)) # warnings: 1
print("category:", caught[0].category.__name__) # category: DeprecationWarningimport warnings
def parse(value):
if value < 0:
warnings.warn("negative value clamped to 0", UserWarning)
value = 0
return value
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
a = parse(5)
b = parse(-3)
print("a:", a)
print("b:", b)
print("warnings raised:", len(caught))
print("first message:", str(caught[0].message))
# ✅ Expected output:
# a: 5
# b: 0
# warnings raised: 1
# first message: negative value clamped to 02 Filtering: simplefilter & filterwarnings
Filters decide what happens to each warning. simplefilter(action) applies one action to everything; filterwarnings(action, category=...) targets a specific category. Actions include "always", "once", "ignore", and "error":
import warnings
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
# Silence just the DeprecationWarnings, keep the rest
warnings.filterwarnings("ignore", category=DeprecationWarning)
warnings.warn("keep me", UserWarning)
warnings.warn("drop me", DeprecationWarning)
# Only the UserWarning survived the filter
print("captured:", [str(w.message) for w in caught])
# captured: ['keep me']Filters are checked in order, most-recently-added first. Inside a catch_warnings block your changes are temporary — the original filter state is restored when the block exits, so you never leak settings into the rest of the program.
import warnings
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
warnings.warn("first", UserWarning)
warnings.warn("second", DeprecationWarning)
warnings.warn("third", UserWarning)
for w in caught:
print(w.category.__name__, "-", w.message)
print("total:", len(caught))
# ✅ Expected output:
# UserWarning - first
# DeprecationWarning - second
# UserWarning - third
# total: 33 Turning Warnings into Errors
The "error" action promotes a warning to a real exception, which you can then catch with try/except. This is invaluable in tests and CI: it makes a deprecated code path fail loudly instead of slipping by unnoticed:
import warnings
with warnings.catch_warnings():
# Promote UserWarnings to exceptions inside this block only
warnings.simplefilter("error", UserWarning)
try:
warnings.warn("this becomes an exception", UserWarning)
print("no error raised")
except UserWarning as exc:
print("caught as error:", exc)
# Outside the block, warnings behave normally again
print("done")import warnings
def risky():
warnings.warn("deprecated API call", DeprecationWarning)
return "result"
with warnings.catch_warnings():
warnings.simplefilter("error", DeprecationWarning)
try:
risky()
outcome = "ran without warning"
except DeprecationWarning:
outcome = "blocked: deprecated path"
print(outcome)
# ✅ Expected output:
# blocked: deprecated path🎯 YOUR TURN: Fill in the Blanks
Fill in each ___ so the code emits a warning and captures it for inspection. (Hint: the function to emit is warn, and the context manager records with record=True.)
import warnings
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
# Emit a UserWarning -> use warnings.___
warnings.___("low battery", UserWarning)
# Inspect what was captured
print("count:", len(caught))
print("category:", caught[0].___.__name__)
print("message:", str(caught[0].message))
# Expected output:
# count: 1
# category: UserWarning
# message: low batteryCommon Errors
❌ Wondering why a DeprecationWarning "didn't fire"
warnings.warn("old", DeprecationWarning) # often shows nothing in a normal run✅ It's hidden by default — enable it explicitly to see it
import warnings
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
warnings.warn("old", DeprecationWarning)
print(len(caught)) # 1❌ Expecting catch_warnings to record without record=True
with warnings.catch_warnings() as caught: # caught is None here!
warnings.warn("hi")
# TypeError: object of type 'NoneType' has no len()✅ Pass record=True to get the list
import warnings
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
warnings.warn("hi")
print(len(caught)) # 1❌ Using a warning where you should raise
# The program can't continue, yet it only warns and limps on
warnings.warn("config file missing") # then crashes later, confusingly✅ Raise an exception when continuing is wrong
raise FileNotFoundError("config file missing") # fail fast, fail clearly🎯 Mini Challenge: Deprecation Audit
Two functions emit warnings. Capture all of them, then count how many are DeprecationWarnings versus other categories — exactly the kind of audit you'd run before a release.
import warnings
def login(token):
if token is None:
warnings.warn("anonymous login", UserWarning)
return "session"
def fetch(url):
warnings.warn("fetch() is deprecated; use get()", DeprecationWarning)
return "data"
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
login(None)
fetch("/api")
login("abc") # valid token -> no warning
deprecations = sum(
1 for w in caught if issubclass(w.category, DeprecationWarning)
)
print("total warnings:", len(caught))
print("deprecations:", deprecations)
print("other:", len(caught) - deprecations)
# ✅ Expected output:
# total warnings: 2
# deprecations: 1
# other: 1📚 Keep Learning
- warnings — Warning control (official docs)
- Warning categories — the built-in warning classes
- Built-in exceptions — when to raise instead of warn
Frequently Asked Questions
Lesson complete — you signal problems gracefully!
You can emit warnings with warnings.warn, choose categories like UserWarning and DeprecationWarning, capture them with catch_warnings(record=True), filter with simplefilter and filterwarnings, and promote them to errors when you want failures to be loud.
Practice quiz
How does a warning differ from an exception?
- A warning always crashes the program; an exception does not
- A warning surfaces a message but the program keeps running; an exception interrupts flow
- They are identical in behavior
- A warning can only be raised inside a try block
Answer: A warning surfaces a message but the program keeps running; an exception interrupts flow. A warning is advisory and execution continues; an exception interrupts the flow and must be caught or the program crashes.
What function emits a warning?
- warnings.raise(...)
- warnings.warn(message, category)
- warnings.alert(...)
- warnings.emit(...)
Answer: warnings.warn(message, category). warnings.warn(message, category) raises a warning and keeps running.
What is the default category for a bare call like warnings.warn('msg')?
- DeprecationWarning
- RuntimeWarning
- UserWarning
- FutureWarning
Answer: UserWarning. A bare warnings.warn defaults to UserWarning, which is shown by default.
Why might a DeprecationWarning appear to 'not fire' in a normal program run?
- DeprecationWarning is hidden by default so end users aren't spammed
- DeprecationWarning does not exist
- It only works in Python 2
- It requires an internet connection
Answer: DeprecationWarning is hidden by default so end users aren't spammed. DeprecationWarning is hidden by default in normal runs, but shown under tests or with -W flags.
To capture warnings into a list for inspection, what must you pass to catch_warnings?
- record=True
- capture=True
- save=True
- list=True
Answer: record=True. with warnings.catch_warnings(record=True) as caught: gives you a list of recorded warnings; without record=True the value is None.
Which filter action turns a matching warning into a real exception?
- 'always'
- 'once'
- 'ignore'
- 'error'
Answer: 'error'. The 'error' action promotes the warning to an exception you can catch with try/except — useful in tests and CI.
What does filterwarnings('ignore', category=DeprecationWarning) do?
- Promotes DeprecationWarnings to errors
- Silences only DeprecationWarnings while leaving other categories alone
- Silences every warning
- Records all warnings to a file
Answer: Silences only DeprecationWarnings while leaving other categories alone. filterwarnings targets one category; 'ignore' on DeprecationWarning drops just those, keeping e.g. UserWarnings.
Why scope simplefilter('error') inside a catch_warnings block?
- Because it runs faster there
- So the filter change is temporary and the original state is restored on exit
- Because simplefilter only works inside that block
- To capture the warning to disk
Answer: So the filter change is temporary and the original state is restored on exit. Inside catch_warnings the filter state is restored when the block exits, so you don't leak 'error' settings into unrelated code.
After a warning is promoted with the 'error' action, how do you handle it?
- With warnings.handle()
- It cannot be handled
- Catch it with try/except, because it is now a real exception
- Call warnings.resetwarnings()
Answer: Catch it with try/except, because it is now a real exception. A promoted warning IS an exception, so you catch it like any other: try: warnings.warn(...) except SomeWarning: ...
When should you raise an exception instead of emitting a warning?
- When the situation is recoverable and the caller can keep going
- When continuing would be wrong or impossible, like a missing required config file
- Whenever a deprecated function is called
- Never — always warn
Answer: When continuing would be wrong or impossible, like a missing required config file. Warn when recoverable; raise when continuing is wrong or impossible (e.g. FileNotFoundError for a missing config) so it fails fast.
Continue this course
- Previous: Benchmarking with timeit
- Next: Introspection with inspect — Examine signatures, source and members of live objects at runtime
- Quick reference: Python cheat sheet
Frequently asked questions
What is the warnings module for?
The warnings module lets your code flag situations that aren't fatal but the caller should know about — a deprecated function, a risky default, a recoverable problem. Unlike an exception, a warning doesn't stop the program; it prints a message (by default to stderr) and execution continues. It's how libraries communicate 'this still works, but...'.
What's the difference between a warning and an exception?
An exception interrupts the flow and must be caught or the program crashes. A warning is advisory: it surfaces a message but the code keeps running. Use a warning when the situation is recoverable and the caller can keep going; raise an exception when continuing would be wrong or impossible.
What are warning categories like DeprecationWarning and UserWarning?
Categories are classes that group warnings so they can be filtered independently. UserWarning is the default for warnings.warn. DeprecationWarning marks features scheduled for removal (and is hidden by default in normal runs). Others include RuntimeWarning, FutureWarning, and SyntaxWarning. You filter by category to silence or escalate specific kinds.
How do I turn warnings into errors?
Use warnings.simplefilter('error') or warnings.filterwarnings('error', category=...). After that, the matching warning is raised as an exception, so you can catch it with try/except. This is great in tests and CI to make sure deprecated code paths are actually fixed rather than silently ignored.