1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-06-03 00:51:11 +03:00

Fixed error when logging out of a class var

This also adds an insane amount of paranoia around getting SOMETHING
dumped when an exception happens.

Fixes #1044
This commit is contained in:
A_D 2021-05-10 15:24:26 +02:00
parent 71e3f8d2b3
commit 92e580e0c7
No known key found for this signature in database
GPG Key ID: 4BE9EB7DF45076C4

View File

@ -41,10 +41,12 @@ import logging.handlers
import os import os
import pathlib import pathlib
import tempfile import tempfile
from contextlib import suppress
# So that any warning about accessing a protected member is only in one place. # So that any warning about accessing a protected member is only in one place.
from sys import _getframe as getframe from sys import _getframe as getframe
from threading import get_native_id as thread_native_id from threading import get_native_id as thread_native_id
from typing import TYPE_CHECKING, Tuple, cast from typing import TYPE_CHECKING, Tuple, cast
from traceback import print_exc
from config import appcmdname, appname, config from config import appcmdname, appname, config
@ -85,7 +87,10 @@ logging.Logger.trace = lambda self, message, *args, **kwargs: self._log( # type
) )
if TYPE_CHECKING: if TYPE_CHECKING:
from types import FrameType
# Fake type that we can use here to tell type checkers that trace exists # Fake type that we can use here to tell type checkers that trace exists
class LoggerMixin(logging.Logger): class LoggerMixin(logging.Logger):
"""LoggerMixin is a fake class that tells type checkers that trace exists on a given type.""" """LoggerMixin is a fake class that tells type checkers that trace exists on a given type."""
@ -318,65 +323,83 @@ class EDMCContextFilter(logging.Filter):
# We've given up, so just return '??' to signal we couldn't get the info # We've given up, so just return '??' to signal we couldn't get the info
return '??', '??', module_name return '??', '??', module_name
try:
args, _, _, value_dict = inspect.getargvalues(frame)
if len(args) and args[0] in ('self', 'cls'):
frame_class: 'object' = value_dict[args[0]]
args, _, _, value_dict = inspect.getargvalues(frame) if frame_class:
if len(args) and args[0] in ('self', 'cls'): # See https://en.wikipedia.org/wiki/Name_mangling#Python for how name mangling works.
frame_class: 'object' = value_dict[args[0]] # For more detail, see _Py_Mangle in CPython's Python/compile.c.
name = frame_info.function
class_name = frame_class.__class__.__name__.lstrip("_")
if name.startswith("__") and not name.endswith("__") and class_name:
name = f'_{class_name}{frame_info.function}'
if frame_class: # Find __qualname__ of the caller
# See https://en.wikipedia.org/wiki/Name_mangling#Python for how name mangling works. fn = inspect.getattr_static(frame_class, name, None)
# For more detail, see _Py_Mangle in CPython's Python/compile.c. if fn is None:
name = frame_info.function # For some reason getattr_static cant grab this. Try and grab it with getattr, bail out
class_name = frame_class.__class__.__name__.lstrip("_") # if we get a RecursionError indicating a property
if name.startswith("__") and not name.endswith("__") and class_name: try:
name = f'_{class_name}{frame_info.function}' fn = getattr(frame_class, name, None)
except RecursionError:
print(
"EDMCLogging:EDMCContextFilter:caller_attributes():"
"Failed to get attribute for function info. Bailing out"
)
# class_name is better than nothing for __qualname__
return class_name, class_name, module_name
# Find __qualname__ of the caller if fn is not None:
fn = inspect.getattr_static(frame_class, name, None) if isinstance(fn, property):
if fn is None: class_name = str(frame_class)
# For some reason getattr_static cant grab this. Try and grab it with getattr, bail out # If somehow you make your __class__ or __class__.__qualname__ recursive,
# if we get a RecursionError indicating a property # I'll be impressed.
try: if hasattr(frame_class, '__class__') and hasattr(frame_class.__class__, "__qualname__"):
fn = getattr(frame_class, name, None) class_name = frame_class.__class__.__qualname__
except RecursionError: caller_qualname = f"{class_name}.{name}(property)"
print(
"EDMCLogging:EDMCContextFilter:caller_attributes():"
"Failed to get attribute for function info. Bailing out"
)
# class_name is better than nothing for __qualname__
return class_name, class_name, module_name
if fn is not None: else:
if isinstance(fn, property): caller_qualname = f"<property {name} on {class_name}>"
class_name = str(frame_class)
# If somehow you make your __class__ or __class__.__qualname__ recursive, I'll be impressed.
if hasattr(frame_class, '__class__') and hasattr(frame_class.__class__, "__qualname__"):
class_name = frame_class.__class__.__qualname__
caller_qualname = f"{class_name}.{name}(property)"
else: elif hasattr(fn, '__qualname__') and fn.__qualname__:
caller_qualname = f"<property {name} on {class_name}>" caller_qualname = fn.__qualname__
elif fn.__qualname__: # Find containing class name(s) of caller, if any
caller_qualname = fn.__qualname__ if (
frame_class.__class__ and hasattr(frame_class.__class__, '__qualname__')
and frame_class.__class__.__qualname__
):
caller_class_names = frame_class.__class__.__qualname__
# Find containing class name(s) of caller, if any # It's a call from the top level module file
if frame_class.__class__ and frame_class.__class__.__qualname__: elif frame_info.function == '<module>':
caller_class_names = frame_class.__class__.__qualname__ caller_class_names = '<none>'
caller_qualname = value_dict['__name__']
# It's a call from the top level module file elif frame_info.function != '':
elif frame_info.function == '<module>': caller_class_names = '<none>'
caller_class_names = '<none>' caller_qualname = frame_info.function
caller_qualname = value_dict['__name__']
elif frame_info.function != '': module_name = cls.munge_module_name(frame_info, module_name)
caller_class_names = '<none>'
caller_qualname = frame_info.function
module_name = cls.munge_module_name(frame_info, module_name) except Exception as e:
print("ALERT! Something went VERY wrong in handling finding info to log")
print("ALERT! Information is as follows")
with suppress(Exception):
# https://docs.python.org/3.7/library/inspect.html#the-interpreter-stack print(f"ALERT! {e=}")
del frame print_exc()
print(f"ALERT! {frame=}")
with suppress(Exception):
print(f"ALERT! {fn=}") # type: ignore
with suppress(Exception):
print(f"ALERT! {cls=}")
finally: # Ensure this always happens
# https://docs.python.org/3.7/library/inspect.html#the-interpreter-stack
del frame
if caller_qualname == '': if caller_qualname == '':
print('ALERT! Something went wrong with finding caller qualname for logging!') print('ALERT! Something went wrong with finding caller qualname for logging!')
@ -398,19 +421,19 @@ class EDMCContextFilter(logging.Filter):
# Go up through stack frames until we find the first with a # Go up through stack frames until we find the first with a
# type(f_locals.self) of logging.Logger. This should be the start # type(f_locals.self) of logging.Logger. This should be the start
# of the frames internal to logging. # of the frames internal to logging.
frame: 'frame' = getframe(0) frame: 'FrameType' = getframe(0)
while frame: while frame:
if isinstance(frame.f_locals.get('self'), logging.Logger): if isinstance(frame.f_locals.get('self'), logging.Logger):
frame = frame.f_back # Want to start on the next frame below frame = cast('FrameType', frame.f_back) # Want to start on the next frame below
break break
frame = frame.f_back frame = cast('FrameType', frame.f_back)
# Now continue up through frames until we find the next one where # Now continue up through frames until we find the next one where
# that is *not* true, as it should be the call site of the logger # that is *not* true, as it should be the call site of the logger
# call # call
while frame: while frame:
if not isinstance(frame.f_locals.get('self'), logging.Logger): if not isinstance(frame.f_locals.get('self'), logging.Logger):
break # We've found the frame we want break # We've found the frame we want
frame = frame.f_back frame = cast('FrameType', frame.f_back)
return frame return frame
@classmethod @classmethod