Как определить, находится ли выражение, содержащее% r, в утверждении assert? - PullRequest
0 голосов
/ 24 октября 2019

Я пытаюсь злоупотребить контекстной информацией, чтобы изменить вывод __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 в целом.

...