Содержание страницы
contextvars – Контекстные переменные¶contextvars – Context Variables
Этот модуль предоставляет API для управления, хранения и доступа к контекстно-локальному состоянию. Класс ContextVar используется для объявления и работы с контекстными переменными. Функция copy_context() и класс Context должны использоваться для управления текущим контекстом в асинхронных фреймворках.
Менеджеры контекста, имеющие состояние, должны использовать контекстные переменные вместо threading.local(), чтобы предотвратить непреднамеренную утечку своего состояния в другой код при использовании в конкурентном коде.
См. также PEP 567 для получения дополнительных сведений.
Добавлено в версии 3.7.
Контекстные переменные¶Context Variables
- class contextvars.ContextVar(name[, *, default])¶
Этот класс используется для объявления новой контекстной переменной, например:
var: ContextVar[int] = ContextVar('var', default=42)
Обязательный параметр name используется для целей интроспекции и отладки.
Необязательный параметр default, передаваемый только по ключу, возвращается
ContextVar.get(), если в текущем контексте не найдено значение для переменной.Важно: Контекстные переменные должны создаваться на верхнем уровне модуля, а не в замыканиях. Объекты
Contextсодержат сильные ссылки на контекстные переменные, что препятствует их корректной сборке мусора.ContextVarявляются обобщёнными по типу содержащегося в них значения.- name¶
Имя переменной. Это свойство только для чтения.
Добавлено в версии 3.7.1.
- get([default])¶
Возвращает значение для контекстной переменной в текущем контексте.
Если в текущем контексте нет значения для переменной, метод:
возвращает значение аргумента default метода, если он был передан; или
возвращает значение по умолчанию для контекстной переменной, если она была создана с ним; или
возбуждает
LookupError.
- set(value)¶
Вызов для установки нового значения контекстной переменной в текущем контексте.
Обязательный аргумент value – это новое значение для контекстной переменной.
Возвращает объект
Token, который можно использовать для восстановления предыдущего значения переменной с помощью методаContextVar.reset().
- reset(token)¶
Сбрасывает контекстную переменную до значения, которое она имела до вызова
ContextVar.set(), создавшего токен.Например:
var = ContextVar('var') token = var.set('new value') # код, который использует 'var'; var.get() возвращает 'new value'. var.reset(token) # После вызова reset переменная снова не имеет значения, поэтому # var.get() вызовет исключение LookupError.
Один и тот же токен нельзя использовать дважды.
- class contextvars.Token¶
Объекты Token возвращаются методом
ContextVar.set(). Их можно передать методуContextVar.reset(), чтобы вернуть значение переменной к тому, каким оно было до соответствующего set. Один токен не может сбросить контекстную переменную более одного раза.Токены являются обобщёнными относительно того же типа, что и
ContextVar, который их создал.- var¶
Свойство только для чтения. Указывает на объект
ContextVar, который создал токен.
- old_value¶
Свойство только для чтения. Установлено в значение, которое переменная имела до вызова метода
ContextVar.set(), создавшего токен. Указывает наToken.MISSING, если переменная не была установлена до вызова.
- MISSING¶
Объект-маркер, используемый
Token.old_value.
Ручное управление контекстом¶Manual Context Management
- contextvars.copy_context()¶
Возвращает копию текущего объекта
Context.Следующий фрагмент получает копию текущего контекста и выводит все переменные и их значения, установленные в нём:
ctx: Context = copy_context() print(list(ctx.items()))
Функция имеет сложность O(1), т.е. работает одинаково быстро для контекстов с несколькими контекстными переменными и для контекстов, содержащих их много.
- class contextvars.Context¶
Отображение
ContextVarsна их значения.Context()создаёт пустой контекст без значений. Чтобы получить копию текущего контекста, используйте функциюcopy_context().Каждый поток имеет собственный результирующий стек объектов
Context. Текущий контекст – это объектContextна вершине стека текущего потока. Все объектыContextв стеках считаются вошедшими.Вход в контекст, который можно выполнить вызовом его метода
run(), делает контекст текущим, помещая его на вершину стека контекстов текущего потока.Выход из текущего контекста, который можно выполнить возвратом из колбэка, переданного методу
run(), восстанавливает текущий контекст до состояния, в котором он был до входа, путём удаления контекста с вершины стека контекстов.Поскольку каждый поток имеет собственный стек контекстов, объекты
ContextVarведут себя аналогичноthreading.local()при присваивании значений в разных потоках.Попытка войти в уже вошедший контекст, включая контексты, вошедшие в других потоках, вызывает
RuntimeError.После выхода из контекста его можно повторно войти (из любого потока).
Любые изменения значений
ContextVarчерез методContextVar.set()записываются в текущий контекст. МетодContextVar.get()возвращает значение, связанное с текущим контекстом. Выход из контекста эффективно отменяет любые изменения контекстных переменных, сделанные во время нахождения в контексте (при необходимости значения можно восстановить, повторно войдя в контекст).Контекст реализует интерфейс
collections.abc.Mapping.- run(callable, *args, **kwargs)¶
Входит в контекст, выполняет
callable(*args, **kwargs), затем выходит из контекста. Возвращает возвращаемое значение вызываемого объекта или распространяет исключение, если оно произошло.Пример:
import contextvars var = contextvars.ContextVar('var') var.set('spam') print(var.get()) # 'spam' ctx = contextvars.copy_context() def main(): # 'var' было установлено в 'spam' до # вызова 'copy_context()' и 'ctx.run(main)', поэтому: print(var.get()) # 'spam' print(ctx[var]) # 'spam' var.set('ham') # Теперь, после установки 'var' в 'ham': print(var.get()) # 'ham' print(ctx[var]) # 'ham' # Любые изменения, которые функция 'main' вносит в 'var' # будут содержаться в 'ctx'. ctx.run(main) # Функция 'main()' была запущена в контексте 'ctx', # поэтому изменения 'var' содержатся в нём: print(ctx[var]) # 'ham' # Однако вне 'ctx' переменная 'var' по-прежнему равна 'spam': print(var.get()) # 'spam'
- copy()¶
Возвращает поверхностную копию объекта контекста.
- var in context
Возвращает
True, если в контексте установлено значение для var; в противном случае возвращаетFalse.
- context[var]
Возвращает значение переменной var
ContextVar. Если переменная не установлена в объекте контекста, возбуждаетсяKeyError.
- get(var[, default])¶
Возвращает значение var, если var имеет значение в объекте контекста. В противном случае возвращает default. Если default не указан, возвращает
None.
- iter(context)
Возвращает итератор по переменным, хранящимся в объекте контекста.
- len(proxy)
Возвращает количество переменных, установленных в объекте контекста.
- keys()¶
Возвращает список всех переменных в объекте контекста.
- values()¶
Возвращает список всех значений переменных в объекте контекста.
- items()¶
Возвращает список кортежей из двух элементов, содержащих все переменные и их значения в объекте контекста.
Поддержка asyncio¶asyncio support
Контекстные переменные нативно поддерживаются в asyncio и готовы к использованию без дополнительной настройки. Например, вот простой эхо-сервер, который использует контекстную переменную, чтобы сделать адрес удаленного клиента доступным в задаче, обрабатывающей этого клиента:
import asyncio
import contextvars
client_addr_var = contextvars.ContextVar('client_addr')
def render_goodbye():
# Адрес текущего обрабатываемого клиента можно получить
# не передавая его явно в эту функцию.
client_addr = client_addr_var.get()
return f'Good bye, client @ {client_addr}\r\n'.encode()
async def handle_request(reader, writer):
addr = writer.transport.get_extra_info('socket').getpeername()
client_addr_var.set(addr)
# В любом вызываемом нами коде теперь можно получить
# адрес клиента, вызвав 'client_addr_var.get()'.
while True:
line = await reader.readline()
print(line)
if not line.strip():
break
writer.write(b'HTTP/1.1 200 OK\r\n') # строка состояния
writer.write(b'\r\n') # заголовки
writer.write(render_goodbye()) # тело
writer.close()
async def main():
srv = await asyncio.start_server(
handle_request, '127.0.0.1', 8081)
async with srv:
await srv.serve_forever()
asyncio.run(main())
# Для тестирования можно использовать telnet или curl:
# telnet 127.0.0.1 8081
# curl 127.0.0.1:8081