154 lines
3.7 KiB
Python
154 lines
3.7 KiB
Python
"""implement of stack.py"""
|
|
|
|
import inspect
|
|
import os
|
|
import os.path
|
|
import sys
|
|
import threading as _threading
|
|
from typing import Optional
|
|
|
|
from .console import pc, WHITE, RED, YELLOW
|
|
from .logger import Logger
|
|
|
|
_RUNTIME_COMPILE = False
|
|
|
|
if _RUNTIME_COMPILE:
|
|
raise RuntimeError()
|
|
|
|
try:
|
|
_TRACE_LEVEL = int(os.getenv('TRACE_LEVEL', 1))
|
|
'''
|
|
0 : print nothing
|
|
1 : print last stack
|
|
2 : print all stack
|
|
'''
|
|
except ValueError:
|
|
_TRACE_LEVEL = 1
|
|
|
|
try:
|
|
_TRACE_CONTEXT = int(os.getenv('TRACE_CONTEXT', 5))
|
|
except ValueError:
|
|
_TRACE_CONTEXT = 5
|
|
|
|
_LOCK = _threading.RLock()
|
|
|
|
|
|
def print_exception(logger: Optional[Logger] = None,
|
|
exc_info=None,
|
|
reverse_order=True):
|
|
if exc_info is None:
|
|
e_class, e_value, e_stack = sys.exc_info()
|
|
else:
|
|
e_class, e_value, e_stack = exc_info
|
|
|
|
if logger is None:
|
|
print(pc(e_class.__name__, YELLOW), ':', pc(e_value, WHITE))
|
|
else:
|
|
logger.log_error(pc(e_class.__name__, YELLOW), ':', pc(e_value, WHITE))
|
|
|
|
if _TRACE_LEVEL == 0:
|
|
return
|
|
|
|
trace = inspect.trace(context=_TRACE_CONTEXT)
|
|
|
|
while e_value.__cause__ is not None:
|
|
e_value = e_value.__cause__
|
|
e_stack = e_value.__traceback__
|
|
trace.extend(inspect.getinnerframes(e_stack, context=_TRACE_CONTEXT))
|
|
|
|
if _TRACE_LEVEL == 1:
|
|
_print_stack_frame(trace[0])
|
|
|
|
else:
|
|
frame = inspect.stack(context=_TRACE_CONTEXT)[2:]
|
|
|
|
if not reverse_order:
|
|
trace = trace[::-1]
|
|
trace.extend(frame)
|
|
|
|
else:
|
|
frame = frame[::-1]
|
|
frame.extend(trace)
|
|
trace = frame
|
|
|
|
for f in trace:
|
|
_print_stack_frame(f)
|
|
|
|
|
|
def print_stack(reverse_order=True):
|
|
if _TRACE_LEVEL == 0:
|
|
return
|
|
|
|
frame = inspect.stack(context=_TRACE_CONTEXT)[1:]
|
|
|
|
if _TRACE_LEVEL == 1:
|
|
_print_stack_frame(frame[0])
|
|
|
|
else:
|
|
if reverse_order:
|
|
frame = reversed(frame)
|
|
|
|
for f in frame:
|
|
_print_stack_frame(f)
|
|
|
|
|
|
def _print_stack_frame(f: inspect.FrameInfo):
|
|
file_path = f.filename
|
|
file_dir = os.path.dirname(file_path)
|
|
file_name = os.path.basename(file_path)
|
|
|
|
cls_name = _get_class_name(file_path, f.lineno)
|
|
|
|
with _LOCK:
|
|
|
|
if cls_name is None:
|
|
print(' ',
|
|
file_dir + ' ' + pc(file_name, WHITE) + ':' + str(f.lineno),
|
|
pc(f.function, RED))
|
|
else:
|
|
print(' ',
|
|
file_dir + ' ' + pc(file_name, WHITE) + ':' + str(f.lineno),
|
|
pc(cls_name, YELLOW) + '.' + pc(f.function, RED))
|
|
|
|
if f.index is not None:
|
|
offset = -f.index
|
|
|
|
for i, content in enumerate(f.code_context):
|
|
line_number = f.lineno + offset + i
|
|
|
|
if line_number == f.lineno:
|
|
print(pc(' %4d %s' % (line_number, content.rstrip()), WHITE))
|
|
else:
|
|
print(' %4d' % line_number, content.rstrip())
|
|
|
|
print()
|
|
|
|
|
|
def _get_class_name(filepath: str, line_number: int) -> Optional[str]:
|
|
class_name = None
|
|
|
|
try:
|
|
with open(filepath) as f:
|
|
for ln, line in enumerate(f): # type: (int, str)
|
|
if ln + 1 == line_number:
|
|
break
|
|
|
|
line = line.strip()
|
|
|
|
if line.startswith('class '):
|
|
i = line.index(' ')
|
|
|
|
try:
|
|
j = line.index('(')
|
|
except ValueError:
|
|
try:
|
|
j = line.index(':')
|
|
except ValueError:
|
|
j = len(line)
|
|
|
|
class_name = line[i + 1:j]
|
|
except FileNotFoundError:
|
|
pass
|
|
|
|
return class_name
|