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

Python 개선 제안 한국어 번역

PEP 711 – PyBI: Python 바이너리 배포를 위한 표준 형식

Author:
Nathaniel J. Smith <njs at pobox.com>
PEP-Delegate:
TODO
Discussions-To:
Discourse thread
Status:
Draft
Type:
Standards Track
Topic:
Packaging
Created:
06-Apr-2023
Post-History:
06-Apr-2023

Table of Contents

번역·라이선스 안내

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

초록

“휠과 비슷하지만, 미리 빌드된 Python 패키지 대신 미리 빌드된 Python 인터프리터입니다”

동기

최종 목표: Pypi.org에 널리 쓰이는 모든 플랫폼용으로 모든 Python 버전의 미리 빌드된 패키지를 갖추어, 자동화 도구가 그중 어느 것이든 쉽게 가져와 설정할 수 있게 하는 것입니다. 그러면 Python 사전 출시 버전을 시험해 보거나, CI에서 Python 버전을 고정하거나, 특정 Python 포인트 릴리스에서만 발생하는 버그 보고를 재현하기 위해 임시 환경을 만드는 일 등이 빠르고 쉬워집니다.

첫 단계(이 PEP): 미리 빌드된 Python 인터프리터를 담는 표준 패키징 파일 형식을 정의하되, 기존 Python 패키징 표준을 최대한 재사용합니다.

예시

예시 pybi 빌드는 pybi.vorpus.org에서 받을 수 있습니다. 이 파일들은 zip 파일이므로, 내부 구성이 어떻게 되어 있는지 감을 잡고 싶다면 압축을 풀어 안을 살펴볼 수 있습니다.

제가 이것들을 만들 때 사용한 도구도 살펴볼 수 있습니다.

명세

파일 이름

파일 이름: {distribution}-{version}[-{build tag}]-{platform tag}.pybi

이는 PEP 427에 정의된 wheel 파일 형식과 같습니다. 다만 {python tag}와 {abi tag}를 빼고, 확장자를 .whl → .pybi로 바꿉니다.

예를 들면 다음과 같습니다:

  • cpython-3.9.3-manylinux_2014.pybi
  • cpython-3.10b2-win_amd64.pybi

휠과 마찬가지로, pybi가 여러 플랫폼을 지원한다면 점으로 구분하여 “압축된 태그 집합”을 만들 수 있습니다:

  • cpython-3.9.5-macosx_11_0_x86_64.macosx_11_0_arm64.pybi

(다만 실제로는 이 방식이 그다지 많이 쓰이지 않을 것입니다. 예를 들어 위 파일 이름은 cpython-3.9.5-macosx_11_0_universal2.pybi로 쓰는 것이 더 관용적입니다.)

파일 내용

.pybi 파일은 zip 파일로, 임의의 위치에 바로 압축을 풀어 독립적인 Python 환경으로 사용할 수 있습니다. .data 디렉터리나 설치 스킴 키는 없습니다. Python 환경은 자신이 어떤 설치 스킴을 사용하는지 알고 있으므로, 처음부터 파일을 올바른 위치에 두면 되기 때문입니다.

“임의의 위치”라는 부분이 중요합니다. pybi에는 하드코딩된 절대 경로가 들어 있어서는 안 됩니다. 특히, 미리 설치된 스크립트는 셔뱅 줄에 절대 경로를 넣어서는 절대로 안 됩니다(MUST NOT).

