Lib/warnings.py
cpython 3.14 @ ab2d84fe1023/Lib/warnings.py
warnings.py is the pure-Python fallback for the warning subsystem. CPython
normally uses _warnings.c (the C implementation), which is loaded at
interpreter startup and shadows this module. The Python version is used when
the C extension is unavailable and serves as the canonical documentation of
the algorithm.
The two central functions are warn (called by user code) and warn_explicit
(the inner worker). warn_explicit scans filters in order, applies the
matching action, and routes the warning to showwarnmsg. The filter list is
a module-global list of 5-tuples; _filters_mutated() notifies the C layer
that its internal cache must be rebuilt whenever the list changes.
Map
| Lines | Symbol | Role | gopy |
|---|---|---|---|
| 1-60 | filters, _filters_mutated, defaultaction, onceregistry | Module-global filter list (initially set from -W flags and PYTHONWARNINGS), the cache-invalidation hook, the default action string, and the per-category once-registry dict. | (stdlib pending) |
| 60-130 | filterwarnings, simplefilter, resetwarnings | Public filter mutators. filterwarnings inserts a 5-tuple (action, message_re, category, module_re, lineno) at the front of filters. simplefilter is a shorthand that inserts (action, None, category, None, 0). Both call _filters_mutated(). | (stdlib pending) |
| 130-280 | warn, warn_explicit | warn resolves the caller's frame and category, then delegates to warn_explicit. warn_explicit runs the filter scan: for each filter tuple it checks action, message regex, category, module regex, and lineno; on a match it executes the action and returns. | (stdlib pending) |
| 280-380 | showwarnmsg_impl, showwarnmsg, formatwarning, _showwarning_orig, _showwarnmsg_impl | The display path. showwarnmsg calls showwarnmsg_impl (or a user-installed showwarning). formatwarning produces the standard file:lineno: category: text\n source_line\n format. | (stdlib pending) |
| 380-500 | catch_warnings, WarningMessage | catch_warnings saves and restores filters, showwarning, and showwarnmsg on entry/exit. When record=True it installs a collector that appends WarningMessage objects to a list instead of printing. | (stdlib pending) |
Reading
warn_explicit filter scan (lines 130 to 280)
cpython 3.14 @ ab2d84fe1023/Lib/warnings.py#L130-280
def warn_explicit(message, category, filename, lineno,
module=None, registry=None, module_globals=None,
source=None):
lineno = int(lineno)
if module is None:
module = filename or "<unknown>"
if module[-3:].lower() == ".py":
module = module[:-3]
if registry is None:
registry = {}
if registry.get('version', 0) != _filters_version:
registry.clear()
registry['version'] = _filters_version
if isinstance(message, Warning):
text = str(message)
category = message.__class__
else:
text = message
message = category(message)
key = (text, category, lineno)
if registry.get(key):
return
for item in filters:
action, msg, cat, mod, ln = item
if ((msg is None or msg.match(text)) and
issubclass(category, cat) and
(mod is None or mod.match(module)) and
(ln == 0 or lineno == ln)):
break
else:
action = defaultaction
...
if action == "error":
raise message
if action == 'ignore':
registry[key] = 1
return
if action == 'always':
pass
elif action == 'all':
pass
elif action in ('default', 'module'):
if action == 'module':
registry[key] = 1
if key in onceregistry:
return
onceregistry[key] = 1
elif action == 'once':
if key in onceregistry:
return
onceregistry[key] = 1
else:
raise RuntimeError("Unrecognized action: %r" % action)
msg = WarningMessage(message, category, filename, lineno, source=source)
showwarnmsg(msg)
registry[key] = 1
The filter scan iterates filters in order. Each entry is a 5-tuple; None
in the message or module position means "match any". The issubclass check
allows category matching to respect inheritance (e.g., a filter on
Warning catches all warning subclasses).
The registry argument is the per-module __warningregistry__ dict. It is
cleared whenever _filters_version changes (i.e., after any call to
filterwarnings or simplefilter). The key tuple (text, category, lineno) is inserted into registry after the warning is shown, so
subsequent identical warnings from the same line are suppressed.
The six actions:
error: raise the warning as an exception.ignore: discard silently, record in registry.always: show every occurrence.all: alias foralways.default: show once per location (file + lineno), record inonceregistry.module: show once per module, record inonceregistry.once: show once globally, record inonceregistry.
catch_warnings save and restore (lines 380 to 500)
cpython 3.14 @ ab2d84fe1023/Lib/warnings.py#L380-500
class catch_warnings(object):
def __init__(self, *, record=False, module=None):
self._record = record
self._module = sys.modules['warnings'] if module is None else module
self._entered = False
def __enter__(self):
if self._entered:
raise RuntimeError("Cannot enter %r twice" % self)
self._entered = True
self._filters = self._module.filters
self._module.filters = self._filters[:]
self._module._filters_mutated()
self._showwarning = self._module.showwarning
self._showwarnmsg_impl = self._module._showwarnmsg_impl
if self._record:
log = []
self._module._showwarnmsg_impl = log.append
self._module.showwarning = self._showwarning_orig
return log
else:
return None
def __exit__(self, *exc_info):
if not self._entered:
raise RuntimeError("Cannot exit %r without entering first" % self)
self._module.filters = self._filters
self._module._filters_mutated()
self._module.showwarning = self._showwarning
self._module._showwarnmsg_impl = self._showwarnmsg_impl
catch_warnings saves three things on __enter__: the filters list (by
taking a shallow copy so mutations inside the block do not affect the saved
original), the showwarning hook, and _showwarnmsg_impl. All three are
restored verbatim on __exit__, regardless of whether an exception occurred.
_filters_mutated() is called on both entry and exit so the C layer's cache
is invalidated at the right times.
When record=True, _showwarnmsg_impl is replaced by log.append, so every
warning that reaches the display path is instead appended to the returned list
as a WarningMessage object. This is the standard pattern in test suites:
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
do_something_that_warns()
assert len(w) == 1
assert issubclass(w[0].category, DeprecationWarning)
onceregistry per-module dedup (lines 1 to 130)
cpython 3.14 @ ab2d84fe1023/Lib/warnings.py#L1-130
onceregistry = {}
defaultaction = "default"
onceregistry is module-global and shared across all callers. For the
"default" and "module" actions, the per-module __warningregistry__
dict (created by the compiler in each module's globals) is the primary
deduplication store; onceregistry is only consulted for the "once" and
"module" actions to enforce global-level deduplication on top of per-module
deduplication. Both dicts are keyed by (text, category, lineno) tuples.
Both are cleared when resetwarnings() is called (which also clears the
filters list back to its default state).
gopy mirror
The Python warnings module is shadowed by _warnings.c at runtime, but
the Python code defines the algorithm. A gopy port must implement the filter
scan and the __warningregistry__ dict protocol in the VM, and expose
filters, onceregistry, showwarning, and _showwarnmsg_impl as mutable
module-level attributes so that catch_warnings can save and restore them.
The _filters_mutated() hook must notify any internal warning cache in the
interpreter core.