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

Python 개선 제안 한국어 번역

PEP 771 – 파이썬 소프트웨어 패키지의 기본 extras

Author:
Thomas Robitaille <thomas.robitaille at gmail.com>, Jonathan Dekhtiar <jonathan at dekhtiar.com>
Sponsor:
Pradyun Gedam <pradyunsg at gmail.com>
Discussions-To:
Discourse thread
Status:
Draft
Type:
Standards Track
Topic:
Packaging
Created:
13-Jan-2025
Post-History:
15-Jan-2025, 06-Feb-2025, 09-Jun-2025

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

PEP 508은 패키지 의존성을 선언하기 위한 미니 언어를 지정합니다. 이 언어의 한 기능은 extras를 지정하는 기능이며, 이는 배포 패키지의 선택적 구성 요소로서 사용하면 추가 의존성을 설치합니다. 이 PEP는 명시적으로 제공된 extras가 없을 경우 하나 이상의 extras를 기본적으로 설치할 수 있도록 하는 메커니즘을 제안합니다.

동기

기본 extras의 다양한 사용 사례와 이 PEP에서 가능한 해결책이 이 DPO 스레드에서 광범위하게 논의되었습니다. 이는 현재 PEP의 동기를 제공하는 두 가지 큰 사례로 나뉩니다.

권장되지만 필수는 아닌 의존성

패키지 유지 관리자는 패키지의 기능이나 성능을 확장하는 선택적 의존성을 선언하기 위해 extras를 자주 사용합니다. 경우에 따라 어떤 의존성을 필수로 지정해야 하고 어떤 의존성을 extras로 분류해야 하는지 판단하기 어려울 수 있습니다. 대부분의 기능을 기본적으로 사용할 수 있기를 선호하는 일반 사용자와, 규모가 큰 선택적 의존성 없이 최소한으로 설치하기를 원하는 사용자 사이에서 균형을 맞춰야 합니다. 기존 파이썬 패키징 인프라를 활용하는 한 가지 해결책은 패키지 유지 관리자가 예를 들어 recommended라는 extra를 정의하여, 필수는 아니지만 권장되는 모든 의존성을 포함하도록 하는 것입니다. 그런 다음 사용자에게 package[recommended]를 사용하여 패키지를 설치하도록 안내하고, 더 세밀한 제어를 선호하는 사용자는 package를 사용할 수 있습니다. 그러나 실제로는 많은 사용자가 [recommended]구문을 알지 못하므로, 일반적인 설치를 위해 이를 알아야 하는 부담이 사용자에게 지워집니다. 권장 의존성을 기본적으로 설치하면서 사용자가 더 최소한의 설치를 요청할 방법을 제공한다면 이 사용 사례를 충족할 수 있으며, 이 PEP는 이에 대한 해결책을 설명합니다.

사용자가 기본적으로 추가 의존성을 포함하도록 권장하여 이러한 패턴을 보여 주는 패키지의 예는 다음과 같습니다.

여러 백엔드 또는 프런트엔드를 지원하는 패키지

extras를 사용하는 또 다른 일반적인 사용 사례는 각 백엔드 또는 프런트엔드에 설치해야 하는 서로 다른 백엔드 또는 프런트엔드와 의존성을 정의하는 것입니다. 패키지가 정상적으로 작동하려면 하나 이상의 백엔드 또는 프런트엔드를 설치해야 할 수 있지만, 어떤 백엔드 또는 프런트엔드인지는 유연하게 선택할 수 있습니다. 이러한 프런트엔드 또는 백엔드의 구체적인 예는 다음과 같습니다.

현재의 패키징 표준에 따르면 유지 관리자는 백엔드 또는 프런트엔드 중 하나를 필수로 지정하거나 사용자에게 항상 extras(예: package[backend])를 지정하도록 요구해야 하며, 따라서 사용자가 package만 설치할 경우 사용할 수 없는 설치 상태가 될 위험이 있습니다. 하나 이상의 기본 백엔드 또는 프런트엔드를 지정하고 이러한 기본값을 재정의할 방법을 제공한다면 사용자에게 훨씬 더 나은 경험을 제공할 수 있으며, 이 PEP에서 설명하는 접근 방식으로 이를 실현할 수 있습니다.

이 PEP는 충돌하거나 호환되지 않는 extras를 금지하는 문제, 예를 들어 패키지에 정확히 하나의 프런트엔드 또는 백엔드 패키지가 필요한 경우를 해결하는 것을 목표로 하지 않는다는 점에 유의하십시오. 현재 파이썬 패키징 인프라에는 충돌하거나 호환되지 않는 extras의 설치를 금지하는 메커니즘이 없으며, 이 PEP도 이를 변경하지 않습니다.

작동하려면 하나 이상의 백엔드 또는 프런트엔드가 필요하고, 백엔드 또는 프런트엔드를 설치할 기본 extra를 권장하는 패키지의 예는 다음과 같습니다.

세 경우 모두 엑스트라를 전혀 지정하지 않고 패키지를 설치하면 손상된 설치가 되며, 이는 이러한 패키지 중 일부에서 흔히 보고되는 지원 문제입니다.

근거

this DPO thread에서뿐 아니라 수많은 이슈와 풀 리퀘스트에서도 커뮤니티는 여러 해 동안 여러 가능한 해결책을 광범위하게 논의해 왔습니다. 아래에 제시하는 해결책은 다음과 같습니다:

  • 옵트인 방식의 해결책이므로 패키지 유지 관리자가 이를 사용할지 여부를 선택할 수 있습니다.
  • Motivation에 설명된 두 가지 주요 사용 사례를 모두 수용할 수 있을 만큼 유연합니다.
  • 관련 PEP 508의 문법을 재사용합니다

논의된 모든 해결책 중 세 가지 기준을 모두 충족하는 유일한 해결책입니다.

사양

Default-Extra 메타데이터 필드

새로운 다중 사용 메타데이터 필드인 Default-Extra핵심 패키지 메타데이터에 추가됩니다. 이 필드의 각 항목은 패키지를 설치할 때 엑스트라를 명시적으로 지정하지 않으면 자동으로 포함될 엑스트라를 지정하는 문자열이어야 합니다.

Default-Extra 항목에는 Provides-Extra 항목에 이미 지정된 항목만 사용할 수 있습니다.

예시:

Default-Extra: recommended
Default-Extra: backend1
Default-Extra: backend2
Default-Extra: backend3

핵심 패키지 메타데이터에 새 필드가 도입되므로, Metadata-Version을 작성 시점의 다음 마이너 버전인 2.5로 올려야 합니다.

[project] 메타데이터 테이블의 새 키

프로젝트 메타데이터에서 원래 PEP 621에 정의되었고 현재는 PyPA 사양에 정의된 [project] 테이블에 새 키가 추가됩니다. 이 키의 이름은 default-optional-dependency-keys이며 설명은 다음과 같습니다:

  • TOML 유형: 문자열 배열
  • 대응하는 핵심 메타데이터 필드: Default-Extra

default-optional-dependency-keys의 각 문자열은 optional-dependencies에 정의된 엑스트라의 이름이어야 하며, 이 배열의 각 엑스트라는 핵심 패키지 메타데이터에서 일치하는 Default-Extra항목으로 변환됩니다. 이전 절에서 제시한 예시 Default-Extra 항목을 생성하는 유효한 사용 예시는 다음과 같습니다:

[project]
default-optional-dependency-keys = [
    "recommended",
]

그리고:

[project]
default-optional-dependency-keys = [
    "backend1",
    "backend2",
    "backend3"
]

기본 엑스트라 재정의

종속성 사양에 엑스트라가 명시적으로 제공되면 기본 엑스트라는 무시됩니다. 그렇지 않으면 기본 엑스트라가 설치됩니다.

예를 들어 패키지가 extra1 기본 엑스트라와 기본값이 아닌 extra2 엑스트라를 정의하고 있으며 사용자가 다음과 같이 패키지를 설치한다고 가정하겠습니다:

$ pip install package

기본 extra1의 종속성이 포함됩니다. 반대로 사용자가 다음과 같이 패키지를 설치하면:

$ pip install package[extra2]

extra2 엑스트라는 설치되지만 기본 extra1엑스트라는 무시됩니다.

동일한 패키지가 설치 명령이나 의존성 트리에서 여러 번 지정된 경우, 해당 패키지의 인스턴스 중 하나라도 extras 없이 지정되었다면 기본 extras를 설치해야 합니다. 예를 들어, 의존성 트리에서 package가 여러 번 나타나는 spam 패키지를 설치하는 경우:

spam
├── tomato
│   ├── package[extra2]
└── egg
    └── package

package가 extras 없이 지정된 경우가 최소 한 번 있으므로 기본 extra를 설치해야 합니다.

package[]와 같은 빈 extras 집합은 패키지가 기본 extras 없이 설치되어야 한다는 의미로 해석해야 합니다(package가 의존성 트리의 다른 곳에 나타나는 경우는 제외하며, 이 경우에는 위에서 언급한 것처럼 기본 extra가 설치될 것입니다). 이는 패키지를 최소한으로 설치하는 보편적인 방법을 제공합니다.

또한 pip와 같은 일부 도구는 현재 인식할 수 없는 extras를 무시하고, 해당 extra가 인식되지 않았음을 사용자에게 알리는 경고를 출력합니다. 예를 들면 다음과 같습니다:

$ pip install package[non-existent-extra]
WARNING: package 3.0.0 does not provide the extra 'non-existent-extra'
...

오류를 발생시키는 대신 이처럼 동작하는 도구의 경우, 의존성 명세에서 extra가 유효하지 않은 것으로 인식되면 이를 무시해야 하며, 지정된 extras가 모두 유효하지 않다면 이는 package가 아니라 package[]와 동등한 것으로 간주하여 어떤 기본 extras도 설치하지 않아야 합니다.

마지막으로, Relying on tooling to deselect any default extras에서도 논의한 것처럼, 패키지 설치 도구는 위 동작을 제어하기 위한 자체 옵션을 구현할 수 있습니다. 예를 들어 이러한 패키지가 의존성 트리의 어디에 나타나는지와 관계없이 일부 또는 모든 패키지에 대해 기본 extras를 비활성화하는 옵션을 구현할 수 있습니다. 이러한 도구별 옵션을 구현하는 경우 도구 개발자는 이를 옵트인 방식으로 제공해야 하며, 사용자는 위의 PEP 771 동작을 기본값으로 경험해야 합니다.

예시

이 절에서는 Motivation 절에서 설명한 사용 사례를 살펴보고, 위에 제시된 사양을 사용하여 이제 이러한 사례를 어떻게 해결할 수 있는지 살펴봅니다.

권장 의존성과 최소 설치

먼저 권장되지만 엄격하게 필수는 아닌 의존성을 기본적으로 설치하면서, 필수 의존성만 설치할 수 있는 방법도 제공하려는 패키지의 경우를 살펴봅니다.

이를 위해 패키지 유지 관리자는 권장되지만 필수는 아닌 의존성을 포함하는 recommended라는 extra를 정의하고 이를 기본 extra에 포함하도록 선택합니다:

[project]
default-optional-dependency-keys = [
    "recommended"
]

[project.optional-dependencies]
recommended = [
    "package1",
    "package2"
]

이 패키지의 이름이 package라면, package를 설치하는 사용자는 package[recommended]와 동등한 결과를 얻게 됩니다. 사용자는 대신 package[]를 설치하여 기본 extras 없이 패키지를 설치할 수도 있습니다.

Motivation 절에서 언급한 구체적인 패키지 사례 중 하나인 astropy 패키지는 현재 기본 설치 안내에서 사용자가 설치하도록 안내하는 recommendedextra를 정의합니다. 이 PEP를 사용하면 recommendedextra를 기본 extra로 선언할 수 있습니다:

[project]
default-optional-dependency-keys = [
    "recommended"
]

[project.optional-dependencies]
recommended = [
    "scipy",
    "..."
]

즉, 다음을 설치하면:

$ pip install astropy

scipy와 같은 선택 사항이지만 권장되는 의존성도 설치됩니다. 최소 설치를 원하는 고급 사용자는 다음을 사용할 수 있습니다:

$ pip install astropy[]

최소 하나의 백엔드 또는 프런트엔드가 필요한 패키지