휠의 <package>-<version>.dist-info 디렉터리와 비슷하게, pybi 아카이브는 pybi-info/라는 이름의 최상위 디렉터리를 포함해야 합니다. (근거: dist-info 대신 pybi-info라고 부르면 도구가 어떤 종류의 메타데이터를 보고 있는지 혼동하지 않게 됩니다. 주어진 디렉터리에는 pybi를 하나만 설치할 수 있으므로 {name}-{version} 부분은 빼도 괜찮습니다.) pybi-info/ 디렉터리에는 적어도 다음 파일이 들어 있습니다:

  • .../PYBI: 아카이브 자체에 대한 메타데이터로, METADATA 및 WHEEL 파일과 같은 RFC822 비슷한 형식입니다:
    Pybi-Version: 1.0
    Generator: {name} {version}
    Tag: {platform tag}
    Tag: {another platform tag}
    Tag: {...and so on...}
    Build: 1   # optional
    
  • .../RECORD: 휠에서와 같지만, 아래의 심볼릭 링크에 관한 참고 사항을 보십시오.
  • .../METADATA: 현재 코어 메타데이터 명세에 설명된 것과 같은 형식이지만, 다음 키는 의미가 없으므로 금지됩니다:
    • Requires-Dist
    • Provides-Extra
    • Requires-Python

    또한 아래에 설명하는 몇 가지 새로운 필수 키도 있습니다.

Pybi 전용 코어 메타데이터

전체 세부 사항을 설명하기 전에 새로운 METADATA 필드의 예시를 먼저 보여 드리겠습니다:

Pybi-Environment-Marker-Variables: {"implementation_name": "cpython", "implementation_version": "3.10.8", "os_name": "posix", "platform_machine": "x86_64", "platform_system": "Linux", "python_full_version": "3.10.8", "platform_python_implementation": "CPython", "python_version": "3.10", "sys_platform": "linux"}
Pybi-Paths: {"stdlib": "lib/python3.10", "platstdlib": "lib/python3.10", "purelib": "lib/python3.10/site-packages", "platlib": "lib/python3.10/site-packages", "include": "include/python3.10", "platinclude": "include/python3.10", "scripts": "bin", "data": "."}
Pybi-Wheel-Tag: cp310-cp310-PLATFORM
Pybi-Wheel-Tag: cp310-abi3-PLATFORM
Pybi-Wheel-Tag: cp310-none-PLATFORM
Pybi-Wheel-Tag: cp39-abi3-PLATFORM
Pybi-Wheel-Tag: cp38-abi3-PLATFORM
Pybi-Wheel-Tag: cp37-abi3-PLATFORM
Pybi-Wheel-Tag: cp36-abi3-PLATFORM
Pybi-Wheel-Tag: cp35-abi3-PLATFORM
Pybi-Wheel-Tag: cp34-abi3-PLATFORM
Pybi-Wheel-Tag: cp33-abi3-PLATFORM
Pybi-Wheel-Tag: cp32-abi3-PLATFORM
Pybi-Wheel-Tag: py310-none-PLATFORM
Pybi-Wheel-Tag: py3-none-PLATFORM
Pybi-Wheel-Tag: py39-none-PLATFORM
Pybi-Wheel-Tag: py38-none-PLATFORM
Pybi-Wheel-Tag: py37-none-PLATFORM
Pybi-Wheel-Tag: py36-none-PLATFORM
Pybi-Wheel-Tag: py35-none-PLATFORM
Pybi-Wheel-Tag: py34-none-PLATFORM
Pybi-Wheel-Tag: py33-none-PLATFORM
Pybi-Wheel-Tag: py32-none-PLATFORM
Pybi-Wheel-Tag: py31-none-PLATFORM
Pybi-Wheel-Tag: py30-none-PLATFORM
Pybi-Wheel-Tag: py310-none-any
Pybi-Wheel-Tag: py3-none-any
Pybi-Wheel-Tag: py39-none-any
Pybi-Wheel-Tag: py38-none-any
Pybi-Wheel-Tag: py37-none-any
Pybi-Wheel-Tag: py36-none-any
Pybi-Wheel-Tag: py35-none-any
Pybi-Wheel-Tag: py34-none-any
Pybi-Wheel-Tag: py33-none-any
Pybi-Wheel-Tag: py32-none-any
Pybi-Wheel-Tag: py31-none-any
Pybi-Wheel-Tag: py30-none-any

