Как работает атрибут __test__ = False magic для обнаружения теста

Итак, я пытаюсь реализовать нечто похожее на то, как unittesting frameworks делают следующее:

class BaseTest(T.TestCase):
    # Disables this test from being run
    __test__ = False

    def test_foo(self): pass


# However this test is picked up because it doesn't directly have __test__ set
class InheritingTest(BaseTest): pass

Я считаю своеобразным:

# >> InheritingTest.__test__
# False

Который указал бы мне, что он не использует metaclass для установки __test__ на True при построении типа.

Я пробовал grepping через библиотеку python find . -name "*.py" | xargs grep '__test__', но, похоже, не нашел ничего связанного с этим.

Мой подход "угадывания" при решении этой проблемы заключается в следующем:

def is_class_tested(cls):
    return cls.__dict__.get('__test__', True)

Однако мне это кажется хрупким... Есть ли более чистый/лучший способ сделать это, который работает во всех случаях? Есть ли вероятность, что класс не будет иметь свойство __dict__?

Ответ 1

Testify, библиотека, которую вы используете, делает следующее в testify/test_discovery.py вокруг строки 140:

    # it not a list, it not a bare module - let see if it an honest-to-god TestCaseBase
    elif isinstance(test_module, MetaTestCase) and (not '__test__' in test_module.__dict__ or bool(test_module.__test__)):
        ...
    # detect unittest test cases
    elif issubclass(test_module, unittest.TestCase) and (not '__test__' in test_module.__dict__ or bool(test_module.__test__)):

Иными словами, он делает именно то, что делает ваш подход "догадки", несколько более подробным образом: он проверяет наличие и значение __test__ в классе __dict__ напрямую.

Ответ 2

Есть ли шанс, что класс не будет иметь свойство __dict__? Моя первоначальная реакция на это - нет. Возможно, это не совсем то, о чем вы спрашивали, потому что вы можете иметь экземпляр класса без __dict__, если он определяет __slots__ и не наследует от класса с __dict__, уже находящегося в нем. См. Описание __slots__ в Описание модели данных Python

ИЗМЕНИТЬ

как указано @nneonneo, комментарии ниже о двойном подчеркивании неверны, потому что есть завершающие символы подчеркивания. Содержимое осталось по историческим причинам.

Поведение, которое вы утверждаете, выглядит странно, я считаю естественным. @Owen был прав, спрашивая о коде, который ожидает чего-то другого, и благодарю вас за отправку ссылки на Yelp/Testify. В этой структуре широко используется double_underscore.

Alex Martelli Ответ SO на double_underscore помогает пролить свет на то, что может происходить, но самый короткий ответ заключается в том, что двойное подчеркивание возвращает результаты выше, потому что точечная обозначение InheritingTest.__test__ проходит через механизм нормального разрешения атрибутов, но структура Testify, решив использовать ведущий double_underscore, зарезервировала право доступа к ней в своей области видимости класса, даже если ваш подкласс отменяет ее.

Испытательные рамки по самой своей природе волшебные звери и использование __test__ слышат, как выглядит с их стороны. Я проверил их документы, и документация кажется редкой, так что ваше использование __test__ вообще не опасно (сигналы с двойным подчеркиванием в любом случае отключают эту переменную "class local" ).