Motivation 에서 설명한 것처럼 일부 패키지는 여러 백엔드 및/또는 프런트엔드를 지원할 수 있으며, 어떤 경우에는 항상 최소 하나의 백엔드 또는 프런트엔드 패키지가 설치되도록 하는 것이 바람직할 수 있습니다. 그렇지 않으면 패키지를 사용할 수 없기 때문입니다. 구체적인 예로는 사용하려면 GUI 라이브러리가 있어야 하지만 여러 GUI 라이브러리를 지원할 수 있는 GUI 애플리케이션이나, 서로 다른 계산 백엔드에 의존할 수 있지만 최소 하나는 설치되어야 하는 패키지를 들 수 있습니다.

이 경우 패키지 유지 관리자는 각 백엔드 또는 프런트엔드에 대해 extra를 정의하고 기본값을 제공하도록 선택할 수 있습니다. 예를 들면 다음과 같습니다:

[project]
default-optional-dependency-keys = [
    "backend1"
]

[project.optional-dependencies]
backend1 = [
    "package1",
    "package2"
]
backend2 = [
    "package3"
]

패키지가 예를 들어 여러 백엔드를 동시에 지원할 수 있고 그중 일부가 항상 설치되어야 한다면, 해당 백엔드의 의존성은 기본 extras 메커니즘을 사용하는 대신 필수 의존성으로 지정해야 합니다.

Motivation 에서 언급한 구체적인 사례 중 하나인 napari 패키지는 PyQt5, PyQt6, PySide2 또는 PySide6 중 하나를 사용할 수 있으며, 현재 사용자는 이 중 하나를 설치하려면 napari[all]을 명시적으로 지정하거나, 예를 들어 프런트엔드 패키지 중 하나를 명시적으로 지정하려면 napari[pyqt5]를 지정해야 합니다. extras 없이 napari를 설치하면 작동하지 않는 패키지가 됩니다. 이 PEP를 사용하면 napari는 다음 구성을 정의할 수 있습니다:

[project]
default-optional-dependency-keys = [
    "pyqt5"
]

[project.optional-dependencies]
pyqt5 = [
    "PyQt5",
    "..."
]
pyside2 = [
    "PySide2",
    "..."
]
pyqt6 = [
    "PyQt6",
    "..."
]
pyside6 = [
    "PySide6",
    "..."
]

즉:

$ pip install napari

기본적으로 바로 작동하면서도 사용자가 프런트엔드를 명시적으로 지정할 수 있는 메커니즘이 여전히 제공됩니다. 예를 들면 다음과 같습니다:

$ pip install napari[pyside6]

기본 extras를 제거하지 않아야 하는 extras 지원

여기서 추가로 고려하는 경우는 패키지 유지 관리자가 기본 extras를 제거하지 않고 사용자가 기본이 아닌 extras를 선택적으로 사용할 수 있도록 지원하려는 경우입니다. 본질적으로 다음을 원할 수 있습니다:

  • package[]로 extras 없이 설치하도록 합니다.
  • package로 권장 의존성을 (recommended extra에 포함하여) 설치하도록 합니다.
  • package[alternative]로 기본 extras는 설치하지 않고, 대체 선택적 의존성 집합을 (alternative extra에 포함하여) 설치하도록 합니다.
  • package[additional]로 권장 의존성과 추가 의존성을 모두 (additional extra에 포함하여) 설치하도록 합니다.

예를 들어 다음과 같이 구현할 수 있습니다:

[project]
default-optional-dependency-keys = [
    "recommended"
]

[project.optional-dependencies]
recommended = [
    "package1",
    "package2"
]
alternative = [
    "package3"
]
additional = [
    "package[recommended]",
    "package4"
]

패키지가 extras에서 자신을 참조하는 기능은 기존 Python 패키징 도구에서 지원됩니다.

다시 구체적인 예를 고려하면, 이 PEP를 통해 astropyrecommended extra를 정의할 수 있습니다 (Recommended dependencies and minimal installations에 설명된 대로). 그러나 이 패키지는 다른 extras도 정의하며, 예를 들어 jupyterJupyter-기반 환경에서 사용자 경험을 향상하는 패키지를 추가합니다. 이 extra를 선택한 사용자도 권장 의존성이 설치되기를 원할 수 있습니다. 이 경우 다음 구성이 이 문제를 해결합니다:

[project]
default-optional-dependency-keys = [
    "recommended"
]

[project.optional-dependencies]
recommended = [
    "scipy",
    "..."
]
jupyter = [
    "astropy[recommended]",
    "ipywidgets",
    "..."
]

다음을 설치하는 사용자는:

$ pip install astropy[jupyter]

그러면 다음과 동일한 결과를 얻게 됩니다:

$ pip install astropy[recommended, jupyter]

여러 종류의 기본값을 사용하는 패키지

경우에 따라 패키지에 여러 종류의 기본값이 필요할 수 있습니다. 예를 들어, Packages requiring at least one backend or frontend에서는 백엔드 또는 프론트엔드 중 하나를 가지는 패키지의 경우를 고려했지만, 어떤 경우에는 패키지가 백엔드 프론트엔드를 모두 지원해야 하며, 하나 이상의 기본 프론트엔드와 하나 이상의 기본 백엔드를 지정하려 할 수 있습니다.

이상적으로는 다음과 같은 동작을 원할 수 있습니다:

$ pip install package  # installs default backend and frontend
$ pip install package[]  # installs no backends or frontends
$ pip install package[backend1]  # installs backend1 and default frontend
$ pip install package[frontend2]  # installs frontend2 and default backend
$ pip install package[backend1, frontend2]  # installs backend1 and frontend2

그러나 이 PEP에서는 예를 들어 backend1을 지정하면 기본 백엔드는 비활성화되고 기본 프론트엔드는 활성화되도록 하는 메커니즘을 제공하지 않기로 했습니다. 이는 복잡성을 증가시키기 때문입니다.

대신 유지 관리자는 당분간 백엔드 또는 프론트엔드가 명시적으로 지정된 경우 백엔드와 프론트엔드를 모두 지정해야 한다고 문서화해야 합니다. 이를 원하는 사용자가 해당 기능을 쉽게 발견하는 데에는 문제가 없을 것입니다. 어떤 백엔드 또는 프론트엔드를 사용할 수 있는지 알아보려면 어차피 문서를 읽어야 하므로, 백엔드와 프론트엔드에 extras를 올바르게 사용하는 방법도 동시에 안내할 수 있기 때문입니다.

