PEP 382 – 네임스페이스 패키지
- Author:
- Martin von Löwis <martin at v.loewis.de>
- Status:
- Rejected
- Type:
- Standards Track
- Created:
- 02-Apr-2009
- Python-Version:
- 3.2
- Post-History:
Table of Contents
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
거부 공지
US PyCon 2012 스프린트 첫날, 우리는 PEP 382와 PEP 402에 대해 길고 유익한 논의를 했습니다. 결국 둘 다 거부했지만, PEP 402의 취지를 이어 가는 새로운 PEP가 작성될 것입니다. Martin von Löwis가 요약을 작성했습니다: [2].
초록
네임스페이스 패키지는 하나의 Python 패키지를 디스크상의 여러 디렉터리에 나누어 두는 메커니즘입니다. 현재 Python 버전에서는 패키지의 __path__를 계산하는 알고리즘을 직접 만들어야 합니다. 여기서 제안하는 개선 사항을 적용하면, 임포트 메커니즘 자체가 패키지를 구성하는 디렉터리 목록을 만듭니다. 이 PEP의 구현은 [1]에서 볼 수 있습니다.
용어
이 PEP에서 패키지라는 용어는 Python의 import 문이 정의하는 Python 패키지를 가리킵니다. 배포라는 용어는 Python 패키지 색인에 저장되어 있고 distutils나 setuptools로 설치되는, 개별적으로 설치할 수 있는 Python 모듈의 묶음을 가리킵니다. 벤더 패키지라는 용어는 운영 체제의 패키징 메커니즘으로 설치되는 파일 묶음을 가리킵니다(예: Linux 시스템에서 설치하는 Debian 패키지나 Redhat 패키지).
포션(portion)이라는 용어는 네임스페이스 패키지를 구성하는, 단일 디렉터리(zip 파일 안에 저장되어 있을 수도 있음)에 있는 파일 묶음을 가리킵니다.
현재의 네임스페이스 패키지
현재 Python은 패키지를 네임스페이스 패키지로 지정할 수 있도록 pkgutil.extend_path를 제공합니다. 권장되는 사용 방법은 다음 내용을:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
패키지의 __init__.py에 넣는 것입니다. 패키지의 어느 포션이 먼저 임포트되든 extend_path가 호출되도록, 모든 배포판은 자신의 __init__.py에 같은 내용을 넣어야 합니다. 그 결과, 어느 포션이 먼저 임포트되는지는 sys.path에 있는 패키지 조각들의 순서에 따라 달라지므로, 패키지의 __init__.py는 사실상 어떤 이름도 정의할 수 없습니다. 특별한 기능으로, extend_path는 <packagename>.pkg라는 이름의 파일을 읽으며, 이 파일로 추가 포션을 선언할 수 있습니다.
setuptools는 이와 비슷한 함수인 pkg_resources.declare_namespace를 제공하며, 다음과 같은 형태로 사용합니다.:
import pkg_resources
pkg_resources.declare_namespace(__name__)
declare_namespace가 sys.modules를 통해 패키지의 __path__를 수정하므로, 포션의 __init__.py에서 __path__에 값을 할당할 필요가 없습니다. 특별한 기능으로, declare_namespace는 zip 파일도 지원하며, 패키지 이름을 내부에 등록해 두어 나중에 setuptools가 sys.path에 항목을 추가할 때 각 패키지에 추가 포션을 제대로 더할 수 있게 합니다.
setuptools는 배포판의 setup.py에서 네임스페이스 패키지를 선언할 수 있게 해 주므로, 배포판 개발자가 마법 같은 __path__ 수정 코드를 __init__.py에 직접 넣을 필요가 없습니다.
근거
네임스페이스 패키지에 대한 현재의 명령형 접근 방식 때문에, 네임스페이스 패키지를 제공하는 메커니즘이 서로 조금씩 호환되지 않는 여러 가지로 생겨났습니다. 예를 들어, pkgutil은 *.pkg 파일을 지원하지만 setuptools는 지원하지 않습니다. 마찬가지로, setuptools는 zip 파일 검사를 지원하고 자신의 _namespace_packages 변수에 포션을 추가하는 기능도 지원하지만, pkgutil은 지원하지 않습니다.
또한 현재 방식은 시스템 벤더에게 문제를 일으킵니다. 벤더 패키지는 일반적으로 서로 겹치는 파일을 제공해서는 안 되며, 이미 디스크에 있는 파일을 포함한 벤더 패키지를 설치하려고 하면 설치가 실패하거나 예측할 수 없는 동작이 발생합니다. 벤더는 배포판들이 모두 네임스페이스 패키지의 단일 디렉터리에 들어가도록 패키징할 수도 있으므로, 모든 부분이 서로 충돌하는 __init__.py 파일을 제공하게 됩니다.
명세
패키지를 임포트하는 데 명령형 메커니즘을 사용하는 대신, 여기서는 선언적 접근 방식을 제안합니다. 이름이 .pyp(Python 패키지를 뜻함)로 끝나는 디렉터리는 패키지의 일부분을 담고 있습니다.
import 문은 P라는 이름의 패키지에 대해, 그 패키지의 __path__ 속성을 __init__.py 파일을 포함하는 P라는 이름의 디렉터리 하나(선택 사항)와 P.pyp라는 이름의 모든 디렉터리로 구성하되, 부모 패키지의 __path__(또는 sys.path)에서 발견되는 순서대로 계산하도록 확장됩니다. 이 중 어느 하나라도 발견되면 패키지의 추가 부분에 대한 검색이 계속됩니다.
한 디렉터리에 P/__init__.py 형식의 패키지와 P.pyp 형식이 모두 들어 있을 수 있습니다.
임포트 메커니즘에는 그 밖의 변경이 없습니다. 모듈(__init__.py 포함) 검색은 계속해서 처음 발견된 모듈에서 멈춥니다. 요약하면, 패키지 foo를 임포트하는 과정은 다음과 같습니다.
- sys.path에서 디렉터리 foo 또는 foo.pyp, 혹은 파일 foo.<ext>를 검색합니다. 디렉터리 없이 파일만 발견되면 그 파일을 모듈로 취급하여 임포트합니다.
- 디렉터리 foo가 발견되면 그 디렉터리에 __init__.py가 들어 있는지 확인합니다. 들어 있다면 해당 __init__.py의 위치를 기억합니다. 그렇지 않으면 그 디렉터리를 건너뜁니다. 일단 __init__.py가 발견되면, 그 뒤에 나오는 foo라는 이름의 디렉터리는 건너뜁니다.
- 디렉터리 foo와 foo.pyp 모두 패키지의 __path__에 추가됩니다.
- __init__ 모듈이 발견되었다면, 모든
.pyp디렉터리로부터 계산된 경로로 __path__를 초기화한 상태에서 그 모듈을 임포트합니다.
임포트 훅에 미치는 영향
네임스페이스 패키지를 지원하려면 PEP 302에 정의된 로더와 파인더를 모두 변경해야 합니다. 아래 프로토콜을 따르지 않으면 패키지가 네임스페이스 패키지로 인식되지 않을 수 있습니다. 이 프로토콜을 지원하지 않는 로더와 파인더는 아래 함수에 접근할 때 AttributeError를 발생시켜야 합니다.
파인더는 위 알고리즘의 1단계에서 *.pth 파일을 찾는 기능을 지원해야 합니다. 이를 위해 경로 훅으로 사용되는 파인더는 다음 메서드를 지원해야 합니다.
finder.find_package_portion(fullname)
이 메서드는 find_module과 같은 방식으로 호출되며, 패키지의 __path__에 추가될 문자열을 반환해야 합니다. 파인더가 패키지의 부분을 찾지 못하면 None을 반환해야 합니다. 위 호출에서 AttributeError를 발생시키면 이 PEP를 따르지 않는 것으로 간주하며, 해당 예외는 무시됩니다. 그 밖의 모든 예외는 보고됩니다.
파인더는 find_module과 find_package_portion 양쪽에서 모두 성공을 보고할 수 있으며, 이를 통해 __init__.py를 포함하는 패키지와 같은 패키지의 한 부분을 함께 허용할 수 있습니다.
find_package_portion에서 반환된 모든 문자열은 .pyp 디렉터리의 모든 경로 이름과 함께 새 패키지의 __path__에 추가됩니다.
논의
이 명세의 초기 버전은 sys.path에서 이 파일들이 사용되는 방식과 비슷하게 *.pth 파일을 추가하자고 제안했습니다. 와일드카드 마커(*)를 사용하면, 패키지는 부모 경로를 살펴보고 적절한 이름의 하위 디렉터리를 검색하여 전체 경로를 도출한다는 것을 나타낼 수 있었습니다.
그 뒤 사람들은 .pth 문법 전체를 지원하는 것이 적절하지 않다고 보았고, 그래서 .pth 파일은 디렉터리가 패키지임을 나타내는 단순한 마커 파일로 바뀌었습니다. Peter Tröger는 Python과 관련된 모든 파일 확장자는 .py로 시작해야 하므로 .pth는 적합하지 않은 파일 확장자라고 제안했습니다. 따라서 마커 파일의 이름이 .pyp로 변경되었습니다.
이후 Dinu Gherman은 마커 파일을 사용할 필요가 없으며, 디렉터리 확장자가 그러한 마커 역할을 충분히 할 수 있다고 지적했습니다. 이것이 현재 이 PEP가 제안하는 내용입니다.
Phillip Eby는 Python의 패키지 문법을 다른 언어의 패키지 문법과 비교한 뒤 이 PEP의 대안으로 PEP 402를 설계했습니다. PEP 402는 마커 파일을 아예 사용하지 않자고 제안합니다. PyCon DE 2011의 논의에서 사람들은 디렉터리가 패키지에 기여한다는 것을 명시적으로 선언하는 것이 장애물이 아니라 오히려 바람직한 특성이라고 언급했습니다. 특히 Jython 개발자들은 Python 패키지를 선언할 필요가 없다면 Jython이 Java 패키지인 디렉터리를 Python 패키지로 쉽게 오인할 수 있다는 점을 발견했습니다.
패키지는 더 이상 네임스페이스 패키지의 __init__.py를 채우지 않아도 됩니다. 그 결과, extend_path와 declare_namespace는 더 이상 필요하지 않게 됩니다.
네임스페이스 패키지는 사소하지 않은 __init__.py 구현을 제공할 수 있게 됩니다. 이를 위해서는 단일 배포판이 네임스페이스 패키지의 __init__.py(그리고 경우에 따라 네임스페이스 패키지 자체에 속하는 다른 모듈들)만 담은 포션을 제공할 것을 권장합니다.
이 메커니즘은 기존 네임스페이스 메커니즘과 대부분 호환됩니다. extend_path는 이 명세에 맞게 조정될 것입니다. 그 외의 다른 메커니즘은 포션이 __path__에 두 번 추가되게 할 수 있습니다.
참고 문헌
Copyright
This document has been placed in the public domain.