warnings — 경고 제어

warnings — 경고 제어

warnings 모듈은 프로그램 안의 어떤 조건에 대해 사용자에게 알리는 것이 유용한 상황에서 경고 메시지를 내보내요. 그 조건이 (보통) 예외를 일으키고 프로그램을 종료시킬 정도는 아닐 때 쓰이죠. 예를 들어 프로그램이 구식 모듈을 사용할 때 경고를 내고 싶을 수 있어요.

출처: Python 표준 라이브러리

본문

경고 메시지는 보통 프로그램 안의 어떤 조건(보통 예외를 일으키고 프로그램을 종료할 이유는 없는)을 사용자에게 알리는 것이 유용한 상황에서 발생합니다. 예를 들어 프로그램이 구식(obsolete) 모듈을 쓸 때 경고를 내고 싶을 수 있어요.

Python 프로그래머는 이 모듈에 정의된 warn() 함수를 호출해 경고를 발생시킵니다. (C 프로그래머는 PyErr_WarnEx()를 사용합니다.)

경고 메시지는 보통 sys.stderr에 기록되지만, 그 처리는 모든 경고를 무시하는 것부터 예외로 바꾸는 것까지 유연하게 바꿀 수 있어요. 경고의 처리는 경고 범주(category), 경고 메시지 텍스트, 경고가 발생한 소스 위치에 따라 달라질 수 있습니다. 같은 소스 위치에 대한 특정 경고의 반복은 보통 억제됩니다.

경고 제어에는 두 단계가 있어요. 첫째, 경고가 발생할 때마다 메시지를 내보낼지 말지 판단합니다. 둘째, 메시지를 내보내기로 했다면 사용자가 설정할 수 있는 훅으로 형식화해 출력합니다.

경고 메시지를 내보낼지 여부는 경고 필터(warning filter)가 제어하며, 이는 일치 규칙과 동작(action)의 시퀀스입니다. 규칙은 filterwarnings()를 호출해 필터에 추가하고, resetwarnings()를 호출해 기본 상태로 리셋할 수 있어요.

경고 메시지의 출력은 showwarning()을 호출해 이루어지며, 이는 오버라이드될 수 있습니다. 이 함수의 기본 구현은 formatwarning()을 호출해 메시지를 형식화하며, 이 역시 커스텀 구현에서 사용할 수 있어요.

참고logging.captureWarnings()로 모든 경고를 표준 logging 인프라로 처리할 수 있습니다.

경고 범주 (Warning Categories)

경고 범주를 나타내는 내장 예외가 여럿 있습니다. 이런 분류는 경고 그룹을 걸러내는 데 유용해요. 이들은 기술적으로 내장 예외이지만 개념적으로 경고 메커니즘에 속하므로 여기서 문서화합니다.

사용자 코드는 표준 경고 범주 중 하나를 하위 클래스화해 추가 경고 범주를 정의할 수 있어요. 경고 범주는 항상 Warning 클래스의 하위 클래스여야 합니다.

현재 정의된 경고 범주 클래스는 다음과 같아요.

클래스 설명
Warning 경고 범주의 기본 클래스. Exception의 하위 클래스.
UserWarning 사용자 코드가 생성한 경고의 기본 클래스. warn()의 기본 범주.
DeprecationWarning 다른 Python 개발자를 대상으로 하는 폐기 기능 경고의 기본 클래스 (기본적으로 무시되며, __main__의 코드가 트리거한 경우 제외).
PendingDeprecationWarning 향후 폐기될 기능에 대한 경고의 기본 클래스 (기본적으로 무시됨).
SyntaxWarning 의심스러운 문법에 대한 경고의 기본 클래스 (보통 Python 소스 컴파일 시 발생하므로 런타임 필터로 억제되지 않을 수 있음).
RuntimeWarning 의심스러운 런타임 동작에 대한 경고의 기본 클래스.
FutureWarning Python으로 작성된 애플리케이션의 최종 사용자를 대상으로 하는 폐기 기능 경고의 기본 클래스.
ImportWarning 모듈을 import하는 과정에서 트리거되는 경고의 기본 클래스 (기본적으로 무시됨).
UnicodeWarning Unicode 관련 경고의 기본 클래스.
EncodingWarning 인코딩 관련 경고의 기본 클래스. 자세한 내용은 Opt-in EncodingWarning 참고.
BytesWarning bytes 및 bytearray 관련 경고의 기본 클래스.
ResourceWarning 리소스 사용 관련 경고의 기본 클래스 (기본적으로 무시됨).

