Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 565 – __main__에서 DeprecationWarning 표시하기

Author:
Alyssa Coghlan <ncoghlan at gmail.com>
Status:
Final
Type:
Standards Track
Created:
12-Nov-2017
Python-Version:
3.7
Post-History:
12-Nov-2017, 25-Nov-2017
Resolution:
Python-Dev message

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

Python 2.7과 Python 3.2에서 기본 경고 필터는 기본적으로 DeprecationWarning을 숨기도록 변경되었습니다. 그 결과 Python으로 작성된 개발 도구(예: 린터, 정적 분석기, 테스트 실행기, 코드 생성기)나 우연히 Python으로 작성되었을 뿐인 다른 애플리케이션에서 발생하는 폐지 경고는, 사용자가 명시적으로 보겠다고 선택하지 않는 한 표시되지 않게 되었습니다.

그러나 이 변경은 DeprecationWarning이 본래 의도한 주된 목적을 달성하는 효과를 현저히 떨어뜨리는 안타까운 부작용을 낳았습니다. 그 목적은 API의 호환성을 깨는 변경(CPython, 표준 라이브러리, 서드파티 라이브러리 중 어디에서 일어나든)을 해당 API의 사용자에게 미리 알리는 것입니다.

이러한 상황을 개선하기 위해 이 PEP는 기본 경고 필터에 단 하나의 조정을 제안합니다. 바로 메인 모듈에서 발생한 것으로 간주되는 폐지 경고를 기본적으로 표시하는 것입니다.

이 변경으로 대화형 프롬프트에 입력한 코드와 단일 파일 스크립트의 코드는 다시 이러한 경고를 기본적으로 보고하게 되며, 임포트 가능한 모듈의 일부로 배포되는 패키지 코드에서는 계속해서 기본적으로 경고가 표시되지 않습니다.

또한 이 PEP는 Python을 처음 접하는 개발자가 경고 하위 시스템에 더 쉽게 접근할 수 있도록 참조 인터프리터와 표준 라이브러리 문서를 여러 군데 조금씩 조정할 것을 제안합니다.

문서 업데이트의 일환으로, unittest 테스트 러너가 테스트 케이스를 실행할 때 기본적으로 모든 경고를 표시한다는 점과 다른 테스트 러너도 이 예를 따르도록 권장한다는 점을 더 명확히 밝힐 것입니다.

명세

새로운 기본 경고 필터 항목

현재 기본 경고 필터 집합은 다음과 같이 구성됩니다.:

ignore::DeprecationWarning
ignore::PendingDeprecationWarning
ignore::ImportWarning
ignore::BytesWarning
ignore::ResourceWarning

그리고 기본 unittest 테스트 러너는 테스트 케이스를 실행하는 동안 warnings.catch_warnings() warnings.simplefilter('default')를 사용하여 기본 필터를 재정의합니다.

이 PEP에서 제안하는 변경은 기본 경고 필터 목록을 다음과 같이 갱신하는 것입니다.:

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

즉, 경고의 명목상 위치(warnings.warn의 stacklevel 매개변수로 결정됨)가 __main__ 모듈인 경우, 각 DeprecationWarning의 첫 번째 발생이 다시 보고됩니다.

이 변경으로 DeprecationWarning은 다음 코드에서 기본적으로 표시됩니다:

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

반면 다음 코드에서는 계속해서 기본적으로 숨겨집니다:

  • zipapp 아카이브의 __main__.py 파일에서 다른 모듈로부터 임포트된 코드
  • 실행 가능한 패키지의 __main__ 하위 모듈에서 다른 모듈로부터 임포트한 코드
  • console_scripts 또는 gui_scripts 진입점 정의를 바탕으로 설치 시점에 생성된 실행 가능한 스크립트 래퍼에서 임포트된 코드

이는 사용자에게 배포하기 위해 설치 가능하거나 실행 가능한 산출물(예: zipapp 아카이브)을 만드는 도구 개발자에게는 현재 상태와 달라지는 점이 없어야 하는 반면, 보다 임시적인 개인 스크립트나 로컬에서 배포되는 스크립트의 사용자는 (Python 2.6 이전에 그랬던 것처럼) 관련 폐지 경고를 다시 보게 될 가능성이 높다는 것을 의미합니다.

FutureWarning의 추가 사용 사례

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

이를 통해 하위 호환성 경고는 서로 다른 세 대상 독자를 위한 다음의 세 가지 뚜렷한 범주로 나뉩니다:

  • PendingDeprecationWarning: 모든 코드에서 기본적으로 숨겨집니다. 대상 독자는 자신의 소프트웨어의 향후 호환성을 보장하는 데 적극적인 관심을 가진 Python 개발자입니다(예: 특정한 지원 의무를 지는 전문 Python 애플리케이션 개발자).
  • DeprecationWarning: __main__ 모듈에서 직접 실행되는 코드에서는 기본적으로 보고되지만(그러한 코드는 전용 테스트 스위트를 갖추고 있을 가능성이 비교적 낮다고 간주되기 때문입니다), 다른 모듈의 코드에서는 기본적으로 숨겨집니다. 대상 독자는 의존성의 업그레이드(Python 자체의 업그레이드 포함)로 인해 자신의 소프트웨어가 깨질 위험이 있는 Python 개발자입니다(예: 의존성 업그레이드 시점을 다른 사람이 통제하는 환경을 Python으로 스크립팅하는 개발자).
  • FutureWarning: 모든 코드에서 기본적으로 보고됩니다. 대상 독자는 다른 Python 개발자가 아니라 Python으로 작성된 애플리케이션의 사용자입니다(예: 설정 파일 형식에서 사용 중단된 설정을 사용하는 것에 대한 경고).

