[Final] PEP 565 - Show DeprecationWarning in main

원문 링크: PEP 565 - Show DeprecationWarning in main

상태: Final 유형: Standards Track 작성일: 12-Nov-2017

PEP 565: __main__ 모듈에서 DeprecationWarning 표시 (Show DeprecationWarning in __main__)

요약 (Abstract)

Python 2.7 및 3.2부터 DeprecationWarning은 기본적으로 숨겨져 있었습니다. 이는 개발 도구나 애플리케이션 사용자들이 불필요한 경고를 보지 않도록 하기 위함이었습니다. 그러나 이로 인해 API 변경 사항을 사용자에게 미리 알리는 DeprecationWarning의 본래 목적이 약화되는 부작용이 발생했습니다. PEP 565는 이 문제를 개선하고자 기본 경고 필터에 한 가지 조정을 제안합니다: __main__ 모듈에서 발생하는 DeprecationWarning을 기본적으로 표시하도록 변경하는 것입니다. 이 변경으로 인해 대화형 프롬프트나 단일 파일 스크립트에서 실행되는 코드는 다시 경고를 기본적으로 보고하게 되며, 패키지화된 코드는 계속해서 기본적으로 경고가 숨겨집니다. 또한, 이 PEP는 warnings 서브시스템을 새로운 Python 개발자들이 더 쉽게 접근할 수 있도록 참조 인터프리터 및 표준 라이브러리 문서에 몇 가지 작은 조정을 제안합니다.

목표

Python 개발자들이 이 PEP의 제안 내용, 도입 배경, 그리고 실제 Python 사용에 미치는 영향을 명확하게 이해할 수 있도록 돕는 것입니다.

명세 (Specification)

새로운 기본 경고 필터 엔트리 (New default warnings filter entry)

현재 기본 경고 필터는 DeprecationWarning, PendingDeprecationWarning, ImportWarning, BytesWarning, ResourceWarning을 모두 ignore합니다. unittest 테스트 러너는 테스트 케이스 실행 시 warnings.catch_warnings()warnings.simplefilter('default')를 사용하여 기본 필터를 재정의합니다.

PEP 565에서 제안하는 변경 사항은 기본 경고 필터 목록을 다음과 같이 업데이트하는 것입니다.

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

이것은 warnings.warnstacklevel 매개변수에 의해 결정되는 경고의 명목상 위치가 __main__ 모듈에 있을 경우, 각 DeprecationWarning의 첫 번째 발생이 다시 보고될 것임을 의미합니다.

이 변경으로 DeprecationWarning은 다음 경우에 기본적으로 표시됩니다.

  • 대화형 프롬프트에서 직접 실행되는 코드
  • 단일 파일 스크립트의 일부로 직접 실행되는 코드

반면, 다음 경우에는 기본적으로 계속 숨겨집니다.

  • 다른 모듈에서 import된 코드
  • zipapp 아카이브의 __main__.py 파일 내 코드
  • 실행 가능한 패키지의 __main__ 서브모듈에서 import된 코드
  • console_scripts 또는 gui_scripts 엔트리 포인트 정의를 기반으로 설치 시 생성된 실행 스크립트 래퍼에서 import된 코드

이는 사용자를 위한 설치 가능하거나 실행 가능한 아티팩트(예: zipapp 아카이브)를 생성하는 도구 개발자에게는 현 상태와 변화가 없지만, 더 임시적이거나 로컬에서 배포되는 스크립트 사용자는 관련 DeprecationWarning을 다시 보게 될 가능성이 높습니다 (Python 2.6 및 이전 버전에서처럼).

FutureWarning의 추가 사용 사례 (Additional use case for FutureWarning)

표준 라이브러리 문서는 애플리케이션 사용자에게 보이도록 의도된 하위 호환성 경고에 대해 FutureWarning ( DeprecationWarning 대신) 사용을 명시적으로 권장하도록 업데이트될 것입니다. (이것은 미래에도 유효한 코드이지만 의미론이 달라질 구성에 대해 경고하는 기존의 FutureWarning 사용에 추가됩니다.)

이로 인해 하위 호환성 경고는 세 가지 다른 대상에 대해 다음과 같은 세 가지 개별적인 범주를 갖게 됩니다.

  • PendingDeprecationWarning: 모든 코드에 대해 기본적으로 숨겨집니다. 대상은 소프트웨어의 미래 호환성을 보장하는 데 적극적인 관심을 갖는 Python 개발자입니다 (예: 특정 지원 의무가 있는 전문 Python 애플리케이션 개발자).
  • DeprecationWarning: __main__ 모듈에서 직접 실행되는 코드에 대해 기본적으로 보고되지만 (이러한 코드는 전용 테스트 스위트를 가질 가능성이 상대적으로 낮다고 간주되므로), 다른 모듈의 코드에 대해서는 기본적으로 숨겨집니다. 대상은 종속성 업그레이드 (Python 자체 업그레이드 포함)로 인해 소프트웨어가 손상될 위험이 있는 Python 개발자입니다 (예: 다른 사람이 종속성 업그레이드 시기를 제어하는 환경에서 Python을 사용하여 스크립트를 작성하는 개발자).
  • FutureWarning: 모든 코드에 대해 기본적으로 보고됩니다. 대상은 다른 Python 개발자가 아닌 Python으로 작성된 애플리케이션 사용자입니다 (예: 구성 파일 형식의 deprecated 설정 사용에 대한 경고).