버전 3.7에서 변경: 이전에는 DeprecationWarningFutureWarning이 기능이 완전히 제거되는지 동작이 바뀌는지로 구분됐습니다. 지금은 의도된 대상 그룹과 기본 경고 필터가 처리하는 방식으로 구분돼요.

경고 필터 (The Warnings Filter)

경고 필터는 경고를 무시할지, 표시할지, 오류(예외 발생)로 바꿀지를 제어합니다.

개념적으로 경고 필터는 필터 사양의 정렬된 목록을 유지합니다. 특정 경고는 일치가 발견될 때까지 목록의 각 필터 사양과 차례로 대조돼요. 필터가 일치의 처리를 결정합니다. 각 항목은 (action, message, category, module, lineno) 형태의 튜플입니다.

action은 다음 문자열 중 하나예요.

처리
"default" 경고가 발생한 각 위치(모듈 + 줄 번호)에 대해 일치하는 경고의 첫 발생만 출력
"error" 일치하는 경고를 예외로 바꿈
"ignore" 일치하는 경고를 절대 출력하지 않음
"always" 일치하는 경고를 항상 출력
"all" "always"의 별칭
"module" 경고가 발생한 각 모듈에 대해 일치하는 경고의 첫 발생만 출력 (줄 번호 무관)
"once" 위치 무관하게 일치하는 경고의 첫 발생만 출력

message는 경고 메시지의 시작 부분이 대소문자 무시로 일치해야 하는 정규식을 담은 문자열입니다. -WPYTHONWARNINGS에서는 message는 경고 메시지의 시작이 (대소문자 무시로) 포함해야 하는 리터럴 문자열이며, 시작·끝의 공백은 무시해요.

category는 경고 범주가 일치하려면 (Warning의) 하위 클래스여야 하는 클래스입니다.

module은 완전한 모듈 이름의 시작 부분이 대소문자 구분으로 일치해야 하는 정규식을 담은 문자열입니다. -WPYTHONWARNINGS에서는 module은 완전한 모듈 이름과 (대소문자 구분으로) 같아야 하는 리터럴 문자열이며, 시작·끝의 공백은 무시해요.

lineno는 경고가 발생한 줄 번호가 일치해야 하는 정수이며, 모든 줄 번호에 일치하려면 0입니다.

Warning 클래스는 내장 Exception 클래스에서 파생되므로, 경고를 오류로 바꾸려면 그냥 category(message)를 일으키면 됩니다.

경고가 보고되는데 등록된 필터와 일치하지 않으면 "default" 동작이 적용됩니다 (그래서 그런 이름이 붙었죠).

반복 경고 억제 기준 (Repeated Warning Suppression Criteria)

반복 경고를 억제하는 필터는 경고가 반복으로 간주되는지 결정하는 다음 기준을 적용합니다.

  • "default": (message, category, module, lineno)가 모두 같을 때만 반복으로 간주.
  • "module": 줄 번호를 무시하고 (message, category, module)이 같으면 반복으로 간주.
  • "once": 모듈과 줄 번호를 무시하고 (message, category)가 같으면 반복으로 간주.

경고 필터 설명하기 (Describing Warning Filters)

경고 필터는 Python 인터프리터 명령줄에 넘긴 -W 옵션과 PYTHONWARNINGS 환경 변수로 초기화됩니다. 인터프리터는 제공된 모든 항목의 인자를 해석 없이 sys.warnoptions에 저장하고, warnings 모듈이 처음 import될 때 이것을 파싱합니다 (잘못된 옵션은 sys.stderr에 메시지를 출력한 뒤 무시돼요).

개별 경고 필터는 콜론으로 구분된 필드의 시퀀스로 지정됩니다.

action:message:category:module:line