명세:

  • Pybi-Environment-Marker-Variables: 이 Pybi를 어디에 설치하든 달라지지 않는 모든 PEP 508 환경 마커 변수의 값을 JSON 딕셔너리로 나타낸 것입니다. 예를 들면 다음과 같습니다:
    • python_version은 항상 존재합니다. Python 3.10 패키지는 항상 python_version == "3.10"이기 때문입니다.
    • platform_version은 일반적으로 존재하지 않습니다. Python이 실행되는 OS에 대한 상세한 정보를 제공하기 때문입니다. 예를 들면 다음과 같습니다:
      #60-Ubuntu SMP Thu May 6 07:46:32 UTC 2021
      

      platform_release에도 비슷한 문제가 있습니다.

    • platform_machine은 대개 존재하지만, macOS universal2 pybi는 예외입니다. 이러한 pybi는 x86-64 모드와 arm64 모드 중 어느 쪽으로든 실행될 수 있으며, 인터프리터가 실제로 호출되기 전까지는 어느 쪽인지 알 수 없으므로 정적 메타데이터에 기록할 수 없습니다.

    근거: 대부분의 경우, 리졸버가 대상 플랫폼의 .pybi 파일에 접근할 수만 있다면 이를 통해 Linux에서 실행되는 리졸버가 Windows의 Python 환경에 대한 패키지 고정(pin)을 계산할 수 있으며, 그 반대도 마찬가지입니다. (Requires-Python 제약 조건은 python_full_version 값을 사용하여 검사할 수 있다는 점에 유의하십시오.) 때로는 몇 가지 키를 제외해야 하지만, 그러한 키는 거의 쓸모가 없거나(platform_version, platform_release) 리졸버가 재구성할 수 있습니다(platform_machine).

    또한 마커는 그 자체로 접근할 수 있으면 대체로 유용한 정보이기도 합니다. 예를 들어 pypy3-7.3.2 pybi가 있고 이것이 어떤 버전의 Python 언어를 지원하는지 알고 싶다면, 그 정보는 python_version 마커에 기록되어 있습니다.

    (참고: platform_version과 platform_release를 폐기하거나 제거하는 편이 좋을지도 모르겠습니다. 이 둘은 문제가 많으며, 이것들이 유용한 경우를 저는 하나도 찾지 못했습니다. 하지만 이는 이 PEP의 범위를 벗어납니다.)

  • Pybi-Paths: 휠을 설치하는 데 필요한 설치 경로(sysconfig.get_paths()와 동일한 키)를 zip 파일의 루트에서 시작하는 상대 경로로 나타낸 JSON 딕셔너리입니다.

    이 경로는 반드시(MUST) 역슬래시가 아닌 슬래시를 구분자로 사용하는 Unix 형식으로 작성해야 합니다.

    {paths["scripts"]}/python을 실행하여 Python 인터프리터를 호출할 수 있어야 합니다. 대체 인터프리터 진입점(예: Windows GUI 앱용 pythonw)이 있다면, 그것들도 버전 번호를 붙이지 않은 관례적인 이름으로 같은 디렉터리에 있어야 합니다. (원한다면 python3.11 심볼릭 링크를 추가로 두어도 됩니다. 이를 금지하는 규칙은 없습니다. 다만 python이 존재하고 동작해야 한다는 것뿐입니다.)

    근거: Pybi-Paths와 Pybi-Wheel-Tag 값들(아래 참조)만 있으면 설치 프로그램이 Python을 실행하지 않고도 wheel을 골라 압축을 푼 pybi 환경에 설치할 수 있습니다. 게다가 인터프리터의 위치는 어딘가에 기록해 두어야 하므로, 일석이조입니다.

  • Pybi-Wheel-Tag: 이 인터프리터가 지원하는 휠 태그를 선호 순서대로(가장 선호하는 것이 처음, 가장 덜 선호하는 것이 마지막) 나열한 것입니다. 단, 최종 설치 시스템에 따라 달라지는 플랫폼 태그는 모두 특수 플랫폼 태그 PLATFORM으로 대체해야 합니다.

    논의: 설치 프로그램이 pybi에 대응하는 wheel 태그를 미리 계산할 수 있다면 좋을 것입니다™. 그러면 태그를 조회하려고 python 인터프리터를 실제로 실행하지 않아도 압축을 푼 pybi에 wheel을 설치할 수 있습니다. 이는 효율성에도 도움이 되고, Linux 호스트에서 Windows 환경을 구성하는 것처럼 더 특이한 사용 사례도 가능하게 합니다.

    하지만 안타깝게도 Python 설치본이 지원하는 플랫폼 태그 전체를 미리 계산하는 것은 불가능합니다. 이 태그들은 최종 시스템에 따라 달라질 수 있기 때문입니다:

    • manylinux_2_12_x86_64로 태그된 pybi는 manylinux_2_12_x86_64로 태그된 휠을 항상 사용할 수 있습니다. 또한 manylinux_2_17_x86_64로 태그된 휠을 사용할 수 있을 수도 있지만, 이는 최종 설치 시스템에 glibc 2.17 이상이 있는 경우에만 해당합니다.
    • macosx_11_0_universal2(= 하나의 바이너리에서 x86-64와 arm64를 모두 지원)로 태그된 pybi는 macosx_11_0_arm64로 태그된 휠을 사용할 수 있을지도 모르지만, 이는 “Apple Silicon” 머신에 설치되어 arm64 모드로 실행되는 경우에만 해당합니다.

    이 두 경우에도 설치 도구는 로컬 플랫폼 태그를 계산하고, Pybi-Wheel-Tag에서 휠 태그 템플릿을 가져와서, 특수 문자열 PLATFORM 자리에 실제로 지원되는 플랫폼을 대입하는 방식으로 적절한 휠 태그 집합을 알아낼 수 있습니다.

    그러나 이보다 훨씬 더 복잡한 경우도 있습니다:

    • 64비트 Windows에서는 (보통) 32비트 앱과 64비트 앱을 모두 실행할 수 있습니다. 따라서 pybi
      설치 프로그램은 현재 플랫폼에서 허용되는 pybi 태그 집합을 [win32, win_amd64]로 계산할 수 있습니다. 하지만 그 집합을 그대로 가져와 pybi의 휠 태그 템플릿에 끼워 넣을 수는 없습니다. 그렇게 하면 말이 안 되는 결과가 나옵니다:
      [
        "cp39-cp39-win32",
        "cp39-cp39-win_amd64",
        "cp39-abi3-win32",
        "cp39-abi3-win_amd64",
        ...
      ]
      

      이를 처리하려면 설치 프로그램은 어떻게든 다음을 이해해야 합니다. manylinux_2_12_x86_64 pybi는 manylinux_2_17_x86_64 wheel을 사용할 수 있습니다. 단, 두 태그가 모두 현재 머신에서 유효한 태그여야 합니다. 반면 win32 pybi는 두 태그가 모두 현재 머신에서 유효하더라도 win_amd64 wheel을 사용할 수 없습니다.

    • macosx_11_0_universal2 태그가 붙은 pybi는 macosx_11_0_x86_64 태그가 붙은 휠을 사용할 수도 있지만, 이는 x86-64 머신에 설치된 경우, 또는 ARM 머신에 설치되었고 동시에 macOS에 바이너리를 x86-64 모드로 실행하라고 지시하는 마법의 주문과 함께 인터프리터가 호출되는 경우에만 가능합니다. 따라서 설치 프로그램이 pybi를 어떻게 실행할 계획인지도 중요합니다!

    따라서 Pybi-Wheel-Tag 값을 실제로 사용하는 것은 보기보다 간단하지 않으며, 이 값은 아마도 상당히 정교한 도구에서만 유용할 것입니다. 하지만 똑똑한 pybi 설치 프로그램이라면 제대로 동작하는 pybi를 고르기 위해 이러한 플랫폼 호환성 문제를 이미 상당 부분 이해하고 있어야 합니다. 또한 크로스 플랫폼 고정(pinning)이나 환경 구축의 경우에는 사용자가 정확히 어떤 플랫폼을 대상으로 하는지 명확히 하는 데 필요한 정보를 무엇이든 제공할 수 있습니다. 따라서 이 값은 여전히 PyBI 메타데이터에 포함할 만큼 유용합니다. 이 값이 유용하지 않은 도구는 그냥 무시하면 됩니다.

