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

Python 개선 제안 한국어 번역

PEP 516 – pip/conda 등을 위한 빌드 시스템 추상화

Author:
Robert Collins <rbtcollins at hp.com>, Nathaniel J. Smith <njs at pobox.com>
BDFL-Delegate:
Alyssa Coghlan <ncoghlan at gmail.com>
Discussions-To:
Distutils-SIG list
Status:
Rejected
Type:
Standards Track
Topic:
Packaging
Created:
26-Oct-2015
Resolution:
Distutils-SIG message

Table of Contents

번역·라이선스 안내

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

초록

이 PEP는 Python 소스 트리(개발자 트리(예: git 트리)와 소스 배포판 모두)를 대상으로 작업할 때 pip [1] 및 기타 배포 또는 설치 도구가 사용할 프로그래밍 인터페이스를 지정합니다.

이 프로그래밍 인터페이스는 다음 두 가지 주요 이유로 pip를 현재의 setuptools [2]에 대한 강한 의존성에서 분리할 수 있게 합니다.

  1. 이 인터페이스를 사용하면 setuptools처럼 보이도록 만들 필요 없이 훨씬 더 쉽게 사용할 수 있는 새로운 빌드 시스템을 사용할 수 있습니다.
  2. 또한 pip를 중단하지 않고 setuptools 자체가 사용자 인터페이스를 변경할 수 있게 하여 결합도를 낮춥니다.

pip가 빌드 시스템을 설치할 수 있도록 하는 데 필요한 이 인터페이스는 패키지의 빌드 시간 요구 사항도 pip가 설치할 수 있게 하며, 이는 pip가 easy-install의 설치 구성 요소와 완전한 기능 동등성을 갖추는 데 중요한 단계입니다.

초안인 PEP 426은 해당 PEP에서 정의한 메타데이터 형식을 활용할 수 없습니다. 그러나 PEP 427 휠은 널리 사용되고 사양도 비교적 잘 정의되어 있으므로, 배포 종속성과 일반 프로젝트 메타데이터를 지정하기 위해 해당 PEP의 METADATA 형식을 채택했습니다. PEP 508은 종속성을 설명하기 위한 자체 완결형 언어를 제공하며, 이를 부트스트랩 종속성을 설명하기 위한 간단한 JSON 스키마로 캡슐화합니다.

Python sdist는 PEP 314에 명시된 소스 트리이기도 하므로, 이 PEP에서는 sdist의 정의를 갱신합니다.

PEP 거부

이 PEP에서 제안한 CLI 기반 접근 방식은 PEP 517에서 제안한 Python API 기반 접근 방식을 채택하기 위해 거부되었습니다. 격리된 서브프로세스로 실행되는 빌드 백엔드와 통신하는 데 사용되는 구체적인 CLI는 프런트엔드 개발자 도구 구현의 구현 세부 사항으로 간주합니다.

동기

현재 Python 패키징 생태계에서는 빌드 시스템과 pip 사이의 종속으로 인해 상당한 불만이 누적되어 있습니다. 이러한 종속을 끊는 것은 pip, setuptools 및 flit [3]와 같은 다른 빌드 시스템에 더 유리합니다.

사양

개요

빌드 도구는 소스 트리의 루트 디렉터리에서 pypa.json파일을 읽어 찾습니다. 해당 파일은 빌드 도구를 가져오는 방법과 도구를 호출하기 위해 실행할 명령의 이름을 설명합니다.

모든 도구는 pip가 기존에 setuptools setup.py 인터페이스를 사용하던 방식을 모델로 한 단일 명령줄 인터페이스를 준수해야 합니다.

pypa.json

pypa.json파일은 소스 트리를 빌드하려는 pip 및 기타 도구가 구성을 확인할 수 있도록 하는 중립적인 구성 파일 역할을 합니다. Python 소스 트리에 pypa.json 파일이 없으면 setuptools 또는 setuptools 호환 빌드 시스템을 사용한다는 의미입니다.