사용자 편의성을 높이는 한 가지 방법은 유지 관리자가 예를 들어 defaultbackenddefaultfrontend라는 extras를 만들어 기본 백엔드와 프론트엔드를 설치하도록 하는 것입니다. 그런 다음 사용자에게 다음을 수행하도록 권장할 수 있습니다:

$ pip install package  # installs default backend and frontend
$ pip install package[]  # installs no backends or frontends
$ pip install package[backend1, defaultfrontend]  # installs backend1 and default frontend
$ pip install package[defaultbackend, frontend2]  # installs frontend2 and default backend
$ pip install package[backend1, frontend2]  # installs backend1 and frontend2

이렇게 하면 (원하는 경우) 기본값이 시간이 지나 변경되더라도 사용자가 권장되는 백엔드를 얻을 수 있습니다.

향후 더 나은 해결책을 구현하려는 요구가 있다면, 이 PEP가 이를 막아서는 안 된다고 생각합니다. 예를 들어, 향후 extra가 비활성화하는 기본 extras가 어떤 것인지 지정할 수 있는 기능을 추가하는 방안을 생각해 볼 수 있으며, 이를 지정하지 않으면 명시적으로 지정된 extras가 모든 기본 extras를 비활성화하도록 할 수 있습니다(현재 PEP와 일관됩니다).

하위 호환성

기본 extras를 사용하지 않는 패키지

패키징 생태계의 도구에 이 PEP에 대한 지원이 추가되면, 기본 extras를 사용하지 않는 패키지는 현재와 같이 계속 작동하며 호환성이 깨지지 않습니다.

기본 extras를 사용하는 패키지

패키지가 기본 extras를 정의하기 시작하면 해당 기본값은 이 PEP를 구현한 최신 버전의 패키징 도구에서만 적용되지만, 이러한 패키지는 이전 패키징 도구로도 계속 설치할 수 있습니다. – 주된 차이는 이전 패키징 도구를 사용할 때 기본 extras가 자동으로 설치되지 않는다는 점입니다.

How to teach this에서 설명한 바와 같이, 패키지 작성자는 사용자층에 따라 기본 extra 기능을 언제 어떻게 도입할지 신중하게 평가해야 합니다. 필수 의존성을 기본 extra로 옮기는 등의 일부 조치는 상당수 사용자가 여전히 기본 extras를 지원하지 않는 이전 버전의 패키지 설치 프로그램을 사용하고 있다면 사용자에게 문제를 일으킬 가능성이 높기 때문입니다. 이러한 의미에서 패키지 작성자는 이 기능을 특정 방식으로 사용하면 사용자에게 하위 호환성 문제가 발생할 수 있음을 인지해야 하며, 따라서 사용자에게 미치는 영향을 최소화할 책임이 있습니다.

패키징 관련 도구

가장 중요한 하위 호환성 측면은 패키징 도구가 extras에 대해 갖는 가정과 관련됩니다. 구체적으로 이 PEP는 의존성 트리에 의존성을 추가한다는 측면에서 extras가 더 이상 전적으로 가산적이지 않다는 가정으로 변경하며, 일부 extras를 지정하면 설치되는 의존성이 줄어들 수 있습니다.

동작 변경의 구체적인 예는 pip에서 볼 수 있습니다. numpy를 필수 의존성으로 가지고 scipy를 포함하는 recommended라는 기본 extra가 있는 package라는 패키지를 생각해 보십시오. 사용자가 package[]를 설치하면 packagenumpy만 설치됩니다. 그 후 사용자가 다음을 실행하면:

$ pip freeze > requirements.txt

그러면 requirements.txt에는 예를 들어 다음이 포함됩니다.:

package==1.0.2
numpy==2.1.0

그러나 사용자가 그 후 다음을 사용하여 이 파일의 요구 사항을 설치하면:

$ pip install -r requirements.txt

그러면 pip는 기본 extra를 포함하는 packagenumpy와 함께 설치하므로 최종 환경에는 scipy가 포함됩니다. 이 경우의 해결책은 사용자가 의존성 트리의 해결을 피하도록 pip install--no-deps를 전달하는 것이지만, 여기서 강조하려는 점은 extra가 미칠 수 있는 영향에 대한 가정이 변경됨에 따라 패키징 도구의 동작도 변경될 수 있다는 것입니다.

최근 승인된 PEP 751은 향후 pip freeze 출력 및 기타 도구와 같은 대안을 대체하기 위한 새로운 파일 형식을 정의한다는 점에 주목할 필요가 있습니다. 새로운 파일 형식은 파일에 포함된 패키지를 의존성을 해결하지 않고 설치하도록 설계되었으며, 이는 이 PEP에서 지정한 기본 extras와 완전히 호환되고 위에서 언급한 pip freeze/pip install -r 문제를 방지한다는 의미입니다.

보안 영향

이 PEP와 관련하여 알려진 보안 영향은 없습니다.

이 내용을 가르치는 방법

이 절에서는 이 PEP의 구현과 관련하여 커뮤니티의 여러 그룹에 속한 사람들에게 제공해야 하는 정보를 개괄합니다. 아래에 설명된 일부 측면은 PEP가 패키징 도구에 완전히 구현되기 전에도 관련이 있습니다. 이 구현에 앞서 미리 수행할 수 있는 준비가 있어 이후 발생할 수 있는 전환을 용이하게 할 수 있기 때문입니다. 아래에서 다루는 그룹은 다음과 같습니다.

패키지 최종 사용자

패키지 사용자에게는 패키지에 어떤 extras를 사용할 수 있고 각각 어떻게 동작하는지 보여 주는 명확한 설치 지침을 제공해야 합니다. 예를 들어 일부 권장 의존성이나 특정 프런트엔드 또는 백엔드가 기본적으로 설치된다는 점, 그리고 사용 가능한 기능에 따라 이를 선택 해제하거나 기본값을 재정의하는 방법을 설명해야 합니다.

패키지 작성자

extras를 정의하는 메커니즘과 이를 사용해야 하는 시점에 관한 관련 규칙은 명확하지만, 패키지 작성자는 패키지에 이 기능을 도입하기 전에 하위 호환성이 의도치 않게 깨지지 않도록 여러 사항을 신중하게 고려해야 합니다.

이전 버전의 패키지 설치 프로그램 지원