각 필드의 의미는 경고 필터에서 설명한 것과 같아요. 한 줄에 여러 필터를 나열할 때(PYTHONWARNINGS처럼) 개별 필터는 쉼표로 구분되며, 나중에 나열된 필터가 앞선 것보다 우선합니다 (왼쪽에서 오른쪽으로 적용되며, 가장 최근에 적용된 필터가 이른 것보다 우선하기 때문).

흔히 쓰는 경고 필터는 모든 경고, 특정 범주의 경고, 또는 특정 모듈·패키지가 발생시킨 경고에 적용됩니다. 몇 가지 예를 들어 볼게요.

default                      # Show all warnings (even those ignored by default)
ignore                       # Ignore all warnings
error                        # Convert all warnings to errors
error::ResourceWarning       # Treat ResourceWarning messages as errors
default::DeprecationWarning  # Show DeprecationWarning messages
ignore,default:::mymodule    # Only report warnings triggered by "mymodule"
error:::mymodule             # Convert warnings to errors in "mymodule"

기본 경고 필터 (Default Warning Filter)

기본적으로 Python은 여러 경고 필터를 설치하며, -W 명령줄 옵션, PYTHONWARNINGS 환경 변수, filterwarnings() 호출로 오버라이드할 수 있어요.

일반 릴리스 빌드에서 기본 경고 필터는 (우선순위 순으로) 다음 항목을 가집니다.

default::DeprecationWarning:__main__
ignore::DeprecationWarning
ignore::PendingDeprecationWarning
ignore::ImportWarning
ignore::ResourceWarning

디버그 빌드에서는 기본 경고 필터 목록이 비어 있습니다.

버전 3.2에서 변경: PendingDeprecationWarning에 더해 DeprecationWarning도 기본적으로 무시됩니다. 버전 3.7에서 변경: __main__의 코드가 직접 트리거하면 DeprecationWarning이 다시 기본적으로 표시됩니다. 버전 3.7에서 변경: BytesWarning은 더 이상 기본 필터 목록에 없고, -b를 두 번 지정할 때 sys.warnoptions를 통해 구성됩니다.

기본 필터 오버라이드하기 (Overriding the default filter)

Python으로 작성된 애플리케이션 개발자는 기본적으로 모든 Python 수준 경고를 사용자에게 숨기고, 테스트를 실행하거나 애플리케이션을 작업할 때만 표시하고 싶을 수 있어요. 필터 구성을 인터프리터에 전달하는 데 쓰던 sys.warnoptions 속성을 경고를 비활성화할지 표시하는 표시자로 쓸 수 있습니다.

import sys

if not sys.warnoptions:
    import warnings
    warnings.simplefilter("ignore")

Python 코드용 테스트 러너 개발자는 대신 테스트 대상 코드에 대해 모든 경고가 기본적으로 표시되도록 해야 합니다. 다음 코드를 쓰세요.

import sys

if not sys.warnoptions:
    import os, warnings
    warnings.simplefilter("default") # Change the filter in this process
    os.environ["PYTHONWARNINGS"] = "default" # Also affect subprocesses

끝으로, __main__이 아닌 네임스페이스에서 사용자 코드를 실행하는 대화형 셸의 개발자는 DeprecationWarning 메시지가 기본적으로 보이도록 해야 합니다. 다음 코드를 쓰면 돼요 (user_ns는 대화형으로 실행되는 코드를 실행하는 데 사용된 모듈).

import warnings
warnings.filterwarnings("default", category=DeprecationWarning,
                                   module=user_ns.get("__name__"))

경고 일시적으로 억제하기 (Temporarily Suppressing Warnings)

폐기된 함수처럼 경고를 일으킬 것으로 아는 코드를 쓰는데, (명령줄로 명시적으로 경고를 구성했어도) 경고를 보고 싶지 않다면 catch_warnings 컨텍스트 관리자로 경고를 억제할 수 있어요.

import warnings

def fxn():
    warnings.warn("deprecated", DeprecationWarning)

with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    fxn()

컨텍스트 관리자 안에 있는 동안 모든 경고는 그냥 무시됩니다. 이를 통해 폐기된 것으로 알려진 코드를 경고를 보지 않고 쓸 수 있으면서, 폐기 코드 사용을 모를 수 있는 다른 코드의 경고는 억제하지 않습니다.

