PEP 648 – 시작 시 인터프리터의 확장 가능한 사용자 지정
- Author:
- Mario Corchero <mariocj89 at gmail.com>
- Sponsor:
- Pablo Galindo Salgado
- Discussions-To:
- Discourse thread
- Status:
- Rejected
- Type:
- Standards Track
- Created:
- 30-Dec-2020
- Python-Version:
- 3.11
- Post-History:
- 16-Dec-2020, 18-Dec-2020
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
이 PEP는 사용자가 시작 시 실행될 파일을 설치할 수 있도록 하여 인터프리터의 확장 가능한 사용자 지정을 지원할 것을 제안합니다.
PEP 거부
PEP 648은 사용 사례가 제한적이고 시작 순서를 더욱 복잡하게 만든다는 이유로 by the steering council 거부되었습니다.
동기
시스템 관리자, 인터프리터를 재패키징하는 도구 및 일부 라이브러리는 시작 시 인터프리터의 측면을 사용자 지정해야 합니다.
시스템 관리자는 일반적으로 sitecustomize.py를 통해 이를 수행하는 반면, 라이브러리는 pth파일을 활용합니다. 이 PEP는 동일한 기능을 보다 사용자 친화적이고 구조화된 방식으로 달성하는 방법을 제안합니다.
pth파일의 한계
라이브러리가 임포트 전에 또는 인터프리터의 일반적인 작동과 관련하여 어떤 사용자 정의든 수행해야 하는 경우, 시작 시 로드되고 site 모듈 [7]을 통해 구현되는 pth 파일에 pth 파일이 평가될 때 실행되는 파이썬 코드를 포함할 수 있다는 사실에 의존하는 경우가 많습니다.
pth 파일은 원래 sys.path에 추가 디렉터리를 더하기 위해 개발되었지만, “import”로 시작하는 줄도 포함할 수 있으며, 이러한 줄은 exec()에 전달된다는 점에 유의하십시오. 사용자는 필요한 사용자 지정을 가능하게 하기 위해 이 기능을 활용해 왔습니다. 예를 들어 setuptools [4] 또는 betterexceptions [5]을 참조하십시오.
이러한 목적으로 pth파일을 사용하는 것은 라이브러리 개발자에게 결코 이상적이지 않습니다. 임포트가 앞에 붙은 한 줄에 코드를 삽입해야 하므로 가독성이 매우 떨어지기 때문입니다. 이러한 방식을 따르는 라이브러리 개발자는 일반적으로 betterexceptions [5]에서처럼 임포트 시 모든 작업을 수행하는 모듈을 만들지만, 이 접근 방식은 여전히 사용자 친화적이라고 하기 어렵습니다.
또한 인터프리터 사용자가 Python 시작 시 실행되는 내용을 검사하려는 경우에도 이상적이지 않습니다. 모든 사이트 경로에 걸쳐 분산되어 있을 수 있는 잠재적인 코드 실행을 확인하기 위해 모든 pth파일을 검토해야 하기 때문입니다. 이러한 pth파일 대부분은 경로만 수정하는 “정상적인” pth파일이므로, “시작 시 내 인터프리터를 변경하는 것은 무엇인가”라는 질문에 답하기가 상당히 복잡해집니다.
마지막으로 pth파일에서 코드 실행을 제거하자는 제안이 여러 차례 있었으며, [1] 및 [2]을 참조하십시오.
sitecustomize.py의 한계
sitecustomize는 허용 가능한 해결책이지만, 시스템과 인터프리터를 한 사람이 담당한다고 가정합니다. 시스템 관리자와 인터프리터 프로비저닝 담당자 모두 인터프리터 시작 시 사용자 지정을 추가하려는 경우, 파일의 내용에 합의하고 모든 변경 사항을 결합해야 합니다. 그러나 이는 큰 한계가 아니며, 이 변경의 주된 동인도 아닙니다. 변경이 이루어진다면 이러한 사용자들의 상황도 개선됩니다. 모든 작업을 수행하는 sitecustomize.py를 두는 대신, 향상하려는 기능의 이름을 딴 사용자 지정 격리 파일을 둘 수 있기 때문입니다. 예를 들어 Ubuntu는 현재의 sitecustomize.py를 단순히 ubuntu_apport_python_hook로 변경할 수 있습니다. 이는 의도를 더 잘 나타낼 뿐만 아니라 인터프리터 사용자에게 인터프리터에서 발생하는 수정 사항을 더 잘 이해할 수 있게 해 줍니다.
근거
이 PEP는 시작 시 sitepackages [8] 또는 usersitepackages [9]에서 __sitecustomize__라는 이름의 디렉터리에서 발견되는 모든 파일을 실행하여 인터프리터의 확장 가능한 사용자 지정을 지원할 것을 제안합니다.
__sitecustomize__인 이유
이 이름은 이미 존재하는 sitecustomize.py 개념을 따르기 위한 것입니다. 해당 디렉터리는 사이트 경로에 있으므로 sys.path내부에 위치하게 됩니다. 따라서 이미 존재하는 sitecustomize.py와 충돌하지 않도록 이름 양쪽에 밑줄 두 개를 사용합니다.
새로운 __sitecustomize__ 디렉터리 찾기
Python 인터프리터는 시작 시 모든 표준 site-packages 경로 내에서 __sitecustomize__라는 이름의 디렉터리를 찾습니다.
이는 일반적으로 Python 시스템 위치와 사용자 위치이지만, 궁극적으로는 site 모듈의 로직에 의해 정의됩니다.
사용자는 site.sitepackages [8] 및 site.usersitepackages [9]를 사용하여 인터프리터가 __sitecustomize__ 디렉터리를 찾을 수 있는 경로를 확인할 수 있습니다.
__sitecustomize__ 검색 시점
__sitecustomize__ 디렉터리는 site.addsitedir [10]의 일부로 site-packages 경로에서 pth 파일을 검색한 직후에 정확히 검색됩니다.
이는 현재 pth 파일에 적용되는 것과 정확히 동일한 순서로 각 site-packages 경로에 대해 반복됩니다.
__sitecustomize__ 내 실행 순서
구현은 각 __sitecustomize__ 디렉터리를 발견할 때 이름순으로 정렬하여 __sitecustomize__내부의 파일을 실행합니다. 그러나 사용자가 실행 순서에 의존하지 않도록 권장합니다.
무작위 순서로 실행하는 방안도 고려했지만, 인터프리터가 해당 파일을 선택하는 방식에 따라 결과가 달라질 수 있습니다. 따라서 다른 파일이 실행되는 순서에 의존하는 것이 좋은 관행은 아니더라도, 인터프리터 시작 시 무작위로 다른 결과가 발생하는 것보다는 이것이 낫다고 생각합니다. 사용자가 파일을 실행하기 전에 경로에 항목을 추가해야 할 경우를 고려하여 pth 파일 이후에 파일을 실행하도록 선택했습니다.
pth 파일과의 상호 작용
pth 파일을 사용하여 sys.path에 경로를 추가할 수 있지만, __sitecustomize__ 검색 프로세스에는 영향을 주지 않아야 합니다. 해당 디렉터리는 site-packages 경로에서만 독점적으로 조회되기 때문입니다.
__sitecustomize__ 내 파일 실행
__sitecustomize__ 디렉터리가 검색되면 그 안에서 .py 확장자를 가진 모든 파일을 io.open_code로 읽고 exec [11]를 사용하여 실행합니다.
서로 다른 파일 간의 예기치 않은 상호 작용을 방지하기 위해 빈 딕셔너리를 exec 함수의 globals로 전달합니다.
오류 처리
파일 실행 중 발생하는 모든 오류는 인터프리터가 자세한 정보 표시 모드로 실행되지 않는 한 기록되지 않으며, 다른 파일의 평가를 중단해서도 안 됩니다. 사용자는 파일 실행에 실패했으며 자세한 정보는 자세한 정보 표시 모드를 사용하여 확인할 수 있다는 메시지를 stderr에서 받게 됩니다. 이 동작은 기존 sitecustomize.py의 동작을 모방합니다.
가상 환경과의 상호 작용
새로운 __sitecustomize__ 솔루션을 통해 인터프리터에 적용된 사용자 지정은 사용자가 가상 환경을 생성한 후에도 sitecustomize.py가 가상 환경과 상호 작용하는 것과 동일한 방식으로 계속 작동합니다.
이는 include-system-site-packages가 활성화되지 않는 한 가상 환경으로 전파되지 않는 pth 파일과는 다른 점입니다.
라이브러리 유지 관리자가 __sitecustomize__를 통해 설치한 기능을 가상 환경으로 전파하지 않으려면 sys.prefix == sys.base_prefix를 확인하여 가상 환경 내에서 실행 중인지 감지해야 합니다. 이 동작은 전역 sitecustomize.py를 수정하는 패키지와 유사합니다.
sitecustomize.py 및 usercustomize.py와의 상호 작용
제거될 때까지 sitecustomize 및 usercustomize는 pth 파일과 유사하게 __sitecustomize__ 이후에 실행됩니다. sitecustomize 및 usercustomize의 제거 계획에 관한 정보는 하위 호환성 섹션을 참조하십시오.
설치된 모든 파일 식별
Python 시작 과정의 디버깅을 용이하게 하기 위해 site 모듈이 호출되면 시작 시 검색될 __sitecustomize__ 디렉터리를 출력합니다.
파일 명명 규칙
패키지 간 충돌을 방지하기 위해 파일 이름에 패키지 이름을 포함하도록 패키지에 권장할 예정입니다. 그러나 파일 이름에 대한 유일한 요구 사항은 인터프리터가 파일을 실행할 수 있도록 .py로 끝나는 것입니다.
시작 파일 비활성화
시작 시간이 중요한 경우와 같은 일부 상황에서는 이 옵션을 완전히 비활성화하는 것이 바람직할 수 있습니다. 이미 존재하는 플래그 -S[3] 는 이 새로운 기능을 포함하여 site 관련 조작을 모두 비활성화합니다. 이 플래그를 전달하면 __sitecustomize__ 디렉터리를 검색하지 않습니다.
또한 이 새로운 기능만 비활성화한 상태로 인터프리터를 시작할 수 있도록 -X아래에 disablesitecustomize라는 새 옵션을 추가하며, 이 옵션은 __sitecustomize__ 검색만 비활성화합니다.
마지막으로 사용자는 site.py 모듈의 여러 옵션 중 하나를 통해 사용자 사이트를 비활성화하여 사용자 사이트에서만 __sitecustomize__ 디렉터리 검색을 비활성화할 수 있습니다.
빌드 백엔드 지원
빌드 백엔드는 이러한 파일을 __sitecustomize__ 디렉터리에 설치하기 쉽게 하는 옵션을 제공할 수 있지만, 이 PEP에서는 이를 직접 다루지 않습니다. pth 파일과 마찬가지로 빌드 백엔드는 __sitecustomize__ 파일에 대해 쉽게 구성할 수 있는 메커니즘을 제공하지 않고, 사용자가 설치 프로세스에 연결하여 이러한 파일을 포함하도록 할 수 있습니다. 빌드 백엔드의 향상된 지원을 이 PEP의 요구 사항으로 보지는 않습니다.
시작 시간에 미치는 영향
이 구현에서 우려되는 점은 이 추가 기능이 Python 인터프리터의 시작 시간에 영향을 미칠 수 있다는 것입니다. 성능 영향은 테스트 중인 Python 환경에 사용자 또는 시스템 관리자가 설치하는 파일의 논리와 밀접하게 연관될 것으로 예상합니다.
인터프리터의 __sitecustomize__ 디렉터리에 파일이 있으면 파일 실행 시간과 코드를 읽는 호출 시간이 시작 시간에 추가됩니다. 이는 sitecustomize.py, usercustomize.py 및 pth 파일의 코드를 통한 코드 실행이 시작 시간에 영향을 미치는 방식과 유사합니다. 따라서 실제로 시작 시간에 추가되는 시간은 해당 파일에서 실행되는 코드에 크게 좌우되므로, 여기서는 이 해결책을 앞의 세 가지와 비교하는 데 집중합니다.
결과는 50회 반복에 걸쳐 perf로 “./python.exe -c pass”를 실행하고, 각 반복마다 명령을 50회 반복 실행한 다음 모든 결과의 기하 평균을 구하여 수집했습니다. 이러한 벤치마크를 실행하는 데 사용한 파일은 참조 구현 [6]에 체크인되어 있습니다.
벤치마크는 다음 매개변수와 시스템 상태에서 PGO 및 LTO를 사용하여 컴파일한 3.10 alpha 7로 실행했습니다.
- Perf 이벤트: 최대 샘플링 속도를 초당 1회로 설정
- CPU 주파수: CPU 17,35의 최소 주파수를 최대 주파수로 설정
- Turbo Boost (MSR): CPU 17에서 Turbo Boost 비활성화: MSR 0x1a0을 0x4000850089로 설정
- IRQ 선호도: 기본 선호도를 CPU 0-16,18-34로 설정
- IRQ 선호도: IRQ 1,3-16,21,25-31,56-59,68-85,87,89-90,92-93,95-104의 선호도를 CPU 0-16,18-34로 설정
- CPU: 논리 CPU 2개 사용: 17,35
- Perf 이벤트: 최대 샘플링 속도: 초당 1회
- ASLR: 완전한 무작위화
- Linux 스케줄러: 격리된 CPU (2/36): 17,35
- Linux 스케줄러: CPU (2/36)에서 RCU 비활성화: 17,35
- CPU 주파수: 0-16,18-34=min=1200 MHz, max=3600 MHz; 17,35=min=max=3600 MHz
- Turbo Boost (MSR): CPU 17,35: 비활성화되어 있습니다
pth 파일, sitecustomize.py, usercustomize.py 및 __sitecustomize__ 내 파일에서 실행되도록 배치되는 코드는 다음과 같습니다:
import time; x = time.time() ** 5
이 파일은 간단한 작업을 실행하기 위한 것이지만, 여전히 무시할 수 있을 정도로 소요 시간이 짧을 것으로 예상합니다. 이는 메커니즘으로 인한 성능 저하를 드러내면서도 실험을 비교적 현실적인 상황에 놓기 위한 것입니다. 또한 import로 시작하고 한 줄로 구성되어 pth 파일에서 사용할 수 있습니다.
| Test | # of files | Time (us) | ||||
|---|---|---|---|---|---|---|
| # | sitecustomize.py |
usercustomize.py |
pth |
__sitecustomize__ |
Run 1 | Run 2 |
| 1 | 0 | 0 | 0 | Dir not created | 13884 | 13897 |
| 2 | 0 | 0 | 0 | 0 | 13871 | 13818 |
| 3 | 0 | 0 | 1 | 0 | 13964 | 13924 |
| 4 | 0 | 0 | 0 | 1 | 13940 | 13939 |
| 5 | 1 | 1 | 0 | 0 | 13990 | 13993 |
| 6 | 0 | 0 | 0 | 2 (system + user) | 14063 | 14040 |
| 7 | 0 | 0 | 50 | 0 | 16011 | 16014 |
| 8 | 0 | 0 | 0 | 50 | 15456 | 15448 |
결과는 참조 구현 [6]에서 제공하는 run-benchmark.py 스크립트로 재현할 수 있습니다.
이러한 결과에서 다음과 같이 해석합니다:
sitecustomize.py및usercustomize.py를 사용하는 것과 비교하여 두 개의__sitecustomize__스크립트를 사용하면 인터프리터가 0.3% 느려집니다. 사용자가 해당 파일을 생성하지 않더라도 인터프리터는 여전히 해당 파일을 import하려고 시도하므로, 향후 릴리스에서sitecustomize.py및usercustomize.py가 제거될 때까지 이러한 성능 저하가 지속될 것으로 예상합니다.- 코드가 포함된 임의의 50개 pth 파일을 대상으로 테스트했을 때, 이를
__sitecustomize__로 옮기면 시작 시간이 약 3.5% 단축됩니다. 이는pth파일 실행에 비해__sitecustomize__파일을 평가하는 로직이 더 단순하기 때문일 가능성이 큽니다. - 전반적으로 모든 측정 결과는 이 추가 기능이 시작 시간에 미치는 영향이 작다는 것을 보여줍니다.
감사 이벤트
보안 검사를 용이하게 하기 위해 __sitecustomize__ 실행 시 새로운 감사 이벤트가 추가되고 트리거됩니다. 이를 위해 sys.audit를 호출하며 [12] 이름은 “sitecustimze.exec_file”로, 인자는 파일 이름으로 지정합니다.
보안 영향
이 PEP는 모든 코드 실행을 pth 파일에서 __sitecustomize__ 디렉터리 내 파일로 옮기는 것을 목표로 합니다. 다음과 같은 이유로 이것이 시스템 관리자에게 개선된 사항이라고 생각합니다:
- 모든
pth파일을 검사하는 대신 단일 디렉터리를 살펴봄으로써 인터프리터가 시작 시 실행하는 코드를 신속하게 식별할 수 있습니다. - 새로 제안된 감사 이벤트를 통해 이 기능의 사용 현황을 추적할 수 있습니다.
__sitecustomize__디렉터리의 권한을 조정할 수 있으므로 더 세밀하게 제어할 수 있으며, 이를 통해 인터프리터 시작 방식을 변경하지 않는 패키지만 사용자가 설치하도록 할 수도 있습니다.
요컨대 악의적인 사용자가 시작 시 실행될 파일을 추가할 수 있게 되기는 하지만, 기존 pth 파일에 비하면 개선된 방식입니다.
교육 방법
인터프리터가 시작 시 사이트 경로에서 __sitecustomize__ 디렉터리를 찾으며, .py 확장자를 가진 파일을 찾으면 파일을 하나씩 실행한다고 간단히 설명하여 문서화하고 교육할 수 있습니다.
시스템 관리자와 인터프리터를 패키징하는 도구에는 이전에 sitecustomize.py를 배치하던 방식처럼 이제 __sitecustomize__에 파일을 배치하도록 권장할 수 있습니다. 또한 원하는 사용자 지정 로직을 처리하는 특정 파일을 제공할 수 있으므로, 다음 사용자가 해당 내용을 덮어쓰지 않을 것이라는 점에서 더욱 안심할 수 있습니다.
라이브러리 개발자는 setuptools와 같은 도구에 이러한 새 파일을 주입하는 새 인자를 지정할 수 있어야 합니다. 예를 들어 sitecustomize_files=["scripts/betterexceptions.py"]와 같이 지정하면 해당 파일을 추가할 수 있습니다. 빌드 백엔드가 이를 지원하지 않는 경우에는 이전에 pth 파일을 사용하던 것처럼 수동으로 설치할 수 있습니다. 파일 이름의 일부로 패키지 이름을 포함하도록 권장할 것입니다.
하위 호환성
이 PEP는 3.11, 3.12 및 3.13에서 sitecustomize.py, usercustomize.py 및 pth 코드 실행에 대한 사용 중단 경고를 추가합니다. 3.14까지 해당 기능을 제거할 계획입니다. 이러한 해결책에서 __sitecustomize__로 마이그레이션하는 작업은 이상적으로 로직을 다른 파일로 옮기는 것에 그쳐야 합니다.
기존 sitecustomize.py 메커니즘은 사이트 경로에 배치하는 시스템 관리자를 대상으로 만들어졌지만, 인터프리터가 시작될 당시에는 실제로 해당 파일을 경로의 어느 위치에나 배치할 수 있었습니다. 새 메커니즘에서는 사용자가 경로의 어느 위치에나 __sitecustomize__ 디렉터리를 배치할 수 없고, 사이트 경로에만 배치할 수 있습니다. 시스템 관리자는 마이그레이션 경로로 __sitecustomize__ 디렉터리에 sitecustomize만 임포트하는 사용자 지정 파일을 추가하여 sitecustomize.py와 유사한 동작을 복원할 수 있습니다.
참조 구현
CPython 테스트 모음을 통과하는 초기 구현을 평가용으로 사용할 수 있습니다 [6].
이 구현은 검토자가 직접 사용해 보고 이 PEP에서 발생할 수 있는 잠재적인 문제를 확인하기 위한 것일 뿐입니다.
거부된 아이디어
아무것도 하지 않기
현재 상태는 “작동”하지만 동기 부여에서 나열한 문제를 드러냅니다. 이 변경의 영향을 분석한 결과, 향상된 사용자 경험을 제공하므로 변경할 가치가 있다고 판단합니다.
pth 파일을 사용하여 공식화
또 다른 방법은 시작 시 코드에 코드를 주입하기 위해 pth 파일을 사용하는 방식을 단순히 미화하고 문서화하는 것이지만, 동기 부여에서 설명한 것처럼 이는 사용자에게 차선의 경험을 제공합니다.
__sitecustomize__를 네임스페이스 패키지로 만들기
디렉터리를 네임스페이스 패키지로 만들고 그 안의 모든 모듈을 임포트하는 방안을 고려했으며, 이를 통해 초기화 시점에 sys.path의 모든 경로를 검색하고 서로 임포트하여 파일 간 종속성을 선언할 수 있었습니다. 그러나 이 방안은 여러 이유로 거부되었습니다.
- 임의의 파일이 실행되는 경로 목록을 불필요하게 확대하기 때문입니다.
- 한 위치에 패키지가
__init__.py파일을 설치하는 경우 어떻게 처리할지와 같은 추가적인 복잡성이 로직에 발생하기 때문입니다. - 실제로 네임스페이스 패키지를 임포트하는 것보다, 사이트 경로에서 이미
pth파일을 찾고 있는 방식으로__sitecustomize__를 검색하는 편이 비용이 적게 듭니다.
종료 시 사용자 지정 지원
init.d 사용자는 사용자가 종료 시에도 코드를 추가할 수 있는 방식으로 이 기능을 구현하고 싶을 수 있지만, Python 사용자는 이미 atexit을 통해 이를 수행할 수 있으므로 추가 지원은 필요하지 않습니다.
entry_points 사용
시작 시 실행해야 하는 파일을 지정할 수 있도록 엔트리 포인트 사용을 확장하는 방안을 고려했지만, 두 가지 주요 이유로 해당 해결책을 폐기했습니다. 첫 번째 이유는 시작 시간에 미치는 영향입니다. 이 접근 방식은 소수의 파일만 실행하기 위해 모든 패키지의 배포 정보를 검색해야 합니다. 사용자가 이 기능을 사용하지 않더라도 성능에 영향을 미치며, 이러한 영향은 환경에 설치된 패키지 수에 따라 선형적으로 증가합니다. 두 번째 이유는 이 PEP에서 제안하는 구현이 패키지와 시스템 관리자 모두를 위한 시작 시 사용자 지정 단일 해결책을 제공한다는 점입니다. 또한 엔트리 포인트의 주된 목적이 라이브러리가 시작 시 파일을 쉽게 설치하도록 하는 것이라면, 해당 기능은 여전히 추가할 수 있으며 빌드 백엔드에서 __sitecustomize__ 디렉터리 안에 파일을 설치하도록 만들 수 있습니다.
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.
Acknowledgements
Thanks Pablo Galindo for contributing to this PEP and offering his PC to run the benchmark.