빌드된 인터프리터에서 다음 스크립트를 실행하면 아마도 이러한 메타데이터 값을 생성할 수 있을 것입니다:

import packaging.markers
import packaging.tags
import sysconfig
import os.path
import json
import sys

marker_vars = packaging.markers.default_environment()
# Delete any keys that depend on the final installation
del marker_vars["platform_release"]
del marker_vars["platform_version"]
# Darwin binaries are often multi-arch, so play it safe and
# delete the architecture marker. (Better would be to only
# do this if the pybi actually is multi-arch.)
if marker_vars["sys_platform"] == "darwin":
    del marker_vars["platform_machine"]

# Copied and tweaked version of packaging.tags.sys_tags
tags = []
interp_name = packaging.tags.interpreter_name()
if interp_name == "cp":
    tags += list(packaging.tags.cpython_tags(platforms=["xyzzy"]))
else:
    tags += list(packaging.tags.generic_tags(platforms=["xyzzy"]))

tags += list(packaging.tags.compatible_tags(platforms=["xyzzy"]))

# Gross hack: packaging.tags normalizes platforms by lowercasing them,
# so we generate the tags with a unique string and then replace it
# with our special uppercase placeholder.
str_tags = [str(t).replace("xyzzy", "PLATFORM") for t in tags]

