• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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