contextvars
— Переменные контекста¶
Модуль предоставляет API для управления, хранения и доступа к
локальному контексту состояния. Класс ContextVar
- используется для
объявления и работой с Переменной контекста. Функция copy_context()
и
класс Context
должны быть использованы, чтобы управлять
текущим контекстом в асинхронных фреймворках.
Менеджеры контекста, у которых есть состояние, должны использовать переменные
контекста вместо threading.local()
, чтобы препятствовать тому, чтобы их состояние
неожиданно кровоточил к другому код, когда используются в конкурентном коде.
Для получения дополнительной информации см. также раздел PEP 567.
Добавлено в версии 3.7.
Переменные контекста¶
-
class
contextvars.
ContextVar
(name[, *, default])¶ Класс - используемый, чтобы объявить новую переменную контекста, например.:
var: ContextVar[int] = ContextVar('var', default=42)
Обязательный параметр name - используемый в целях отладки и самоанализе.
Дополнительный ключевой параметр default только для возвращения
ContextVar.get()
, когда нет значения для переменной найденой в текущем контексте.Важно: переменные контекста должны создаваться на верхнем уровне модуля, а не в замыканиях. Объекты
Context
содержат сильные ссылки на переменные контекста, которые препятствуют правильному сбору мусора переменных контекста.-
name
¶ Имя переменной. Это свойство доступно только для чтения.
Добавлено в версии 3.7.1.
-
get
([default])¶ Возвращает значение переменной контекст для текущего контекст.
Если значение переменной в текущем контексте отсутствует, метод будет:
- возвращает значение аргумента default метода, если оно указано; или
- возвращает значение по умолчанию для переменной контекст, если она была создана с единичной; или
- поднимает
LookupError
.
-
set
(value)¶ Вызов для установки нового значения переменной контекста в текущем контексте.
Обязательный аргумент value является новым значением переменной контекста.
Возвращает объект
Token
, который может быть использован, чтобы вернуть переменной ее предыдущее значение через методContextVar.reset()
.
-
reset
(token)¶ Сбросить переменную контекста до значения, которое она имела до того, как
ContextVar.set()
, который созданный token был использован.Например:
var = ContextVar('var') token = var.set('new value') # код, который использует 'var'; var.get() возвращает 'new value'. var.reset(token) # После вызова сброса переменная снова не имеет значения, поэтому # var.get() поднимет LookupError.
-
-
class
contextvars.
Token
¶ Token объекты возвращаются
ContextVar.set()
метод. Они могут быть переданыContextVar.reset()
методу, чтобы вернуть значение переменной до того, что было до соответствующего set.-
Token.
var
¶ Свойство только для чтения. Указывает на объект
ContextVar
, создавший маркер.
-
Token.
old_value
¶ Свойство, доступное только для чтения. Установить значение, которое переменная имела перед вызовом
ContextVar.set()
метода, который создал маркер. Это указатель наToken.MISSING
, переменная, не был установлен перед вызовом.
-
Token.
MISSING
¶ Объект маркера используемый в
Token.old_value
.
-
Ручное управление контекстом¶
-
contextvars.
copy_context
()¶ Возвращает копию текущего объекта
Context
.Следующий фрагмент получает копию текущего контекст и печатает все переменные и их значения, заданные в нем:
ctx: Context = copy_context() print(list(ctx.items()))
Функция имеет сложность O(1), то есть работает одинаково быстро для контекстов с несколькими контекстыми переменными и для контекстов, в которых их много.
-
class
contextvars.
Context
¶ Отображение
ContextVars
с их значениями.Context()
создает пустой контекст без значений в нем. Для получения копии текущего контекст используйте функциюcopy_context()
.Контекст реализует интерфейс
collections.abc.Mapping
.-
run
(callable, *args, **kwargs)¶ Выполните
callable(*args, **kwargs)
код в объекте контекста, для которого вызывается run метод. Возвращает результат выполнения или распространяет исключение, если оно произошло.Любые изменения любых переменных контекста, которые делает callable, будут содержаться в объекте контекста:
var = ContextVar('var') var.set('spam') def main(): # 'var' был установлен как 'spam' перед тем # вызывать 'copy_context()' и 'ctx.run(main)', таким образом: # var.get() == ctx[var] == 'spam' var.set('ham') # Теперь, после установки 'var' в 'ham': # var.get() == ctx[var] == 'ham' ctx = copy_context() # Любые изменения, которые функция 'main' вносит в 'var' # будет содержаться в 'ctx'. ctx.run(main) # Функция 'main()' была запущена в контексте 'ctx', # поэтому изменения в 'var' содержатся в нем: # ctx[var] == 'ham' # Тем не менее, вне 'ctx', 'var' по-прежнему установлен в 'spam': # var.get() == 'spam'
Метод поднимает
RuntimeError
, когда его называют на том же объекте контекста больше чем от одной потока OS, или когда его называют рекурсивно.
-
copy
()¶ Возвращение неглубокой копии объекта контекста.
-
var in context
Возвращает
True
, если context имеет значение для заданного var; возвращаетFalse
в противном случае.
-
context[var]
Возвращает значение переменной var
ContextVar
. Если переменная не задана в объекте контекста, возникаетKeyError
.
-
get
(var[, default])¶ Возвращает значение для var, если var имеет значение в объекте контекст. Верните default в противном случае. Если default не задан, возвращает
None
.
-
iter(context)
Возвращает итератор по переменным, хранящимся в объекте контекст.
-
len(proxy)
Возвращает число переменных, заданных в объекте контекст.
-
keys
()¶ Возвращает список всех переменных в объекте контекст.
-
values
()¶ Возвращает список значений всех переменных в объекте контекст.
-
items
()¶ Возвращает список 2-кортежей, содержащих все переменные и их значения в объекте контекст.
-
Поддержка asyncio¶
Переменные контекста с рождения поддержаны в 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}\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(line)
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:
# telnet 127.0.0.1 8081