JSON에는 다음 스키마가 있습니다. 추가 키는 무시되므로 pypa.json을 관련된 다른 도구의 구성 파일로 사용할 수 있습니다. 그렇게 하는 경우 선택한 키는 tools 아래에 네임스페이스를 지정해야 합니다.:

{"tools": {"flit": ["Flits content here"]}}
스키마
스키마의 버전입니다. 이 PEP는 버전 “1”을 정의합니다. 값이 없으면 “1”이 기본값입니다. 파일을 읽는 모든 도구는 인식할 수 없는 스키마 버전에 대해 오류를 발생시켜야 합니다.
bootstrap_requires
빌드 도구를 실행하기 전에 설치해야 하는 선택적 PEP 508의 의존성 사양 목록입니다. 예를 들어 flit을 사용하는 경우 요구 사항은 다음과 같을 수 있습니다.:
bootstrap_requires: ["flit"]
build_command
필수 키이며, 실행할 명령을 설명하는 Python 형식 문자열 [6]의 목록입니다. 예를 들어 flit을 사용하는 경우 빌드 명령은 다음과 같을 수 있습니다.:
build_command: ["flit"]

실행 가능한 모듈 fred를 사용하는 명령의 경우:

build_command: ["{PYTHON}", "-m", "fred"]

프로세스 인터페이스

실행할 명령은 간단한 Python 형식 문자열 [6]로 정의합니다.

이는 전용 스크립트를 사용하거나 “python -m somemodule”을 사용하여 호출되는 빌드 시스템을 허용합니다.

프로세스는 현재 작업 디렉터리가 소스 트리의 루트로 설정된 상태에서 실행됩니다.

실행될 때 프로세스는 표준 입력에서 읽지 않아야 합니다. 현재 pip은 표준 입력을 자체 표준 입력에 연결한 상태로 빌드 시스템을 실행하지만, 표준 출력과 표준 오류는 리디렉션되므로 사용자와 통신할 수 없습니다.

프로세스의 경우와 마찬가지로 0이 아닌 종료 상태는 오류를 나타냅니다.

사용 가능한 형식 변수

PYTHON
사용 중인 Python 인터프리터입니다. 이는 Python 진입점에 불과한 항목을 호출할 수 있도록 하는 데 중요합니다.
{PYTHON} -m foo

사용 가능한 환경 변수

이러한 변수는 빌드 시스템의 호출자가 설정하며 항상 사용할 수 있습니다.

PATH
표준 시스템 경로입니다.
PYTHON
형식 변수와 동일합니다.
PYTHONPATH
일반적인 Python 메커니즘에 따라 sys.path를 제어하는 데 사용됩니다.

하위 명령

빌드 시스템이 지원해야 하는 여러 개의 별도 하위 명령이 있습니다. 아래 예제에서는 설명을 위해 flit을 build_command로 사용합니다.

build_requires
빌드 요구 사항을 조회합니다. 빌드 요구 사항은 하나의 키 build_requires키로 구성된 UTF-8 인코딩 JSON 문서로 반환되며, PEP 508에 따른 의존성 사양 목록을 포함합니다. 추가 키는 무시해야 합니다. build_requires 명령은 빌드 환경을 설정하지 않고 실행되는 유일한 명령입니다.

예제 명령:

flit build_requires
메타데이터
프로젝트 메타데이터를 조회합니다. 메타데이터만 UTF-8 인코딩으로 stdout에 출력해야 합니다. pip는 다른 어떤 패키지를 다운로드하고 설치해야 하는지 확인하기 위해 metadata를 한 번만 실행합니다. 메타데이터는 PEP 427에 따른 휠 METADATA 파일로 출력됩니다.

metadata 명령으로 생성된 메타데이터와 생성된 휠에 포함된 메타데이터는 동일해야 합니다.

예제 명령:

