A warning is Python telling you something is probably wrong without stopping the program. Unlike an error it is easy to ignore — which is exactly why it is worth learning to control.
import warnings
warnings.warn("This function will be removed in version 3", DeprecationWarning)Why you are not seeing them#
Python shows each unique warning once per location, and hides DeprecationWarning entirely unless it comes from __main__. So a library quietly telling you a function is going away may never reach you.
python -W always myscript.py # show every warning, every time
python -W error myscript.py # turn them all into errorsOr from inside the program:
import warnings
warnings.simplefilter("always")Run this once on a project you have been ignoring warnings in. It is usually informative.
The categories worth recognising#
| Category | Means |
|---|---|
DeprecationWarning |
Going away in a future version — fix it now |
FutureWarning |
Behaviour will change; aimed at end users |
UserWarning |
The default when code calls warnings.warn |
RuntimeWarning |
Something dubious happened, e.g. overflow |
ResourceWarning |
A file or socket was not closed |
DeprecationWarning is the one that saves you time. Acting on it today prevents a hard upgrade later.
Silencing precisely#
The temptation is warnings.filterwarnings("ignore"). Resist it — you will silence the warning that mattered along with the one that annoyed you.
import warnings
# One category, from one module
warnings.filterwarnings(
"ignore",
category=DeprecationWarning,
module="some_old_library",
)
# One specific message
warnings.filterwarnings("ignore", message=".*deprecated alias.*")Better still, silence it only around the call that produces it:
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
result = noisy_library_call()
# Normal warning behaviour resumes hereTurning warnings into errors#
In tests, and while cleaning up a codebase, this is the useful setting:
import warnings
warnings.simplefilter("error", DeprecationWarning)Now anything deprecated raises and gives you a full traceback pointing at the exact line, which is far easier to act on than a one-line message with no context.
Raising your own#
import warnings
def old_calculate(values):
warnings.warn(
"old_calculate() is deprecated; use calculate() instead",
DeprecationWarning,
stacklevel=2,
)
return calculate(values)stacklevel=2 is the detail people miss. Without it the warning points at your own warnings.warn line; with it, it points at the caller — which is the line that actually needs changing.
Warning or exception?#
- Warn when the program can carry on correctly but the caller should know something.
- Raise when carrying on would produce a wrong result.
If in doubt, raise. A warning that nobody reads is the same as silence.
Questions people ask#
Why does a warning only print once?
The default filter shows each unique warning once per code location. simplefilter("always") shows every occurrence.
How do I log warnings instead of printing them?
logging.captureWarnings(True) routes them into the py.warnings logger, which is what you want on a server.
Do warnings slow my program down?
No meaningfully. The filtering happens once per location.
What is the difference from a linter warning?
A linter inspects code without running it. These appear while the program runs, so they catch things only visible with real data.