summaryrefslogtreecommitdiffstats
path: root/Lib/logging
diff options
context:
space:
mode:
authorJouke Witteveen <j.witteveen@gmail.com>2022-03-27 13:49:28 (GMT)
committerGitHub <noreply@github.com>2022-03-27 13:49:28 (GMT)
commit5ca6d7469be53960843df39bb900e9c3359f127f (patch)
tree0a0b96d2c1f2e68f993941833fe87da5f5f2274a /Lib/logging
parent5fd8c574e016aec85725ddc5ced8742267b0e1b3 (diff)
downloadcpython-5ca6d7469be53960843df39bb900e9c3359f127f.zip
cpython-5ca6d7469be53960843df39bb900e9c3359f127f.tar.gz
cpython-5ca6d7469be53960843df39bb900e9c3359f127f.tar.bz2
bpo-45171: Fix stacklevel handling in logging. (GH-28287)
Diffstat (limited to 'Lib/logging')
-rw-r--r--Lib/logging/__init__.py67
1 files changed, 35 insertions, 32 deletions
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
index 160b1af..e8054fb 100644
--- a/Lib/logging/__init__.py
+++ b/Lib/logging/__init__.py
@@ -159,8 +159,8 @@ def addLevelName(level, levelName):
finally:
_releaseLock()
-if hasattr(sys, '_getframe'):
- currentframe = lambda: sys._getframe(3)
+if hasattr(sys, "_getframe"):
+ currentframe = lambda: sys._getframe(1)
else: #pragma: no cover
def currentframe():
"""Return the frame object for the caller's stack frame."""
@@ -184,13 +184,18 @@ else: #pragma: no cover
_srcfile = os.path.normcase(addLevelName.__code__.co_filename)
# _srcfile is only used in conjunction with sys._getframe().
-# To provide compatibility with older versions of Python, set _srcfile
-# to None if _getframe() is not available; this value will prevent
-# findCaller() from being called. You can also do this if you want to avoid
-# the overhead of fetching caller information, even when _getframe() is
-# available.
-#if not hasattr(sys, '_getframe'):
-# _srcfile = None
+# Setting _srcfile to None will prevent findCaller() from being called. This
+# way, you can avoid the overhead of fetching caller information.
+
+# The following is based on warnings._is_internal_frame. It makes sure that
+# frames of the import mechanism are skipped when logging at module level and
+# using a stacklevel value greater than one.
+def _is_internal_frame(frame):
+ """Signal whether the frame is a CPython or logging module internal."""
+ filename = os.path.normcase(frame.f_code.co_filename)
+ return filename == _srcfile or (
+ "importlib" in filename and "_bootstrap" in filename
+ )
def _checkLevel(level):
@@ -1558,33 +1563,31 @@ class Logger(Filterer):
f = currentframe()
#On some versions of IronPython, currentframe() returns None if
#IronPython isn't run with -X:Frames.
- if f is not None:
- f = f.f_back
- orig_f = f
- while f and stacklevel > 1:
- f = f.f_back
- stacklevel -= 1
- if not f:
- f = orig_f
- rv = "(unknown file)", 0, "(unknown function)", None
- while hasattr(f, "f_code"):
- co = f.f_code
- filename = os.path.normcase(co.co_filename)
- if filename == _srcfile:
- f = f.f_back
- continue
- sinfo = None
- if stack_info:
- sio = io.StringIO()
- sio.write('Stack (most recent call last):\n')
+ if f is None:
+ return "(unknown file)", 0, "(unknown function)", None
+ while stacklevel > 0:
+ next_f = f.f_back
+ if next_f is None:
+ ##TODO: We've got options here
+ ## If we want to use the last (deepest) frame:
+ break
+ ## If we want to mimic the warnings module:
+ #return ("sys", 1, "(unknown function)", None)
+ ## If we want to be pedantic:
+ #raise ValueError("call stack is not deep enough")
+ f = next_f
+ if not _is_internal_frame(f):
+ stacklevel -= 1
+ co = f.f_code
+ sinfo = None
+ if stack_info:
+ with io.StringIO() as sio:
+ sio.write("Stack (most recent call last):\n")
traceback.print_stack(f, file=sio)
sinfo = sio.getvalue()
if sinfo[-1] == '\n':
sinfo = sinfo[:-1]
- sio.close()
- rv = (co.co_filename, f.f_lineno, co.co_name, sinfo)
- break
- return rv
+ return co.co_filename, f.f_lineno, co.co_name, sinfo
def makeRecord(self, name, level, fn, lno, msg, args, exc_info,
func=None, extra=None, sinfo=None):