API 호환성 경고를 사용자가 더 확실하게 볼 수 있도록 하려는 라이브러리 및 프레임워크 작성자에게는 Python 3.7 이상에서는 DeprecationWarning에서, 그 이전 버전에서는 FutureWarning에서 파생된 사용자 정의 경고 클래스를 사용할 것을 권장합니다.

테스트 러너를 위한 권장 필터 설정

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

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

이렇게 하면 -Wd 명령줄 옵션을 전달한 것처럼 사실상 모든 경고가 기본적으로 활성화됩니다.

테스트 스위트에서 BytesWarning을 실제로 활성화하려면 여전히 명령줄에서 인터프리터에 -b 옵션을 전달해야 한다는 점에 유의하십시오. 암시적 바이트 변환 및 바이트 비교 경고의 경우, 경고 필터 메커니즘은 해당 경고를 경고로 출력할지 예외로 발생시킬지를 결정하는 데에만 사용됩니다 - 명령줄 플래그가 설정되지 않으면 인터프리터는 애초에 경고를 내보내지도 않습니다.

대화형 셸을 위한 권장 필터 설정

대화형 셸 개발자는 사용자 코드가 입력되고 실행되는 네임스페이스에서 DeprecationWarning을 활성화하는 필터를 추가하는 것이 좋습니다.

해당 네임스페이스가 (기본 CPython REPL에서처럼) __main__이라면, 이 PEP에 포함된 것 외에는 변경이 필요하지 않습니다.

__main__ 이외의 네임스페이스를 사용하는 대화형 셸 구현은 자체 필터를 추가해야 합니다. 예를 들어, IPython은 적절한 필터를 설정하기 위해 다음 명령([6])을 사용합니다:

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

기타 문서 업데이트

경고 시스템에 대한 현재 참조 문서에는 특정한 최종 결과를 얻기 위한 -W 명령줄 옵션이나 PYTHONWARNINGS 환경 변수의 가능한 설정에 대한 구체적인 예시가 비교적 부족합니다.

이 PEP 구현의 일부로 다음과 같은 개선 사항을 제안합니다:

  • PYTHONWARNINGS 환경 변수 설명 아래에 다음 항목을 명시적으로 나열합니다:
    PYTHONWARNINGS=error # Convert to exceptions
    PYTHONWARNINGS=always # Warn every time
    PYTHONWARNINGS=default # Warn once per call location
    PYTHONWARNINGS=module # Warn once per calling module
    PYTHONWARNINGS=once # Warn once per Python process
    PYTHONWARNINGS=ignore # Never warn
    
  • -W 명령줄 스위치 문서에 나열된 각 경고 동작에 대응하는 짧은 옵션(-We, -Wa, -Wd, -Wm, -Wo, -Wi)을 명시적으로 나열합니다
  • action::category 및 action::category:module 표기법을 사용하여 warnings 모듈 문서에 기본 필터 집합을 명시적으로 나열합니다
  • PYTHONWARNINGS 또는 -W 명령줄 스위치를 통해 경고를 다시 켤 수 있도록 하면서도 Python 애플리케이션에서 기본적으로 모든 경고를 끄는 권장 방법으로서, warnings.simplefilter 문서에 다음 코드 조각을 명시적으로 나열합니다:
    if not sys.warnoptions:
        warnings.simplefilter("ignore")
    

이 중 어느 것도 새로운 것은 아니지만(아직 지원되는 모든 Python 버전에서 이미 동작합니다), 관련 문서의 현재 구조상 특별히 눈에 띄지는 않습니다.

참조 구현

참조 구현은 이 PEP에 대한 관련 트래커 이슈 [5]에 링크된 PR [4]에서 확인할 수 있습니다.

이 PEP 구현의 부수 효과로, 내부 경고 필터 목록은 (기존의 컴파일된 정규 표현식 사용에 더해) 필터 정의의 일부로 일반 문자열을 사용하는 것을 허용하게 됩니다. 일반 문자열이 있는 경우, 해당 문자열은 정확히 일치하는지만 비교됩니다. 이 접근 방식을 사용하면 re 모듈에 조기에 접근할 필요 없이 인터프리터 시작 중에 새 기본 필터를 추가할 수 있습니다.

동기

[1]에서 논의되고 [2]에서 언급된 바와 같이, 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 개발자에게 버그를 보고하는 것뿐인데도, 이 경고는 호출할 때마다 표시되기 때문입니다.

