비동기식 컨텍스트 매니저는 상당히 논리적으로 비동기식 환경에서 작동하는 컨텍스트 매니저 개념의 확장이며 비동기식 기반 라이브러리 인터페이스에서 많이 사용된다.
비동기 컨텍스트 매니저는 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__와 깔끔하게 미러링한다.