函数的源代码可以通过inspect.getsourcelines(func)
函数接收。有没有办法为上下文管理器做同样的事情?
with test():
print('123')
# How to get "print('123')" as line here?
import traceback
class ContextManagerContent(object):
def __enter__(self):
return
def __exit__(self, _type, value, _traceback):
stack = traceback.extract_stack()
f, last_line = self._get_origin_info(stack)
with open(f) as fin:
lines = list(fin)
search = 'with {cls_name}'.format(cls_name=self.__class__.__name__)
for i, x in enumerate(lines[:last_line + 1][::-1]):
if search in x:
first_line = len(lines) - i
break
selected_lines = lines[first_line:last_line + 1]
print ''.join(selected_lines)
def _get_origin_info(self, stack):
origin = None
for i, x in enumerate(stack[::-1]):
if x[2] == '__exit__':
origin = stack[::-1][i + 1]
break
return origin[0], origin[1] - 1
with ContextManagerContent():
print '123'
print '456'
print '789'
如果将其保存在
.py
文件中并运行它,您可以看到打印数字 123、456 和 789,之后您可以看到上下文管理器的块。请注意,我没有处理可能的异常或输出格式,并且某些部分可以改进,但我认为这是一个很好的起点。
from inspect import currentframe, getframeinfo
from contextlib import contextmanager
@contextmanager
def debug(string):
# before block
cf = currentframe()
first_line = cf.f_back.f_back.f_lineno
filename = getframeinfo(cf.f_back.f_back).filename
yield
# after block
cf = currentframe()
last_line = cf.f_back.f_back.f_lineno
with open(filename) as f:
lines = f.readlines()[first_line:last_line]
print(string + '\n' + ''.join(lines).rstrip())
if __name__ == '__main__':
with debug("show this code in stdout:"):
a = 1
b = 2
a, b = b, a
import inspect, linecache
from pathlib import Path
import tempfile
class ContextCodeCapture():
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
cf = inspect.currentframe().f_back
filename = cf.f_code.co_filename
line = cf.f_lineno
lines = []
indent = 0
i = 1
while True:
next_line = linecache.getline(filename, line + i)
next_line_indent = len(next_line) - len(next_line.lstrip())
if indent == 0:
indent = next_line_indent
elif (next_line_indent < indent and len(next_line.strip()) > 0) or next_line == "":
break
if next_line == "\n": # preserve newlines
lines.append(next_line)
else:
lines.append(next_line[indent:])
i += 1
content = "".join(lines)
print(content)
if __name__ == "__main__":
with ContextCodeCapture():
def say_hello(name):
print(f"Hello {name}")
foo = 2
if True:
for i in range(100):
foo *= i
运行此示例将打印以下输出
def say_hello(name):
print(f"Hello {name}")
foo = 2
if True:
for i in range(100):
foo *= i