API 호환성 경고가 사용자에게 더 안정적으로 보이도록 하려는 라이브러리 및 프레임워크 작성자의 경우, Python 3.7+에서는 DeprecationWarning에서 파생되고 이전 버전에서는 FutureWarning에서 파생되는 사용자 정의 경고 클래스를 사용하는 것이 좋습니다.

테스트 러너 개발자는 기본 경고 필터를 결정할 때 다음과 유사한 로직을 구현하는 것이 좋습니다.

if not sys.warnoptions:
    warnings.simplefilter("default")

이는 warnings.simplefilter("default")를 통해 sys.warnoptions가 설정되지 않은 경우, 사실상 -Wd 명령줄 옵션이 전달된 것처럼 모든 경고를 기본적으로 활성화합니다.

대화형 셸 개발자는 사용자 코드가 입력되고 실행되는 네임스페이스에 DeprecationWarning을 활성화하는 필터를 추가하는 것이 좋습니다. 해당 네임스페이스가 __main__이라면 (기본 CPython REPL의 경우처럼), 이 PEP의 변경 사항 외에는 추가 변경이 필요하지 않습니다. __main__이 아닌 다른 네임스페이스를 사용하는 대화형 셸 구현은 자체 필터를 추가해야 합니다. 예를 들어, IPython은 다음 명령을 사용하여 적절한 필터를 설정합니다.

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

기타 문서 업데이트 (Other documentation updates)

warnings 시스템에 대한 현재 참조 문서는 특정 최종 결과를 달성하는 -W 명령줄 옵션 또는 PYTHONWARNINGS 환경 변수의 가능한 설정에 대한 구체적인 예제가 상대적으로 부족합니다.

이 PEP 구현의 일환으로 다음 개선 사항이 제안됩니다.

  • PYTHONWARNINGS 환경 변수 설명 아래에 다음 엔트리를 명시적으로 나열합니다.
    • PYTHONWARNINGS=error # 예외로 변환
    • PYTHONWARNINGS=always # 매번 경고
    • PYTHONWARNINGS=default # 호출 위치당 한 번 경고
    • PYTHONWARNINGS=module # 호출 모듈당 한 번 경고
    • PYTHONWARNINGS=once # Python 프로세스당 한 번 경고
    • PYTHONWARNINGS=ignore # 절대 경고하지 않음
  • -W 명령줄 스위치 문서에 나열된 각 경고 동작에 해당하는 짧은 옵션 (-We, -Wa, -Wd, -Wm, -Wo, -Wi)을 명시적으로 나열합니다.
  • warnings 모듈 문서에 action::categoryaction::category:module 표기법을 사용하여 기본 필터 세트를 명시적으로 나열합니다.
  • warnings.simplefilter 문서에 다음 스니펫을 Python 애플리케이션에서 모든 경고를 기본적으로 끄면서 PYTHONWARNINGS 또는 -W 명령줄 스위치를 통해 다시 켤 수 있도록 하는 권장 접근 방식으로 명시적으로 나열합니다.
    if not sys.warnoptions:
        warnings.simplefilter("ignore")
    

이러한 내용은 새로운 것이 아니지만 (이미 지원되는 모든 Python 버전에서 작동), 관련 문서의 현재 구조를 고려할 때 특별히 명확하지 않습니다.

동기 (Motivation)

Python 2.7 및 Python 3.2에서는 DeprecationWarning의 기본 처리 방식이 변경되어, 일반 코드 실행 중에는 경고가 기본적으로 숨겨지고 unittest 테스트 러너는 테스트 실행 시 이를 다시 활성화하도록 업데이트되었습니다.

이는 다음과 같은 툴링 출력을 피하기 위함이었습니다.

$ devtool mycode/
/usr/lib/python3.6/site-packages/devtool/cli.py:1: DeprecationWarning: 'async' and 'await' will become reserved keywords in Python 3.7
async = True
... actual tool output ...

devtool이 Python 프로그래머를 위한 도구이더라도, 최종 사용자가 취할 수 있는 주요 유용한 조치가 devtool 개발자에게 버그를 보고하는 것인데도 불구하고 호출할 때마다 표시되는 이러한 경고는 특별히 유용하지 않았습니다.

