Хорошо, я нашел решение, которое также должно работать, если класс не находится под моим контролем.Это решение предназначено только для AttributeError
, но должно быть расширяемым в случае необходимости обнаружения других ошибок.
У нас все еще одна и та же функция тестирования и тот же класс Dummy
def test(first, second):
print("My name is " + first.age + " and I am here with " + second.age)
class Dummy(object):
def __init__(self):
pass
Мы можемиспользуйте объект Proxy для переноса каждого значения, которое мы передаем в тестовую функцию.Этот прокси-объект записывает, видит ли он AttributeError
, устанавливая флаг _had_exception
.
class Proxy(object):
def __init__(self, object_a):
self._object_a = object_a
self._had_exception: bool = False
def __getattribute__(self, name):
if name == "_had_exception":
return object.__getattribute__(self, name)
obj = object.__getattribute__(self, '_object_a')
try:
return getattr(obj, name)
except AttributeError as e:
# Flag this object as a cause for an exception
self._had_exception = True
raise e
И вызов функции выглядит следующим образом
d = Dummy()
d.__setattr__("age", "25")
p1 = Proxy(d)
p2 = Proxy(Dummy())
try:
test(p1, p2)
except AttributeError as e:
# Get the local variables from when the Error happened
locals = e.__traceback__.tb_next.tb_frame.f_locals
offender_names = []
# Check if one of the local items is the same
# as one of our inputs that caused an Error
for key, val in locals.items():
if p1._had_exception:
if p1 is val:
offender_names.append(key)
if p2._had_exception:
if p2 is val:
offender_names.append(key)
print(offender_names) # ['second']
Конечный результатсписок со всеми именами локальных переменных - используемых в вызываемой функции - которые соответствуют нашим упакованным входам, что вызвало исключение.