비동기식 컨텍스트 매니저는 상당히 논리적으로 비동기식 환경에서 작동하는 컨텍스트 매니저 개념의 확장이며 비동기식 기반 라이브러리 인터페이스에서 많이 사용된다.
비동기 컨텍스트 매니저는 async with
문에서 사용할 수 있는 객체이다. 이에 대한 예는 다음과 같다.
async with FlowProvider(store_url) as provider:
async with provider.open_read(flow_id, config=config) as reader:
frames = await reader.read(720, count=480)
# Do other things using reader
...
# Do other things using provider
...
# Do something with frames
...
위의 예에서 FlowProvider
메소드는 비동기 컨텍스트 매니저를 반환하며 provider.open_read
도 그러하다.
FlowProvider
에 대해 일부 리소스를 가져오거나 설정하고 그 결과는 provider
에게 바인딩 된다.procider.open_read
에 대해 추가 리소스를 가져오거나 설정을 수행하고 그 결과는 reader
에게 바인딩 된다.reader
객체를 사용할 수 있으므로 프레임 목록을 반환하는 코루틴 reader.read
를 await할 수 있다.reader
를 사용하는 다른 작업이 수행된다.async with
문의 코드 블록이 일부 구성을 완료했으므로 reader
에 대한 리소스 할당이 취소된다.provider
를 사용하는 다른 작업이 수행된다.async with
문의 코드 블록이 일부 구성을 완료했으므로 provider
에 대한 리소스 할당이 취소된다.reader
, provider
정리가 완료되었지만 frames
와 같은 변수는 여전히 액세스할 수 있고 values를 보유한다.이것은 본질적으로 with
문을 사용하는 일반 동기 컨텍스트 매니저의 사용 및 프로세스와 동일하다.
차이점은 시작(entry)과 종료(exit) 시 수행되는 설정(setup) 및 해제(teardown)는 비동기 코루틴을 기다리면서 수행된다는 점이다.
이는 컨텍스트의 시작 및 종료를 위해 비동기식 컨텍스트 매니저에서 제공되는 코드가 비동기식 코드일 수 있고(즉 await
, 명령문을 포함할 수 있음) 또한 async with
비동기식 코드가 허용되는 컨텍스트에서만 사용할 수 있음을 의미한다.(예: 코루틴 함수의 코드 블록 내부).
그러나 메소드 FlowProvider
및 provider.open_read
는 코루틴 메소드가 아니며 비동기 컨텍스트 매니저 객체를 반환하는 일반적인 메서드이다. 이는 일반적이며 비동기 코루틴을 반환하는 코루틴을 보는 것은 매우 드물다.
사실 async with
문은 await
문을 포함한 더 복잡한 코드블럭을 작성하기 위한 줄임말이다.
async with AsyncCM as ctx:
...
# Is the same as:
ctx = await AsyncCM.__aenter__()
try:
...
except Exception as e:
if not await AsyncCM.__aexit__(type(e), e, e.__traceback__):
raise e
else:
await AsyncCM.__aexit__(None, None, None)
매직 코루틴 매소드를 수행하는 클래스를 만들어 자신만의 비동기 컨택스트 매니저를 쉽게 정의 가능하다.
async def __aenter__(self):
...
async def __aexit__(self, exc_t, exc_v, exc_tb):
...
__aexit__
를 취하는 파라미터와 이러한 코루틴의 반환 값들은 다음과 같이 정의된다.
__aenter__
의 반환 값은 무엇이든 될 수 있다. 반환 값이 무엇이든async with
문에서 as
절에 의해 바인딩 될 객체이다.async with
문의 코드블럭이 예외 없이 끝에 도달하면 __aexit__
는 세 개의 매개변수를 모두 None으로 사용하여 호출되며 반환 값은 무시된다.async with
문의 코드블럭이 예외를 발생하면 __aexit__
는 세 개의 매개변수를 예외의 타입, 예외 객체 그 자체, 그리고 "traceback"으로 호출한다. 만약 Ture
를 반환한다면 (또는 사실로로 평가되는 것) 시스템은 예외가 처리 및 수정된 것으로 가정하고 더 이상 propagate(전파)하지 않는다. False
, None
, 거짓으로 평가되는것, 아무것도 반환되지 않으면 예외가 계속 전파된다.이 동작은 동기 컨택스트 매니저를 정의할 때 사용되는 매직메소드 __enter__
, __exit__
와 깔끔하게 미러링한다.