flit metadata
wheel -d OUTPUT_DIR
프로젝트의 휠을 빌드하기 위해 실행하는 명령입니다. OUTPUT_DIR는 휠을 출력할 기존 디렉터리를 가리킵니다. stdout와 stderr에는 의미가 없습니다. 파일 하나만 출력해야 합니다. 둘 이상 출력되면 pip는 사용할 파일을 임의로 선택합니다.

예제 명령:

flit wheel -d /tmp/pip-build_1234
develop [–prefix PREFIX]
프로젝트를 현재 위치에 ‘개발’ 설치하기 위해 실행하는 명령입니다. stdout와 stderr에는 의미가 없습니다.

모든 빌드 시스템이 develop 설치를 수행할 수 있는 것은 아닙니다. 빌드 시스템이 develop 설치를 수행할 수 없다면 실행될 때 오류를 발생시켜야 합니다. 이렇게 하면 pip install -e foo와 같은 사용 작업이 실패한다는 점에 유의하십시오.

prefix 옵션은 설치를 위한 대체 prefix를 정의하는 데 사용됩니다. setuptools에는 --root--user 옵션이 있지만, --prefix를 사용하여 동등하게 수행할 수 있으며, --root 또는 --user 옵션을 허용하는 pip나 기타 도구는 이를 적절히 변환해야 합니다.

root 옵션은 명령이 작동해야 하는 대체 루트를 정의하는 데 사용됩니다.

예를 들어:

flit develop --root /tmp/ --prefix /usr/local

사용 중인 Python 환경에서 sys.prefix가 /usr/라고 보고하더라도 /tmp/usr/local/bin아래에 스크립트를 설치해야 하며, 그렇지 않으면 /tmp/usr/bin/을 사용하게 됩니다. 패키지 파일 등에도 유사한 논리가 적용됩니다.

빌드 환경

build_requires 명령을 제외한 모든 명령은 빌드 환경 내에서 실행됩니다. 특정 구현은 요구되지 않지만, 빌드 환경은 다음 요구 사항을 충족해야 합니다.

  1. 프로젝트의 build_requires에 지정된 모든 의존성은 $PYTHON에서 가져올 수 있어야 합니다.
  1. 빌드에 필요한 패키지가 제공하는 모든 명령줄 스크립트는 $PATH에 존재해야 합니다.

이에 따른 결과로, 빌드 시스템은 build_requires로 선언되지 않았거나 Python 표준 라이브러리에 속하지 않는 Python 패키지에 대한 접근을 가정할 수 없습니다.

격리형 빌드

이 명세는 빌드가 격리형이어야 하는지 여부를 규정하지 않습니다. setuptools와 같은 기존 빌드 도구는 빌드 시점 요구 사항에 대해 설치된 버전(예: setuptools_scm)을 사용하며, 버전 충돌이나 의존성 누락이 있을 때만 다른 버전을 설치합니다. 그러나 항상 격리된 빌드를 수행하고 지정된 의존성만 사용하면 더 나은 일관성을 확보할 수 있을 가능성이 큽니다.

그러나 여기에는 일부 패키지의 의존성을 충족하는 빌드 요구 사항의 잘못된 버전을 사용하지 않도록 사용자가 강제하려면 어떻게 해야 하는지와 같은 미묘한 문제가 있습니다. 향후 PEP에서 이 문제를 다룰 수 있지만, 현재는 범위에 포함되지 않습니다. 이 문제는 빌드 시스템과 빌드를 수행해야 하는 항목 간의 조정에 필요한 메타데이터에 영향을 주지 않으므로 PEP에서 다룰 내용이 아닙니다.

업그레이드

‘pypa.json’은 호환성을 요구하지 않고 향후 변경을 허용할 수 있도록 버전이 지정됩니다.

