contextvars — 컨텍스트 변수
contextvars — 컨텍스트 변수
이 모듈은 컨텍스트-로컬 상태를 관리, 저장, 접근하기 위한 API를 제공합니다. ContextVar 클래스는 컨텍스트 변수를 선언하고 다루는 데 사용되며, copy_context() 함수와 Context 클래스는 비동기 프레임워크에서 현재 컨텍스트를 관리하는 데 사용됩니다.
본문
상태를 갖는 컨텍스트 매니저는 동시 실행 코드에서 상태가 다른 코드로 의도치 않게 새어나가는 것을 막기 위해 threading.local() 대신 컨텍스트 변수를 사용해야 합니다. 자세한 내용은 PEP 567을 참고하세요. 컨텍스트 변수는 Python 3.7에서 추가되었습니다.
contextvars.ContextVar(name[, *, default]) 클래스는 새로운 컨텍스트 변수를 선언하는 데 사용합니다. 예를 들어 다음처럼 선언합니다:
var: ContextVar[int] = ContextVar('var', default=42)
필수 name 매개변수는 인트로스펙션(introspection)과 디버그 목적으로 사용됩니다. 선택적인 키워드 전용 default 매개변수는 현재 컨텍스트에서 변수 값이 없을 때 ContextVar.get()이 반환하는 값입니다.
중요: 컨텍스트 변수는 모듈 최상위 레벨에서 만들어야 하며 클로저 안에서는 만들면 안 됩니다. Context 객체는 컨텍스트 변수에 대한 강한 참조를 유지하므로 컨텍스트 변수가 가비지 컬렉션되지 못할 수 있습니다. ContextVar는 담고 있는 값의 타입에 대해 제네릭(generic)입니다.
ContextVar는 다음 속성과 메서드를 제공합니다:
name— 변수의 이름입니다. 읽기 전용 속성입니다 (3.7.1에서 추가).get([default])— 현재 컨텍스트에서 컨텍스트 변수의 값을 반환합니다. 값이 없으면 메서드의default인자를 반환하거나, 변수가 기본값으로 생성되었으면 그 기본값을 반환하며, 둘 다 아니면LookupError를 발생시킵니다.set(value)— 현재 컨텍스트에서 컨텍스트 변수에 새 값을 설정합니다.ContextVar.reset()으로 변수를 이전 값으로 복원하는 데 사용할 수 있는Token객체를 반환합니다.reset(token)— 컨텍스트 변수를 해당token을 만든ContextVar.set()호출 이전의 값으로 되돌립니다. 같은 토큰은 두 번 사용할 수 없습니다.
contextvars.Token 객체는 ContextVar.set()이 반환하며, ContextVar.reset()에 전달하여 변수 값을 이전 상태로 되돌릴 수 있습니다. 하나의 토큰으로 컨텍스트 변수를 두 번 이상 리셋할 수는 없습니다. 토큰은 컨텍스트 매니저 프로토콜을 지원하여 컨텍스트 변수를 자동으로 리셋할 수 있게 합니다 (3.14에서 추가). Token.var는 토큰을 만든 ContextVar 객체를 가리키는 읽기 전용 속성이고, Token.old_value는 set 호출 이전의 값을 담고 있으며, 값이 없던 경우 Token.MISSING을 가리킵니다.
contextvars.copy_context()는 현재 Context 객체의 복사본을 반환합니다. 다음 코드는 현재 컨텍스트의 복사본을 얻어 설정된 모든 변수와 값을 출력합니다:
ctx: Context = copy_context()
print(list(ctx.items()))
이 함수는 O(1) 복잡도를 갖습니다.
contextvars.Context는 컨텍스트 변수와 그 값의 매핑입니다. Context()는 값이 없는 빈 컨텍스트를 만들고, 현재 컨텍스트의 복사본은 copy_context()로 얻습니다. 각 스레드는 자신만의 Context 객체 스택을 가지며, 현재 컨텍스트는 현재 스레드 스택의 최상위에 있습니다. 컨텍스트의 run()을 호출해 들어가면 현재 컨텍스트가 되고, run()에 전달한 콜백에서 반환하면 나가면서 이전 컨텍스트가 복원됩니다. 이미 들어간 컨텍스트(다른 스레드에서 들어간 것 포함)에 다시 들어가려 하면 RuntimeError가 발생합니다.
컨텍스트 변수는 비동기 프로그래밍에서 특히 유용합니다. asyncio의 Task는 각각 자신의 컨텍스트 복사본을 가지므로, 요청을 처리하는 Task에서 원격 클라이언트의 주소 같은 값을 인자로 명시적으로 전달하지 않고도 컨텍스트 변수로 접근할 수 있습니다. 예를 들어 client_addr_var.set(addr)로 설정해 두면 연결된 코드 어디서든 client_addr_var.get()으로 클라이언트 주소를 얻을 수 있습니다.