PEP 794 – 임포트 이름 메타데이터
- Author:
- Brett Cannon <brett at python.org>
- Discussions-To:
- Discourse thread
- Status:
- Accepted
- Type:
- Standards Track
- Topic:
- Packaging
- Created:
- 05-Jun-2025
- Post-History:
- 02-May-2025, 05-Jun-2025
- Resolution:
- 05-Sep-2025
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
이 PEP는 Python 패키징의 코어 메타데이터 명세를 확장하여, 프로젝트가 설치된 후 제공하는 임포트 이름을 기록하기 위한 반복 가능한 새 필드 두 개, Import-Name과 Import-Namespace를 추가할 것을 제안합니다. 새 코어 메타데이터 필드의 값을 제공하기 위해 pyproject.toml의 [project] 테이블에 import-names와 import-namespaces라는 새 키가 추가됩니다. 이에 따라 코어 메타데이터 버전 2.5도 도입됩니다.
동기
Python 패키징에서는 프로젝트 이름이 그 프로젝트에서 임포트할 수 있는 이름과 일치해야 한다는 요구 사항이 없습니다. 따라서 임포트 이름에서 프로젝트 이름을 찾거나 그 반대로 찾는 깔끔하고 쉽고 정확한 방법이 없습니다. 이 때문에 임포트 이름을 알고 있을 때 설치할 올바른 프로젝트를 찾도록 돕거나, 프로젝트가 설치된 후 어떤 임포트 이름을 제공할지 알려 주려는 도구가 어려움을 겪을 수 있습니다.
예를 들어, 코드 편집기는 선택된 가상 환경에서 사용자에게 충족되지 않은 임포트가 있음을 감지할 수 있습니다. 하지만 여러 프로젝트가 어떤 임포트 이름을 제공하는지 확실하게 알 방법이 없으므로, 코드 편집기는 그 임포트 요구 사항을 충족하기 위해 설치할 수 있는 후보 프로젝트 목록을 사용자에게 정확하게 제공할 수 없습니다(예를 들어 import PIL이 사용자가 Pillow 프로젝트를 설치하기를 원한다는 뜻일 가능성이 매우 높다는 것은 분명하지 않습니다). 이는 사용자가 프로젝트 이름은 어렴풋이 기억하지만 임포트 이름은 기억하지 못하다가, 패키지가 제공하는 임포트 이름 목록을 보고 메모리가 되살아나는 경우에도 적용됩니다. 마지막으로, 도구는 사용자가 프로젝트를 설치하면 어떤 임포트 이름을 사용할 수 있게 되는지 알려 줄 수 있게 됩니다.
또한 두 프로젝트가 제공하는 임포트 이름을 바탕으로 두 프로젝트를 설치했을 때 서로 충돌할지 알 수 있는 쉬운 방법도 없습니다. 예를 들어, 서로 다른 두 프로젝트에 모두 _utils 모듈이 있다면, 한 프로젝트의 _utils 모듈이 다른 프로젝트의 파일을 덮어써서 우선하게 되므로 두 프로젝트를 모두 설치할 때 충돌이 발생합니다. 이 문제는 실제로 발생한 적이 있습니다.
이는 스팸 탐지에도 도움이 될 수 있습니다. 어떤 프로젝트가 매우 인기 있는 프로젝트와 같은 임포트 이름을 지정한다면, 이는 덜 인기 있는 쪽 프로젝트의 타당성을 자세히 살펴보라는 신호가 될 수 있습니다. 제공하는 임포트 이름에 대해 거짓 정보를 기재한 것으로 밝혀진 프로젝트도 또 다른 신호가 될 것입니다.
근거
이 PEP는 프로젝트 소유자가 프로젝트를 특정 플랫폼에 설치했을 때 제공하는 최상위 임포트 이름을 지정할 수 있도록 패키징 Core metadata specifications를 확장할 것을 제안합니다.
이 메타데이터를 코어 메타데이터에 넣는다는 것은, 그 데이터가 sdist나 wheel과 무관하게 색인 서버에서 (잠재적으로) 제공된다는 뜻입니다. 따라서 예컨대 wheel 전체를 다운로드하지 않아도 되도록 메타데이터를 도구에 노출하는 방법을 따로 마련할 필요가 없어집니다.
이 메타데이터가 모든 릴리스 산출물에서 동일하면, 프로젝트는 릴리스된 모든 파일을 확인하는 대신 단일 파일의 코어 메타데이터만 확인하여 가능한 모든 임포트 이름을 얻을 수 있습니다. 이는 또한 코어 메타데이터를 읽을 때 파일이 누락되었는지 걱정할 필요가 없으며, 메타데이터가 제공된다면 sdist만으로도 작업할 수 있다는 뜻입니다. 아울러 메타데이터가 같은 버전 안에서 릴리스된 파일마다 달라지지 않고 프로젝트 버전 전체에 걸쳐 일관되므로, pyproject.toml에 project.import-names 키와 project.import-namespaces 키를 두는 것도 간단해집니다.
모듈과 패키지를 담은 배포 파일은 모듈/패키지 수준에서 공개 API와 비공개 API를 어떤 조합으로든 가질 수 있습니다. 배포 파일에 어떤 종류의 모듈이나 패키지도 전혀 없을 수도 있습니다. 이러한 상황을 모두 구별할 수 있으면 사용자에게 도움이 될 수 있는 다양한 도구 활용이 가능합니다. 예를 들어, 공개 여부와 관계없이 모든 임포트 이름을 알면 설치 시점에 충돌을 감지하는 데 도움이 됩니다. 반면에 무엇이 명시적으로 공개 또는 비공개인지 알면, 편집기 같은 도구가 자동 완성에서 비공개 임포트 이름을 제안하지 않을 수 있습니다.
이 PEP는 제안된 메타데이터에 무엇을 나열해야 하는지(또는 나열하지 말아야 하는지)에 대해 의도적으로 지나치게 엄격하지 않습니다. Python 임포트 시스템이 매우 유연하기 때문에, 나열할 수 있는 항목을 어느 정도 엄격하게 규정하는 명세를 프로젝트가 정확히 따르고 있는지 빌드 백엔드가 검증하도록 하는 것은 제대로 해내기가 거의 불가능합니다. 따라서 이 PEP는 유효한 임포트 이름을 사용할 것과 프로젝트가 거짓 정보를 기재하지 않을 것만을 요구합니다(후자의 요구 사항은 프로그램으로 검증할 수 없다는 점을 인정합니다). 다만 프로젝트는 나열한 이름의 모든 수준을 고려해야 합니다(예를 들어 a.b.c를 나열하면서 a와 a.b를 고려하지 않을 수는 없습니다).
이 문제를 해결하려는 다른 시도도 여러 가지 있었지만, 모두 절충을 감수해야 합니다. 예를 들어 모든 프로젝트 릴리스의 모든 휠을 내려받아 Binary distribution format을 통해 어떤 파일이 제공되는지 살펴볼 수도 있지만, 이는 정적인 정보를 얻는 데 CPU와 대역폭을 많이 소모합니다(HTTP 범위 요청으로 zip 파일의 목차만 읽는 등 데이터 요청을 줄이는 요령을 쓸 수는 있습니다). 또한 이런 종류의 계산은 현재 PyPI 같은 중앙 색인 서버가 메타데이터를 호스팅하는 대신 모두가 각자 독립적으로 반복하고 있습니다. 또한 sdist는 휠의 구조를 아직 알 수 없으므로 이 방법이 통하지 않으며, 따라서 설치될 코드의 구조를 추론할 수 없습니다. 게다가 이러한 해결책은 프로젝트 소유자가 명시적으로 제공한 정보가 아니라 추론에 기반하므로 반드시 정확하다고 할 수 없습니다. 정보를 수집하는 계산 비용을 피하려고 색인이 그 정보를 호스팅하도록 하더라도 이러한 정확성 문제는 모두 그대로 남습니다.
명세
이 PEP는 코어 메타데이터에 새 필드를 도입하므로, 최신 코어 메타데이터 버전을 2.5로 올립니다.
Import-Name과 Import-Namespace 필드는 “다중 사용” 필드입니다. 두 필드의 각 항목은 반드시 유효한 임포트 이름이어야 하며, Import-Name의 경우에는 비어 있을 수도 있습니다. 지정된 모든 이름은 같은 버전의 프로젝트를 어떤 플랫폼에 설치했을 때 반드시 임포트할 수 있어야 합니다(예를 들어 메타데이터는 한 프로젝트 릴리스의 모든 sdist와 휠에 걸쳐 반드시 일관되어야 합니다). 이는 곧 해당 정보가 그 정보가 들어 있는 배포 아티팩트에 특정한 것이 아니라, 그 배포 아티팩트가 속한 릴리스 버전에 특정한 것임을 의미합니다.
임포트 이름 뒤에는 세미콜론과 “private”라는 용어가 올 수 있습니다(예: ; private). 이는 해당 임포트 이름이 프로젝트의 공개 API에 속하지 않음을 도구에 알립니다. ; 앞뒤에는 공백이 몇 개든 올 수 있습니다.
Import-Name에는 프로젝트가 설치되었을 때 배타적으로 제공하게 될 임포트 이름을 나열합니다(즉, Import-Name에 같은 임포트 이름을 나열한 두 프로젝트가 설치되면, 한 프로젝트가 다른 프로젝트의 이름을 가리게 됩니다). Import-Namespace에는 설치되었을 때 프로젝트가 제공하지만 배타적으로 제공하지는 않는 임포트 이름을 나열합니다(즉, Import-Namespace에 같은 임포트 이름을 나열한 프로젝트들이 함께 설치되더라도 그 공유된 이름을 서로 가리지 않습니다).
pyproject.toml specification에 import-names 키가 추가됩니다. 이 키는 Import-Name에 기록될 내용을 저장하는 문자열 배열입니다. 사용자가 project.dynamic에 이 키를 선언한 경우, 빌드 백엔드는 원한다면 사용자를 대신해 값을 동적으로 계산하는 기능을 지원할 수 있습니다. Import-Namespace에 대응하는 import-namespaces에도 마찬가지로 적용됩니다.
프로젝트는 프로젝트가 배타적으로 제공하는 가장 짧은 임포트 이름 중 모든 임포트 이름 상황을 포괄하는 것을 모두 나열해야 합니다. 가장 짧은 이름 중 점으로 구분된 이름이 있다면, 그 이름부터 최상위 이름까지의 모든 중간 이름도 Import-Namespace 및/또는 Import-Name에 적절히 나열해야 합니다. 예를 들어, 여러 하위 모듈을 가진 spam이라는 단일 패키지로 이루어진 프로젝트는 project.import-names = ["spam"]만 나열하면 됩니다. spam.bacon.eggs를 나열하는 프로젝트는 spam과 spam.bacon도 import-names와 import-namespaces에서 적절히 고려해야 합니다. 모든 이름을 나열하면 임포트 이름의 의도가 예상과 일치하는지 확인하는 검사 역할을 합니다. 또한 프로젝트는 공개 여부와 관계없이 모든 임포트 이름을 나열해야 하며, 필요에 따라 ; private 수식어를 사용해야 합니다.
프로젝트가 Import-Name과 Import-Namespace 양쪽에 같은 이름을 나열하면, 도구는 모호성을 이유로 반드시 오류를 발생시켜야 합니다. 이는 import-names와 import-namespaces에도 각각 동일하게 적용됩니다.
도구가 함께 설치하려는 두 프로젝트(즉, 같은 명령/작업으로 설치되는 프로젝트)의 Import-Name 항목에 서로 겹치는 이름이 있으면, 도구는 오류를 발생시켜야 합니다(SHOULD). 이는 어떤 프로젝트가 예기치 않게 다른 프로젝트의 코드를 가리는 일을 막기 위함입니다. 어떤 프로젝트의 Import-Name 항목이 다른 프로젝트의 Import-Namespace 항목과 겹칠 때에도 마찬가지입니다. 서로 겹치는 Import-Namespace 항목에는 이 규칙이 적용되지 않습니다. 그렇게 겹치는 것이 바로 네임스페이스 패키지의 목적이기 때문입니다. 이미 설치된 프로젝트와 임포트 이름이 겹치는 기존 환경에 프로젝트를 설치할 때, 도구는 경고하거나 오류를 발생시킬 수 있습니다(MAY). 이것이 “SHOULD”가 아니라 “MAY”인 이유는, 일부 사용자가 설치를 여러 단계에 걸쳐 수행할 때(예: 같은 환경에 서로 다른 설치 프로그램을 사용하는 경우) 의도적으로 임포트 이름을 덮어쓰기 때문입니다.
프로젝트는 pyproject.toml 파일에서 import-names를 빈 배열로 설정하고(예: import-names = []) import-namespaces는 아예 설정하지 않을 수 있습니다(MAY). 이에 맞추어 프로젝트는 메타데이터에 빈 Import-Name 필드를 둘 수 있습니다(MAY). 이는 공개든 비공개든 임포트 이름이 전혀 없는 프로젝트를 나타냅니다(즉, 배포 파일에 어떤 종류의 Python 모듈도 없습니다).
프로젝트에는 Import-Name 메타데이터가 없을 수 있으므로(MAY)(프로젝트가 이전 메타데이터 버전을 사용하거나 아무것도 지정하지 않았기 때문에), 이 경우 도구는 프로젝트가 어떤 이름을 제공하는지에 대한 정보를 갖지 못합니다. 그러나 실제로는 대다수 프로젝트의 프로젝트 이름이 임포트 이름과 일치합니다. 따라서 어떤 답이 필요하다면, 프로젝트 이름을 어떤 방식으로 정규화하여 얻은 임포트 이름(예: packaging.utils.canonicalize_name(name, validate=True).replace("-", "_"))을 사용할 수 있다고 가정하는 것이 합리적입니다.
프로젝트는 프로젝트 이름이 곧 임포트 이름이라는 것을 명시적으로 선언하기 위해, import-names 또는 import-namespaces를(그리고 각각에 해당하는 Import-Name 또는 Import-Namespace를) 프로젝트 이름(정규화 여부와 무관)과 일치하는 임포트 이름으로 설정할 수 있습니다(MAY).
예시
scikit-learn 1.7.0의 경우:
[project]
import-names = ["sklearn"]
pytest 8.3.5의 경우 예상되는 항목은 3개입니다:
[project]
# The pytest docs list code out of all of these modules, so it isn't
# obvious whether they would mark any as private.
import-names = ["_pytest", "py", "pytest"]
azure-mgmt-search 9.1.0의 경우, 네임스페이스 항목 두 개와 azure.mgmt.search에 대한 이름 항목 하나가 있어야 합니다:
[project]
import-names = ["azure.mgmt.search"]
import-namespaces = ["azure", "azure.mgmt"]
하위 호환성
이것은 코어 메타데이터의 새 필드이자 새 코어 메타데이터 버전이므로 하위 호환성 문제는 없을 것입니다.
보안 영향
도구는 메타데이터가 부정확할 수 있다고 간주해야 합니다. 따라서 제공된 메타데이터를 바탕으로 내리는 모든 결정은 어떤 식으로든 악의적일 수 있다고 가정해야 합니다.
교육 방법
프로젝트 소유자에게는 이제 자신의 프로젝트가 임포트용으로 어떤 이름을 제공하는지 기록할 수 있다는 점을 알려야 합니다. 프로젝트 이름이 프로젝트가 제공하는 모듈이나 패키지 이름과 일치한다면 아무것도 하지 않아도 됩니다. 하지만 차이가 있다면, 프로젝트가 제공하는 모든 임포트 이름을 가능한 한 가장 짧은 이름으로 기록해야 합니다. 이름 중 암묵적 네임스페이스가 있다면 그 이름은 pyproject.toml의 project.import-namespaces에 넣고, 그렇지 않은 이름은 project.import-names에 넣습니다.
프로젝트 사용자가 이 새 메타데이터를 반드시 알아야 하는 것은 아닙니다. 도구를 통해 이 메타데이터를 접할 수는 있지만, 그 데이터가 어디에서 왔는지에 대한 세부 사항은 중요하지 않습니다. 색인 서버가 이 메타데이터를 노출한다면(예: Import-Name의 값을 나열하고, 파일 구조가 메타데이터의 주장을 뒷받침하는지 표시한다면) 사용자가 이를 접할 수도 있지만, 그렇더라도 사용자가 이 PEP의 기술적 세부 사항을 알아야 하는 것은 아닙니다.
참조 구현
https://github.com/brettcannon/packaging/tree/pep-794 는 이 PEP를 지원하도록 ‘packaging’ 패키징 라이브러리를 갱신하는 브랜치입니다.
기각된 아이디어
Import-Namespace의 값 추론
이 PEP의 이전 버전은 Import-Name에 있는 점으로 구분된 이름을 바탕으로 Import-Namespace의 값이 되었을 내용을 추론했습니다. 명시적으로 지정하는 편이 더 낫다고 결정되었습니다. 그렇게 하면 암묵적 네임스페이스로 해석될 항목을 실수로 나열하는 오류를 피할 수 있을 뿐만 아니라, 데이터가 더 자기 문서화적이 되기 때문입니다.
Import-Namespace에 나열된 이름이 Import-Name의 이름에 절대 포함되지 않도록 요구
Python의 임포트 시스템이 기본적으로 동작하는 방식상, 임포트 이름이 네임스페이스를 포함하는 것은 불가능합니다. 그러나 Python의 임포트 시스템은 사용자 코드가 이를 가능하게 만들 수 있을 만큼 유연합니다. 따라서 임포트 이름이 네임스페이스 이름을 포함하는 경우 – import-names = ["spam"]과 import-namespaces = ["spam.bacon"] – 도구가 오류를 내야 한다는 요구 사항은 제거되었습니다.
Provides 필드의 용도 변경
메타데이터 버전 1.1에서 도입되고 1.2에서 폐기된 Provides 필드는 비슷한 정보를 제공하기 위한 것이었지만, 이 PEP가 제안하는 것처럼 구별되는 네임스페이스가 아니라 프로젝트가 제공하는 모든 이름을 대상으로 한다는 점이 달랐습니다. 이러한 차이와 함께, Provides가 폐기되어 기존 코드가 이를 무시할 수 있다는 사실을 근거로 새 필드를 도입하기로 결정했습니다.
필드 이름을 Namespace로 지정
“네임스페이스”라는 용어는 임포트 관점에서 기술적으로 정확하지만, 암묵적 네임스페이스 패키지와 혼동될 수 있습니다.
RECORD 파일 제공
이 PEP의 PEP 이전 버전에 대한 논의 중에, 이 새 메타데이터 대신 wheel의 RECORD 파일을 색인 서버에서 제공하자는 제안이 있었습니다. 그렇게 하면 즉시 구현할 수 있다는 이점이 있었을 것입니다. 그러나 동등한 정보를 제공하려면 wheel이 설치할 내용의 파일 구조를 바탕으로 추론해야 했을 것입니다. 이는 부정확한 정보로 이어질 수 있습니다. 또한 sdist를 지원하지 않습니다.
결국 투표가 진행되었고, 이 PEP가 취한 접근 방식이 채택되었습니다.
프로젝트가 지정하는 내용을 더 엄격하게 규정
이 PEP의 이전 버전은 Import-Name에 넣을 수 있는 내용을 훨씬 더 엄격하게 제한했습니다. 여기에는 일부 “SHOULD” 지침을 “MUST” 요구 사항으로 바꾸는 것과, 프로젝트가 “소유”한 것을 계산하는 방법을 구체적으로 정하는 것이 포함되었습니다. 결국 이는 지나치게 제한적이며, 잘못 구현되거나 명세가 예상치 못하게 너무 엄격해질 위험이 있다고 결정되었습니다.
이 메타데이터는 완전한지 검증할 수 없으므로 애초에 완전할 것으로 기대되지 않았고, 그래서 현재 이 PEP에 있는 더 느슨한 명세를 대신 선택했습니다.
미해결 문제
해당 없음
감사의 글
이 메타데이터가 제공할 유용성을 ~~불평하며~~ 꾸준히 제기해 주신 HeeJae Chang에게 감사드립니다. 이 PEP의 초안을 검토하고 피드백을 주신 Josh Cannon(친척 관계 아님)에게 감사드립니다. 또한 이 주제에 관한 이전 논의에 참여해 주신 모든 분께 감사드립니다.
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.