파이썬 개발 모드

버전 3.7에 추가.

파이썬 개발 모드에는 기본적으로 활성화하기에 너무 비싼 추가 실행 시간 검사를 도입합니다. 코드가 올바르면 기본값보다 더 상세하지(verbose) 않아야 합니다; 새로운 경고는 문제가 감지될 때만 발생합니다.

-X dev 명령 줄 옵션을 사용하거나 PYTHONDEVMODE 환경 변수를 1로 설정하여 활성화할 수 있습니다.

파이썬 개발 모드의 효과

파이썬 개발 모드를 활성화하는 것은 다음 명령과 유사하지만, 아래에 설명된 추가 효과가 있습니다:

PYTHONMALLOC=debug PYTHONASYNCIODEBUG=1 python3 -W default -X faulthandler

파이썬 개발 모드의 효과:

  • default 경고 필터를 추가합니다. 다음과 같은 경고가 표시됩니다:

    일반적으로, 위의 경고는 기본 경고 필터가 필터링합니다.

    -W default 명령 줄 옵션이 사용된 것처럼 작동합니다.

    경고를 에러로 처리하려면 -W error 명령 줄 옵션을 사용하거나 PYTHONWARNINGS 환경 변수를 error로 설정하십시오.

  • 메모리 할당자에 디버그 훅을 설치하여 다음을 확인합니다:

    • 버퍼 언더플로

    • 버퍼 오버플로

    • 메모리 할당자 API 위반

    • GIL의 안전하지 않은 사용

    PyMem_SetupDebugHooks() C 함수를 참조하십시오.

    PYTHONMALLOC 환경 변수가 debug로 설정된 것처럼 동작합니다.

    메모리 할당자에 디버그 훅을 설치하지 않고 파이썬 개발 모드를 사용하려면, PYTHONMALLOC 환경 변수를 default로 설정하십시오.

  • 파이썬 시작 시 faulthandler.enable()을 호출하여 SIGSEGV, SIGFPE, SIGABRT, SIGBUSSIGILL 시그널에 대한 처리기를 설치하여 충돌 시 파이썬 트레이스백을 덤프합니다.

    -X faulthandler 명령 줄 옵션이 사용되거나 PYTHONFAULTHANDLER 환경 변수가 1로 설정된 것처럼 작동합니다.

  • asyncio 디버그 모드를 활성화합니다. 예를 들어, asyncio는 어웨이트 하지 않은 코루틴을 확인하고 이를 로그 합니다.

    PYTHONASYNCIODEBUG 환경 변수가 1로 설정된 것처럼 동작합니다.

  • 문자열 인코딩과 디코딩 연산에 대해 encodingerrors 인자를 확인합니다. 예: open(), str.encode()bytes.decode().

    기본적으로, 최상의 성능을 위해, errors 인자는 첫 번째 인코딩/디코딩 에러에서만 검사되며 빈 문자열에 대해서는 encoding 인자가 무시되는 경우가 있습니다.

  • io.IOBase 파괴자는 close() 예외를 로그 합니다.

  • sys.flagsdev_mode 어트리뷰트를 True로 설정합니다.

파이썬 개발 모드는 (성능과 메모리에 대한) 오버헤드 비용이 너무 비싸서, 기본적으로 tracemalloc 모듈을 활성화하지 않습니다. tracemalloc 모듈을 활성화하면 일부 에러의 원인에 대한 추가 정보가 제공됩니다. 예를 들어, ResourceWarning은 자원이 할당된 곳의 트레이스백을 로그하고, 버퍼 오버플로 에러는 메모리 블록이 할당된 곳의 트레이스백을 로그 합니다.

파이썬 개발 모드는 -O 명령 줄 옵션이 assert 문을 제거하거나 __debug__False로 설정하는 것을 막지 않습니다.

버전 3.8에서 변경: io.IOBase 파괴자는 이제 close() 예외를 로그 합니다.

버전 3.9에서 변경: encodingerrors 인자는 이제 문자열 인코딩과 디코딩 연산을 검사합니다.

ResourceWarning 예

명령 줄에 지정된 텍스트 파일의 줄 수를 세는 스크립트의 예:

import sys

def main():
    fp = open(sys.argv[1])
    nlines = len(fp.readlines())
    print(nlines)
    # 파일이 묵시적으로 닫힙니다

if __name__ == "__main__":
    main()

스크립트는 파일을 명시적으로 닫지 않습니다. 기본적으로, 파이썬은 아무런 경고도 하지 않습니다. 269 줄이 있는 README.txt를 사용하는 예:

$ python3 script.py README.txt
269

파이썬 개발 모드를 사용하면 ResourceWarning 경고가 표시됩니다:

$ python3 -X dev script.py README.txt
269
script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='README.rst' mode='r' encoding='UTF-8'>
  main()
ResourceWarning: Enable tracemalloc to get the object allocation traceback

또한, tracemalloc을 활성화하면 파일이 열린 줄이 표시됩니다:

$ python3 -X dev -X tracemalloc=5 script.py README.rst
269
script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='README.rst' mode='r' encoding='UTF-8'>
  main()
Object allocated at (most recent call last):
  File "script.py", lineno 10
    main()
  File "script.py", lineno 4
    fp = open(sys.argv[1])

수선은 파일을 명시적으로 닫는 것입니다. 컨텍스트 관리자를 사용하는 예:

def main():
    # 블록에서 빠져나갈 때 파일을 명시적으로 닫습니다
    with open(sys.argv[1]) as fp:
        nlines = len(fp.readlines())
    print(nlines)

자원을 명시적으로 닫지 않으면 예상보다 오래 자원을 열어둘 수 있습니다; 파이썬을 종료할 때 심각한 문제가 발생할 수 있습니다. CPython에서도 나쁘지만, PyPy에서는 더 나쁩니다. 리소스를 명시적으로 닫으면 응용 프로그램을 더 결정적이고 안정적으로 만들 수 있습니다.

잘못된 파일 기술자 에러 예

자신의 첫 줄을 표시하는 스크립트:

import os

def main():
    fp = open(__file__)
    firstline = fp.readline()
    print(firstline.rstrip())
    os.close(fp.fileno())
    # 파일이 묵시적으로 닫힙니다

main()

기본적으로, 파이썬은 아무런 경고도 하지 않습니다:

$ python3 script.py
import os

파이썬 개발 모드는 ResourceWarning을 표시하고 파일 객체를 파이널라이즈 할 때 "잘못된 파일 기술자(Bad file descriptor)" 에러를 로그 합니다:

$ python3 script.py
import os
script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='script.py' mode='r' encoding='UTF-8'>
  main()
ResourceWarning: Enable tracemalloc to get the object allocation traceback
Exception ignored in: <_io.TextIOWrapper name='script.py' mode='r' encoding='UTF-8'>
Traceback (most recent call last):
  File "script.py", line 10, in <module>
    main()
OSError: [Errno 9] Bad file descriptor

os.close(fp.fileno())는 파일 기술자를 닫습니다. 파일 객체 파이널라이저가 파일 기술자를 다시 닫으려고 하면, Bad file descriptor 에러로 실패합니다. 파일 기술자는 한 번만 닫아야 합니다. 최악의 시나리오에서는, 두 번 닫을 때 충돌이 발생할 수 있습니다 (예는 bpo-18748을 참조하십시오).

수선은 os.close(fp.fileno()) 줄을 제거하거나, closefd=False로 파일을 여는 것입니다.