참고 — 여러 스레드나 async 함수를 쓰는 프로그램에서 catch_warnings 컨텍스트 관리자의 동시성 안전성에 대한 자세한 내용은 컨텍스트 관리자의 동시성 안전성을 참고하세요.

경고 테스트하기 (Testing Warnings)

코드가 발생시키는 경고를 테스트하려면 catch_warnings 컨텍스트 관리자를 사용해요. 이것으로 경고 필터를 임시로 변경해 테스트를 쉽게 할 수 있습니다. 예를 들어 발생한 모든 경고를 잡아 확인하려면 다음을 합니다.

import warnings

def fxn():
    warnings.warn("deprecated", DeprecationWarning)

with warnings.catch_warnings(record=True) as w:
    # Cause all warnings to always be triggered.
    warnings.simplefilter("always")
    # Trigger a warning.
    fxn()
    # Verify some things
    assert len(w) == 1
    assert issubclass(w[-1].category, DeprecationWarning)
    assert "deprecated" in str(w[-1].message)

erroralways 대신 쓰면 모든 경고를 예외로 만들 수도 있어요. 알아 둘 점은, 경고가 once/default 규칙으로 이미 발생했다면 어떤 필터를 설정해도 그 경고는 경고 관련 registry를 비우지 않는 한 다시 보이지 않는다는 것입니다.

컨텍스트 관리자가 끝나면 경고 필터는 컨텍스트에 들어왔을 때의 상태로 복원됩니다. 이는 테스트가 테스트 사이에 경고 필터를 뜻밖에 바꿔 결정적이지 않은 테스트 결과를 내는 것을 막아요.

참고 — 여러 스레드나 async 함수를 쓰는 프로그램에서 catch_warnings 컨텍스트 관리자의 동시성 안전성은 컨텍스트 관리자의 동시성 안전성을 참고하세요.

같은 종류의 경고를 발생시키는 여러 작업을 테스트할 때는 각 작업이 새 경고를 발생시키는지 확인하는 방식으로 테스트하는 것이 중요해요 (예: 경고가 예외가 되도록 설정하고 작업이 예외를 일으키는지 확인하거나, 각 작업 후 경고 목록 길이가 계속 늘어나는지 확인하거나, 각 새 작업 전에 경고 목록에서 이전 항목을 삭제).

새 버전 의존성에 맞게 코드 갱신하기 (Updating Code For New Versions of Dependencies)

주로 Python 개발자에게 관심 있는 경고 범주(Python으로 작성된 애플리케이션의 최종 사용자가 아닌)는 기본적으로 무시됩니다.

특히 이 "기본적으로 무시됨" 목록에는 DeprecationWarning(__main__을 제외한 모든 모듈)이 포함됩니다. 이는 미래의 API 변경(표준 라이브러리든 제3자 패키지든)에 대한 적시의 알림을 받으려면 개발자가 보통 무시되는 경고를 보이게 만들어 코드를 테스트해야 한다는 뜻이에요.

이상적인 경우 코드는 적절한 테스트 모음이 있고, 테스트 러너가 테스트 실행 시 모든 경고를 암묵적으로 활성화해 줍니다 (unittest 모듈이 제공하는 테스트 러너가 이렇게 해요). 덜 이상적인 경우, 애플리케이션이 폐기 인터페이스를 쓰는지 -Wd(이것은 -W default의 줄임)를 Python 인터프리터에 넘기거나 환경에 PYTHONWARNINGS=default를 설정해 확인할 수 있습니다. 그러면 기본적으로 무시되는 경고를 포함한 모든 경고에 기본 처리가 활성화됩니다. 마주친 경고에 취할 동작을 바꾸려면 -W에 넘기는 인자를 바꾸면 됩니다 (예: -W error).

사용 가능한 함수 (Available Functions)

warnings.warn(message, category=None, stacklevel=1, source=None, *, skip_file_prefixes=())

