1 """Python part of the warnings subsystem."""
2
3 import sys
4
5
6 __all__ = ["warn", "warn_explicit", "showwarning",
7 "formatwarning", "filterwarnings", "simplefilter",
8 "resetwarnings", "catch_warnings"]
9
10 def showwarning(message, category, filename, lineno, file=None, line=None):
11 """Hook to write a warning to a file; replace if you like."""
12 msg = WarningMessage(message, category, filename, lineno, file, line)
13 _showwarnmsg_impl(msg)
14
15 def formatwarning(message, category, filename, lineno, line=None):
16 """Function to format a warning the standard way."""
17 msg = WarningMessage(message, category, filename, lineno, None, line)
18 return _formatwarnmsg_impl(msg)
19
20 def _showwarnmsg_impl(msg):
21 file = msg.file
22 if file is None:
23 file = sys.stderr
24 if file is None:
25 # sys.stderr is None when run with pythonw.exe:
26 # warnings get lost
27 return
28 text = _formatwarnmsg(msg)
29 try:
30 file.write(text)
31 except OSError:
32 # the file (probably stderr) is invalid - this warning gets lost.
33 pass
34
35 def _formatwarnmsg_impl(msg):
36 category = msg.category.__name__
37 s = f"{msg.filename}:{msg.lineno}: {category}: {msg.message}\n"
38
39 if msg.line is None:
40 try:
41 import linecache
42 line = linecache.getline(msg.filename, msg.lineno)
43 except Exception:
44 # When a warning is logged during Python shutdown, linecache
45 # and the import machinery don't work anymore
46 line = None
47 linecache = None
48 else:
49 line = msg.line
50 if line:
51 line = line.strip()
52 s += " %s\n" % line
53
54 if msg.source is not None:
55 try:
56 import tracemalloc
57 # Logging a warning should not raise a new exception:
58 # catch Exception, not only ImportError and RecursionError.
59 except Exception:
60 # don't suggest to enable tracemalloc if it's not available
61 tracing = True
62 tb = None
63 else:
64 tracing = tracemalloc.is_tracing()
65 try:
66 tb = tracemalloc.get_object_traceback(msg.source)
67 except Exception:
68 # When a warning is logged during Python shutdown, tracemalloc
69 # and the import machinery don't work anymore
70 tb = None
71
72 if tb is not None:
73 s += 'Object allocated at (most recent call last):\n'
74 for frame in tb:
75 s += (' File "%s", lineno %s\n'
76 % (frame.filename, frame.lineno))
77
78 try:
79 if linecache is not None:
80 line = linecache.getline(frame.filename, frame.lineno)
81 else:
82 line = None
83 except Exception:
84 line = None
85 if line:
86 line = line.strip()
87 s += ' %s\n' % line
88 elif not tracing:
89 s += (f'{category}: Enable tracemalloc to get the object '
90 f'allocation traceback\n')
91 return s
92
93 # Keep a reference to check if the function was replaced
94 _showwarning_orig = showwarning
95
96 def _showwarnmsg(msg):
97 """Hook to write a warning to a file; replace if you like."""
98 try:
99 sw = showwarning
100 except NameError:
101 pass
102 else:
103 if sw is not _showwarning_orig:
104 # warnings.showwarning() was replaced
105 if not callable(sw):
106 raise TypeError("warnings.showwarning() must be set to a "
107 "function or method")
108
109 sw(msg.message, msg.category, msg.filename, msg.lineno,
110 msg.file, msg.line)
111 return
112 _showwarnmsg_impl(msg)
113
114 # Keep a reference to check if the function was replaced
115 _formatwarning_orig = formatwarning
116
117 def _formatwarnmsg(msg):
118 """Function to format a warning the standard way."""
119 try:
120 fw = formatwarning
121 except NameError:
122 pass
123 else:
124 if fw is not _formatwarning_orig:
125 # warnings.formatwarning() was replaced
126 return fw(msg.message, msg.category,
127 msg.filename, msg.lineno, msg.line)
128 return _formatwarnmsg_impl(msg)
129
130 def filterwarnings(action, message="", category=Warning, module="", lineno=0,
131 append=False):
132 """Insert an entry into the list of warnings filters (at the front).
133
134 'action' -- one of "error", "ignore", "always", "default", "module",
135 or "once"
136 'message' -- a regex that the warning message must match
137 'category' -- a class that the warning must be a subclass of
138 'module' -- a regex that the module name must match
139 'lineno' -- an integer line number, 0 matches all warnings
140 'append' -- if true, append to the list of filters
141 """
142 assert action in ("error", "ignore", "always", "default", "module",
143 "once"), "invalid action: %r" % (action,)
144 assert isinstance(message, str), "message must be a string"
145 assert isinstance(category, type), "category must be a class"
146 assert issubclass(category, Warning), "category must be a Warning subclass"
147 assert isinstance(module, str), "module must be a string"
148 assert isinstance(lineno, int) and lineno >= 0, \
149 "lineno must be an int >= 0"
150
151 if message or module:
152 import re
153
154 if message:
155 message = re.compile(message, re.I)
156 else:
157 message = None
158 if module:
159 module = re.compile(module)
160 else:
161 module = None
162
163 _add_filter(action, message, category, module, lineno, append=append)
164
165 def simplefilter(action, category=Warning, lineno=0, append=False):
166 """Insert a simple entry into the list of warnings filters (at the front).
167
168 A simple filter matches all modules and messages.
169 'action' -- one of "error", "ignore", "always", "default", "module",
170 or "once"
171 'category' -- a class that the warning must be a subclass of
172 'lineno' -- an integer line number, 0 matches all warnings
173 'append' -- if true, append to the list of filters
174 """
175 assert action in ("error", "ignore", "always", "default", "module",
176 "once"), "invalid action: %r" % (action,)
177 assert isinstance(lineno, int) and lineno >= 0, \
178 "lineno must be an int >= 0"
179 _add_filter(action, None, category, None, lineno, append=append)
180
181 def _add_filter(*item, append):
182 # Remove possible duplicate filters, so new one will be placed
183 # in correct place. If append=True and duplicate exists, do nothing.
184 if not append:
185 try:
186 filters.remove(item)
187 except ValueError:
188 pass
189 filters.insert(0, item)
190 else:
191 if item not in filters:
192 filters.append(item)
193 _filters_mutated()
194
195 def resetwarnings():
196 """Clear the list of warning filters, so that no filters are active."""
197 filters[:] = []
198 _filters_mutated()
199
200 class ESC[4;38;5;81m_OptionError(ESC[4;38;5;149mException):
201 """Exception used by option processing helpers."""
202 pass
203
204 # Helper to process -W options passed via sys.warnoptions
205 def _processoptions(args):
206 for arg in args:
207 try:
208 _setoption(arg)
209 except _OptionError as msg:
210 print("Invalid -W option ignored:", msg, file=sys.stderr)
211
212 # Helper for _processoptions()
213 def _setoption(arg):
214 parts = arg.split(':')
215 if len(parts) > 5:
216 raise _OptionError("too many fields (max 5): %r" % (arg,))
217 while len(parts) < 5:
218 parts.append('')
219 action, message, category, module, lineno = [s.strip()
220 for s in parts]
221 action = _getaction(action)
222 category = _getcategory(category)
223 if message or module:
224 import re
225 if message:
226 message = re.escape(message)
227 if module:
228 module = re.escape(module) + r'\Z'
229 if lineno:
230 try:
231 lineno = int(lineno)
232 if lineno < 0:
233 raise ValueError
234 except (ValueError, OverflowError):
235 raise _OptionError("invalid lineno %r" % (lineno,)) from None
236 else:
237 lineno = 0
238 filterwarnings(action, message, category, module, lineno)
239
240 # Helper for _setoption()
241 def _getaction(action):
242 if not action:
243 return "default"
244 if action == "all": return "always" # Alias
245 for a in ('default', 'always', 'ignore', 'module', 'once', 'error'):
246 if a.startswith(action):
247 return a
248 raise _OptionError("invalid action: %r" % (action,))
249
250 # Helper for _setoption()
251 def _getcategory(category):
252 if not category:
253 return Warning
254 if '.' not in category:
255 import builtins as m
256 klass = category
257 else:
258 module, _, klass = category.rpartition('.')
259 try:
260 m = __import__(module, None, None, [klass])
261 except ImportError:
262 raise _OptionError("invalid module name: %r" % (module,)) from None
263 try:
264 cat = getattr(m, klass)
265 except AttributeError:
266 raise _OptionError("unknown warning category: %r" % (category,)) from None
267 if not issubclass(cat, Warning):
268 raise _OptionError("invalid warning category: %r" % (category,))
269 return cat
270
271
272 def _is_internal_filename(filename):
273 return 'importlib' in filename and '_bootstrap' in filename
274
275
276 def _is_filename_to_skip(filename, skip_file_prefixes):
277 return any(filename.startswith(prefix) for prefix in skip_file_prefixes)
278
279
280 def _is_internal_frame(frame):
281 """Signal whether the frame is an internal CPython implementation detail."""
282 return _is_internal_filename(frame.f_code.co_filename)
283
284
285 def _next_external_frame(frame, skip_file_prefixes):
286 """Find the next frame that doesn't involve Python or user internals."""
287 frame = frame.f_back
288 while frame is not None and (
289 _is_internal_filename(filename := frame.f_code.co_filename) or
290 _is_filename_to_skip(filename, skip_file_prefixes)):
291 frame = frame.f_back
292 return frame
293
294
295 # Code typically replaced by _warnings
296 def warn(message, category=None, stacklevel=1, source=None,
297 *, skip_file_prefixes=()):
298 """Issue a warning, or maybe ignore it or raise an exception."""
299 # Check if message is already a Warning object
300 if isinstance(message, Warning):
301 category = message.__class__
302 # Check category argument
303 if category is None:
304 category = UserWarning
305 if not (isinstance(category, type) and issubclass(category, Warning)):
306 raise TypeError("category must be a Warning subclass, "
307 "not '{:s}'".format(type(category).__name__))
308 if not isinstance(skip_file_prefixes, tuple):
309 # The C version demands a tuple for implementation performance.
310 raise TypeError('skip_file_prefixes must be a tuple of strs.')
311 if skip_file_prefixes:
312 stacklevel = max(2, stacklevel)
313 # Get context information
314 try:
315 if stacklevel <= 1 or _is_internal_frame(sys._getframe(1)):
316 # If frame is too small to care or if the warning originated in
317 # internal code, then do not try to hide any frames.
318 frame = sys._getframe(stacklevel)
319 else:
320 frame = sys._getframe(1)
321 # Look for one frame less since the above line starts us off.
322 for x in range(stacklevel-1):
323 frame = _next_external_frame(frame, skip_file_prefixes)
324 if frame is None:
325 raise ValueError
326 except ValueError:
327 globals = sys.__dict__
328 filename = "sys"
329 lineno = 1
330 else:
331 globals = frame.f_globals
332 filename = frame.f_code.co_filename
333 lineno = frame.f_lineno
334 if '__name__' in globals:
335 module = globals['__name__']
336 else:
337 module = "<string>"
338 registry = globals.setdefault("__warningregistry__", {})
339 warn_explicit(message, category, filename, lineno, module, registry,
340 globals, source)
341
342 def warn_explicit(message, category, filename, lineno,
343 module=None, registry=None, module_globals=None,
344 source=None):
345 lineno = int(lineno)
346 if module is None:
347 module = filename or "<unknown>"
348 if module[-3:].lower() == ".py":
349 module = module[:-3] # XXX What about leading pathname?
350 if registry is None:
351 registry = {}
352 if registry.get('version', 0) != _filters_version:
353 registry.clear()
354 registry['version'] = _filters_version
355 if isinstance(message, Warning):
356 text = str(message)
357 category = message.__class__
358 else:
359 text = message
360 message = category(message)
361 key = (text, category, lineno)
362 # Quick test for common case
363 if registry.get(key):
364 return
365 # Search the filters
366 for item in filters:
367 action, msg, cat, mod, ln = item
368 if ((msg is None or msg.match(text)) and
369 issubclass(category, cat) and
370 (mod is None or mod.match(module)) and
371 (ln == 0 or lineno == ln)):
372 break
373 else:
374 action = defaultaction
375 # Early exit actions
376 if action == "ignore":
377 return
378
379 # Prime the linecache for formatting, in case the
380 # "file" is actually in a zipfile or something.
381 import linecache
382 linecache.getlines(filename, module_globals)
383
384 if action == "error":
385 raise message
386 # Other actions
387 if action == "once":
388 registry[key] = 1
389 oncekey = (text, category)
390 if onceregistry.get(oncekey):
391 return
392 onceregistry[oncekey] = 1
393 elif action == "always":
394 pass
395 elif action == "module":
396 registry[key] = 1
397 altkey = (text, category, 0)
398 if registry.get(altkey):
399 return
400 registry[altkey] = 1
401 elif action == "default":
402 registry[key] = 1
403 else:
404 # Unrecognized actions are errors
405 raise RuntimeError(
406 "Unrecognized action (%r) in warnings.filters:\n %s" %
407 (action, item))
408 # Print message and context
409 msg = WarningMessage(message, category, filename, lineno, source)
410 _showwarnmsg(msg)
411
412
413 class ESC[4;38;5;81mWarningMessage(ESC[4;38;5;149mobject):
414
415 _WARNING_DETAILS = ("message", "category", "filename", "lineno", "file",
416 "line", "source")
417
418 def __init__(self, message, category, filename, lineno, file=None,
419 line=None, source=None):
420 self.message = message
421 self.category = category
422 self.filename = filename
423 self.lineno = lineno
424 self.file = file
425 self.line = line
426 self.source = source
427 self._category_name = category.__name__ if category else None
428
429 def __str__(self):
430 return ("{message : %r, category : %r, filename : %r, lineno : %s, "
431 "line : %r}" % (self.message, self._category_name,
432 self.filename, self.lineno, self.line))
433
434
435 class ESC[4;38;5;81mcatch_warnings(ESC[4;38;5;149mobject):
436
437 """A context manager that copies and restores the warnings filter upon
438 exiting the context.
439
440 The 'record' argument specifies whether warnings should be captured by a
441 custom implementation of warnings.showwarning() and be appended to a list
442 returned by the context manager. Otherwise None is returned by the context
443 manager. The objects appended to the list are arguments whose attributes
444 mirror the arguments to showwarning().
445
446 The 'module' argument is to specify an alternative module to the module
447 named 'warnings' and imported under that name. This argument is only useful
448 when testing the warnings module itself.
449
450 If the 'action' argument is not None, the remaining arguments are passed
451 to warnings.simplefilter() as if it were called immediately on entering the
452 context.
453 """
454
455 def __init__(self, *, record=False, module=None,
456 action=None, category=Warning, lineno=0, append=False):
457 """Specify whether to record warnings and if an alternative module
458 should be used other than sys.modules['warnings'].
459
460 For compatibility with Python 3.0, please consider all arguments to be
461 keyword-only.
462
463 """
464 self._record = record
465 self._module = sys.modules['warnings'] if module is None else module
466 self._entered = False
467 if action is None:
468 self._filter = None
469 else:
470 self._filter = (action, category, lineno, append)
471
472 def __repr__(self):
473 args = []
474 if self._record:
475 args.append("record=True")
476 if self._module is not sys.modules['warnings']:
477 args.append("module=%r" % self._module)
478 name = type(self).__name__
479 return "%s(%s)" % (name, ", ".join(args))
480
481 def __enter__(self):
482 if self._entered:
483 raise RuntimeError("Cannot enter %r twice" % self)
484 self._entered = True
485 self._filters = self._module.filters
486 self._module.filters = self._filters[:]
487 self._module._filters_mutated()
488 self._showwarning = self._module.showwarning
489 self._showwarnmsg_impl = self._module._showwarnmsg_impl
490 if self._filter is not None:
491 simplefilter(*self._filter)
492 if self._record:
493 log = []
494 self._module._showwarnmsg_impl = log.append
495 # Reset showwarning() to the default implementation to make sure
496 # that _showwarnmsg() calls _showwarnmsg_impl()
497 self._module.showwarning = self._module._showwarning_orig
498 return log
499 else:
500 return None
501
502 def __exit__(self, *exc_info):
503 if not self._entered:
504 raise RuntimeError("Cannot exit %r without entering first" % self)
505 self._module.filters = self._filters
506 self._module._filters_mutated()
507 self._module.showwarning = self._showwarning
508 self._module._showwarnmsg_impl = self._showwarnmsg_impl
509
510
511 _DEPRECATED_MSG = "{name!r} is deprecated and slated for removal in Python {remove}"
512
513 def _deprecated(name, message=_DEPRECATED_MSG, *, remove, _version=sys.version_info):
514 """Warn that *name* is deprecated or should be removed.
515
516 RuntimeError is raised if *remove* specifies a major/minor tuple older than
517 the current Python version or the same version but past the alpha.
518
519 The *message* argument is formatted with *name* and *remove* as a Python
520 version (e.g. "3.11").
521
522 """
523 remove_formatted = f"{remove[0]}.{remove[1]}"
524 if (_version[:2] > remove) or (_version[:2] == remove and _version[3] != "alpha"):
525 msg = f"{name!r} was slated for removal after Python {remove_formatted} alpha"
526 raise RuntimeError(msg)
527 else:
528 msg = message.format(name=name, remove=remove_formatted)
529 warn(msg, DeprecationWarning, stacklevel=3)
530
531
532 # Private utility function called by _PyErr_WarnUnawaitedCoroutine
533 def _warn_unawaited_coroutine(coro):
534 msg_lines = [
535 f"coroutine '{coro.__qualname__}' was never awaited\n"
536 ]
537 if coro.cr_origin is not None:
538 import linecache, traceback
539 def extract():
540 for filename, lineno, funcname in reversed(coro.cr_origin):
541 line = linecache.getline(filename, lineno)
542 yield (filename, lineno, funcname, line)
543 msg_lines.append("Coroutine created at (most recent call last)\n")
544 msg_lines += traceback.format_list(list(extract()))
545 msg = "".join(msg_lines).rstrip("\n")
546 # Passing source= here means that if the user happens to have tracemalloc
547 # enabled and tracking where the coroutine was created, the warning will
548 # contain that traceback. This does mean that if they have *both*
549 # coroutine origin tracking *and* tracemalloc enabled, they'll get two
550 # partially-redundant tracebacks. If we wanted to be clever we could
551 # probably detect this case and avoid it, but for now we don't bother.
552 warn(msg, category=RuntimeWarning, stacklevel=2, source=coro)
553
554
555 # filters contains a sequence of filter 5-tuples
556 # The components of the 5-tuple are:
557 # - an action: error, ignore, always, default, module, or once
558 # - a compiled regex that must match the warning message
559 # - a class representing the warning category
560 # - a compiled regex that must match the module that is being warned
561 # - a line number for the line being warning, or 0 to mean any line
562 # If either if the compiled regexs are None, match anything.
563 try:
564 from _warnings import (filters, _defaultaction, _onceregistry,
565 warn, warn_explicit, _filters_mutated)
566 defaultaction = _defaultaction
567 onceregistry = _onceregistry
568 _warnings_defaults = True
569 except ImportError:
570 filters = []
571 defaultaction = "default"
572 onceregistry = {}
573
574 _filters_version = 1
575
576 def _filters_mutated():
577 global _filters_version
578 _filters_version += 1
579
580 _warnings_defaults = False
581
582
583 # Module initialization
584 _processoptions(sys.warnoptions)
585 if not _warnings_defaults:
586 # Several warning categories are ignored by default in regular builds
587 if not hasattr(sys, 'gettotalrefcount'):
588 filterwarnings("default", category=DeprecationWarning,
589 module="__main__", append=1)
590 simplefilter("ignore", category=DeprecationWarning, append=1)
591 simplefilter("ignore", category=PendingDeprecationWarning, append=1)
592 simplefilter("ignore", category=ImportWarning, append=1)
593 simplefilter("ignore", category=ResourceWarning, append=1)
594
595 del _warnings_defaults