1"""Python part of the warnings subsystem.""" 2 3import sys 4 5 6__all__ = ["warn", "warn_explicit", "showwarning", 7 "formatwarning", "filterwarnings", "simplefilter", 8 "resetwarnings", "catch_warnings", "deprecated"] 9 10def 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 15def 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 20def _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 35def _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 suggest_tracemalloc = False 62 tb = None 63 else: 64 try: 65 suggest_tracemalloc = not tracemalloc.is_tracing() 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 suggest_tracemalloc = False 71 tb = None 72 73 if tb is not None: 74 s += 'Object allocated at (most recent call last):\n' 75 for frame in tb: 76 s += (' File "%s", lineno %s\n' 77 % (frame.filename, frame.lineno)) 78 79 try: 80 if linecache is not None: 81 line = linecache.getline(frame.filename, frame.lineno) 82 else: 83 line = None 84 except Exception: 85 line = None 86 if line: 87 line = line.strip() 88 s += ' %s\n' % line 89 elif suggest_tracemalloc: 90 s += (f'{category}: Enable tracemalloc to get the object ' 91 f'allocation traceback\n') 92 return s 93 94# Keep a reference to check if the function was replaced 95_showwarning_orig = showwarning 96 97def _showwarnmsg(msg): 98 """Hook to write a warning to a file; replace if you like.""" 99 try: 100 sw = showwarning 101 except NameError: 102 pass 103 else: 104 if sw is not _showwarning_orig: 105 # warnings.showwarning() was replaced 106 if not callable(sw): 107 raise TypeError("warnings.showwarning() must be set to a " 108 "function or method") 109 110 sw(msg.message, msg.category, msg.filename, msg.lineno, 111 msg.file, msg.line) 112 return 113 _showwarnmsg_impl(msg) 114 115# Keep a reference to check if the function was replaced 116_formatwarning_orig = formatwarning 117 118def _formatwarnmsg(msg): 119 """Function to format a warning the standard way.""" 120 try: 121 fw = formatwarning 122 except NameError: 123 pass 124 else: 125 if fw is not _formatwarning_orig: 126 # warnings.formatwarning() was replaced 127 return fw(msg.message, msg.category, 128 msg.filename, msg.lineno, msg.line) 129 return _formatwarnmsg_impl(msg) 130 131def filterwarnings(action, message="", category=Warning, module="", lineno=0, 132 append=False): 133 """Insert an entry into the list of warnings filters (at the front). 134 135 'action' -- one of "error", "ignore", "always", "default", "module", 136 or "once" 137 'message' -- a regex that the warning message must match 138 'category' -- a class that the warning must be a subclass of 139 'module' -- a regex that the module name must match 140 'lineno' -- an integer line number, 0 matches all warnings 141 'append' -- if true, append to the list of filters 142 """ 143 if action not in {"error", "ignore", "always", "default", "module", "once"}: 144 raise ValueError(f"invalid action: {action!r}") 145 if not isinstance(message, str): 146 raise TypeError("message must be a string") 147 if not isinstance(category, type) or not issubclass(category, Warning): 148 raise TypeError("category must be a Warning subclass") 149 if not isinstance(module, str): 150 raise TypeError("module must be a string") 151 if not isinstance(lineno, int): 152 raise TypeError("lineno must be an int") 153 if lineno < 0: 154 raise ValueError("lineno must be an int >= 0") 155 156 if message or module: 157 import re 158 159 if message: 160 message = re.compile(message, re.I) 161 else: 162 message = None 163 if module: 164 module = re.compile(module) 165 else: 166 module = None 167 168 _add_filter(action, message, category, module, lineno, append=append) 169 170def simplefilter(action, category=Warning, lineno=0, append=False): 171 """Insert a simple entry into the list of warnings filters (at the front). 172 173 A simple filter matches all modules and messages. 174 'action' -- one of "error", "ignore", "always", "default", "module", 175 or "once" 176 'category' -- a class that the warning must be a subclass of 177 'lineno' -- an integer line number, 0 matches all warnings 178 'append' -- if true, append to the list of filters 179 """ 180 if action not in {"error", "ignore", "always", "default", "module", "once"}: 181 raise ValueError(f"invalid action: {action!r}") 182 if not isinstance(lineno, int): 183 raise TypeError("lineno must be an int") 184 if lineno < 0: 185 raise ValueError("lineno must be an int >= 0") 186 _add_filter(action, None, category, None, lineno, append=append) 187 188def _add_filter(*item, append): 189 # Remove possible duplicate filters, so new one will be placed 190 # in correct place. If append=True and duplicate exists, do nothing. 191 if not append: 192 try: 193 filters.remove(item) 194 except ValueError: 195 pass 196 filters.insert(0, item) 197 else: 198 if item not in filters: 199 filters.append(item) 200 _filters_mutated() 201 202def resetwarnings(): 203 """Clear the list of warning filters, so that no filters are active.""" 204 filters[:] = [] 205 _filters_mutated() 206 207class _OptionError(Exception): 208 """Exception used by option processing helpers.""" 209 pass 210 211# Helper to process -W options passed via sys.warnoptions 212def _processoptions(args): 213 for arg in args: 214 try: 215 _setoption(arg) 216 except _OptionError as msg: 217 print("Invalid -W option ignored:", msg, file=sys.stderr) 218 219# Helper for _processoptions() 220def _setoption(arg): 221 parts = arg.split(':') 222 if len(parts) > 5: 223 raise _OptionError("too many fields (max 5): %r" % (arg,)) 224 while len(parts) < 5: 225 parts.append('') 226 action, message, category, module, lineno = [s.strip() 227 for s in parts] 228 action = _getaction(action) 229 category = _getcategory(category) 230 if message or module: 231 import re 232 if message: 233 message = re.escape(message) 234 if module: 235 module = re.escape(module) + r'\Z' 236 if lineno: 237 try: 238 lineno = int(lineno) 239 if lineno < 0: 240 raise ValueError 241 except (ValueError, OverflowError): 242 raise _OptionError("invalid lineno %r" % (lineno,)) from None 243 else: 244 lineno = 0 245 filterwarnings(action, message, category, module, lineno) 246 247# Helper for _setoption() 248def _getaction(action): 249 if not action: 250 return "default" 251 if action == "all": return "always" # Alias 252 for a in ('default', 'always', 'ignore', 'module', 'once', 'error'): 253 if a.startswith(action): 254 return a 255 raise _OptionError("invalid action: %r" % (action,)) 256 257# Helper for _setoption() 258def _getcategory(category): 259 if not category: 260 return Warning 261 if '.' not in category: 262 import builtins as m 263 klass = category 264 else: 265 module, _, klass = category.rpartition('.') 266 try: 267 m = __import__(module, None, None, [klass]) 268 except ImportError: 269 raise _OptionError("invalid module name: %r" % (module,)) from None 270 try: 271 cat = getattr(m, klass) 272 except AttributeError: 273 raise _OptionError("unknown warning category: %r" % (category,)) from None 274 if not issubclass(cat, Warning): 275 raise _OptionError("invalid warning category: %r" % (category,)) 276 return cat 277 278 279def _is_internal_filename(filename): 280 return 'importlib' in filename and '_bootstrap' in filename 281 282 283def _is_filename_to_skip(filename, skip_file_prefixes): 284 return any(filename.startswith(prefix) for prefix in skip_file_prefixes) 285 286 287def _is_internal_frame(frame): 288 """Signal whether the frame is an internal CPython implementation detail.""" 289 return _is_internal_filename(frame.f_code.co_filename) 290 291 292def _next_external_frame(frame, skip_file_prefixes): 293 """Find the next frame that doesn't involve Python or user internals.""" 294 frame = frame.f_back 295 while frame is not None and ( 296 _is_internal_filename(filename := frame.f_code.co_filename) or 297 _is_filename_to_skip(filename, skip_file_prefixes)): 298 frame = frame.f_back 299 return frame 300 301 302# Code typically replaced by _warnings 303def warn(message, category=None, stacklevel=1, source=None, 304 *, skip_file_prefixes=()): 305 """Issue a warning, or maybe ignore it or raise an exception.""" 306 # Check if message is already a Warning object 307 if isinstance(message, Warning): 308 category = message.__class__ 309 # Check category argument 310 if category is None: 311 category = UserWarning 312 if not (isinstance(category, type) and issubclass(category, Warning)): 313 raise TypeError("category must be a Warning subclass, " 314 "not '{:s}'".format(type(category).__name__)) 315 if not isinstance(skip_file_prefixes, tuple): 316 # The C version demands a tuple for implementation performance. 317 raise TypeError('skip_file_prefixes must be a tuple of strs.') 318 if skip_file_prefixes: 319 stacklevel = max(2, stacklevel) 320 # Get context information 321 try: 322 if stacklevel <= 1 or _is_internal_frame(sys._getframe(1)): 323 # If frame is too small to care or if the warning originated in 324 # internal code, then do not try to hide any frames. 325 frame = sys._getframe(stacklevel) 326 else: 327 frame = sys._getframe(1) 328 # Look for one frame less since the above line starts us off. 329 for x in range(stacklevel-1): 330 frame = _next_external_frame(frame, skip_file_prefixes) 331 if frame is None: 332 raise ValueError 333 except ValueError: 334 globals = sys.__dict__ 335 filename = "<sys>" 336 lineno = 0 337 else: 338 globals = frame.f_globals 339 filename = frame.f_code.co_filename 340 lineno = frame.f_lineno 341 if '__name__' in globals: 342 module = globals['__name__'] 343 else: 344 module = "<string>" 345 registry = globals.setdefault("__warningregistry__", {}) 346 warn_explicit(message, category, filename, lineno, module, registry, 347 globals, source) 348 349def warn_explicit(message, category, filename, lineno, 350 module=None, registry=None, module_globals=None, 351 source=None): 352 lineno = int(lineno) 353 if module is None: 354 module = filename or "<unknown>" 355 if module[-3:].lower() == ".py": 356 module = module[:-3] # XXX What about leading pathname? 357 if registry is None: 358 registry = {} 359 if registry.get('version', 0) != _filters_version: 360 registry.clear() 361 registry['version'] = _filters_version 362 if isinstance(message, Warning): 363 text = str(message) 364 category = message.__class__ 365 else: 366 text = message 367 message = category(message) 368 key = (text, category, lineno) 369 # Quick test for common case 370 if registry.get(key): 371 return 372 # Search the filters 373 for item in filters: 374 action, msg, cat, mod, ln = item 375 if ((msg is None or msg.match(text)) and 376 issubclass(category, cat) and 377 (mod is None or mod.match(module)) and 378 (ln == 0 or lineno == ln)): 379 break 380 else: 381 action = defaultaction 382 # Early exit actions 383 if action == "ignore": 384 return 385 386 # Prime the linecache for formatting, in case the 387 # "file" is actually in a zipfile or something. 388 import linecache 389 linecache.getlines(filename, module_globals) 390 391 if action == "error": 392 raise message 393 # Other actions 394 if action == "once": 395 registry[key] = 1 396 oncekey = (text, category) 397 if onceregistry.get(oncekey): 398 return 399 onceregistry[oncekey] = 1 400 elif action == "always": 401 pass 402 elif action == "module": 403 registry[key] = 1 404 altkey = (text, category, 0) 405 if registry.get(altkey): 406 return 407 registry[altkey] = 1 408 elif action == "default": 409 registry[key] = 1 410 else: 411 # Unrecognized actions are errors 412 raise RuntimeError( 413 "Unrecognized action (%r) in warnings.filters:\n %s" % 414 (action, item)) 415 # Print message and context 416 msg = WarningMessage(message, category, filename, lineno, source) 417 _showwarnmsg(msg) 418 419 420class WarningMessage(object): 421 422 _WARNING_DETAILS = ("message", "category", "filename", "lineno", "file", 423 "line", "source") 424 425 def __init__(self, message, category, filename, lineno, file=None, 426 line=None, source=None): 427 self.message = message 428 self.category = category 429 self.filename = filename 430 self.lineno = lineno 431 self.file = file 432 self.line = line 433 self.source = source 434 self._category_name = category.__name__ if category else None 435 436 def __str__(self): 437 return ("{message : %r, category : %r, filename : %r, lineno : %s, " 438 "line : %r}" % (self.message, self._category_name, 439 self.filename, self.lineno, self.line)) 440 441 442class catch_warnings(object): 443 444 """A context manager that copies and restores the warnings filter upon 445 exiting the context. 446 447 The 'record' argument specifies whether warnings should be captured by a 448 custom implementation of warnings.showwarning() and be appended to a list 449 returned by the context manager. Otherwise None is returned by the context 450 manager. The objects appended to the list are arguments whose attributes 451 mirror the arguments to showwarning(). 452 453 The 'module' argument is to specify an alternative module to the module 454 named 'warnings' and imported under that name. This argument is only useful 455 when testing the warnings module itself. 456 457 If the 'action' argument is not None, the remaining arguments are passed 458 to warnings.simplefilter() as if it were called immediately on entering the 459 context. 460 """ 461 462 def __init__(self, *, record=False, module=None, 463 action=None, category=Warning, lineno=0, append=False): 464 """Specify whether to record warnings and if an alternative module 465 should be used other than sys.modules['warnings']. 466 467 For compatibility with Python 3.0, please consider all arguments to be 468 keyword-only. 469 470 """ 471 self._record = record 472 self._module = sys.modules['warnings'] if module is None else module 473 self._entered = False 474 if action is None: 475 self._filter = None 476 else: 477 self._filter = (action, category, lineno, append) 478 479 def __repr__(self): 480 args = [] 481 if self._record: 482 args.append("record=True") 483 if self._module is not sys.modules['warnings']: 484 args.append("module=%r" % self._module) 485 name = type(self).__name__ 486 return "%s(%s)" % (name, ", ".join(args)) 487 488 def __enter__(self): 489 if self._entered: 490 raise RuntimeError("Cannot enter %r twice" % self) 491 self._entered = True 492 self._filters = self._module.filters 493 self._module.filters = self._filters[:] 494 self._module._filters_mutated() 495 self._showwarning = self._module.showwarning 496 self._showwarnmsg_impl = self._module._showwarnmsg_impl 497 if self._filter is not None: 498 simplefilter(*self._filter) 499 if self._record: 500 log = [] 501 self._module._showwarnmsg_impl = log.append 502 # Reset showwarning() to the default implementation to make sure 503 # that _showwarnmsg() calls _showwarnmsg_impl() 504 self._module.showwarning = self._module._showwarning_orig 505 return log 506 else: 507 return None 508 509 def __exit__(self, *exc_info): 510 if not self._entered: 511 raise RuntimeError("Cannot exit %r without entering first" % self) 512 self._module.filters = self._filters 513 self._module._filters_mutated() 514 self._module.showwarning = self._showwarning 515 self._module._showwarnmsg_impl = self._showwarnmsg_impl 516 517 518class deprecated: 519 """Indicate that a class, function or overload is deprecated. 520 521 When this decorator is applied to an object, the type checker 522 will generate a diagnostic on usage of the deprecated object. 523 524 Usage: 525 526 @deprecated("Use B instead") 527 class A: 528 pass 529 530 @deprecated("Use g instead") 531 def f(): 532 pass 533 534 @overload 535 @deprecated("int support is deprecated") 536 def g(x: int) -> int: ... 537 @overload 538 def g(x: str) -> int: ... 539 540 The warning specified by *category* will be emitted at runtime 541 on use of deprecated objects. For functions, that happens on calls; 542 for classes, on instantiation and on creation of subclasses. 543 If the *category* is ``None``, no warning is emitted at runtime. 544 The *stacklevel* determines where the 545 warning is emitted. If it is ``1`` (the default), the warning 546 is emitted at the direct caller of the deprecated object; if it 547 is higher, it is emitted further up the stack. 548 Static type checker behavior is not affected by the *category* 549 and *stacklevel* arguments. 550 551 The deprecation message passed to the decorator is saved in the 552 ``__deprecated__`` attribute on the decorated object. 553 If applied to an overload, the decorator 554 must be after the ``@overload`` decorator for the attribute to 555 exist on the overload as returned by ``get_overloads()``. 556 557 See PEP 702 for details. 558 559 """ 560 def __init__( 561 self, 562 message: str, 563 /, 564 *, 565 category: type[Warning] | None = DeprecationWarning, 566 stacklevel: int = 1, 567 ) -> None: 568 if not isinstance(message, str): 569 raise TypeError( 570 f"Expected an object of type str for 'message', not {type(message).__name__!r}" 571 ) 572 self.message = message 573 self.category = category 574 self.stacklevel = stacklevel 575 576 def __call__(self, arg, /): 577 # Make sure the inner functions created below don't 578 # retain a reference to self. 579 msg = self.message 580 category = self.category 581 stacklevel = self.stacklevel 582 if category is None: 583 arg.__deprecated__ = msg 584 return arg 585 elif isinstance(arg, type): 586 import functools 587 from types import MethodType 588 589 original_new = arg.__new__ 590 591 @functools.wraps(original_new) 592 def __new__(cls, *args, **kwargs): 593 if cls is arg: 594 warn(msg, category=category, stacklevel=stacklevel + 1) 595 if original_new is not object.__new__: 596 return original_new(cls, *args, **kwargs) 597 # Mirrors a similar check in object.__new__. 598 elif cls.__init__ is object.__init__ and (args or kwargs): 599 raise TypeError(f"{cls.__name__}() takes no arguments") 600 else: 601 return original_new(cls) 602 603 arg.__new__ = staticmethod(__new__) 604 605 original_init_subclass = arg.__init_subclass__ 606 # We need slightly different behavior if __init_subclass__ 607 # is a bound method (likely if it was implemented in Python) 608 if isinstance(original_init_subclass, MethodType): 609 original_init_subclass = original_init_subclass.__func__ 610 611 @functools.wraps(original_init_subclass) 612 def __init_subclass__(*args, **kwargs): 613 warn(msg, category=category, stacklevel=stacklevel + 1) 614 return original_init_subclass(*args, **kwargs) 615 616 arg.__init_subclass__ = classmethod(__init_subclass__) 617 # Or otherwise, which likely means it's a builtin such as 618 # object's implementation of __init_subclass__. 619 else: 620 @functools.wraps(original_init_subclass) 621 def __init_subclass__(*args, **kwargs): 622 warn(msg, category=category, stacklevel=stacklevel + 1) 623 return original_init_subclass(*args, **kwargs) 624 625 arg.__init_subclass__ = __init_subclass__ 626 627 arg.__deprecated__ = __new__.__deprecated__ = msg 628 __init_subclass__.__deprecated__ = msg 629 return arg 630 elif callable(arg): 631 import functools 632 import inspect 633 634 @functools.wraps(arg) 635 def wrapper(*args, **kwargs): 636 warn(msg, category=category, stacklevel=stacklevel + 1) 637 return arg(*args, **kwargs) 638 639 if inspect.iscoroutinefunction(arg): 640 wrapper = inspect.markcoroutinefunction(wrapper) 641 642 arg.__deprecated__ = wrapper.__deprecated__ = msg 643 return wrapper 644 else: 645 raise TypeError( 646 "@deprecated decorator with non-None category must be applied to " 647 f"a class or callable, not {arg!r}" 648 ) 649 650 651_DEPRECATED_MSG = "{name!r} is deprecated and slated for removal in Python {remove}" 652 653def _deprecated(name, message=_DEPRECATED_MSG, *, remove, _version=sys.version_info): 654 """Warn that *name* is deprecated or should be removed. 655 656 RuntimeError is raised if *remove* specifies a major/minor tuple older than 657 the current Python version or the same version but past the alpha. 658 659 The *message* argument is formatted with *name* and *remove* as a Python 660 version tuple (e.g. (3, 11)). 661 662 """ 663 remove_formatted = f"{remove[0]}.{remove[1]}" 664 if (_version[:2] > remove) or (_version[:2] == remove and _version[3] != "alpha"): 665 msg = f"{name!r} was slated for removal after Python {remove_formatted} alpha" 666 raise RuntimeError(msg) 667 else: 668 msg = message.format(name=name, remove=remove_formatted) 669 warn(msg, DeprecationWarning, stacklevel=3) 670 671 672# Private utility function called by _PyErr_WarnUnawaitedCoroutine 673def _warn_unawaited_coroutine(coro): 674 msg_lines = [ 675 f"coroutine '{coro.__qualname__}' was never awaited\n" 676 ] 677 if coro.cr_origin is not None: 678 import linecache, traceback 679 def extract(): 680 for filename, lineno, funcname in reversed(coro.cr_origin): 681 line = linecache.getline(filename, lineno) 682 yield (filename, lineno, funcname, line) 683 msg_lines.append("Coroutine created at (most recent call last)\n") 684 msg_lines += traceback.format_list(list(extract())) 685 msg = "".join(msg_lines).rstrip("\n") 686 # Passing source= here means that if the user happens to have tracemalloc 687 # enabled and tracking where the coroutine was created, the warning will 688 # contain that traceback. This does mean that if they have *both* 689 # coroutine origin tracking *and* tracemalloc enabled, they'll get two 690 # partially-redundant tracebacks. If we wanted to be clever we could 691 # probably detect this case and avoid it, but for now we don't bother. 692 warn(msg, category=RuntimeWarning, stacklevel=2, source=coro) 693 694 695# filters contains a sequence of filter 5-tuples 696# The components of the 5-tuple are: 697# - an action: error, ignore, always, default, module, or once 698# - a compiled regex that must match the warning message 699# - a class representing the warning category 700# - a compiled regex that must match the module that is being warned 701# - a line number for the line being warning, or 0 to mean any line 702# If either if the compiled regexs are None, match anything. 703try: 704 from _warnings import (filters, _defaultaction, _onceregistry, 705 warn, warn_explicit, _filters_mutated) 706 defaultaction = _defaultaction 707 onceregistry = _onceregistry 708 _warnings_defaults = True 709except ImportError: 710 filters = [] 711 defaultaction = "default" 712 onceregistry = {} 713 714 _filters_version = 1 715 716 def _filters_mutated(): 717 global _filters_version 718 _filters_version += 1 719 720 _warnings_defaults = False 721 722 723# Module initialization 724_processoptions(sys.warnoptions) 725if not _warnings_defaults: 726 # Several warning categories are ignored by default in regular builds 727 if not hasattr(sys, 'gettotalrefcount'): 728 filterwarnings("default", category=DeprecationWarning, 729 module="__main__", append=1) 730 simplefilter("ignore", category=DeprecationWarning, append=1) 731 simplefilter("ignore", category=PendingDeprecationWarning, append=1) 732 simplefilter("ignore", category=ImportWarning, append=1) 733 simplefilter("ignore", category=ResourceWarning, append=1) 734 735del _warnings_defaults 736