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

Python 개선 제안 한국어 번역

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 382PEP 402에 대해 오랫동안 유익한 논의를 진행했습니다. 결국 둘 다 거부했지만, PEP의 정신을 이어갈 새로운 PEP가 작성될 예정입니다. 402. Martin von Löwis가 요약을 작성했습니다: [2].

초록

네임스페이스 패키지는 하나의 Python 패키지를 디스크의 여러 디렉터리로 분할하기 위한 메커니즘입니다. 현재 Python 버전에서는 패키지의 __path__를 계산하는 알고리즘을 정식으로 정의해야 합니다. 여기서 제안하는 개선 사항을 적용하면 가져오기 기능 자체가 패키지를 구성하는 디렉터리 목록을 생성합니다. 이 PEP의 구현은 [1]에서 사용할 수 있습니다.

용어

이 PEP에서 패키지라는 용어는 Python의 import 문으로 정의되는 Python 패키지를 의미합니다. 배포라는 용어는 Python 패키지 색인에 저장되고 distutils 또는 setuptools로 설치되는, 개별적으로 설치 가능한 Python 모듈 집합을 의미합니다. 벤더 패키지라는 용어는 운영 체제의 패키징 메커니즘으로 설치되는 파일 그룹을 의미합니다(예를 들어 Debian 또는 Redhat 패키지는 Linux 시스템에 설치됩니다).

부분이라는 용어는 네임스페이스 패키지에 기여하는 단일 디렉터리의 파일 집합을 의미합니다(이 디렉터리는 zip 파일에 저장될 수도 있습니다).

현재의 네임스페이스 패키지

Python은 현재 패키지를 네임스페이스 패키지로 나타내기 위한 pkgutil.extend_path를 제공합니다. 이를 사용하는 권장 방법은 다음을 넣는 것입니다.:

from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)

패키지의 __init__.py에 모든 배포 패키지는 해당 __init__.py에 동일한 내용을 제공해야 하므로, 패키지의 어느 부분이 먼저 가져와지는지와 관계없이 extend_path가 호출됩니다. 그 결과 패키지의 __init__.py는 패키지 조각이 sys.path에 배치된 순서와 어느 부분이 먼저 가져와지는지에 따라 달라지므로 실제로는 이름을 정의할 수 없습니다. 특별한 기능으로 extend_path는 <packagename>.pkg라는 이름의 파일을 읽으며, 이를 통해 추가 부분을 선언할 수 있습니다.

setuptools는 pkg_resources.declare_namespace라는 유사한 함수를 제공하며, 다음과 같은 형식으로 사용합니다.:

import pkg_resources
pkg_resources.declare_namespace(__name__)

부분의 __init__.py에서는 __path__에 할당할 필요가 없습니다. declare_namespace가 sys.modules를 통해 패키지의 __path__를 수정하기 때문입니다. 특별한 기능으로 declare_namespace는 zip 파일도 지원하며, 패키지 이름을 내부적으로 등록하므로 setuptools가 나중에 sys.path에 추가하는 항목을 통해 각 패키지에 추가 부분을 올바르게 추가할 수 있습니다.

setuptools를 사용하면 배포 패키지의 setup.py에서 네임스페이스 패키지를 선언할 수 있으므로, 배포 패키지 개발자가 __init__.py에 마법 같은 __path__ 수정 코드를 직접 넣을 필요가 없습니다.

근거

현재의 네임스페이스 패키지에 대한 명령형 접근 방식은 네임스페이스 패키지를 제공하기 위한, 서로 약간 호환되지 않는 여러 메커니즘을 만들어 냈습니다. 예를 들어 pkgutil은 *.pkg 파일을 지원하지만 setuptools는 지원하지 않습니다. 마찬가지로 setuptools는 zip 파일 검사를 지원하고 해당 부분을 자체 _namespace_packages 변수에 추가하는 것을 지원하지만, pkgutil은 이를 지원하지 않습니다.

또한 현재 접근 방식은 시스템 공급업체에 문제를 일으킵니다. 공급업체 패키지는 일반적으로 서로 겹치는 파일을 제공해서는 안 되며, 이미 디스크에 존재하는 파일을 가진 공급업체 패키지를 설치하려고 하면 실패하거나 예측할 수 없는 동작이 발생합니다. 공급업체가 배포 패키지를 네임스페이스 패키지용 단일 디렉터리에 모두 들어가도록 패키징할 수도 있으므로, 모든 부분이 서로 충돌하는 __init__.py 파일을 제공하게 됩니다.

명세

패키지를 임포트하는 명령형 메커니즘 대신, 여기서는 선언적 접근 방식을 제안합니다. 이름이 .pyp로 끝나는 디렉터리(Python 패키지용)는 패키지의 한 부분을 포함합니다.

임포트 문은 패키지 이름이 P인 패키지의 __path__속성을, __init__.py 파일을 포함하는 P라는 이름의 디렉터리를 선택적으로 하나 포함하고, 부모 패키지의 __path__(또는 sys.path)에서 발견되는 순서대로 P.pyp라는 이름의 모든 디렉터리를 추가한 것으로 계산하도록 확장됩니다. 이들 중 하나라도 발견되면 패키지의 추가 부분을 계속 검색합니다.

디렉터리에는 P/__init__.py에 있는 패키지와 P.pyp 형식이 모두 포함될 수 있습니다.

임포트 메커니즘에는 그 밖의 변경을 하지 않으며, 모듈( __init__.py 포함) 검색은 계속해서 처음 발견되는 모듈에서 중지됩니다. 요약하면, foo 패키지를 임포트하는 과정은 다음과 같습니다.

  1. sys.path에서 foo 또는 foo.pyp 디렉터리나 foo.<ext> 파일을 검색합니다. 파일이 발견되고 디렉터리는 발견되지 않으면, 해당 파일을 모듈로 취급하여 임포트합니다.
  2. foo 디렉터리가 발견되면, 해당 디렉터리에 __init__.py가 포함되어 있는지 확인합니다. 포함되어 있으면 __init__.py의 위치를 기억합니다. 그렇지 않으면 해당 디렉터리를 건너뜁니다. __init__.py를 하나 찾으면, foo라는 이름의 이후 디렉터리는 건너뜁니다.
  3. foo 디렉터리와 foo.pyp 디렉터리 모두에 대해, 해당 디렉터리를 패키지의 __path__에 추가합니다.
  4. __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__에 일부가 두 번 추가될 수 있습니다.

참고 문헌