contextvars — Context Variables


이 모듈은 컨텍스트-로컬 상태를 관리, 저장, 액세스하기 위한 API를 제공합니다. ContextVar 클래스는 컨텍스트 변수 를 선언하고 사용하는 데 쓰입니다. copy_context() 함수와 Context 클래스는 비동기 프레임워크에서 현재 컨텍스트를 관리하는 데 사용해야 합니다.

Context managers that have state should use Context Variables instead of threading.local() to prevent their state from bleeding to other code unexpectedly, when used in concurrent code.

자세한 내용은 PEP 567을 참조하십시오.

Added in version 3.7.

컨텍스트 변수

class contextvars.ContextVar(name[, *, default])

이 클래스는 새로운 컨텍스트 변수를 선언하는 데 사용됩니다. 예:

var: ContextVar[int] = ContextVar('var', default=42)

필수 name 매개 변수는 인트로스팩션 및 디버그 목적으로 사용됩니다.

선택적 키워드 전용 default 매개 변수는 변수에 대한 값이 현재 컨텍스트에서 발견되지 않으면 ContextVar.get() 에 의해 반환됩니다.

중요: 컨텍스트 변수는 최상위 모듈 수준에서 만들어져야 하고 클로저에서 만들어져서는 안 됩니다. Context 객체는 컨텍스트 변수에 대해 강한 참조를 유지해서 컨텍스트 변수가 제대로 가비지 수집되지 못하게 합니다.

name

변수의 이름. 읽기 전용 프로퍼티입니다.

Added in version 3.7.1.

get([default])

현재 컨텍스트의 컨텍스트 변수에 대한 값을 반환합니다.

현재 컨텍스트에서 변수에 대한 값이 없는 경우 메서드는:

  • 제공된 경우 메서드의 default 인자 값을 반환합니다; 또는

  • 생성 시에 제공된 경우, 컨텍스트 변수의 기본값을 반환합니다; 또는

  • LookupError 를 발생시킵니다.

set(value)

현재 컨텍스트에서 컨텍스트 변수의 새 값을 설정하려면 호출합니다.

필수 value 인자는 컨텍스트 변수의 새 값입니다.

ContextVar.reset() 메서드를 통해 변수를 이전 값으로 복원하는 데 사용할 수 있는 Token 객체를 반환합니다.

reset(token)

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

A read-only property. Set to the value the variable had before the ContextVar.set() method call that created the token. It points to Token.MISSING if the variable was not set before the call.

MISSING

Token.old_value 에 의해 사용되는 표지 객체.

수동 컨텍스트 관리

contextvars.copy_context()

현재 Context 객체의 복사본을 반환합니다.

다음 코드 조각은 현재 컨텍스트의 복사본을 가져와서 모든 변수와 그 변수에 설정된 값을 출력합니다:

ctx: Context = copy_context()
print(list(ctx.items()))

The function has an O(1) complexity, i.e. works equally fast for contexts with a few context variables and for contexts that have a lot of them.

class contextvars.Context

ContextVars 에서 그 값으로의 매핑.

Context() 는 값이 없는 빈 컨텍스트를 만듭니다. 현재 컨텍스트의 복사본을 얻으려면 copy_context() 함수를 사용하십시오.

Each thread has its own effective stack of Context objects. The current context is the Context object at the top of the current thread’s stack. All Context objects in the stacks are considered to be entered.

Entering a context, which can be done by calling its run() method, makes the context the current context by pushing it onto the top of the current thread’s context stack.

Exiting from the current context, which can be done by returning from the callback passed to the run() method, restores the current context to what it was before the context was entered by popping the context off the top of the context stack.

Since each thread has its own context stack, ContextVar objects behave in a similar fashion to threading.local() when values are assigned in different threads.

Attempting to enter an already entered context, including contexts entered in other threads, raises a RuntimeError.

After exiting a context, it can later be re-entered (from any thread).

Any changes to ContextVar values via the ContextVar.set() method are recorded in the current context. The ContextVar.get() method returns the value associated with the current context. Exiting a context effectively reverts any changes made to context variables while the context was entered (if needed, the values can be restored by re-entering the context).

Context는 collections.abc.Mapping 인터페이스를 구현합니다.

run(callable, *args, **kwargs)

Enters the Context, executes callable(*args, **kwargs), then exits the Context. Returns callable’s return value, or propagates an exception if one occurred.

Example:

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

contextvar 의 값이 설정되었으면 True 를, 그렇지 않으면 False를 반환합니다.

context[var]

var ContextVar 변수의 값을 돌려줍니다. 컨텍스트 객체에 변수가 설정되어 있지 않으면 KeyError 가 발생합니다.

get(var[, default])

컨텍스트 객체에 var 의 값이 있으면, var 의 값을 돌려줍니다. 그렇지 않으면 default 를 반환합니다. default 가 주어지지 않으면 None 을 반환합니다.

iter(context)

컨텍스트 객체에 저장된 변수에 대한 이터레이터를 반환합니다.

len(proxy)

컨텍스트 객체에 설정된 변수의 개수를 반환합니다.

keys()

컨텍스트 객체의 모든 변수 목록을 반환합니다.

values()

컨텍스트 객체의 모든 변수의 값 목록을 반환합니다.

items()

컨텍스트 객체에서 모든 변수와 해당 값을 포함하는 2-튜플의 목록을 반환합니다.

asyncio 지원

컨텍스트 변수는 asyncio 에서 기본적으로 지원되며 추가 구성없이 사용할 수 있습니다. 예를 들어, 이것은 컨텍스트 변수를 사용하여, 원격 클라이언트의 주소를 해당 클라이언트를 처리하는 Task에서 사용할 수 있도록 하는 간단한 메아리 서버입니다:

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