27.5. contextlib – утилиты для контекстов оператора with¶contextlib – Utilities for with-statement contexts
Этот модуль предоставляет утилиты для типичных задач, связанных с оператором with. Дополнительную информацию см. также в разделах Типы менеджеров контекста и Менеджеры контекста оператора with.
Предоставляемые функции:
- contextlib.contextmanager(func)¶
Эта функция – декоратор, который можно использовать для определения фабричной функции для менеджеров контекста оператора with, без необходимости создавать класс или отдельные методы __enter__() и __exit__().
Простой пример (это не рекомендуется как реальный способ генерации HTML!):
from contextlib import contextmanager @contextmanager def tag(name): print("<%s>" % name) yield print("</%s>" % name) >>> with tag("h1"): ... print("foo") ... <h1> foo </h1>
Декорируемая функция при вызове должна возвращать генератор-итератор. Этот итератор должен передать (yield) ровно одно значение, которое будет связано с целями в предложении as оператора with, если оно есть.
В точке, где генератор выполняет yield, выполняется блок, вложенный в оператор with. Затем генератор возобновляется после выхода из блока. Если в блоке возникает необработанное исключение, оно повторно возбуждается внутри генератора в точке, где произошёл yield. Таким образом, можно использовать конструкцию try...except...finally для перехвата ошибки (если она есть) или для обеспечения выполнения очистки. Если исключение перехватывается лишь для того, чтобы залогировать его или выполнить какое-либо действие (а не подавить его полностью), генератор должен возобновить (reraise) это исключение. В противном случае менеджер контекста-генератор сообщит оператору with, что исключение обработано, и выполнение продолжится с оператора, непосредственно следующего за оператором with.
- contextlib.nested(mgr1[, mgr2[, ...]])¶
Объединяет несколько менеджеров контекста в один вложенный менеджер контекста.
This function has been deprecated in favour of the multiple manager form of the with statement.
The one advantage of this function over the multiple manager form of the with statement is that argument unpacking allows it to be used with a variable number of context managers as follows:
from contextlib import nested with nested(*managers): do_something()
Note that if the __exit__() method of one of the nested context managers indicates an exception should be suppressed, no exception information will be passed to any remaining outer context managers. Similarly, if the __exit__() method of one of the nested managers raises an exception, any previous exception state will be lost; the new exception will be passed to the __exit__() methods of any remaining outer context managers. In general, __exit__() methods should avoid raising exceptions, and in particular they should not re-raise a passed-in exception.
This function has two major quirks that have led to it being deprecated. Firstly, as the context managers are all constructed before the function is invoked, the __new__() and __init__() methods of the inner context managers are not actually covered by the scope of the outer context managers. That means, for example, that using nested() to open two files is a programming error as the first file will not be closed promptly if an exception is thrown when opening the second file.
Secondly, if the __enter__() method of one of the inner context managers raises an exception that is caught and suppressed by the __exit__() method of one of the outer context managers, this construct will raise RuntimeError rather than skipping the body of the with statement.
Developers that need to support nesting of a variable number of context managers can either use the warnings module to suppress the DeprecationWarning raised by this function or else use this function as a model for an application specific implementation.
Устарело с версии 3.1: Оператор with теперь напрямую поддерживает эту функциональность (без запутанных и подверженных ошибкам особенностей).
- contextlib.closing(thing)¶
Возвращает контекстный менеджер, который закрывает thing после завершения блока. По сути эквивалентно следующему:
from contextlib import contextmanager @contextmanager def closing(thing): try: yield thing finally: thing.close()
И позволяет писать код следующим образом:
from contextlib import closing from urllib.request import urlopen with closing(urlopen('http://www.python.org')) as page: for line in page: print(line)
без необходимости явно закрывать page. Даже если произойдёт ошибка, page.close() будет вызван при выходе из блока with.