@@ -159,8 +159,8 @@ def addLevelName(level, levelName):
159
159
finally :
160
160
_releaseLock ()
161
161
162
- if hasattr (sys , ' _getframe' ):
163
- currentframe = lambda : sys ._getframe (3 )
162
+ if hasattr (sys , " _getframe" ):
163
+ currentframe = lambda : sys ._getframe (1 )
164
164
else : #pragma: no cover
165
165
def currentframe ():
166
166
"""Return the frame object for the caller's stack frame."""
@@ -184,13 +184,18 @@ def currentframe():
184
184
_srcfile = os .path .normcase (addLevelName .__code__ .co_filename )
185
185
186
186
# _srcfile is only used in conjunction with sys._getframe().
187
- # To provide compatibility with older versions of Python, set _srcfile
188
- # to None if _getframe() is not available; this value will prevent
189
- # findCaller() from being called. You can also do this if you want to avoid
190
- # the overhead of fetching caller information, even when _getframe() is
191
- # available.
192
- #if not hasattr(sys, '_getframe'):
193
- # _srcfile = None
187
+ # Setting _srcfile to None will prevent findCaller() from being called. This
188
+ # way, you can avoid the overhead of fetching caller information.
189
+
190
+ # The following is based on warnings._is_internal_frame. It makes sure that
191
+ # frames of the import mechanism are skipped when logging at module level and
192
+ # using a stacklevel value greater than one.
193
+ def _is_internal_frame (frame ):
194
+ """Signal whether the frame is a CPython or logging module internal."""
195
+ filename = os .path .normcase (frame .f_code .co_filename )
196
+ return filename == _srcfile or (
197
+ "importlib" in filename and "_bootstrap" in filename
198
+ )
194
199
195
200
196
201
def _checkLevel (level ):
@@ -1558,33 +1563,31 @@ def findCaller(self, stack_info=False, stacklevel=1):
1558
1563
f = currentframe ()
1559
1564
#On some versions of IronPython, currentframe() returns None if
1560
1565
#IronPython isn't run with -X:Frames.
1561
- if f is not None :
1562
- f = f .f_back
1563
- orig_f = f
1564
- while f and stacklevel > 1 :
1565
- f = f .f_back
1566
- stacklevel -= 1
1567
- if not f :
1568
- f = orig_f
1569
- rv = "(unknown file)" , 0 , "(unknown function)" , None
1570
- while hasattr (f , "f_code" ):
1571
- co = f .f_code
1572
- filename = os .path .normcase (co .co_filename )
1573
- if filename == _srcfile :
1574
- f = f .f_back
1575
- continue
1576
- sinfo = None
1577
- if stack_info :
1578
- sio = io .StringIO ()
1579
- sio .write ('Stack (most recent call last):\n ' )
1566
+ if f is None :
1567
+ return "(unknown file)" , 0 , "(unknown function)" , None
1568
+ while stacklevel > 0 :
1569
+ next_f = f .f_back
1570
+ if next_f is None :
1571
+ ##TODO: We've got options here
1572
+ ## If we want to use the last (deepest) frame:
1573
+ break
1574
+ ## If we want to mimic the warnings module:
1575
+ #return ("sys", 1, "(unknown function)", None)
1576
+ ## If we want to be pedantic:
1577
+ #raise ValueError("call stack is not deep enough")
1578
+ f = next_f
1579
+ if not _is_internal_frame (f ):
1580
+ stacklevel -= 1
1581
+ co = f .f_code
1582
+ sinfo = None
1583
+ if stack_info :
1584
+ with io .StringIO () as sio :
1585
+ sio .write ("Stack (most recent call last):\n " )
1580
1586
traceback .print_stack (f , file = sio )
1581
1587
sinfo = sio .getvalue ()
1582
1588
if sinfo [- 1 ] == '\n ' :
1583
1589
sinfo = sinfo [:- 1 ]
1584
- sio .close ()
1585
- rv = (co .co_filename , f .f_lineno , co .co_name , sinfo )
1586
- break
1587
- return rv
1590
+ return co .co_filename , f .f_lineno , co .co_name , sinfo
1588
1591
1589
1592
def makeRecord (self , name , level , fn , lno , msg , args , exc_info ,
1590
1593
func = None , extra = None , sinfo = None ):
0 commit comments