pip 또는 uv와 같은 패키지 설치 프로그램은 기본 extras 지원을 동시에 구현하지 않을 수 있으며, 지원을 구현한 후에도 패키지 작성자는 최신 버전의 패키지 설치 프로그램을 사용하지 않는 사용자를 계속 지원하려 할 가능성이 높습니다. 이 경우 다음 권고 사항이 적용됩니다.

  • 패키지를 필수 의존성에서 기본 extra로 옮기는 것은 호환성을 깨뜨리는 변경입니다. 이전 버전의 패키지 설치 프로그램은 기본 extras라는 개념을 인식하지 못하고 의존성이 더 적은 상태로 패키지를 설치하게 되며, 이는 이러한 의존성에 의존해 온 사용자에게 영향을 줄 수 있기 때문입니다. 따라서 기존 패키지에서 의존성을 필수 의존성에서 기본 extra로 변경하는 작업은 개발자가 이 PEP를 구현하는 설치 프로그램을 사용하는 사용자만 지원하려는 경우에 한해 향후에 수행해야 합니다.
  • 기존 extra를 기본값으로 만드는 작업은 더 안전해야 합니다. 예를 들어 astropyrecommended를 기본 extra로 만들 수 있지만, 이전 버전의 패키지 설치 프로그램을 사용하는 사용자를 지원하려면 대부분 또는 모든 사용자가 이 PEP를 구현하는 패키지 설치 프로그램을 사용한다는 점이 분명해질 때까지 가능한 한 오랫동안 문서에서 해당 extra를 명시적으로 언급해야 합니다. extra를 명시적으로 언급한 상태로 유지하는 데에는 단점이 없지만, 이렇게 하면 문서를 읽지 않는 최신 도구 사용자가 (사용자 커뮤니티에서 무시할 수 없는 비율일 수 있습니다) 기본적으로 권장 의존성을 받기 시작하게 됩니다.
  • 이 PEP 이전에는 package[]package와 동등했으므로, 작성자는 package[]를 최소 설치를 수행하는 하위 호환성을 갖춘 보편적인 방법으로 문서화할 수 있습니다. 기본 extras를 정의하는 패키지의 경우 package[]를 설치하면 pip와 같은 패키징 도구의 이전 버전을 사용하더라도 항상 최소 설치가 수행되며, 특정 패키지에 기본 extras가 도입되기 전에 출시된 이 패키지의 릴리스도 package[]로 설치할 수 있습니다(다만 이러한 경우에는 package와 동등합니다). 기본 extras를 정의하지 않는 패키지의 경우 package[]는 계속 package와 동등합니다.

많은 기본 의존성 추가를 피하기

작성자는 여러 의존성을 기본적으로 포함하고 싶을 수 있습니다. 이러한 의존성은 해당 의존성을 선택하지 않을 방법을 제공할 수 있기 때문입니다. 그러나 설치 결과가 불필요하게 비대해지고 의존성 트리가 복잡해지는 것을 피하려면, 기본적으로 무엇을 포함할지 작성자가 신중하게 고려할 것을 권장합니다. 기본 extras를 사용한다고 해서 모든 extra가 기본값이어야 하는 것은 아니며, 사용자가 기본값이 아닌 extras를 명시적으로 선택할 여지도 여전히 있습니다.

기본 extras는 일반적으로 필수 의존성과 동일한 “비중”으로 취급해야 합니다. 패키지가 널리 사용되는 경우 기본 extra를 도입하면 모든 하위 패키지가 최소 설치 사양을 사용하여 명시적으로 선택 해제하도록 업데이트되지 않는 한 해당 extra의 의존성이 전이적으로 포함됩니다.

예를 들어, pytest패키지에는 현재 이에 의존하는 플러그인이 거의 1,500개 있습니다. pytest가 기본 extra를 추가하고 해당 플러그인들이 그에 맞게 업데이트되지 않는다면, 플러그인을 설치할 때 기본 extras의 의존성이 포함됩니다. 그렇다고 기본 extras를 사용할 수 없는 것은 아니지만, 기본 extras를 추가하려면 그 하위 요소에 미치는 영향을 신중하게 평가해야 합니다.

기본 extras에서 상속하기

패키지 작성자가 extra를 기본적으로 설치되도록 선택한다면, 사용자가 Supporting extras that should not remove default extras에 설명된 접근 방식을 사용하지 않는 한 다른 extra를 명시적으로 지정할 때 기본 extra가 설치되지 않을 수 있다는 점을 알고 있어야 합니다.

상호 교환 가능한 백엔드와 같이 extra를 명시적으로 지정한 경우 기본값을 무시하는 것이 올바른 경우도 있습니다. 그러나 최소 설치 방법을 제공하면서 권장 의존성을 포함하기 위해 기본 extras를 사용하는 경우와 같이, 다른 extras 중 상당수가 기본 extra를 명시적으로 ‘상속’해야 할 수도 있으므로 패키지 작성자는 어떤 경우에 기본 extras를 설치하려는지 신중하게 고려해야 합니다.

호환되지 않는 extras

경우에 따라 패키지에 서로 호환되지 않는 extras가 있을 수 있습니다. 이 경우 다른 의존성과 호환되지 않을 수 있는 의존성을 포함하는 extra에는 기본 extra 기능을 사용하지 않는 것이 좋습니다.

extras가 package[A]package[B]인 패키지를 생각해 보십시오. 사용자는 이미 package[A]package[B] 또는 package[A,B]를 설치하려고 시도할 수 있으며, 그러면 설치가 손상되지만 두 extra가 모두 설치된다는 점은 적어도 명시적입니다. 그러나 A를 기본 extra로 만들면 직관에 어긋나는 문제가 발생할 수 있습니다. 사용자는 다음을 수행할 수 있습니다.

$ pip install package  # this installs package[A]
$ pip install package[B]

그러면 A와 B를 명시적으로 모두 설치한 적이 없는데도 설치가 손상됩니다. 이러한 이유로 이 문제가 발생할 가능성이 있는 의존성에는 기본 extras를 사용하지 않는 것이 좋습니다.

순환 의존성

순환 의존성이 존재할 때는 작성자가 각별히 주의해야 합니다. 예를 들어 다음 의존성 트리를 생각해 보십시오.:

package1
└── package2
    └── package1

package1recommended라는 이름의 기본 extra가 있다면:

$ pip install package1[]

package2package1에 계속 의존하는 경우(추가 기능을 지정하지 않더라도) recommended 엑스트라가 여전히 설치되는 결과를 낳습니다. 의존성 트리를 다음과 같이 변경하면 해결할 수 있습니다.:

package1
└── package2
    └── package1[]

실제로 package2package1의 추가 의존성이 제공하는 기능에 의존하지 않는다고 가정할 때입니다. 따라서 작성자는 package2의 작성자와 협력하면서 마이그레이션 계획을 신중하게 고려해야 합니다.

기본 엑스트라를 사용하는 패키지 문서화

기본 엑스트라를 어떻게 사용하든 패키지 작성자는 패키지 문서에서 엑스트라의 사용 방법을 명확하게 설명하도록 노력해야 합니다. ‘모범 사례’ 문서에는 다음 내용이 포함되어야 합니다.

  • package를 설치하는 것이 package[<default extras>]를 설치하는 것과 동일하다는 점
  • package[]를 설치하면 최소/필수 의존성만 포함되지만, 의존성 트리의 다른 위치에 package가 나타나는 경우 선택적 의존성이 설치되지 않는다는 것을 보장하지는 않는다는 점
  • 사용할 수 있는 다른 선택적 엑스트라와 해당 엑스트라가 기본 엑스트라를 비활성화하는지 여부(Supporting extras that should not remove default extras에 설명된 방식으로 이를 제어할 수 있기 때문입니다)
  • 기본 백엔드 프런트엔드가 있을 수 있는 패키지에 관한 특정 지침(Packages with multiple kinds of defaults에 설명된 내용)입니다.

패키징 저장소 유지 관리자

conda, Homebrew, 리눅스 패키지 설치 프로그램(예: aptyum) 등 서로 다른 배포판을 위해 Python 라이브러리를 재패키징하는 개인에게 미치는 영향을 고려해야 합니다. 모든 패키지 배포판이 설명된 접근 방식에 부합하는 메커니즘을 갖추고 있는 것은 아닙니다. 실제로 conda와 같은 일부 배포판에는 엑스트라라는 개념조차 없습니다.

여기서 고려해야 할 경우는 두 가지입니다.

  • 여러 conda-forge 레시피의 경우처럼 재패키징을 수작업으로 수행하며, 특히 엑스트라에 상응하는 기능이 없는 경우에는, 어떤 의존성을 포함할지에 대해 이미 수동으로 결정해야 하므로 기본 엑스트라의 도입이 큰 영향을 미치지는 않을 것입니다(예를 들어 astropy 패키지에 대한 conda-forge 레시피는 Motivation에서 언급된 것처럼 사용자가 이를 명시적으로 요청할 다른 방법이 없으므로 모든 recommended 의존성을 기본적으로 포함합니다).
  • 재패키징을 자동화된 방식으로 수행하는 경우에는 배포판 유지 관리자가 기본 엑스트라를 어떻게 처리할지 신중하게 고려해야 하며, 이로 인해 상당한 작업과 논의가 필요할 수 있습니다.

이와 같은 PEP가 서로 다른 각 패키지 배포판을 빠짐없이 고려하는 것은 불가능합니다. 그러나 궁극적으로 기본 엑스트라는 패키지 작성자가 대다수 사용자를 위해 패키지를 설치하고자 하는 방식으로 이해해야 하며, 이는 기본 엑스트라를 수동 또는 자동으로 처리하는 방법에 관한 결정을 알려야 합니다.

참조 구현

다음 저장소에는 기본 엑스트라를 사용하는 완전히 작동하는 데모 패키지가 포함되어 있습니다.

https://github.com/wheel-next/pep_771

이 패키지는 여러 패키지의 수정된 브랜치를 사용하며, 다음 링크는 해당 브랜치를 가리킵니다.

또한 this branch 에는 Flit 패키지의 수정된 버전이 포함되어 있습니다.

위 구현은 현재 개념 증명 단계이며, 기존 변경 사항은 아직 관련 유지 관리자들의 검토를 받지 않았습니다. 그럼에도 관심 있는 유지 관리자가 이를 시험해 볼 수 있을 만큼 충분히 작동합니다.

거부된 아이디어

권장 설치를 위한 메타패키지 사용

기존 패키징 도구와 인프라를 사용하면, 일부 사용자에게는 최소 설치를 제공하고 일반 사용자에게는 기본 비최소 설치(예: 권장 의존성 또는 기본 백엔드 포함)를 제공하려는 패키지 유지 관리자는 하나가 아닌 두 개의 패키지를 배포할 의향만 있다면 기술적으로 이미 이를 달성할 수 있습니다. 예를 들어 최소 의존성을 가진 주 패키지인 package-core와, 선택적 의존성을 활성화한 상태로 package-core에 의존하는 메타패키지인 package를 배포하는 방식입니다.

Motivation 섹션의 구체적인 예를 다시 들면, astropy 패키지는 현재 사용자가 기본 설치 지침에 따라 설치하도록 안내받는 recommended 추가 기능을 정의합니다. 원칙적으로 기존 astropy 패키지의 이름을 예를 들어 astropy-core로 변경한 다음, 다음과 같은 의존성 섹션을 포함하는 메타패키지인 새로운 astropy 패키지를 만들 수 있습니다.

dependencies = [
    "astropy-core[recommended]"
]

사용자가 astropy 메타패키지에 버전을 고정하거나 버전 제약 조건을 지정할 수 있으므로(예: astropy>5.0), 메타패키지는 코어 패키지와 동일한 버전을 따라야 하며 의존성 섹션에서 예를 들어 다음과 같이 엄격한 버전 고정을 사용해야 합니다.

version = "7.1.0"
dependencies = [
    "astropy-core[recommended]==7.1.0"
]

이 아이디어는 기술적으로 이미 실현 가능하므로 매력적으로 보일 수 있습니다. 그러나 실제로는 여러 프로젝트가 여러 가지 이유로 이를 선택하지 않았으며, 이제 그 이유를 살펴보겠습니다. 이러한 이유 중 일부는 앞으로 만들어질 새로운 프로젝트에는 적용되지 않을 수 있지만, 일부는 오래된 프로젝트와 새로운 프로젝트를 막론하고 모든 프로젝트에 적용됩니다.

패키지 이름과 모듈 이름의 불일치