(base_path,) = sysconfig.get_config_vars("installed_base")
# For some reason, macOS framework builds report their
# installed_base as a directory deep inside the framework.
while "Python.framework" in base_path:
    base_path = os.path.dirname(base_path)
paths = {key: os.path.relpath(path, base_path).replace("\\", "/") for (key, path) in sysconfig.get_paths().items()}

json.dump({"marker_vars": marker_vars, "tags": str_tags, "paths": paths}, sys.stdout)

이 스크립트는 pybi 전용 태그 집합마다 별도의 항목을 담은 JSON 딕셔너리를 stdout으로 출력합니다.

심볼릭 링크

현재 모든 Unix Python 설치에서는 기본적으로 심볼릭 링크를 사용합니다(예: bin/python3 -> bin/python3.9). 게다가 .pybi 파일에 macOS 프레임워크 빌드를 저장하려면 심볼릭 링크가 반드시 필요합니다. 따라서 wheel 파일과 달리 .pybi 파일이 조금이라도 쓸모 있으려면 심볼릭 링크를 반드시 지원해야 합니다.

zip 파일에서 심볼릭 링크 표현하기

zip 파일에서 심볼릭 링크를 표현하는 사실상의 표준은 Info-Zip 심볼릭 링크 확장이며, 다음과 같이 동작합니다:

  • 심볼릭 링크의 대상 경로는 마치 파일 내용인 것처럼 저장됩니다
  • Unix 권한 필드의 상위 4비트는 0xa로 설정됩니다. 즉, permissions & 0xf000 == 0xa000입니다
  • Unix 권한 필드는 다시 “외부 속성” 필드의 상위 16비트로 저장됩니다.

따라서 Python의 zipfile 모듈을 사용한다면, 다음과 같이 하여 ZipInfo가 심볼릭 링크를 나타내는지 확인할 수 있습니다:

(zip_info.external_attr >> 16) & 0xf000 == 0xa000