경고를 발생시키거나, 무시하거나, 예외를 일으킵니다. category 인자는 주어지면 경고 범주 클래스여야 하며, 기본은 UserWarning이에요. 또는 messageWarning 인스턴스일 수 있는데, 그 경우 category는 무시되고 message.__class__가 사용됩니다. 이때 메시지 텍스트는 str(message)가 돼요. 이 함수는 특정 경고가 경고 필터에 의해 오류로 바뀌면 예외를 일으킵니다.

stacklevel 인자는 파이썬으로 작성된 래퍼 함수에서 이렇게 쓸 수 있어요.

def deprecated_api(message):
    warnings.warn(message, DeprecationWarning, stacklevel=2)

이렇게 하면 경고가 deprecated_api 자체의 소스가 아니라 deprecated_api의 호출자를 가리키게 됩니다 (후자는 경고 메시지의 목적을 무너뜨리니까요).

skip_file_prefixes 키워드 인자는 스택 수준을 셀 때 무시할 스택 프레임을 나타냅니다. 고정 stacklevel이 모든 호출 경로에 맞지 않거나 유지하기 어려울 때, 경고가 항상 패키지 밖의 호출 지점에 나타나길 원한다면 유용해요. 제공한다면 문자열 튜플이어야 합니다. 접두어가 제공되면 stacklevel은 암묵적으로 max(2, stacklevel)로 오버라이드됩니다. 현재 패키지 밖의 호출자에 경고가 귀속되게 하려면 이렇게 쓸 수 있습니다.

# example/lower.py
_warn_skips = (os.path.dirname(__file__),)

def one_way(r_luxury_yacht=None, t_wobbler_mangrove=None):
    if r_luxury_yacht:
        warnings.warn("Please migrate to t_wobbler_mangrove=.",
                      skip_file_prefixes=_warn_skips)

# example/higher.py
from . import lower

def another_way(**kw):
    lower.one_way(**kw)

그러면 경고가 example 패키지 밖에 있는 호출 코드에서만 example.lower.one_way()example.higher.another_way() 호출 지점을 가리키게 됩니다.

source는 제공되면 ResourceWarning을 발생시킨 파괴된 객체입니다.

버전 3.6에서 변경: source 매개변수 추가. 버전 3.12에서 변경: skip_file_prefixes 추가.

warnings.warn_explicit(message, category, filename, lineno, module=None, registry=None, module_globals=None, source=None)

warn() 기능을 위한 저수준 인터페이스로, message, category, filename, lineno와 선택적으로 다른 인자를 명시적으로 넘깁니다. message는 문자열이어야 하고 categoryWarning의 하위 클래스이거나, messageWarning 인스턴스일 수 있는데 그 경우 category는 무시됩니다.

module은 주어지면 모듈 이름이어야 해요. 모듈을 넘기지 않으면 .py가 제거된 filename이 사용됩니다.

registry는 주어지면 모듈의 __warningregistry__ 딕셔너리여야 합니다. registry를 넘기지 않으면 각 경고가 첫 발생으로 취급되어, 필터 동작 "default", "module", "once""always"로 처리됩니다.

module_globals는 주어지면 경고가 발생하는 코드가 사용하는 전역 네임스페이스여야 합니다. (이 인자는 zipfile이나 다른 비-파일시스템 import 소스에서 찾은 모듈의 소스 표시를 지원하는 데 사용됩니다.)

source는 제공되면 ResourceWarning을 발생시킨 파괴된 객체입니다.

버전 3.6에서 변경: source 매개변수 추가.

warnings.showwarning(message, category, filename, lineno, file=None, line=None)

경고를 파일에 씁니다. 기본 구현은 formatwarning(message, category, filename, lineno, line)을 호출하고 결과 문자열을 기본적으로 sys.stderrfile에 씁니다. warnings.showwarning에 할당해 이 함수를 어떤 callable로든 교체할 수 있어요. line은 경고 메시지에 포함할 소스 코드 한 줄입니다. line을 주지 않으면 showwarning()filenamelineno가 지정하는 줄을 읽으려 시도해요.

warnings.formatwarning(message, category, filename, lineno, line=None)

경고를 표준 방식으로 형식화합니다. 포함된 개행이 있을 수 있고 개행으로 끝나는 문자열을 반환해요. line은 경고 메시지에 포함할 소스 코드 한 줄입니다. line을 주지 않으면 formatwarning()filenamelineno가 지정하는 줄을 읽으려 시도합니다.

