Spec-Zone.ru › Python 3.13

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(), который создал токен.

Например:

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. Текущий контекст — это объект 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' was set to 'spam' before
    # calling 'copy_context()' and 'ctx.run(main)', so:
    print(var.get())  # 'spam'
    print(ctx[var])  # 'spam'

    var.set('ham')

    # Now, after setting 'var' to 'ham':
    print(var.get())  # 'ham'
    print(ctx[var])  # 'ham'

# 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:
print(ctx[var])  # 'ham'

# However, outside of 'ctx', 'var' is still set to 'spam':
print(var.get())  # 'spam'
copy()

Возвращает поверхностную копию объекта контекста.

var in context

Возвращает True если у контекста есть значение для переменной; в противном случае возвращает False.

context[var]

Возвращает значение переменной var ContextVar. Если переменная не установлена в объекте контекста, возникает исключение KeyError.

get(var[, default])

Возвращает значение для переменной, если переменная имеет значение в объекте контекста. В противном случае возвращает default. Если default не указан, возвращает None.

iter(context)

Возвращает итератор по переменным, хранящимся в объекте контекста.

len(proxy)

Возвращает количество установленных переменных в объекте контекста.

keys()

Возвращает список всех переменных в объекте контекста.

values()

Возвращает список всех значений переменных в объекте контекста.

items()

Возвращает список пар кортежей, содержащих все переменные и их значения в объекте контекста.

Поддержка 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}\r\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(b'HTTP/1.1 200 OK\r\n')  # status line
    writer.write(b'\r\n')  # headers
    writer.write(render_goodbye())  # body
    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 or curl:
#     telnet 127.0.0.1 8081
#     curl 127.0.0.1:8081

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

Spec-Zone.ru

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