|
15 | 15 |
|
16 | 16 | from __future__ import absolute_import
|
17 | 17 |
|
| 18 | +import io |
18 | 19 | import os
|
19 | 20 | import sys
|
20 | 21 | import logging
|
|
69 | 70 | _srcfile = get_normalized_file_path(__file__)
|
70 | 71 |
|
71 | 72 |
|
72 |
| -def find_caller(*args, **kwargs): |
| 73 | +def find_caller(stack_info=False, stacklevel=1): |
73 | 74 | """
|
74 | 75 | Find the stack frame of the caller so that we can note the source file name, line number and
|
75 | 76 | function name.
|
76 | 77 |
|
77 | 78 | Note: This is based on logging/__init__.py:findCaller and modified so it takes into account
|
78 |
| - this file - https://hg.python.org/cpython/file/2.7/Lib/logging/__init__.py#l1233 |
| 79 | + this file: |
| 80 | + https://github.com/python/cpython/blob/2.7/Lib/logging/__init__.py#L1240-L1259 |
| 81 | +
|
| 82 | + The Python 3.x implementation adds in a new argument `stack_info` and `stacklevel` |
| 83 | + and expects a 4-element tuple to be returned, rather than a 3-element tuple in |
| 84 | + the python 2 implementation. |
| 85 | + We derived our implementation from the Python 3.9 source code here: |
| 86 | + https://github.com/python/cpython/blob/3.9/Lib/logging/__init__.py#L1502-L1536 |
| 87 | +
|
| 88 | + We've made the appropriate changes so that we're python 2 and python 3 compatible depending |
| 89 | + on what runtine we're working in. |
79 | 90 | """
|
80 |
| - rv = '(unknown file)', 0, '(unknown function)' |
| 91 | + if six.PY2: |
| 92 | + rv = '(unknown file)', 0, '(unknown function)' |
| 93 | + else: |
| 94 | + # python 3, has extra tuple element at the end for stack information |
| 95 | + rv = '(unknown file)', 0, '(unknown function)', None |
81 | 96 |
|
82 | 97 | try:
|
83 |
| - f = logging.currentframe().f_back |
| 98 | + f = logging.currentframe() |
| 99 | + # On some versions of IronPython, currentframe() returns None if |
| 100 | + # IronPython isn't run with -X:Frames. |
| 101 | + if f is not None: |
| 102 | + f = f.f_back |
| 103 | + orig_f = f |
| 104 | + while f and stacklevel > 1: |
| 105 | + f = f.f_back |
| 106 | + stacklevel -= 1 |
| 107 | + if not f: |
| 108 | + f = orig_f |
| 109 | + |
84 | 110 | while hasattr(f, 'f_code'):
|
85 | 111 | co = f.f_code
|
86 | 112 | filename = os.path.normcase(co.co_filename)
|
87 | 113 | if filename in (_srcfile, logging._srcfile): # This line is modified.
|
88 | 114 | f = f.f_back
|
89 | 115 | continue
|
90 |
| - rv = (filename, f.f_lineno, co.co_name) |
| 116 | + |
| 117 | + if six.PY2: |
| 118 | + rv = (filename, f.f_lineno, co.co_name) |
| 119 | + else: |
| 120 | + # python 3, new stack_info processing and extra tuple return value |
| 121 | + sinfo = None |
| 122 | + if stack_info: |
| 123 | + sio = io.StringIO() |
| 124 | + sio.write('Stack (most recent call last):\n') |
| 125 | + traceback.print_stack(f, file=sio) |
| 126 | + sinfo = sio.getvalue() |
| 127 | + if sinfo[-1] == '\n': |
| 128 | + sinfo = sinfo[:-1] |
| 129 | + sio.close() |
| 130 | + rv = (filename, f.f_lineno, co.co_name, sinfo) |
91 | 131 | break
|
92 | 132 | except Exception:
|
93 | 133 | pass
|
|
0 commit comments