새 PEP에서 두 스키마 중 하나를 업그레이드하는 절차는 다음과 같습니다.

  1. 업데이트된 스키마를 정의하는 새 PEP를 발행합니다. 스키마가 완전한 하위 호환성을 갖지 않는다면 새 버전 번호를 정의해야 합니다.
  2. 사용 주체(예: pip)가 새 스키마 버전에 대한 지원을 구현합니다.
  3. 패키지 작성자는 새 스키마 버전에 대한 지원을 도입한 ‘pip’ 버전(및 잠재적으로 다른 사용 주체)에 대한 의존성을 추가해도 괜찮다고 판단할 때 새 스키마를 선택합니다.

이 PEP의 최초 배포에도 same 프로세스가 적용됩니다. setuptools shim없이 이 PEP를 사용할 수 있는 기능의 전파는 이를 지원하는 최초 pip 버전의 채택률에 따라 크게 좌우됩니다.

sdist의 정적 메타데이터

이 PEP는 현재 sdist의 정적 메타데이터를 신뢰할 수 없는 문제를 다루지 않습니다. 이는 sdist에서 비롯되었는지 여부와 관계없이 소스 트리에서 사용 중인 빌드 시스템을 식별하고 사용하는 문제와는 별개의 문제입니다.

컴파일러 옵션 처리

서로 다른 컴파일러 옵션의 처리는 이 명세의 범위에 포함되지 않습니다.

현재 pip는 setuptools를 실행할 때 실행하는 명령줄에 사용자가 제공한 문자열을 추가하는 방식으로 컴파일러 옵션을 처리합니다. 이 접근 방식은 이 PEP에서 정의한 빌드 시스템 인터페이스와 함께 사용하기에 충분하지만, 서로 다른 빌드 시스템이 발전함에 따라 전역적으로 지정된 옵션이 더 이상 전역적으로 작동하지 않게 된다는 예외가 있습니다. 이 문제는 상호 운용성에 영향을 주지 않고 pip(또는 conda나 다른 설치 관리자)에서 해결할 수 있습니다.

장기적으로는 한 컴파일러 또는 옵션으로 빌드된 휠과 다른 컴파일러 또는 옵션으로 빌드된 휠 간의 차이를 휠에서 표현할 수 있어야 하며, 이는 PEP에서 다룰 내용입니다.

예제

flit을 사용하기 위한 ‘pypa.json’ 예제:

{"bootstrap_requires": ["flit"],
 "build_command": "flit"}

‘pip’가 이를 읽으면 flit을 사용하기 전에 flit이 포함된 환경을 준비합니다.

flit에는 현재 setup-requires 지원이 없으므로 flit build_requires는 상수 문자열만 출력합니다.:

{"build_requires": []}

flit metadataflit.ini를 조사하여 메타데이터를 휠 METADATA 파일로 마샬링하고, 이를 stdout으로 출력합니다.

flit wheel은 휠을 출력할 위치를 알려 주는 -d매개변수를 받아들여야 합니다(pip에 필요합니다).

하위 호환성

이전 버전의 pip는 대체 빌드 시스템을 처리할 수 없는 상태로 남습니다. 이는 현재 상태보다 나쁘지 않으며, 개별 빌드 시스템 프로젝트에서 shim setup.py를 포함할지 여부를 결정할 수 있습니다.

휠을 생성하고 develop 설치를 수행할 수 있는 모든 기존 빌드 시스템은 이 추상화에서 작동할 수 있어야 하며, 해당 시스템을 위한 특정 어댑터만 작성하여 PyPI에 게시하면 됩니다.

pypa.json 파일이 없으면 pip와 같은 도구는 setuptools 빌드 시스템을 가정하고 setuptools 명령을 직접 사용해야 합니다.

네트워크 효과

setuptools와 호환되지 않는 빌드 시스템을 채택한 프로젝트, 즉 setup.py가 없거나 setup.py가 기존 도구에서 사용하려는 명령을 허용하지 않는 프로젝트는 해당 기존 도구로 설치할 수 없습니다.

