# `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 인자는 컨텍스트 변수의 새 값입니다.

    `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`

    읽기 전용 프로퍼티. 토큰을 생성 한 `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)

    run 메서드가 호출된 컨텍스트 객체에서 `callable(*args, **kwargs)` 코드를 실행합니다. 실행 결과를 반환하거나 예외가 발생하면 예외를 전파합니다.

    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'

    이 메서드는 둘 이상의 OS 스레드에서 같은 컨텍스트 객체에 대해 호출될 때나 재귀적으로 호출될 때 `RuntimeError` 를 발생시킵니다.

  `copy`()

    컨텍스트 객체의 얕은 복사본을 반환합니다.

  `var in context`

    context 에 var 의 값이 설정되었으면 `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}\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