Я пытаюсь злоупотребить контекстной информацией, чтобы изменить вывод __repr__()
, отображаемый при сбое утверждения . Из сценария должно быть очевидно, что я уже пробовал:
#!/usr/bin/env python3
import sys
import traceback
class Foo:
def __repr__(self):
print("inside repr()")
for entry in traceback.format_stack(limit=2)[:-1]:
for line in entry.splitlines():
print("\t> {}".format(line))
if (None, None, None) != sys.exc_info():
return "<%s object at 0x%08X> [parent node: %s]" % (
type(self).__name__,
id(self), "this should be evaluated during a failing assert")
return "<%s object at 0x%08X>" % (type(self).__name__, id(self))
@property
def bar(self):
return False
def __format__(self, format_spec):
return repr(self)
def errstr(self, fmtstr, *args):
return fmtstr.format(*args)
if __name__ == "__main__":
foo = Foo()
try:
print("before assertion #1")
assert not foo.bar, "Something wrong with your Foo instance: %r" % (foo)
print("after assertion #1")
except Exception:
print("Inside exception handler #1: %r" % (foo))
try:
print("before assertion #2")
assert foo.bar, "Something wrong with your Foo instance: %r" % (foo)
print("after assertion #2")
except Exception:
print("Inside exception handler #2: %r" % (foo))
# assert foo.bar, "Something wrong with your Foo instance: {}".format(foo) # same as previous line
assert foo.bar, foo.errstr("Something wrong with your Foo instance: {}", foo) # effectively the same
Вывод этого сценария:
before assertion #1
after assertion #1
before assertion #2
inside repr()
> File "./test.py", line 37, in <module>
> assert foo.bar, "Something wrong with your Foo instance: %r" % (foo)
inside repr()
> File "./test.py", line 40, in <module>
> print("Inside exception handler #2: %r" % (foo))
Inside exception handler #2: <Foo object at 0x7F2CF2791F98> [parent node: this should be evaluated during a failing assert]
inside repr()
> File "./test.py", line 22, in __format__
> return repr(self)
Traceback (most recent call last):
File "./test.py", line 42, in <module>
assert foo.bar, foo.errstr("Something wrong with your Foo instance: {}", foo) # effectively the same
AssertionError: Something wrong with your Foo instance: <Foo object at 0x7F2CF2791F98>
Теперь, пока я - по общему признанию - не знаком со всеми тонкостямииз того, как Python оценивает оператор assert
, совершенно очевидно, что __repr__
оценивается не так, как я изначально думал. Или, по крайней мере, в то время, когда выполняется правая часть оператора assert
, он каким-то образом не находится внутри AssertionError
, и поэтому (None, None, None) == sys.exc_info()
.
Это имеет смысл, учитывая (см. Выше)Документация оператора assert
гласит (для Python 3.8):
Расширенная форма, assert expression1, expression2
, эквивалентна
if __debug__:
if not expression1: raise AssertionError(expression2)
Однако, понимание этоговсе еще не помогает мне достичь условного вывода, основанного на том, провалилось ли утверждение или нет. Так как бы мне этого добиться?
NB: Для краткости я бы предпочел, если бы я мог избежать эквивалентной более длинной формы и придерживаться assert
в целом.