이러한 프로젝트가 다른 프로젝트에서 사용되면 이 효과가 연쇄적으로 확산됩니다.

특히 pip는 현재 setup-requires를 처리하지 않으므로, 어떤 프로젝트든 (A) setuptools와 호환되지 않는 빌드 시스템을 채택하고 다음 프로젝트에서 pypa.json을 갖도록 아직 전환하지 않은 두 번째 프로젝트(B)의 setup-requirement는 어떤 버전의 pip에서도 B를 설치할 수 없게 만듭니다. 이는 pip가 ‘setup.py egg_info’를 실행할 때 B의 setup.py가 easy-install을 트리거하고, 이 과정에서 A를 설치하려다 실패하기 때문입니다.

따라서 현재 setup-requires로 사용되는 도구는 setuptools shim을 유지하도록 보장하거나, 해당 도구를 사용하는 프로젝트를 찾아 자신들이 전환하기 전에 모두 pypa.json을 사용하도록 업그레이드시키기를 권장합니다. 현실적으로 이는 불가능하므로, pbr 및 setuptools_scm과 같은 프로젝트뿐 아니라 numpy와 같은 프로젝트에서도 setuptools shim을 무기한 유지하는 것이 권고 사항입니다.

setuptools shim

setup.py처럼 보이면서 내부적으로 pypa.json을 사용하여 빌드를 구동하는 범용 setuptools shim을 작성할 수 있습니다. 이는 pip가 이 시스템을 사용하는 데 필요하지 않지만, 패키지 작성자가 이전 버전의 pip와의 호환성을 유지하면서 새로운 기능을 사용할 수 있게 합니다.

근거

이 PEP는 distutils-sig의 긴 메일링 리스트 스레드 [4]에서 시작되었습니다. 그 후 사람들이 제기한 모든 입장을 검토하기 위한 온라인 회의가 열렸습니다. 그 회의록은 [5]에 게시되었습니다.

이 명세는 그곳에서 도출된 합의 내용을 PEP 형식으로 옮긴 것이며, 아직 남아 있는 사소한 문제들에 대해서는 몇 가지 임의의 선택을 함께 포함합니다.

이 설계의 기본적인 경험칙은 해당 추상화에 엄격히 연관되지 않은 개발을 요구하지 않으면서 추상화를 도입하는 데 초점을 맞추는 것이었습니다. 개선까지의 격차가 작거나 기존 인터페이스를 사용하는 비용이 매우 높은 경우에는 개선 사항을 종속성으로 포함했지만, 그 외의 경우에는 이를 향후 반복 작업으로 미루었습니다.

pip가 필요한 모든 데이터를 METADATA 파일에 인코딩하는 wheel .dist-info 디렉터리를 이미 처리할 수 있으므로, 새로운 명세를 정의하는 대신 wheel METADATA 파일을 선택했습니다. PEP 426은 아직 초안이므로 사용할 수 없으며, 새로운 메타데이터 형식을 정의하는 일은 그렇게 해야 하기는 하지만 별도의 문제입니다. 디스크상의 디렉터리를 사용해도 인터페이스에 어떠한 가치도 더해지지 않습니다(pip는 setuptools CLI의 제한 때문에 현재 그렇게 해야 합니다).

명령으로 ‘develop’을 사용하는 이유는 ‘setuptools develop’이 수행하는 작업의 상호 운용성을 명시한 PEP가 없기 때문입니다. 따라서 pip가 ‘develop’ 단계를 수행할 책임을 맡기 전에 이를 정의해야 합니다. 그 작업이 완료되면 이 PEP의 후속 PEP를 발표할 수 있습니다.

Python API 대신 명령줄 API를 사용하는 것은 다소 논란의 여지가 있습니다. 근본적으로 무엇이든 작동하도록 만들 수 있으며, pip 유지 관리자들은 오늘날 pip에서 성숙하고 견고한 프로세스 기반 인터페이스를 유지하는 것을 강력히 지지해 왔습니다.

