PEP 3147 – PYC 저장소 디렉터리
- Author:
- Barry Warsaw <barry at python.org>
- Status:
- Final
- Type:
- Standards Track
- Created:
- 16-Dec-2009
- Python-Version:
- 3.2
- Post-History:
- 30-Jan-2010, 25-Feb-2010, 03-Mar-2010, 12-Apr-2010
- Resolution:
- Python-Dev message
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
이 PEP는 Python의 임포트 메커니즘에 대한 확장을 설명합니다. 이 확장은 설치된 여러 버전의 Python 인터프리터가 Python 소스 코드 파일을 더 잘 공유할 수 있게 합니다. 이를 위해 하나의 Python 소스 파일(.py 파일)과 같은 위치에 둘 이상의 바이트 컴파일 파일(.pyc 파일)을 둘 수 있게 합니다. 여기에서 설명하는 확장은 Unladen Swallow(PEP 3146)를 적용한 C Python이 생성할 수 있는 JIT 출력처럼 다른 종류의 파이썬 컴파일 캐시를 지원하는 데에도 사용할 수 있습니다.
배경
CPython은 소스 코드를 “바이트코드”로 컴파일하며, 성능상의 이유로 소스 파일이 변경될 때마다 이 바이트코드를 파일 시스템에 캐시합니다. 컴파일 단계를 건너뛸 수 있으므로 Python 모듈을 훨씬 빠르게 로드할 수 있습니다. 소스 파일이 foo.py이면, CPython은 소스 바로 옆에 있는 foo.pyc 파일에 바이트코드를 캐시합니다.
바이트코드 파일에는 32비트 빅 엔디언 숫자 두 개가 들어 있고, 그 뒤에 마셜링된 [2] 코드 객체가 이어집니다. 이 32비트 숫자들은 매직 넘버와 타임스탬프를 나타냅니다. 매직 넘버는 Python이 바이트 코드 형식을 바꿀 때마다 변경됩니다. 가상 머신에 새 바이트 코드를 추가하는 경우가 그 예입니다. 이렇게 하면 이전 버전의 VM용으로 만들어진 pyc 파일이 문제를 일으키지 않습니다. 타임스탬프는 pyc 파일이 자신을 만드는 데 사용된 py 파일과 일치하는지 확인하는 데 사용됩니다. 매직 넘버나 타임스탬프 중 하나라도 일치하지 않으면 py 파일을 다시 컴파일하고 새로운 pyc 파일을 기록합니다.
실제로 pyc 파일이 Python 메이저 릴리스 간에 호환되지 않는다는 것은 잘 알려져 있습니다. Python 소스 코드의 import.c [3]를 읽어 보면, 최근 메모리가 닿는 범위 안에서 새로운 CPython 메이저 릴리스마다 pyc 매직 넘버가 올라갔다는 것을 확인할 수 있습니다.
근거
Ubuntu [4]나 Debian [5] 같은 Linux 배포판은 사용자에게 둘 이상의 Python 버전을 동시에 제공합니다. 예를 들어 Ubuntu 9.10 Karmic Koala 사용자는 Python 2.5, 2.6, 3.1을 설치할 수 있으며, 이 중 Python 2.6이 기본값입니다.
이 때문에 시스템이 설치한 서드파티 Python 소스 파일에서 충돌이 일어납니다. 하나의 Python 소스 파일을 한 번에 여러 Python 버전용으로 컴파일할 수는 없기 때문입니다. Python은 매직 넘버가 맞지 않는 pyc 파일을 발견하면 더 느린 방법인 소스 재컴파일로 대체합니다. 따라서 시스템이 /usr/share/python/foo.py를 설치했다면, 서로 다른 두 버전의 파이썬이 pyc 파일을 두고 다투면서 소스를 컴파일할 때마다 그 파일을 다시 기록하게 됩니다. (이러한 배포판에는 여러 버전의 stdlib가 실제로 설치되어 있으므로 표준 라이브러리는 이 문제의 영향을 받지 않습니다.)
게다가 이러한 배포판의 운영 체제 패키지 작성자들의 부담을 덜기 위해, 배포 패키지에는 Python 버전 번호가 포함되지 않습니다 [6]. 배포 패키지는 시스템에 설치된 모든 Python 버전이 공유합니다. 패키지에 Python 버전 번호를 넣으면 유지보수가 악몽이 될 것입니다. 배포판에 새 Python 릴리스가 추가되거나 제거될 때마다 모든 패키지와 그 의존성을 업데이트해야 하기 때문입니다. 사용 가능한 패키지의 수가 엄청나게 많기 때문에 이만한 작업량은 감당할 수 없습니다.
(서로 다른 Python 버전 간 서드파티 확장 모듈의 바이너리 호환성 문제를 해결하기 위해 PEP 384가 제안되었습니다.)
이러한 배포판은 pyc 파일을 공유할 수 없으므로, 소스 코드는 계속 공유하면서 생성된 pyc 파일은 공유되지 않는 위치에 두는 정교한 메커니즘들이 개발되었습니다. 심볼릭 링크 기반의 Debian 방식인 python-support [8]와 python-central [9]가 그 예입니다. 이러한 방식 때문에 다양한 사용자에게 Python 애플리케이션을 제공하는 정책이 훨씬 복잡하고, 깨지기 쉽고, 이해하기 어렵고, 파편화됩니다. 업스트림 tarball보다 운영 체제 벤더를 통해 Python을 얻는 사용자가 더 많다고 볼 수도 있습니다. 따라서 이러한 벤더에게는 CPython의 pyc 공유 문제를 해결하는 것이 높은 우선순위입니다.
이 PEP는 이 문제에 대한 해결책을 제안합니다.
제안
Python의 임포트 장치는 모든 Python 패키지 디렉터리 안의 단일 디렉터리에 바이트 코드 캐시 파일을 쓰고 그곳에서 찾도록 확장됩니다. 이 디렉터리의 이름은 __pycache__입니다.
또한 pyc 파일 이름에는 해당 파일이 어느 Python 버전용으로 컴파일되었는지 구별하는 매직 문자열(“태그”라고 부릅니다)이 들어갑니다. 이렇게 하면 하나의 Python 소스 파일에 대해 바이트 컴파일된 캐시 파일 여러 개가 함께 존재할 수 있습니다.
매직 태그는 구현에서 정의하지만, 구현 이름과 버전 번호 약칭을 포함해야 합니다(예: cpython-32). 매직 태그는 모든 Python 버전 사이에서 고유해야 하며, 매직 넘버가 올라갈 때마다 새 매직 태그를 정의해야 합니다. 따라서 파이썬 3.2용 pyc 파일의 예는 foo.cpython-32.pyc입니다.
매직 태그는 imp 모듈의 get_tag() 함수로 얻을 수 있습니다. 이는 imp.get_magic() 함수와 짝을 이룹니다.
이 방식에는 Python 패키지 디렉터리를 덜 어수선하게 해 준다는 부가적인 이점도 있습니다.
Python 소스 파일을 처음 임포트할 때 패키지 디렉터리에 __pycache__ 디렉터리가 아직 없으면 새로 만듭니다. 임포트된 소스의 pyc 파일은 매직 태그 형식의 이름으로 __pycache__ 디렉터리에 쓰입니다. __pycache__ 디렉터리를 만들거나 그 안에 pyc 파일을 만드는 데 실패하더라도, PEP 3147 이전과 마찬가지로 임포트는 성공합니다.
py 소스 파일이 없으면 __pycache__ 안의 pyc 파일은 무시됩니다. 이로써 오래된 pyc 파일을 실수로 임포트하는 문제가 사라집니다.
하위 호환성을 위해 Python은 pyc 파일만 있는 배포도 계속 지원합니다. 다만 pyc 파일이 py 파일이 있었을 디렉터리에 있을 때만 지원하며, __pycache__ 디렉터리 안에 있을 때는 지원하지 않습니다. __pycache__ 밖의 pyc 파일은 py 소스 파일이 없을 때만 임포트됩니다.
py_compile [15]이나 compileall [16] 같은 도구는 PEP 3147 형식의 레이아웃을 자동으로 만들도록 확장되지만, pyc 파일만 있는 배포 레이아웃을 만드는 옵션도 제공합니다.
예시
실제로는 어떤 모습이 될까요?
beta라는 하위 패키지를 포함하는 alpha라는 Python 패키지가 있다고 합시다. 바이트 컴파일 전의 소스 디렉터리 레이아웃은 다음과 같을 수 있습니다:
alpha/
__init__.py
one.py
two.py
beta/
__init__.py
three.py
four.py
이 패키지를 Python 3.2로 바이트 컴파일하면 다음과 같은 구조가 됩니다:
alpha/
__pycache__/
__init__.cpython-32.pyc
one.cpython-32.pyc
two.cpython-32.pyc
__init__.py
one.py
two.py
beta/
__pycache__/
__init__.cpython-32.pyc
three.cpython-32.pyc
four.cpython-32.pyc
__init__.py
three.py
four.py
참고: 목록의 순서는 플랫폼에 따라 다를 수 있습니다.
새 Python 버전 두 개, 즉 Python 3.3과 Unladen Swallow가 설치되었다고 합시다. 바이트 컴파일 후 파일 시스템은 다음과 같은 모습이 됩니다:
alpha/
__pycache__/
__init__.cpython-32.pyc
__init__.cpython-33.pyc
__init__.unladen-10.pyc
one.cpython-32.pyc
one.cpython-33.pyc
one.unladen-10.pyc
two.cpython-32.pyc
two.cpython-33.pyc
two.unladen-10.pyc
__init__.py
one.py
two.py
beta/
__pycache__/
__init__.cpython-32.pyc
__init__.cpython-33.pyc
__init__.unladen-10.pyc
three.cpython-32.pyc
three.cpython-33.pyc
three.unladen-10.pyc
four.cpython-32.pyc
four.cpython-33.pyc
four.unladen-10.pyc
__init__.py
three.py
four.py
보시다시피 Python 버전 식별자 문자열이 고유하기만 하면 pyc 파일이 몇 개든 함께 존재할 수 있습니다. 이 식별자 문자열은 아래에서 더 자세히 설명합니다.
이 레이아웃의 좋은 점은 일반적으로 __pycache__ 디렉터리를 무시할 수 있어서, 보통의 디렉터리 목록이 다음과 같이 보인다는 것입니다:
alpha/
__pycache__/
__init__.py
one.py
two.py
beta/
__pycache__/
__init__.py
three.py
four.py
이는 현재의 Python보다도 훨씬 덜 어수선합니다.
Python 동작
Python이 임포트할 모듈(예를 들어 foo)을 찾을 때에는 여러 상황 중 하나를 만날 수 있습니다. 현재 Python 규칙에서 “일치하는 pyc”라는 용어는 매직 넘버가 현재 인터프리터의 매직 넘버와 일치하고, 소스 파일의 타임스탬프가 pyc 파일에 기록된 타임스탬프와 정확히 일치한다는 뜻입니다.
사례 0: 정상 상태
Python이 모듈 foo를 임포트하라는 요청을 받으면 sys.path를 따라 foo.py 파일(또는 foo 패키지이지만, 이 논의에서는 중요하지 않습니다)을 찾습니다. 파일을 찾으면 Python은 일치하는 __pycache__/foo.<magic>.pyc 파일이 있는지 확인하고, 있다면 그 pyc 파일을 로드합니다.
사례 1: 첫 번째 임포트
Python이 foo.py를 찾았을 때 __pycache__/foo.<magic>.pyc 파일이 없으면 Python은 이 파일을 생성하며, 필요하면 __pycache__ 디렉터리도 함께 생성합니다. Python은 foo.py 파일을 파싱하고 바이트 컴파일한 뒤 바이트코드를 __pycache__/foo.<magic>.pyc에 저장합니다.
사례 2: 두 번째 임포트
Python이 모듈 foo를 두 번째로(물론 다른 프로세스에서) 임포트하라는 요청을 받으면 다시 sys.path를 따라 foo.py 파일을 찾습니다. Python은 foo.py 파일을 찾으면 일치하는 __pycache__/foo.<magic>.pyc를 찾고, 이를 발견하면 바이트코드를 읽은 뒤 평소처럼 진행합니다.
사례 3: 소스가 없는 __pycache__/foo.<magic>.pyc
캐시된 pyc 파일은 파일 시스템에 그대로 남아 있는데 어떤 이유로 foo.py 파일만 삭제되었을 수 있습니다. __pycache__/foo.<magic>.pyc 파일은 있지만 이를 생성하는 데 사용한 foo.py 파일이 없으면, Python은 foo를 임포트하라는 요청을 받았을 때 ImportError를 발생시킵니다. 즉, 소스 파일이 없으면 Python은 캐시 디렉터리의 pyc 파일을 임포트하지 않습니다.
사례 4: 레거시 pyc 파일과 소스 없는 임포트
소스 파일이 옆에 있으면 Python은 모든 레거시 pyc 파일을 무시합니다. 즉, foo.py 파일 옆에 foo.pyc 파일이 있으면 그 pyc 파일은 어떤 경우에도 무시됩니다.
다만 소스 없는 배포를 계속 지원하기 위해, 소스 파일이 없으면 Python은 원래 소스 파일이 있었을 위치에 있는 단독 pyc 파일을 임포트합니다.
사례 5: 읽기 전용 파일 시스템
소스가 읽기 전용 파일 시스템에 있거나 그 밖의 이유로 __pycache__ 디렉터리나 pyc 파일을 쓸 수 없는 경우에도 모든 규칙이 똑같이 적용됩니다. __pycache__가 그 안에 pyc 파일을 쓸 수 없는 권한으로 만들어진 경우에도 마찬가지입니다.
흐름도
다음은 모듈이 로드되는 과정을 나타낸 흐름도입니다:
대체 Python 구현
Jython [11], IronPython [12], PyPy [13], Pynie [14], Unladen Swallow 같은 대체 Python 구현도 __pycache__ 디렉터리를 사용하여 각자의 플랫폼에 맞는 컴파일 산출물을 무엇이든 저장할 수 있습니다. 예를 들어 Jython은 모듈의 클래스 파일을 __pycache__/foo.jython-32.class에 저장할 수 있습니다.
구현 전략
이 기능은 Python 3.2를 대상으로 하며, 3.2와 그 이후의 모든 버전에서 이 문제를 해결합니다. Python 2.7로 백포트될 수도 있습니다. 벤더는 적절하다고 판단하는 대로 이 변경 사항을 이전 배포판에 자유롭게 백포트할 수 있습니다. 이 기능을 Python 2로 백포트하는 경우, -U 플래그를 사용하면 foo.cpython-27u.pyc 같은 파일이 쓰일 수 있습니다.
기존 코드에 미치는 영향
이 PEP를 채택하면 Python 내부와 외부 모두에서 기존 코드와 관용구에 영향을 미칩니다. 이 절에서는 이러한 영향 중 일부를 열거합니다.
PEP 3147 지원 여부 감지하기
사용 중인 Python 버전이 PEP 3147 기능을 제공하는지 감지하는 가장 쉬운 방법은 다음과 같이 검사하는 것입니다:
>>> import imp
>>> has3147 = hasattr(imp, 'get_tag')
__file__
Python 3에서 모듈을 임포트하면 그 모듈의 __file__ 속성은 소스 py 파일을 가리킵니다(Python 2에서는 pyc 파일을 가리킵니다). 패키지의 __file__은 그 패키지의 __init__.py에 해당하는 py 파일을 가리킵니다. 예를 들면 다음과 같습니다.:
>>> import foo
>>> foo.__file__
'foo.py'
# baz is a package
>>> import baz
>>> baz.__file__
'baz/__init__.py'
이 PEP의 어떤 내용도 __file__의 의미를 변경하지 않습니다.
이 PEP는 모듈에 __cached__ 속성을 추가할 것을 제안하며, 이 속성은 항상 실제로 읽거나 기록한 pyc 파일을 가리킵니다. 환경 변수 $PYTHONDONTWRITEBYTECODE가 설정되어 있거나, -B 옵션이 주어졌거나, 소스가 읽기 전용 파일 시스템에 있는 경우, __cached__ 속성은 pyc 파일이 존재하지 않았다면 기록되었을 위치를 가리킵니다. 물론 이 위치의 경로에는 __pycache__ 하위 디렉터리가 포함됩니다.
pyc 파일을 지원하지 않는 대체 Python 구현에서는 __cached__ 속성이 적절하다고 판단되는 어떤 정보든 가리킬 수 있습니다. 예를 들어 Jython에서는 이것이 해당 모듈의 .class 파일, 즉 __pycache__/foo.jython-32.class일 수 있습니다. 일부 구현은 모듈을 생성하기 위해 여러 개의 컴파일된 파일을 사용할 수 있으며, 이 경우 __cached__는 튜플일 수 있습니다. __cached__의 정확한 내용은 Python 구현에 따라 다릅니다.
적절한 값을 계산할 수 없는 경우, 구현이 __cached__ 속성을 None으로 설정할 것을 권장합니다.
py_compile과 compileall
Python에는 내장 임포트 메커니즘과 별도로 Python 모듈을 컴파일할 수 있도록 지원하는 두 모듈, py_compile [15] 및 compileall [16]이 함께 제공됩니다. 특히 py_compile은 바이트 컴파일에 대해 깊이 알고 있으므로, 이 모듈들은 새로운 레이아웃을 이해하도록 갱신될 것입니다. 레거시 .pyc 바이트 컴파일 파일 경로 이름으로 기록하기 위한 -b 플래그가 compileall에 추가됩니다.
bdist_wininst와 Windows 설치 프로그램
이 도구들도 설치 시 모듈을 명시적으로 컴파일합니다. 이 도구들이 py_compile과 compileall을 사용하지 않는다면, 이 도구들 역시 새로운 레이아웃을 이해하도록 수정되어야 합니다.
파일 확장자 검사
.pyc로 끝나는 파일을 검사한 뒤 마지막 문자를 단순히 잘라 내어 대응하는 .py 파일을 찾는 코드가 일부 존재합니다. 이 PEP가 구현되면 이러한 코드는 당연히 실패하게 됩니다.
이러한 사용 사례를 지원하기 위해 imp 패키지 [17]에 두 개의 새로운 메서드를 추가합니다:
imp.cache_from_source(py_path)->pyc_pathimp.source_from_cache(pyc_path)->py_path
대체 구현은 이 PEP를 각자 지원하는 방식에 따라 적절한 값을 반환하도록 이 함수들을 자유롭게 재정의할 수 있습니다. 구현(또는 사용 중인 PEP 302 로더)이 어떤 이유로든 적절한 파일 이름을 계산할 수 없는 경우, 이 메서드들은 None을 반환할 수 있습니다. 이 메서드들은 예외를 발생시켜서는 안 됩니다.
백포트
3.2 이전 버전의 Python(그리고 경우에 따라 2.7)에는 이 PEP를 백포트할 수 있습니다. 그러나 Python 3.2(그리고 경우에 따라 2.7)에서는 이 동작이 기본으로 켜지며, 사실상 기존 동작을 대체하게 됩니다. 백포트는 기본적으로 기존 레이아웃을 지원해야 합니다. 이 기능을 활성화하는 수단으로 $PYTHONENABLECACHEDIR이라는 환경 변수나 -Xenablecachedir 명령줄 스위치를 사용하여 PEP 3147을 지원할 것을 제안합니다.
Makefile 및 기타 의존성 도구
.pyc 파일에 대한 의존성을 계산하는 Makefile 및 기타 도구(예를 들어 .pyc가 없을 때 소스를 바이트 컴파일하는 도구)는 새 경로를 확인하도록 갱신해야 합니다.
대안
이 절에서는 PEP를 개발하는 동안 검토했지만 채택하지 않은 몇 가지 대안적 접근 방식이나 세부 사항을 설명합니다.
16진수 매직 태그
__pycache__ 디렉터리 안의 pyc 파일은 파일 이름에 매직 태그를 포함합니다. 이 태그는 임포터가 사용하는 실제 매직 넘버를 기억하기 쉽게 나타낸 것입니다. 바이너리 매직 넘버의 16진수 표현 [10]을 고유 식별자로 사용할 수도 있었습니다. 예를 들어 Python 3.2에서는 다음과 같습니다.:
>>> from binascii import hexlify
>>> from imp import get_magic
>>> 'foo.{}.pyc'.format(hexlify(get_magic()).decode('ascii'))
'foo.580c0d0a.pyc'
하지만 이 방식은 사람이 알아보기에 그다지 편하지 않으므로, 이 PEP에서는 매직 태그를 제안합니다.
PEP 304
이 PEP의 목표는 철회된 PEP 304의 목표와 일부 겹칩니다. 그러나 PEP 304는 사용자가 pyc 파일을 저장할 섀도 파일 시스템 계층을 만들 수 있게 합니다. pyc 파일을 위한 이 섀도 계층 개념을 활용하면 이 PEP의 목표를 달성할 수도 있습니다. PEP 304에는 철회된 이유가 나와 있지 않지만, 섀도 디렉터리에는 여러 가지 문제가 있습니다. 섀도 pyc 파일의 위치는 쉽게 찾을 수 없으며, 시스템과 최종 사용자 모두가 $PYTHONBYTECODE 환경 변수를 올바르고 일관되게 사용하는지에 좌우됩니다. 전역적인 영향도 있습니다. 즉, 시스템은 pyc 파일을 섀도 처리하고 싶어 하더라도 사용자는 그렇지 않을 수 있는데, 이 PEP는 전부 적용하거나 전혀 적용하지 않는 방식만 정의합니다.
이 문제의 예로, 데이터 파일의 위치를 찾을 때 흔히 쓰는 (다만 깨지기 쉬운) Python 관용구는 다음과 같이 하는 것입니다.:
from os import dirname, join
import foo.bar
data_file = join(dirname(foo.bar.__file__), 'my.dat')
foo.bar.__file__는 섀도 디렉터리에 있는 pyc 파일의 위치를 가리키므로, 거기서부터는 소스 디렉터리를 기준으로 my.dat 파일을 찾지 못할 수 있어 문제가 됩니다.
팻 바이트 컴파일 파일
이 PEP의 이전 버전에서는 “팻(fat)” Python 바이트코드 파일을 설명했습니다. 이 파일은 여러 pyc 파일에 해당하는 내용을 하나의 pyf 파일에 담고, 해당 매직 넘버를 키로 하는 조회 테이블을 가집니다. 이는 확장 가능한 파일 형식이어서, 처음 5개의 병렬 Python 구현을 상당히 효율적으로 지원하면서도, 확장 조회 테이블을 통해 pyf 바이트코드 객체를 필요한 만큼 크게 늘릴 수 있었습니다.
fat 바이트 컴파일 파일은 상당히 복잡했고 본질적으로 까다로운 경쟁 조건을 일으켰기 때문에, 디렉터리를 사용하는 현재의 단순화된 방식이 제안되었습니다. fat pyc 파일 형식으로 zip 파일을 사용하는 경우에도 같은 문제가 적용됩니다.
여러 파일 확장자
PEP 작성자는 여러 개의 thin 바이트 컴파일 파일을 같은 위치에 두되, 서로 다른 파일 확장자로 Python 버전을 구분하는 방식도 검토했습니다. 예를 들어 foo.pyc25, foo.pyc26, foo.pyc31 등입니다. 이 방식은 서로 다른 파일을 너무 많이 작성하게 되어 생기는 난잡함 때문에 거부되었습니다. 여러 확장자를 사용하는 방식에서는 파일 확장자에 의존하는 도구를 갱신하기가 더 어려워지며, 이는 계속 이어지는 작업이 됩니다.
.pyc
__pycache__ 디렉터리의 이름을 .pyc 또는 다른 점 파일(dot-file) 이름으로 하자는 제안이 나왔습니다. 이렇게 하면 *nix 시스템에서는 디렉터리가 숨겨지는 효과가 있습니다. BDFL [20]이 이 제안을 거부한 이유는 많은데, 그중에는 점 파일이 일부 플랫폼에서만 특별하게 취급된다는 점과, 실제로 이 파일들을 사용자에게서 완전히 숨기기를 원하지 않는다는 점이 포함됩니다.
참조 구현
이 코드에 대한 작업은 Python 3.2에 병합할 준비가 될 때까지 Launchpad의 Bazaar 브랜치 [22]에서 추적됩니다. 진행 중인 작업의 diff도 볼 수 있으며 [23], 새 변경 사항이 업로드될 때마다 자동으로 갱신됩니다.
2010-04-01 자로 Rietveld 코드 리뷰 이슈 [24]가 열렸습니다(아니요, 만우절 농담이 아닙니다 :).
참고 문헌
[21] importlib: https://docs.python.org/3.1/library/importlib.html
감사의 글
Barry Warsaw가 처음 떠올린 아이디어는 fat Python 바이트 코드 파일이었습니다. Martin von Loewis는 이 PEP의 초기 초안을 검토하고, 기존의 pyc와 pyo 파일을 디렉터리에 저장하는 방식으로 단순화할 것을 제안했습니다. 그 밖에도 많은 분이 이 PEP의 초기 버전을 검토하고 유용한 피드백을 주셨습니다. 다음은 그중 일부입니다:
- David Malcolm
- Josselin Mouette
- Matthias Klose
- Michael Hudson
- Michael Vogt
- Piotr Ożarowski
- Scott Kitterman
- Toshio Kuratomi
Copyright
This document has been placed in the public domain.