Статьи

Сфинкс doctests и пространство имен выполнения


Наконец-то я начал работу над документацией для
релиза
mock 0.8, и большая часть этого включает преобразование записей, которые я сделал в
записях блога .

Макетная документация построена на превосходном Sphinx (конечно!), И как можно больше примеров в документации являются тестами , чтобы убедиться, что примеры все еще актуальны для новых выпусков.

doctests подражают выполнению ваших примеров в интерактивном переводчике, но они не совсем одинаковы. Одно большое отличие состоит в том, что контекст выполнения для doctest — это не реальный модуль, а словарь. Это особенно важно для ложных примеров, потому что следующий код будет работать на интерактивном интерпретаторе, но не на тестовом документе:

>>> from mock import patch
>>> class Foo(object):
...      pass
...
>>> with patch('__main__.Foo') as mock_foo:
...   assert Foo is mock_foo
...

Имя ( __name__ ) пространства имен выполнения doctest — __builtin__ , но это ложь. Пространство имен — это словарь, внутренний для DocTest . При выполнении doctests под Sphinx, настоящий модуль __main__ — это скрипт sphinx-builder .

Чтобы приведенный выше пример кода работал, мне нужно либо переписать его (и сделать его менее читабельным — возможно, помещая объект класса, который я вставляю в модуль), либо мне нужно каким-то образом преобразовать текущий контекст выполнения в __main__ .

К счастью, расширение doctest Sphinx предоставляет опцию конфигурации doctest_global_setup . Это позволяет мне вставить строку в мой файл conf.py , который будет выполняться перед док-тестами каждой страницы в моей документации (тесты с каждой страницы имеют общий контекст выполнения).

Я решил проблему, создав прокси-объект, который делегирует доступ к текущему словарю globals () . Я помещаю это в sys.modules как модуль __main__ (не забывая хранить ссылку на реальный __main__, чтобы он не собирал мусор). Когда патч получает доступ или изменяет атрибут на __main__, он фактически использует текущий контекст выполнения.

Вот код из conf.py :

doctest_global_setup = """
import sys
import __main__

# keep a reference to __main__
sys.modules['__main'] = __main__

class ProxyModule(object):
    def __getattr__(self, name):
        return globals()[name]
    def __setattr__(self, name, value):
        globals()[name] = value
    def __delattr__(self, name):
        del globals()[name]

sys.modules['__main__'] = ProxyModule()
"""

doctest_global_cleanup = """
sys.modules['__main__'] = sys.modules['__main']
"""

Соответствующая опция doctest_global_cleanup восстанавливает реальное __main__ после завершения теста.

Запись

В комментариях Ник Коглан предлагает упрощение для ProxyModule :

class ProxyModule(object):
    def __init__(self):
        self.__dict__ = globals()

Источник: 
http://www.voidspace.org.uk/python/weblog/arch_d7_2011_12_31.shtml#e1228