summaryrefslogtreecommitdiff
path: root/Lib/warnings.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/warnings.py')
-rw-r--r--Lib/warnings.py73
1 files changed, 54 insertions, 19 deletions
diff --git a/Lib/warnings.py b/Lib/warnings.py
index 70d087e6d9..c6631fcbf4 100644
--- a/Lib/warnings.py
+++ b/Lib/warnings.py
@@ -21,9 +21,15 @@ def showwarning(message, category, filename, lineno, file=None, line=None):
def formatwarning(message, category, filename, lineno, line=None):
"""Function to format a warning the standard way."""
- import linecache
s = "%s:%s: %s: %s\n" % (filename, lineno, category.__name__, message)
- line = linecache.getline(filename, lineno) if line is None else line
+ if line is None:
+ try:
+ import linecache
+ line = linecache.getline(filename, lineno)
+ except Exception:
+ # When a warning is logged during Python shutdown, linecache
+ # and the import machinery don't work anymore
+ line = None
if line:
line = line.strip()
s += " %s\n" % line
@@ -50,13 +56,8 @@ def filterwarnings(action, message="", category=Warning, module="", lineno=0,
assert isinstance(module, str), "module must be a string"
assert isinstance(lineno, int) and lineno >= 0, \
"lineno must be an int >= 0"
- item = (action, re.compile(message, re.I), category,
- re.compile(module), lineno)
- if append:
- filters.append(item)
- else:
- filters.insert(0, item)
- _filters_mutated()
+ _add_filter(action, re.compile(message, re.I), category,
+ re.compile(module), lineno, append=append)
def simplefilter(action, category=Warning, lineno=0, append=False):
"""Insert a simple entry into the list of warnings filters (at the front).
@@ -72,11 +73,20 @@ def simplefilter(action, category=Warning, lineno=0, append=False):
"once"), "invalid action: %r" % (action,)
assert isinstance(lineno, int) and lineno >= 0, \
"lineno must be an int >= 0"
- item = (action, None, category, None, lineno)
- if append:
- filters.append(item)
- else:
+ _add_filter(action, None, category, None, lineno, append=append)
+
+def _add_filter(*item, append):
+ # Remove possible duplicate filters, so new one will be placed
+ # in correct place. If append=True and duplicate exists, do nothing.
+ if not append:
+ try:
+ filters.remove(item)
+ except ValueError:
+ pass
filters.insert(0, item)
+ else:
+ if item not in filters:
+ filters.append(item)
_filters_mutated()
def resetwarnings():
@@ -160,6 +170,20 @@ def _getcategory(category):
return cat
+def _is_internal_frame(frame):
+ """Signal whether the frame is an internal CPython implementation detail."""
+ filename = frame.f_code.co_filename
+ return 'importlib' in filename and '_bootstrap' in filename
+
+
+def _next_external_frame(frame):
+ """Find the next frame that doesn't involve CPython internals."""
+ frame = frame.f_back
+ while frame is not None and _is_internal_frame(frame):
+ frame = frame.f_back
+ return frame
+
+
# Code typically replaced by _warnings
def warn(message, category=None, stacklevel=1):
"""Issue a warning, or maybe ignore it or raise an exception."""
@@ -169,16 +193,28 @@ def warn(message, category=None, stacklevel=1):
# Check category argument
if category is None:
category = UserWarning
- assert issubclass(category, Warning)
+ if not (isinstance(category, type) and issubclass(category, Warning)):
+ raise TypeError("category must be a Warning subclass, "
+ "not '{:s}'".format(type(category).__name__))
# Get context information
try:
- caller = sys._getframe(stacklevel)
+ if stacklevel <= 1 or _is_internal_frame(sys._getframe(1)):
+ # If frame is too small to care or if the warning originated in
+ # internal code, then do not try to hide any frames.
+ frame = sys._getframe(stacklevel)
+ else:
+ frame = sys._getframe(1)
+ # Look for one frame less since the above line starts us off.
+ for x in range(stacklevel-1):
+ frame = _next_external_frame(frame)
+ if frame is None:
+ raise ValueError
except ValueError:
globals = sys.__dict__
lineno = 1
else:
- globals = caller.f_globals
- lineno = caller.f_lineno
+ globals = frame.f_globals
+ lineno = frame.f_lineno
if '__name__' in globals:
module = globals['__name__']
else:
@@ -186,7 +222,7 @@ def warn(message, category=None, stacklevel=1):
filename = globals.get('__file__')
if filename:
fnl = filename.lower()
- if fnl.endswith((".pyc", ".pyo")):
+ if fnl.endswith(".pyc"):
filename = filename[:-1]
else:
if module == "__main__":
@@ -372,7 +408,6 @@ try:
defaultaction = _defaultaction
onceregistry = _onceregistry
_warnings_defaults = True
-
except ImportError:
filters = []
defaultaction = "default"