warnings.filterwarnings(action, message='', category=Warning, module='', lineno=0, append=False)

경고 필터 사양 목록에 항목을 삽입합니다. 항목은 기본적으로 맨 앞에 삽입되며, append가 참이면 끝에 삽입돼요. 이 함수는 인자의 타입을 검사하고 messagemodule 정규식을 컴파일한 뒤 튜플로 경고 필터 목록에 삽입합니다. 목록 앞쪽에 더 가까운 항목은 특정 경고에 둘 다 일치하면 뒤쪽 항목보다 우선합니다. 생략된 인자는 모든 것에 일치하는 값이 기본값이에요.

warnings.simplefilter(action, category=Warning, lineno=0, append=False)

경고 필터 사양 목록에 간단한 항목을 삽입합니다. 함수 매개변수의 의미는 filterwarnings()와 같지만, 삽입되는 필터는 범주와 줄 번호만 일치하면 어떤 모듈의 어떤 메시지와도 항상 일치하므로 정규식이 필요 없습니다.

warnings.resetwarnings()

경고 필터를 리셋합니다. 이는 이전 filterwarnings() 호출의 효과를 모두 버리며, -W 명령줄 옵션과 simplefilter() 호출도 포함해요.

@warnings.deprecated(message, /, *, category=DeprecationWarning, stacklevel=1)

클래스, 함수 또는 오버로드가 폐기되었음을 나타내는 데코레이터입니다.

객체에 적용하면 그 객체를 사용할 때 런타임에 폐기(deprecation) 경고가 발생할 수 있어요. 정적 타입 검사기도 폐기된 객체의 사용에 진단을 생성합니다.

사용:

from warnings import deprecated
from typing import overload

@deprecated("Use B instead")
class A:
    pass

@deprecated("Use g instead")
def f():
    pass

@overload
@deprecated("int support is deprecated")
def g(x: int) -> int: ...
@overload
def g(x: str) -> int: ...

category로 지정된 경고는 폐기된 객체의 사용 시 런타임에 발생합니다. 함수는 호출될 때, 클래스는 인스턴스화와 하위 클래스 생성 시 발생해요. categoryNone이면 런타임에 경고가 발생하지 않습니다. stacklevel은 경고가 어디서 발생할지 결정합니다. 1(기본값)이면 폐기된 객체의 직접 호출자에서, 더 높으면 스택 더 위에서 발생합니다. 정적 타입 검사기 동작은 categorystacklevel 인자의 영향을 받지 않아요.

데코레이터에 넘긴 폐기 메시지는 데코레이션된 객체의 __deprecated__ 속성에 저장됩니다. 오버로드에 적용하면 typing.get_overloads()가 반환하는 오버로드에 속성이 존재하려면 데코레이터가 @~typing.overload 데코레이터 뒤에 있어야 해요.

버전 3.13에서 추가: PEP 702 참고.

사용 가능한 컨텍스트 관리자 (Available Context Managers)

class warnings.catch_warnings(*, record=False, module=None, action=None, category=Warning, lineno=0, append=False)

경고 필터와 showwarning() 함수를 복사하고, 종료 시 복원하는 컨텍스트 관리자입니다. record 인자가 False(기본)이면 컨텍스트 관리자는 들어갈 때 None을 반환해요. recordTrue이면 커스텀 showwarning() 함수(출력을 sys.stderr로 억제하기도 함)가 본 객체로 목록이 점진적으로 채워지고 그 목록이 반환됩니다. 목록의 각 객체는 다음 속성을 갖는 것이 보장돼요.

  • message — 경고 메시지 (Warning의 인스턴스).
  • category — 경고 범주 (Warning의 하위 클래스).
  • filename — 경고가 발생한 파일 이름 (str).
  • lineno — 파일의 줄 번호 (int).
  • file — 출력에 사용된 파일 객체 (있을 때), 또는 None.
  • line — 소스 코드 한 줄 (가능할 때), 또는 None.
  • source — 경고를 생성한 원래 객체 (가능할 때), 또는 None.

