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
Вернуться на верх