Документация Python неофициальный перевод

28.7. contextlib – Утилиты для контекстов оператора withcontextlib – Utilities for with-statement contexts

Новое в версии 2.5.

Исходный код: Lib/contextlib.py


Этот модуль предоставляет утилиты для типовых задач, связанных с оператором with. Для получения дополнительной информации см. также Типы менеджеров контекста и Менеджеры контекста оператора with.

Предоставляемые функции:

contextlib.contextmanager(func)

Эта функция – декоратор, который можно использовать для определения фабричной функции для контекстных менеджеров оператора with, без необходимости создавать класс или отдельные методы __enter__() и __exit__().

Хотя многие объекты изначально поддерживают использование в with-выражениях, иногда требуется управлять ресурсом, который сам по себе не является менеджером контекста, и не реализует метод close() для использования с contextlib.closing

Абстрактным примером может служить следующий код для обеспечения корректного управления ресурсами:

from contextlib import contextmanager

@contextmanager
def managed_resource(*args, **kwds):
    # Код для получения ресурса, например:
    resource = acquire_resource(*args, **kwds)
    try:
        yield resource
    finally:
        # Код для освобождения ресурса, например:
        release_resource(resource)

>>> with managed_resource(timeout=3600) as resource:
...     # Ресурс освобождается в конце этого блока,
...     # даже если код в блоке вызывает исключение

Декорируемая функция при вызове должна возвращать генератор-итератор. Этот итератор должен возвращать ровно одно значение, которое будет связано с целями в предложении as оператора with, если таковые имеются.

В момент, где генератор делает yield, выполняется блок, вложенный в оператор with. Затем генератор возобновляется после выхода из блока. Если в блоке возникает необработанное исключение, оно повторно возбуждается внутри генератора в точке, где произошел yield. Таким образом, можно использовать оператор tryexceptfinally для перехвата ошибки (если есть) или для выполнения очистки. Если исключение перехватывается только для того, чтобы зарегистрировать его или выполнить какое-либо действие (а не подавить полностью), генератор должен повторно возбудить это исключение. В противном случае контекстный менеджер генератора сообщит оператору with, что исключение обработано, и выполнение продолжится с оператора, следующего сразу за оператором with.

contextlib.nested(mgr1[, mgr2[, ...]])

Объединяет несколько менеджеров контекста в один вложенный менеджер контекста.

Эта функция устарела в пользу формы с несколькими менеджерами оператора with.

Единственное преимущество этой функции перед формой с несколькими менеджерами оператора with заключается в том, что распаковка аргументов позволяет использовать её с переменным числом менеджеров контекста следующим образом:

from contextlib import nested

with nested(*managers):
    do_something()

Обратите внимание: если метод __exit__() одного из вложенных менеджеров контекста указывает, что исключение должно быть подавлено, информация об исключении не будет передана ни одному из оставшихся внешних менеджеров контекста. Аналогично, если метод __exit__() одного из вложенных менеджеров вызывает исключение, любое предыдущее состояние исключения будет потеряно; новое исключение будет передано методам __exit__() оставшихся внешних менеджеров контекста. В общем случае методы __exit__() должны избегать возбуждения исключений, и в особенности они не должны повторно возбуждать переданное исключение.

У этой функции есть два существенных недостатка, из-за которых она была объявлена устаревшей. Во-первых, поскольку все менеджеры контекста создаются до вызова функции, методы __new__() и __init__() внутренних менеджеров контекста на самом деле не находятся в области действия внешних менеджеров контекста. Это означает, например, что использование nested() для открытия двух файлов является ошибкой программирования, так как первый файл не будет своевременно закрыт, если при открытии второго возникнет исключение.

Во-вторых, если метод __enter__() одного из внутренних менеджеров контекста вызывает исключение, которое перехватывается и подавляется методом __exit__() одного из внешних менеджеров контекста, то данная конструкция возбудит RuntimeError вместо того, чтобы пропустить тело оператора with.

Разработчикам, которым требуется поддержка вложения переменного числа менеджеров контекста, можно либо использовать модуль warnings для подавления DeprecationWarning, возбуждаемого этой функцией, либо взять эту функцию за образец для реализации в своём приложении.

Устарело с версии 2.7: Оператор with теперь напрямую поддерживает эту функциональность (без сбивающих с толку чреватых ошибками странностей).

contextlib.closing(thing)

Возвращает контекстный менеджер, который закрывает thing после завершения блока. По сути эквивалентно следующему:

from contextlib import contextmanager

@contextmanager
def closing(thing):
    try:
        yield thing
    finally:
        thing.close()

И позволяет писать код следующим образом:

from contextlib import closing
import urllib

with closing(urllib.urlopen('http://www.python.org')) as page:
    for line in page:
        print line

без необходимости явно закрывать page. Даже если произойдет ошибка, page.close() будет вызван при выходе из блока with.

См. также

PEP 343 – Оператор «with»

Спецификация, предыстория и примеры для инструкции with в Python.