메타패키지 접근 방식을 사용하려는 패키지의 이름 지정에는 두 가지 주요 선택지가 있습니다.

  • 첫 번째 선택지는 기존 패키지를 현재 상태로 유지하는 것입니다. 즉, package는 최소 설치를 제공하고, 그런 다음 package-all과 같이 다른 이름을 가진 새 메타패키지를 만드는 것입니다. 그러나 이는 애초에 이 PEP를 제안하게 만든 문제 중 하나를 안고 있습니다. 사용자는 흔히 package[recommended]와 같이 설치할 수 있다는 사실을 알지 못하므로, 마찬가지로 package-all이 존재한다는 사실도 깨닫지 못할 수 있습니다. 이는 일부 부담을 더 숙련된 사용자에게 전가하는 대신, 일반 사용자가 이를 알아내야 하는 부담을 다시 안게 합니다.
  • 두 번째 선택지는 기존 패키지의 이름을 예를 들어 package-core로 변경하고, 새 메타패키지의 이름을 package로 지정하는 것입니다. 이는 첫 번째 선택지보다 나은 방법이지만 이상적이지는 않습니다. 패키지 이름과 모듈 이름 사이에 직관적이지 않은 불일치가 생기기 때문입니다. 즉, package-corepackage 모듈을 제공하고 package는 어떤 모듈도 제공하지 않습니다. 이것이 혼란을 일으키는 이유의 한 예로, 일반 사용자는 package 모듈을 제거하려면 예를 들어 다음과 같이 해야 한다고 생각할 수 있습니다.
    $ pip uninstall package
    

    그러나 실제로는 그렇지 않으며(package 모듈은 여전히 작동합니다), 이 사용자에게 package-core 패키지가 실제로 존재한다는 사실이 명확하지 않을 수 있습니다.

여러 저장소 또는 모노레포

이 접근 방식에서는 하나 대신 두 개의 저장소를 유지 관리하거나, 두 패키지를 모두 포함하는 모노레포를 사용하는 방식으로 전환해야 합니다. 어느 선택지도 이상적이지 않습니다.

  • 두 개의 저장소로 분할하면 유지 관리자에게 장기적으로 추가 부담이 발생합니다. 유지 관리자는 이 저장소들이 버전뿐 아니라 Synchronizing metadata에서 논의할 추가 기능과 같은 다른 측면에서도 동기화된 상태를 유지하도록 해야 합니다. 또한 Mismatch between package and module name에서 언급한 이름 지정 문제는 여기에서 추가적인 복잡성을 일으킵니다. 저장소 이름이 패키지 이름과 일치한다고 하면, 이전 package 저장소를 체크아웃한 사용자는 원격 URL 또는 git 클론 URL을 package-core 저장소를 가리키도록 업데이트해야 합니다. 대안은 package 저장소를 유지하여 package-core 패키지를 포함하게 하고 메타패키지에는 다른 이름을 지정하는 것이지만, 이는 혼란을 일으킬 수 있습니다.
  • 모노레포로 전환하는 것은 일부 프로젝트에 상당한 변화일 수 있으며, 도구가 기본적으로 단일 저장소가 단일 패키지에 대응한다고 가정하는 경우도 흔합니다. 이러한 도구를 모노레포에서 작동하도록 구성할 수 있는 경우가 많지만, 이는 유지 관리자에게 추가 부담이 됩니다. 또한 주 패키지를 모노레포의 하위 디렉터리로 이동하면, 저장소 URL에서 패키지를 예를 들어 pip로 설치하는 사용자는 하위 디렉터리에서 설치하도록 이를 조정해야 하며(저장소 URL에 subdirectory=를 추가), 저장소를 클론하고 이전 구조를 가정하는 기존 워크플로도 중단됩니다.

최소 패키지에 대한 의존성

분할이 이루어진 최초 버전보다 오래된 패키지 버전에 의존해야 하는 패키지는 최소 패키지에 쉽게 의존할 수 없습니다. 반면 이 PEP의 주요 제안에 따르면, 하위 사용자는 기본 추가 기능이 도입되기 전의 version을 지정하여 예를 들어 package[]>version에 의존할 수 있습니다. 그러나 분할 접근 방식에서는 하위 사용자가 예를 들어 package-core>version에 의존할 수 없습니다. package-core가 이전에는 존재하지 않았기 때문입니다.

가능한 해결책은 개발자가 패키지의 모든 이전 버전에 대해 아무 작업도 수행하지 않는 메타데이터 패키지를 릴리스하는 것이지만, 이는 유지 관리자에게 상당한 추가 부담이 됩니다.

제거

Mismatch between package and module name에서 이름 지정 문제를 언급하며 암시했듯이, 패키지 제거는 더 이상 사용자가 예상하는 방식으로 작동하지 않습니다. 다음과 같이 실행한 사용자는

$ pip uninstall package

여전히 package-core를 남겨 두게 되지만, 이를 깨닫지 못할 수 있습니다. 이는 단순히 혼란스러운 문제에 그치지 않고, 여러 기존 워크플로에 영향을 줄 수 있는 사실상의 호환성 파괴 변경입니다.

패키지 배포

하나 대신 두 개의 패키지를 사용하면 하나가 아니라 두 개의 패키지를 릴리스해야 하므로 패키지 배포의 장기적인 유지 관리 비용이 증가하며, 경우에 따라 각 릴리스마다 추가적인 수작업이 발생합니다.

메타데이터 동기화

주 패키지와 메타패키지 간에 동기화해야 하는 중요한 메타데이터는 버전입니다. 코어 패키지의 새 릴리스가 이루어질 때마다 메타패키지의 버전과 의존성에 지정된 코어 패키지의 버전 고정을 함께 업데이트해야 합니다.

또한 코어 패키지에 정의된 모든 엑스트라를 메타패키지에도 다시 정의하고 동기화된 상태로 유지해야 합니다. 예를 들어 packageadditional 엑스트라를 정의하는 경우, 사용자는 계속해서 package[additional]를 설치할 수 있어야 하지만, package-core 패키지를 설치하는 사용자에게도 package-core[additional]를 사용할 수 있는 선택지가 제공되어야 합니다.

동기화해야 하는 기타 메타데이터에는 예를 들어 작성자 정보와 프로젝트 URL이 포함됩니다.

요약