파일 형식으로 JSON을 선택한 것은 여러 제약 조건 사이의 타협입니다. 첫째, 표준 라이브러리에는 YAML 인터프리터가 없으며, 마찰이 적은 다른 구조화된 파일 형식을 위한 것도 없습니다. 둘째, INIParser는 여러 이유로 형편없는 형식인데, 주된 이유는 구조가 매우 최소한이라는 점이지만, pip 관리자들은 이를 선호하지 않습니다. JSON은 표준 라이브러리에 포함되어 있으며, 임베디드 DSL이 필요 없이 앞으로 원하는 무엇이든 포함할 수 있을 만큼 충분한 구조를 갖추고 있습니다.

Donald는 새로운 것을 고안하기보다는 setup.cfg와 기존 setuptools 명령줄을 사용할 것을 제안했습니다. 그렇게 하면 눈에 덜 띄는 변경으로 상호운용성을 허용할 수 있지만, pip 측에서도 거의 그만큼의 엔지니어링이 필요합니다 - setup.cfg에서 새로운 키를 찾고, 빌드를 실행할 설치되지 않은 환경을 구현해야 합니다. 그리고 setuptools처럼 보이지만 상당히 다르게 동작하는 것을 제공함으로써 사용자를 혼란스럽게 하고 싶지 않다는 다른 빌드 시스템 작성자들의 바람은, pip가 커스텀 빌드 도구를 호출하는 법을 배우는 것보다 더 큰 문제로 보입니다.

metadata 명령과 wheel 명령은, pip가 메타데이터를 읽고 그에 따라 행동한 다음 결과로 나온 wheel이 호환되지 않는 요구사항을 갖게 되는 경쟁 조건을 피하기 위해 일관된 메타데이터를 가져야 합니다. 그러한 경쟁 조건은 오늘날 PEP 426 환경 마커를 지원하지 않는 구버전 pip에서 동작하기 위해, 환경 마커를 사용하는 패키지들에 의해 악용되고 있습니다. setuptools shim이 사용 중이거나(구버전 pip와 함께) 환경 마커를 지원하는 pip가 사용 중이므로, 이 PEP에서는 그러한 악용이 필요하지 않습니다. setuptools shim은 구버전 pip가 요구하는 차이를 악용하는 것을 처리할 수 있습니다.

저희는 sdist 동사를 두는 것에 대해 논의했습니다. 이것의 주된 동기는 빌드 시스템이 pip가 빌드할 수 있는 sdist를 만들 수 있도록 보장하는 것이었지만, 이는 순환 논리입니다: 이 PEP의 핵심은 pip가 setuptools 구현을 요구하지 않고도 그러한 sdist나 VCS 소스 트리를 안정적으로 소비할 수 있게 하는 것입니다. 기존 소스 트리로부터 새 sdist를 만들 수 있는 것은 오늘날 pip가 하는 일이 아니며, 소스로부터 빌드하는 과정의 일부로 그렇게 하는 PR이 있긴 하지만, 논란의 여지가 있고 합의가 부족합니다. 모든 빌드 시스템에 요구사항을 부과하기보다는, 저희는 이를 YAGNI로 간주하며, 필요하다면 향후 버전의 인터페이스에서 그러한 동사를 추가할 것입니다. sdist에 대한 기존 PEP 314 요구사항은 여전히 적용되며, distutils나 setuptools 사용자는 setup.py sdist를 사용하여 sdist를 만들 수 있습니다. 다른 도구들은 PEP 314와 호환되는 sdist를 만들어야 합니다. pip 자체는 PEP 314 호환성을 요구하지 않는다는 점에 유의하십시오 - pip는 sdist의 메타데이터를 전혀 사용하지 않으며, 이를 디스크나 버전 관리 시스템의 소스 트리처럼 취급합니다.

참고 자료