Spec-Zone.ru › Python 3.9

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')
# 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().

Контекст реализует интерфейс 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

© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.9/library/contextvars.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API