From 1989b272eb6ad0938efb2599ce626132a350e601 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Tue, 28 Jul 2020 08:17:50 +0100 Subject: [PATCH] Use `inspect` to get frame information for qualname Also includes some minor tweaks to make PyCharm happier: * @classmethod caller_class_and_qualname(). * No need to () when "returning a tuple". --- EDMCLogging.py | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/EDMCLogging.py b/EDMCLogging.py index 0b81f1d2..00cf504d 100644 --- a/EDMCLogging.py +++ b/EDMCLogging.py @@ -5,11 +5,14 @@ members on the logging.LogRecord instance for use in logging.Formatter() strings. """ -import sys +from sys import _getframe as getframe +import inspect import logging -from typing import Tuple +from typing import TYPE_CHECKING, Tuple +# if TYPE_CHECKING: + # TODO: Tests: # # 1. Call from bare function in file. @@ -115,7 +118,8 @@ class EDMCContextFilter(logging.Filter): return True - def caller_class_and_qualname(self) -> Tuple[str, str]: + @classmethod + def caller_class_and_qualname(cls) -> Tuple[str, str]: """ Figure out our caller's class name(s) and qualname @@ -126,7 +130,7 @@ class EDMCContextFilter(logging.Filter): # Go up through stack frames until we find the first with a # type(f_locals.self) of logging.Logger. This should be the start # of the frames internal to logging. - frame = sys._getframe(0) + frame: 'frameobject' = getframe(0) while frame: if isinstance(frame.f_locals.get('self'), logging.Logger): frame = frame.f_back # Want to start on the next frame below @@ -143,20 +147,21 @@ class EDMCContextFilter(logging.Filter): caller_qualname = caller_class_names = '' if frame: - if frame.f_locals and 'self' in frame.f_locals: + # + frame_info = inspect.getframeinfo(frame) + args, _, _, value_dict = inspect.getargvalues(frame) + if len(args) and args[0] == 'self': + frame_class = value_dict['self'] # Find __qualname__ of the caller - # Paranoia checks - if frame.f_code and frame.f_code.co_name: - fn = getattr(frame.f_locals['self'], frame.f_code.co_name) + fn = getattr(frame_class, frame_info.function) - if fn and fn.__qualname__: - caller_qualname = fn.__qualname__ + if fn and fn.__qualname__: + caller_qualname = fn.__qualname__ # Find containing class name(s) of caller, if any - frame_class = frame.f_locals['self'].__class__ - # Paranoia checks if frame_class and frame_class.__qualname__: caller_class_names = frame_class.__qualname__ + # If the frame caller is a bare function then there's no 'self' elif frame.f_code.co_name and frame.f_code.co_name in frame.f_globals: fn = frame.f_globals[frame.f_code.co_name] @@ -174,6 +179,9 @@ class EDMCContextFilter(logging.Filter): # In case the condition above tests a tuple of values caller_class_names = f'<{caller_class_names}>' + # https://docs.python.org/3.7/library/inspect.html#the-interpreter-stack + del frame + if caller_qualname == '': print('ALERT! Something went wrong with finding caller qualname for logging!') caller_qualname = '' @@ -182,4 +190,4 @@ class EDMCContextFilter(logging.Filter): print('ALERT! Something went wrong with finding caller class name(s) for logging!') caller_class_names = '' - return (caller_class_names, caller_qualname) + return caller_class_names, caller_qualname