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])¶ Возвращает значение для контекстной переменной для текущего контекста.
Если в текущем контексте нет значения для переменной, метод выполнится:
возвращать значение аргумента по умолчанию метода, если он предоставлен; или
возвращает значение по умолчанию для контекстной переменной, если она была создана с таким значением; или
вызвать
LookupError.
-
set(value)¶ Вызов для установки нового значения для контекстной переменной в текущем контексте.
Необходимый аргумент value - это новое значение для контекстной переменной.
Возвращает объект
Token, который может быть использован для восстановления предыдущего значения переменной с помощью методаContextVar.reset().
-
reset(token)¶ Сброс контекстной переменной до значения, которое она имела до использования
ContextVar.set(), создавшего token.Например:
var = ContextVar('var') token = var.set('new value') # code that uses 'var'; var.get() returns 'new value'. var.reset(token) # After the reset call the var has no value again, so # var.get() would raise a LookupError.
-
-
class
contextvars.Token¶ Объекты Token возвращаются методом
ContextVar.set(). Они могут быть переданы методуContextVar.reset()для возврата значения переменной к тому, каким оно было до соответствующего set.-
var¶ Свойство, доступное только для чтения. Указывает на объект
ContextVar, который создал маркер.
-
old_value¶ Свойство, доступное только для чтения. Устанавливается в значение, которое переменная имела до вызова метода
ContextVar.set(), создавшего маркер. Указывает наToken.MISSING, если переменная не была установлена до вызова.
-
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().Context реализует интерфейс
collections.abc.Mapping.-
run(callable, *args, **kwargs)¶ Выполнить код
callable(*args, **kwargs)в объекте контекста, на котором вызван метод run. Верните результат выполнения или распространите исключение, если оно произошло.Любые изменения любых переменных контекста, которые производит callable, будут содержаться в объекте контекста:
var = ContextVar('var') var.set('spam') def main(): # 'var' was set to 'spam' before # calling 'copy_context()' and 'ctx.run(main)', so: # var.get() == ctx[var] == 'spam' var.set('ham') # Now, after setting 'var' to 'ham': # var.get() == ctx[var] == 'ham' ctx = copy_context() # Any changes that the 'main' function makes to 'var' # will be contained in 'ctx'. ctx.run(main) # The 'main()' function was run in the 'ctx' context, # so changes to 'var' are contained in it: # ctx[var] == 'ham' # However, outside of 'ctx', 'var' is still set to 'spam': # var.get() == 'spam'
Метод вызывает ошибку
RuntimeErrorпри вызове одного и того же контекстного объекта из более чем одного потока ОС или при рекурсивном вызове.
-
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()¶ Возвращает список из 2-кортежей, содержащий все переменные и их значения в объекте контекста.
-
поддержка asyncio¶
Контекстные переменные изначально поддерживаются в asyncio и готовы к использованию без дополнительной настройки. Например, вот простой эхо-сервер, который использует контекстную переменную, чтобы сделать адрес удаленного клиента доступным в задаче, которая обрабатывает этого клиента:
import asyncio
import contextvars
client_addr_var = contextvars.ContextVar('client_addr')
def render_goodbye():
# The address of the currently handled client can be accessed
# without passing it explicitly to this function.
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)
# In any code that we call is now possible to get
# client's address by calling '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())
# To test it you can use telnet:
# telnet 127.0.0.1 8081