버전 3.6에서 변경: source 속성 추가.

이 객체의 타입은 지정되지 않았고 바뀔 수 있지만, 이 속성이 존재하는 것만 보장됩니다.

module 인자는 warnings를 import할 때 반환되는 모듈 대신 그 필터가 보호될 모듈을 사용합니다. 이 인자는 주로 warnings 모듈 자체를 테스트하기 위해 존재해요.

action 인자가 None이 아니면, 나머지 인자는 컨텍스트에 들어갈 때 즉시 호출된 것처럼 simplefilter()에 전달됩니다. categorylineno 매개변수의 의미는 경고 필터를 참고하세요.

참고 — 여러 스레드나 async 함수를 쓰는 프로그램에서 catch_warnings 컨텍스트 관리자의 동시성 안전성은 컨텍스트 관리자의 동시성 안전성을 참고하세요. 버전 3.11에서 변경: action, category, lineno, append 매개변수 추가.

컨텍스트 관리자의 동시성 안전성 (Concurrent safety of Context Managers)

catch_warnings 컨텍스트 관리자의 동작은 sys.flags.context_aware_warnings 플래그에 따라 달라집니다. 플래그가 참이면 컨텍스트 관리자가 동시성 안전하게 동작하고, 아니면 그렇지 않아요. 동시성 안전하다는 것은 스레드 안전하면서도 asyncio 코루틴·태스크 안에서 쓰기에 안전하다는 뜻입니다. 스레드 안전하다는 것은 멀티 스레드 프로그램에서 동작이 예측 가능하다는 뜻이에요. 플래그는 free-threaded 빌드에서 기본적으로 참이고, 그 외에는 거짓입니다.

context_aware_warnings 플래그가 거짓이면 catch_warningswarnings 모듈의 전역 속성을 수정합니다. 이는 여러 스레드나 asyncio 코루틴을 쓰는 동시 프로그램에서 안전하지 않아요. 예를 들어 두 개 이상의 스레드가 동시에 catch_warnings 클래스를 쓰면 동작이 정의되지 않습니다.

플래그가 참이면 catch_warnings는 전역 속성을 수정하지 않고, 대신 ContextVar를 사용해 새로 설정된 경고 필터링 상태를 저장합니다. 컨텍스트 변수는 스레드 로컬 저장소를 제공하며 catch_warnings 사용을 스레드 안전하게 만들어요.

컨텍스트 핸들러의 record 매개변수도 플래그 값에 따라 다르게 동작합니다. record가 참이고 플래그가 거짓이면 컨텍스트 관리자는 모듈의 showwarning() 함수를 바꾼 뒤 나중에 복원하는 방식으로 동작합니다. 이는 동시성 안전하지 않아요.

record가 참이고 플래그가 참이면 showwarning() 함수는 교체되지 않습니다. 대신 기록 상태는 컨텍스트 변수의 내부 속성으로 표시됩니다. 이 경우 컨텍스트 핸들러를 나갈 때 showwarning() 함수는 복원되지 않아요.

context_aware_warnings 플래그는 -X context_aware_warnings 명령줄 옵션이나 PYTHON_CONTEXT_AWARE_WARNINGS 환경 변수로 설정할 수 있습니다.

참고warnings 모듈의 스레드 안전 동작을 원하는 대부분의 프로그램은 thread_inherit_context 플래그도 참으로 설정하고 싶어할 것입니다. 이 플래그는 threading.Thread가 만든 스레드가 시작한 스레드의 컨텍스트 변수 복사본으로 시작하게 해줘요. 참이면 한 스레드에서 catch_warnings가 확립한 컨텍스트가 그것이 시작한 새 스레드에도 적용됩니다. 거짓이면 새 스레드는 빈 경고 컨텍스트 변수로 시작하므로, catch_warnings 컨텍스트 관리자가 확립한 어떤 필터링도 더 이상 활성화되지 않아요.

버전 3.14에서 변경: sys.flags.context_aware_warnings 플래그와, 플래그가 참일 때 catch_warnings의 컨텍스트 변수 사용이 추가되었습니다. 이전 Python 버전은 플래그가 항상 거짓인 것처럼 동작했습니다.