또는 Rust의 zip 크레이트를 사용한다면, 이에 해당하는 검사는 다음과 같습니다:

fn is_symlink(zip_file: &zip::ZipFile) -> bool {
    match zip_file.unix_mode() {
        Some(mode) => mode & 0xf000 == 0xa000,
        None => false,
    }
}

Unix를 사용 중이라면 zip 및 unzip 명령은 아마 이미 이 형식을 이해하고 있을 것입니다.

RECORD 파일에서 심볼릭 링크 표현하기

일반적으로 RECORD 파일은 각 파일 + 그 해시 + 그 길이를 나열합니다:

my/favorite/file,sha256=...,12345

심볼릭 링크의 경우에는 대신 다음과 같이 씁니다:

name/of/symlink,symlink=path/to/symlink/target,

즉, symlink라는 특별한 “해시 함수”를 사용하고, 실제 심볼릭 링크 대상을 “해시 값”으로 저장합니다. 그리고 길이는 비워 둡니다.

근거: 이미 RECORD 파일에 주 아카이브의 모든 항목에 대한 중복 검사를 담기로 했으므로, 심볼릭 링크에 대해서도 최소한 어떤 종류의 해시와 함께 이것이 심볼릭 링크임을 나타내는 어떤 종류의 플래그를 저장해야 합니다. 심볼릭 링크 대상 문자열은 해시와 크기가 대략 비슷하므로, 차라리 그것을 직접 저장하는 편이 낫습니다. 또한 이렇게 하면 Info-Zip 심볼릭 링크 확장을 이해하지 못하는 도구도 심볼릭 링크 정보에 더 쉽게 접근할 수 있으며, Windows 시스템에서 Unix pybi를 손실 없이 풀었다가 다시 묶을 수도 있습니다. 누군가는 언젠가 이것이 편리하다고 느낄 수도 있습니다.

pybi 파일에 심볼릭 링크 저장하기

pybi 생성자가 심볼릭 링크를 저장할 때는 위에서 정의한 두 가지 메커니즘을 모두 사용해야 합니다(MUST). 즉, Info-Zip 표현을 사용하여 zip 아카이브에 직접 저장하고, RECORD 파일에도 기록해야 합니다.

Pybi 소비자는 아카이브의 심볼릭 링크와 RECORD 파일의 심볼릭 링크가 서로 일치하는지 검증해야 합니다(SHOULD).

심볼릭 링크를 저장하는 데 오직 RECORD 파일만 사용하는 방안도 고려했지만, 그렇게 하면 기본 unzip 도구로는 심볼릭 링크를 풀 수 없게 되고, 셸 스크립트에서 pybi를 설치하기가 어려워집니다.

제약 사항

심볼릭 링크는 잠재적으로 많은 혼란을 일으킬 수 있습니다. 상황을 통제하기 위해 다음과 같은 제한을 둡니다:

  • Windows 또는 일급(first-class) 심볼릭 링크 지원이 없는 다른 플랫폼을 대상으로 하는 .pybi에서는 심볼릭 링크를 사용해서는 안 됩니다(MUST NOT).
  • pybi-info 디렉터리 안에서는 심볼릭 링크를 사용해서는 안 됩니다(MUST NOT). (근거: 그럴 필요가 없으며, 전체 아카이브를 풀지 않고 pybi-info에서 정보를 추출해야 하는 리졸버의 작업이 더 단순해집니다.)
  • 심볼릭 링크의 대상은 상대 경로여야 하며(MUST), pybi 디렉터리 안에 있어야 합니다(MUST).
  • A/B/...가 아카이브에 심볼릭 링크로 기록되어 있다면, 아카이브에 A/B/.../C와 같은 이름의 다른 항목이 있어서는 안 됩니다(MUST NOT).

    예를 들어, 아카이브에 foo -> bar 심볼릭 링크가 있고 아카이브의 뒷부분에 foo/blah.py라는 일반 파일이 있다면, 순진한 언패커는 결국 bar/blah.py라는 파일을 쓰게 될 수 있습니다. 순진하게 굴지 마십시오.