전반적으로 이 해결책은 초기 설정 및 전환 측면에서뿐만 아니라(이미 규모가 크고 확립된 프로젝트에서는 이것만으로도 감당하기 어려울 수 있습니다) 장기적인 유지 관리 측면에서도 훨씬 더 큰 유지 관리 부담을 초래합니다. 또한 사용자 작업 흐름, 특히 저장소 및 설치 제거와 관련된 작업 흐름을 중단시킬 가능성이 있습니다. 이러한 모든 이유로 현재 PEP에 대한 설득력 있는 대안이라고 여기지 않습니다.

엑스트라 선택 해제 구문

주요 경쟁 접근법 중 하나는 다음과 같았습니다. 엑스트라가 명시적으로 제공된 경우 기본값을 선택 해제 상태로 두는 대신, 기본 엑스트라를 명시적으로 선택 해제해야 한다는 것입니다.

이 접근법에서는 PEP 508에 정의된 미니 언어의 확장으로 엑스트라를 선택 해제하는 새로운 구문이 도입됩니다. 패키지가 기본 엑스트라를 정의한 경우, 사용자는 엑스트라 이름 앞에 마이너스 기호 (-)를 사용하여 이러한 기본값을 선택하지 않을 수 있습니다. 제안된 구문 업데이트는 다음과 같았을 것입니다.:

extras_list   = (-)?identifier (wsp* ',' wsp* (-)?identifier)*

이 새로운 구문의 유효한 예는 다음과 같습니다.

  • package[-recommended]
  • package[-backend1, backend2]
  • package[pdf, -svg]

그러나 이 접근법에는 두 가지 주요 문제가 있습니다.

  • 동일한 의존성 지정에 엑스트라와 그 부정된 버전이 모두 포함된 경우(예: package[pdf, -pdf]) 또는 의존성 트리에 package[pdf]package[-pdf]가 모두 포함된 경우와 같은 경계 사례를 해석하는 규칙을 여러 개 정의해야 하며, 이러한 규칙은 사용자에게 직관적이지 않을 것입니다.
  • 더 중요한 문제는 의존성 지정에 새로운 구문을 도입한다는 것입니다. 즉, 어떤 패키지라도 새로운 구문을 사용하여 의존성을 정의하면 해당 패키지와 그 패키지에 의존하는 다른 모든 패키지를 기존 패키징 도구로 더 이상 설치할 수 없으므로, 이는 하위 호환성을 크게 깨뜨리는 변경이 됩니다.

이러한 이유로 이 대안은 최종 제안에 포함되지 않았습니다.

extras_require에 특수 항목 추가

새로운 Default-Extra 메타데이터 필드를 도입하는 대신, ‘특수’한 이름의 엑스트라를 사용하는 방안이 대안으로 검토되었습니다.

한 가지 예는 빈 문자열을 사용하는 것입니다.:

Provides-Extra:
Requires-Dist: numpy ; extra == ''

‘빈’ 엑스트라의 일부로 설치되는 의존성은 다른 엑스트라가 지정되지 않은 경우에만 설치된다는 아이디어입니다. 이에 대한 구현이 https://github.com/pypa/setuptools/pull/1503 에서 제안되었지만, 기존 사용 방식과의 호환성을 깨뜨리지 않고 이를 작동시킬 방법은 없다는 사실이 밝혀졌습니다. 예를 들어, setup.py 파일을 통해 Setuptools를 사용하는 패키지는 다음과 같이 할 수 있습니다.:

setup(
    ...
    extras_require={'': ['package_a']},
)

이는 유효하며 package_ainstall_requires에 정의된 것과 동일하므로, 빈 문자열의 의미를 변경하면 호환성이 깨집니다.

또한 기존 패키지에서 하위 호환성이 있는 유효한 extras 이름으로 사용될 수 있는 모든 문자열이 이미 사용되고 있을 수 있으므로, 'default'와 같은 다른 문자열을 특수 문자열로 사용할 수 없습니다.

특수한 None Python 변수를 사용하자는 제안도 있었지만, setup.py 파일에서는 None을 사용할 수 있더라도 setup.cfg 또는 pyproject.toml과 같은 선언적 파일에서는 사용할 수 없고, 더 나아가 최종적으로 extras 이름은 패키지 메타데이터에서 문자열로 변환되어야 하므로 역시 불가능합니다. 다음을 지정하면:

Provides-Extra: None

이미 Python 패키지에서 extra 이름으로 사용되고 있을 수 있는 문자열 ‘None’과 구별할 수 없게 됩니다. 문자열이 아닌 ‘특수’ extras 이름을 허용하도록 핵심 메타데이터 구문을 수정한다면, 결국 핵심 메타데이터 사양을 수정해야 하므로 Default-Extra를 도입하는 것보다 나을 것이 없습니다.

모든 기본 extras를 선택 해제하도록 도구에 의존하기

extras를 선택 해제하는 또 다른 방법은 패키징 도구 수준에서 이를 구현하는 것입니다. 예를 들어 pip는 다음과 같은 옵션을 포함할 수 있습니다.

$ pip install package --no-default-extras

이 옵션은 --no-binary 옵션과 유사하게 모든 패키지 또는 특정 패키지에 적용할 수 있습니다. 예를 들면 다음과 같습니다.

$ pip install package --no-default-extras :all:

이 접근 방식의 장점은 기본 extras를 지원하는 도구가 해당 extras의 선택 해제도 지원할 수 있다는 것입니다. 이 접근 방식은 apt 도구의 --no-install-recommends 옵션과 유사합니다.

그러나 이 해결책만으로는 패키지가 종속성의 기본 extras 중 일부를 필요로 하지 않는다고 스스로 지정할 수 없으므로 이상적이지 않습니다. 또한 대규모 종속성 트리에서 모든 기본 extras를 비활성화할 수 있는 사용자에게 위험을 초래할 수 있으며, 그 결과 트리 내에서 어느 시점에든 기본 extras에 의존하는 패키지가 손상될 가능성이 있습니다.

그럼에도 이 PEP는 이 접근 방식을 금지하지 않으며, 이러한 종류의 옵션을 지원할지 여부는 각 패키징 도구의 유지 관리자들이 결정할 사항입니다. 이는 적어도 종속성 트리에서 기본 extras가 의존되고 있는 위치를 식별하려는 패키지 유지 관리자에게 유용한 플래그일 수 있습니다. 그러나 지원되는 경우, 이 플래그를 사용한다고 해서 작동하는 환경이 보장되는 것은 아니라는 점을 명확히 해야 합니다.