이 경고는 Python 이외의 여러 언어에 걸쳐 사용되는 범용 개발자 도구에서는 더욱 도움이 되지 않으며, 단지 Python으로 작성되었을 뿐 반드시 개발자를 대상으로 하지도 않는 애플리케이션에서는 거의 전혀 도움이 *안* 됩니다.

그러나 이 변경은 다음 대상에게 의도하지 않은 결과를 초래한 것으로 드러났습니다:

  • unittest에 내장된 기본 테스트 실행기가 아닌 다른 테스트 실행기를 사용하는 모든 사람(서드파티 테스트 실행기에 기본 경고 필터를 변경해 달라는 요청은 한 번도 명시적으로 이루어진 적이 없으므로, 그중 상당수는 여전히 배포된 애플리케이션에 맞게 설계된 인터프리터 기본값에 의존합니다)
  • 기본 unittest 테스트 러너로 하위 프로세스에서 Python 코드를 테스트하는 모든 사람(unittest조차도 현재 프로세스의 경고 설정만 조정하기 때문입니다)
  • Python 수준의 테스트 스위트가 전혀 없는 상태에서 대화형 프롬프트나 직접 실행되는 스크립트로 Python 코드를 작성하는 모든 사람

이러한 경우 DeprecationWarning은 결국 PendingDeprecationWarning과 거의 완전히 같아졌습니다. 경고가 아예 전혀 보이지 않았기 때문입니다.

PEP 범위의 한계

이 PEP는 3.7에서 기본 경고 필터에 추가하자고 제안하는 항목을 설명하는 것과 동시에, 예전 Python 2.7과 3.2에서 DeprecationWarning 처리 방식을 처음 변경한 근거를 더 명확하게 밝히기 위해 작성되었습니다.

이 PEP가 현재의 폐지 예정 경고 처리 방식에 있는 알려진 문제를 모두 해결하지는 않습니다. 대표적인 문제는 다음과 같습니다:

  • 경고 필터 재정의는 테스트 실행 중에만 적용되고 테스트 발견 및 로딩 중에는 적용되지 않으므로, 현재 기본 unittest 테스트 러너는 모듈 임포트 시점에 발생한 폐지 예정 경고를 보고하지 않습니다.
  • 경고 필터 재정의는 PYTHONWARNINGS 환경 변수가 아니라 로드된 warnings 모듈에 직접 적용되므로, 현재 기본 unittest 테스트 러너는 하위 프로세스에서 발생한 폐지 예정 경고를 보고하지 않습니다.
  • 표준 라이브러리에는 특정 의존성을 업그레이드하기 전에 그 의존성이 발생시키는 모든 경고를 보도록 선택하는 간단한 방법이 없습니다(서드파티 warn 모듈 [3]이 이 기능을 제공하지만, 이를 활성화하려면 표준 라이브러리의 warnings 모듈을 몽키패치해야 합니다).
  • 소프트웨어를 지원 모듈로 분리했더라도 그 모듈의 자동화된 테스트 커버리지가 거의 또는 전혀 없다면, __main__에서 폐지 예정 경고를 다시 기본으로 활성화해도 API 호환성 문제를 찾는 데 도움이 될 가능성은 낮습니다. 단기적으로 현재 가장 좋은 방법은 영향을 받는 애플리케이션을 PYTHONWARNINGS=default::DeprecationWarning 또는 python -W default::DeprecationWarning으로 실행하고 그 stderr 출력을 주의 깊게 살펴보는 것입니다. 장기적으로 이는 사실 Python 코드의 정적 분석을 연구하는 사람들이 풀어야 할 문제입니다. 즉, API를 제공하는 코드나 그 API에 접근하는 코드를 실제로 실행하지 않고도 폐지 예정 API의 사용을 신뢰성 있게 찾아내는 방법과, warnings.warn 호출을 근거로 API나 매개변수가 폐지 예정임을 추론하는 방법이 문제입니다.

이들은 현 상황의 실제 문제이지만, 기본 경고 필터에 항목 하나를 추가하는 것보다 더 복잡한 해결책이 필요하고, 적어도 잠재적으로는 PEP 절차를 거치지 않고도 해결할 수 있으므로 이 PEP에서는 다루지 않습니다.

이를 더 추진하는 데 관심이 있는 사람이라면, 처음 두 가지는 unittest 모듈 개선 요청이 되고, 세 번째는 warnings 모듈 개선 요청이 됩니다. 마지막 것은 어노테이션의 내용으로부터 API 폐지를 추론하는 것이 다루기 어려운 코드 분석 문제로 판단되어 대신 어노테이션에 명시적인 함수 및 매개변수 마커 문법을 도입하자는 제안이 나오는 경우에만 PEP가 필요합니다.

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

이 PEP에서 제안하는 기본 필터 변경과는 별개로, 이슈 32229 [7]은 warnings.hide_warnings API를 추가하자는 제안입니다. 이 API를 사용하면 애플리케이션 개발자가 정상 운영 중에는 경고를 더 간단하게 숨기고, 테스트할 때는 경고를 쉽게 표시할 수 있습니다.

참고 문헌