언패커는 이 규칙들이 지켜지는지 반드시 검증해야 합니다(MUST). 그렇지 않으면 공격자가 foo -> /etc/passwd나 foo -> ../../../../../etc + foo/passwd -> ... 같은 악의적인 심볼릭 링크를 만들어 큰 혼란을 일으킬 수 있기 때문입니다.

비규범적 설명

그냥 conda를 쓰면 되지 않습니까?

이 질문은 사실 이 PEP의 범위에 속하지 않지만, conda는 바이너리 Python 인터프리터를 배포하는 널리 쓰이는 방법이므로 자연스럽게 떠오르는 질문입니다.

간단한 답은 이렇습니다: conda는 훌륭합니다! 하지만 conda를 사용하지 않는 Python 사용자도 많으며, 그들도 좋은 것을 누릴 자격이 있습니다. 이 PEP는 그들에게 또 하나의 선택지를 줄 뿐입니다.

더 근본적인 답은 이렇습니다. PyPI에 패키지를 업로드하는 유지 관리자는 Python 생태계의 근간입니다. 그들은 Python 패키징 도구의 첫 번째 사용자층입니다. 그리고 그들이 원하는 것 중 하나는 패키지를 한 번만 업로드하면 Python이 배포되는 온갖 방식에서 모두 사용할 수 있게 하는 것입니다: Debian과 Fedora와 Homebrew와 FreeBSD에서, Conda 환경에서, 대기업의 모노레포에서, Nix에서, Blender 플러그인에서, RenPy 게임에서, ….. 무슨 말인지 아실 것입니다.

이 환경들은 모두 패키지와 의존성을 관리하기 위한 자체 도구와 전략을 가지고 있습니다. 그래서 PyPI와 휠의 특별한 점은, 이러한 모든 다운스트림 시스템이 받아들여 각자의 로컬 관례로 변환할 수 있도록 의존성을 표준적이고 추상적인 방식으로 기술하게 설계되었다는 것입니다. 그래서 패키지 유지 관리자는 Python 전용 메타데이터를 사용하고 PyPI에 업로드합니다. 그렇게 하면 이 모든 시스템을 동시에 지원할 수 있기 때문입니다. conda용 Python 패키지를 빌드할 때마다 중간 단계의 휠이 생성되는데, 휠은 Python 패키지 빌드 시스템과 conda가 서로 소통하는 데 사용할 수 있는 공통 언어이기 때문입니다.

그런데 sdist와 wheel을 릴리스하는 유지 관리자라면 릴리스하는 것을 당연히 테스트하고 싶을 것입니다. 그리고 그 결과물은 임의의 PyPI 패키지와 버전에 의존할 수 있습니다. 따라서 PyPI에서 직접 Python 환경을 구축하는 도구가 필요한데, conda는 근본적으로 그런 일을 하도록 설계되지 않았습니다. 따라서 conda와 pip는 서로 다른 경우에 각각 필요하며, 이 제안은 공교롭게도 그중 pip 쪽을 대상으로 하고 있습니다.

Sdist (또는 그렇지 않은 것)

pybi에도 “sdist”에 해당하는 것이 있으면 좋을 수 있습니다. 즉, 미리 빌드된 pybi를 사용할 수 없는 플랫폼에서 도구가 Python 소스 릴리스를 자동으로 가져와 pybi로 빌드할 수 있을 만큼 충분히 구조화된 일종의 형식입니다. 하지만 이것은 MVP에 꼭 필요하지 않고 복잡한 문제를 여럿 불러오므로, 나중에 걱정합시다.

pybi 안에 어떤 패키지를 번들로 포함해야 합니까?