그러나 이 변경은 다음 대상에게 의도치 않은 결과를 초래했습니다.

  • unittest에 내장된 기본 테스트 러너 외의 다른 테스트 러너를 사용하는 사람 (타사 테스트 러너에게 기본 경고 필터를 변경하도록 명시적으로 요청한 적이 없으므로, 많은 러너가 배포된 애플리케이션에 적합하도록 설계된 인터프리터 기본값에 계속 의존했습니다.)
  • unittest 기본 테스트 러너를 사용하여 서브프로세스에서 Python 코드를 테스트하는 사람 ( unittest조차 현재 프로세스에서만 경고 설정을 조정하기 때문입니다.)
  • 대화형 프롬프트에서 Python 코드를 작성하거나 Python 수준 테스트 스위트가 전혀 없는 직접 실행되는 스크립트의 일부로 코드를 작성하는 사람.

이러한 경우 DeprecationWarning은 거의 PendingDeprecationWarning과 완전히 동일하게 되어, 단순히 전혀 보이지 않게 되었습니다.

PEP 범위의 제한 사항 (Limitations on PEP Scope)

이 PEP는 3.7의 기본 경고 필터에 제안된 추가 사항과 Python 2.7 및 3.2에서 DeprecationWarning 처리 방식에 대한 원래 변경의 근거를 더 명확하게 설명하기 위해 존재합니다.

이 PEP는 현재 deprecation warning 처리 방식의 알려진 모든 문제를 해결하지는 않습니다. 가장 주목할 만한 점은 다음과 같습니다.

  • 기본 unittest 테스트 러너는 모듈 import 시점에 발생하는 deprecation warning을 현재 보고하지 않습니다. 경고 필터 재정의는 테스트 실행 중에만 적용되고 테스트 검색 및 로딩 중에는 적용되지 않기 때문입니다.
  • 기본 unittest 테스트 러너는 서브프로세스에서 발생하는 deprecation warning을 현재 보고하지 않습니다. 경고 필터 재정의는 로드된 warnings 모듈에 직접 적용되며, PYTHONWARNINGS 환경 변수에는 적용되지 않기 때문입니다.
  • 표준 라이브러리는 특정 종속성을 업그레이드하기 전에 해당 종속성에서 발생하는 모든 경고를 볼 수 있는 간단한 방법을 제공하지 않습니다. (타사 warn 모듈은 이를 제공하지만, 활성화하려면 표준 라이브러리의 warnings 모듈을 monkeypatching해야 합니다.)
  • 소프트웨어가 지원 모듈로 분리되었지만, 해당 모듈에 자동화된 테스트 커버리지가 거의 또는 전혀 없는 경우, __main__에서 deprecation warning을 기본적으로 다시 활성화하는 것이 API 호환성 문제를 찾는 데 도움이 될 가능성은 낮습니다. 단기적으로 현재 사용 가능한 최선의 방법은 영향을 받는 애플리케이션을 PYTHONWARNINGS=default::DeprecationWarning 또는 python -W default::DeprecationWarning으로 실행하고 stderr 출력을 주의 깊게 확인하는 것입니다. 장기적으로 이것은 Python 코드의 정적 분석 작업을 하는 연구자들을 위한 질문입니다. 즉, deprecated API 사용법을 안정적으로 찾고, API를 제공하는 코드 또는 접근하는 코드를 실제로 실행하지 않고도 warnings.warn 호출을 기반으로 API 또는 매개변수가 deprecated되었는지 추론하는 방법입니다.

이러한 문제는 현 상태의 실제 문제이지만, 단일 추가 엔트리보다 더 복잡한 해결책이 필요하고, 해결이 PEP 프로세스를 거치지 않아도 될 수 있으므로 이 PEP의 고려 대상에서 제외되었습니다.

이 문제에 대해 더 깊이 탐구하는 데 관심이 있는 사람들을 위해, 첫 두 가지는 unittest 모듈 개선 요청, 세 번째는 warnings 모듈 개선 요청이 될 것이며, 마지막은 내용에서 API deprecation을 추론하는 것이 다루기 힘든 코드 분석 문제로 간주되고 어노테이션에 명시적인 함수 및 매개변수 마커 구문이 대신 제안되는 경우에만 PEP가 필요할 것입니다.

CPython 참조 구현에는 3.7에서 다음과 같은 관련 변경 사항도 포함될 것입니다.

이 PEP에서 제안된 기본 필터 변경과 별도로, issue 32229는 애플리케이션 개발자가 일반 작동 중에 경고를 숨기면서 테스트 시 쉽게 볼 수 있도록 warnings.hide_warnings API를 추가하자는 제안입니다.

⚠️ 알림: 이 문서는 AI를 활용하여 번역되었으며, 기술적 정확성을 보장하지 않습니다. 정확한 내용은 반드시 원문을 확인하시기 바랍니다.

Comments