contextlib — with 문 컨텍스트 유틸리티

contextlib — with 문 컨텍스트 유틸리티

(소스: Lib/contextlib.py)

이 모듈은 with 문과 관련된 공통 작업을 위한 유틸리티를 제공해요. 자세한 내용은 "Context Manager Types"와 "With Statement Context Managers"를 함께 보세요.

출처: Python documentation

본문

유틸리티

  • class contextlib.AbstractContextManager: __enter__()__exit__()을 구현하는 클래스들의 추상 기본 클래스. __enter__()의 기본 구현은 self를 반환하고, __exit__()은 기본 None을 반환하는 추상 메서드예요. (3.6 추가)
  • class contextlib.AbstractAsyncContextManager: __aenter__()__aexit__()을 구현하는 클래스들의 추상 기본 클래스. __aenter__()의 기본 구현은 self를 반환하고, __aexit__()은 기본 None을 반환하는 추상 메서드예요. (3.7 추가)

@contextlib.contextmanager

with 문 컨텍스트 매니저용 팩토리 함수를, 클래스를 만들거나 별도의 __enter__()·__exit__() 메서드를 만들지 않고 정의할 수 있게 해 주는 데코레이터예요. 많은 객체가 with 문을 네이티브로 지원하지만, 때로는 그 자체로 컨텍스트 매니저가 아니고 contextlib.closing에 쓸 close() 메서드도 구현하지 않은 리소스를 관리해야 해요. 올바른 리소스 관리를 보장하는 추상 예시를 볼게요.

from contextlib import contextmanager
@contextmanager
def managed_resource(*args, **kwds):
    resource = acquire_resource(*args, **kwds)
    try:
        yield resource
    finally:
        release_resource(resource)

이렇게 사용할 수 있어요.

>>> with managed_resource(timeout=3600) as resource:
...     # Resource is released at the end of this block,
...     # even if code in the block raises an exception

데코레이트된 함수는 호출 시 generator-iterator를 반환해야 해요. 이 iterator는 정확히 하나의 값을 yield해야 하고, 그 값이 with 문의 as 절 타깃에 바인딩돼요. generator가 yield하는 지점에서 with 문 안에 중첩된 블록이 실행되고, 블록을 빠져나온 후 generator가 재개돼요. 블록에서 처리되지 않은 예외가 발생하면 generator 안의 yield가 일어난 지점에서 다시 발생해요. 그래서 try…except…finally 문으로 오류를 잡거나 정리를 보장할 수 있어요. 예외를 완전히 억제하기보다 단지 로그를 남기거나 어떤 동작을 하기 위해 잡았다면 generator는 그 예외를 다시 발생시켜야 해요. 그렇지 않으면 컨텍스트 매니저가 with 문에 예외가 처리됐다고 알리고 with 문 바로 다음 문장에서 실행이 재개돼요.

@contextmanagerContextDecorator를 사용하므로 만든 컨텍스트 매니저를 with 문뿐 아니라 데코레이터로도 쓸 수 있어요. 데코레이터로 쓰면 각 함수 호출 시 새 generator 인스턴스가 암시적으로 생성돼요(이로써 "원샷" 컨텍스트 매니저가 데코레이터로 쓰이기 위해 필요한 여러 번 호출을 지원). (3.2에서 ContextDecorator 사용)

@contextlib.asynccontextmanager

@contextmanager와 비슷하지만 비동기 컨텍스트 매니저를 만들어요. async with 문용 비동기 컨텍스트 매니저 팩토리 함수를, 클래스를 만들거나 별도의 __aenter__()·__aexit__() 메서드를 만들지 않고 정의할 수 있어요. 비동기 generator 함수에 적용해야 해요.

기타 유틸리티

  • contextlib.closing(thing): thing.close()를 블록 종료 시 호출하는 컨텍스트 매니저를 반환. 닫힘을 지원하지만 컨텍스트 매니저가 아닌 것을 with 문으로 다룰 때 유용.
  • contextlib.aclosing(thing): 비동기 버전. __aexit__에서 await thing.aclose()를 호출.
  • @contextlib.asynccontextmanager, contextlib.nullcontext(enter_result=None): 아무 일도 하지 않는 컨텍스트 매니저. 무엇을 해야 할지 결정할 수 없을 때 특정 자원을 선택적으로 사용하는 데 유용. enter_result를 as 절에 반환.
  • @contextlib.suppress(*exceptions): 명시된 예외를 억제하는 컨텍스트 매니저. with 블록에서 그 예외 중 하나가 발생하면 정상 흐름을 계속.
  • @contextlib.redirect_stdout(new_target) / @contextlib.redirect_stderr(new_target): 블록 동안 sys.stdout/sys.stderrnew_target으로 임시 리다이렉트.
  • class contextlib.ContextDecorator: 컨텍스트 매니저를 데코레이터로도 쓸 수 있게 해 주는 기본 클래스.
  • class contextlib.ExitStack: 다른 (동적) 컨텍스트 매니저와 정리 함수를 관리하는 스택. 프로토콜 처리, 한 문장에서 종료, 예외가 있으면 __exit__에서 예외를 다시 발생시키는 등의 유연한 조합을 가능하게 해요. enter_context(cm), push(cm), callback(func, /, *args, **kwds), pop_all(), close() 메서드 제공.
  • class contextlib.AsyncExitStack: 비동기 버전. enter_async_context(cm) 제공.

예제와 레시피

이 모듈의 예제·레시피 문서는 다음을 소개해요.

  • 가변 개수의 컨텍스트 매니저 지원: ExitStack가 컨텍스트 매니저 수가 가변적일 때 어떻게 쓰는지.
  • __enter__ 메서드의 예외 잡기: ExitStack.push() 등으로 __enter__에서 발생한 예외를 처리.
  • __enter__ 구현에서 정리: ExitStack.push로 자원 획득을 추적하고 실패 시 정리.
  • try-finally와 플래그 변수 대체: @contextmanagerExitStack로 정리를 단순화.
  • 컨텍스트 매니저를 함수 데코레이터로 사용하기: ContextDecorator@contextmanager 조합.
  • 단일 사용·재사용·재진입 컨텍스트 매니저: @contextmanager로 만든 매니저는 원샷이지만, 무한 생성기나 재진입 가능하게 만들 수 있는 방법. 재진입 컨텍스트 매니저는 이미 진입한 상태에서 다시 진입할 수 있고, 재사용 컨텍스트 매니저는 여러 번 사용할 수 있어요.

더 알아보기 (Learn more)