pybi 빌더는 안에 정확히 무엇을 넣을지 마음대로 고를 수 있습니다. 예를 들어, pybi의 site-packages 디렉터리에 미리 설치된 패키지를 몇 개 포함하거나, 원하지 않는 표준 라이브러리 일부를 잘라낼 수 있습니다. 저희는 이를 막을 수 없습니다! 다만 패키지를 미리 설치한다면, Pip이나 다른 도구가 무슨 일이 일어나고 있는지 파악할 수 있도록 올바른 메타데이터(.dist-info 등)도 함께 포함할 것을 강력히 권장합니다.

제가 만든 프로토타입 “범용” pybi에서 선택한 방식은 다음과 같습니다.

  • site-packages가 비어 있도록 하십시오.

    근거: 최종 사용자를 대상으로 하는 전통적인 독립형 Python 설치 프로그램이라면, 부트스트래핑 문제(PEP 453)를 피하기 위해 최소한 pip은 포함하는 것이 좋을 것입니다. 하지만 pybi는 다릅니다. pybi는 더 큰 자동화 배포 과정의 일부로 pybi를 사용하는 “똑똑한” 도구에 의해 설치되도록 설계되었습니다. 이러한 설치 프로그램은 원할 수도 있고 원하지 않을 수도 있는 패키지가 미리 설치된 상태에서 시작하는 것보다, 빈 상태에서 시작해 필요한 것을 추가하는 편이 더 쉽습니다. (게다가 python -m ensurepip을 실행할 수도 있습니다.)

  • test를 제외한 전체 표준 라이브러리를 포함하십시오.

    근거: 최상위 test 모듈에는 CPython 자체의 테스트 스위트가 들어 있습니다. 이 모듈은 매우 크며(test가 없는 CPython은 약 37 MB인데, test가 그 위에 약 25 MB를 더합니다!), 일반 사용자 코드에서는 사실상 전혀 사용되지 않습니다. 또한 선례로서, 공식 nuget 패키지, 공식 manylinux 이미지, 여러 Linux 배포판이 모두 이 모듈을 제외하고 있으며, 이로 인해 큰 문제가 생긴 적은 없습니다.

    따라서 이것이 폭넓은 호환성과 적절한 다운로드/설치 크기 사이에서 균형을 맞추는 가장 좋은 방법으로 보입니다.

  • .pyc 파일은 전혀 포함하지 않습니다. 이 파일들은 다운로드 용량을 차지하고, 최종 시스템에서 최소한의 비용으로 생성할 수 있으며, 이를 제외하면 위치 의존성의 원인 하나가 사라집니다. (.pyc 파일은 대응하는 .py 파일의 절대 경로를 저장하고 이를 트레이스백에 포함합니다. 하지만 pybi는 재배치 가능하므로, 설치가 끝나기 전까지는 올바른 경로를 알 수 없습니다.)

하위 호환성

하위 호환성과 관련하여 고려할 사항은 없습니다.

보안 영향

바이너리를 배포하겠다고 나서는 사람은 누구든지 보안 관리 계획(예를 들어, OpenSSL CVE가 공개된 후 새 빌드를 만들지 여부)을 세워야 한다는 점 외에는 보안 영향이 없습니다. 하지만 전체적으로 보면 저희 Python 코어 개발자들은 이미 모든 주요 플랫폼용 바이너리 빌드를 유지 관리하고 있으므로(macOS와 Windows는 python.org를 통해, Linux 빌드는 공식 manylinux 이미지를 통해), PyPI에 공식 CPython 빌드를 릴리스하기 시작하더라도 새로운 보안 문제가 실제로 생기지는 않습니다.

교육 방법

이 PEP는 최종 사용자를 대상으로 하지 않습니다. 최종 사용자는 예를 들어 pyenv나 tox 실행이 마법처럼 더 빠르고 안정적으로 바뀌는 경험을 할 뿐입니다(해당 프로젝트의 유지 관리자가 이 PEP를 활용하기로 결정한다면 말입니다).