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

Python 개선 제안 한국어 번역

PEP 668 – Python 기본 환경을 “외부 관리됨”으로 표시하기

Author:
Geoffrey Thomas <geofft at ldpreload.com>, Matthias Klose <doko at ubuntu.com>, Filipe Laíns <lains at python.org>, Donald Stufft <donald at stufft.io>, Tzu-ping Chung <uranusjr at gmail.com>, Stefano Rivera <stefanor at debian.org>, Elana Hashman <ehashman at debian.org>, Pradyun Gedam <pradyunsg at gmail.com>
PEP-Delegate:
Paul Moore <p.f.moore at gmail.com>
Discussions-To:
Discourse thread
Status:
Final
Type:
Standards Track
Topic:
Packaging
Created:
18-May-2021
Post-History:
28-May-2021
Resolution:
Discourse message

Table of Contents

번역·라이선스 안내

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

Important

This PEP is a historical document. The up-to-date, canonical spec, Externally Managed Environments, is maintained on the PyPA specs page.

×

See the PyPA specification update process for how to propose changes.

초록

Python 사용자에게 오랫동안 존재해 온 실질적인 문제는 OS 패키지 관리자와 pip 같은 Python 전용 패키지 관리 도구 간의 충돌입니다. 이러한 충돌에는 Python 수준의 API 비호환성과 파일 소유권을 둘러싼 충돌이 모두 포함됩니다.

역사적으로 Python 전용 패키지 관리 도구는 암시적인 전역 컨텍스트에 패키지를 설치하는 것을 기본값으로 삼았습니다. 가상 환경이 표준화되고 널리 사용됨에 따라, 대부분의 사용 사례에서는 Python 전용 패키지 관리 도구를 가상 환경 내에서만 사용하는 것이 더 나은 해결책입니다.

이 PEP는 Python 설치가 pip 같은 도구에 해당 설치의 전역 패키지 설치 컨텍스트가 OS 패키지 관리자처럼 Python 외부의 수단으로 관리된다는 사실을 전달할 수 있는 메커니즘을 제안합니다. 이 문서는 Python 전용 패키지 관리 도구가 기본적으로 인터프리터의 전역 컨텍스트에 패키지를 설치하거나 제거해서는 안 되며, 대신 최종 사용자가 가상 환경을 사용하도록 안내해야 한다고 명시합니다.

또한 이 문서는 sysconfig 스킴의 해석을 표준화하여, Python 전용 패키지 관리자가 인터프리터 전체 컨텍스트에 패키지를 설치하려는 경우 외부 패키지 관리자와의 충돌을 피하고 외부 패키지 관리자가 제공한 소프트웨어를 손상시킬 위험을 줄이는 방식으로 설치할 수 있도록 합니다.

용어

이 PEP에서 사용되는 몇 가지 용어는 이 문서가 다루는 여러 컨텍스트에서 서로 다른 의미를 가집니다. 명확성을 위해 이 PEP에서는 다음 용어를 특정한 방식으로 사용합니다.

디스트로
“distribution”의 약어로, 이상적으로는 서로 올바르게 작동하도록 설계된 다양한 종류의 소프트웨어 모음이며, 여기에는 이 문서와 관련된 컨텍스트에서 Python 인터프리터 자체, Python으로 작성된 소프트웨어, 다른 언어로 작성된 소프트웨어가 포함됩니다. 즉, “Linux distro” 또는 “Berkeley Software Distribution”과 같은 표현에서 사용되는 의미입니다.

디스트로는 Debian, Fedora 또는 FreeBSD와 같은 자체 운영 체제(OS)일 수 있습니다. 또한 Homebrew 또는 MacPorts와 같이 기존 OS 위에 설치되는 오버레이 배포판일 수도 있습니다.

이 문서에서는 “distro”라는 짧은 용어를 사용합니다. Python 패키징 컨텍스트에서 “distribution”이라는 용어는 다른 의미, 즉 단일 Python 언어 소프트웨어의 소스 또는 바이너리 배포 패키지를 가지기 때문이며, 이는 setuptools.dist.Distribution 또는 “sdist”의 의미입니다. 혼동을 피하기 위해 이 문서에서는 “distribution”이라는 일반 용어를 전혀 사용하지 않습니다. Python 패키징의 의미에서는 “배포 패키지”라는 전체 표현이나 단순히 “패키지”라는 표현을 사용합니다(아래 참조).

디스트로의 제공자, 즉 소프트웨어를 수집하고 게시하며 필요한 수정을 수행하는 팀 또는 회사는 해당 디스트로의 배포자입니다.

패키지
Python 내에서 설치하고 사용할 수 있는 소프트웨어 단위입니다. 즉, 이는 Python 전용 패키징 도구에서 일반적으로 “distribution package” 또는 간단히 “distribution”이라고 부르는 것을 의미합니다. 구어적인 약칭인 “package”는 Python 패키지 색인이라는 의미로 사용합니다.

이 문서에서는 Python 모듈을 포함하는 임포트 가능한 이름이라는 의미로 “package”를 사용하지 않습니다. 다만 많은 경우 배포 패키지는 동일한 이름의 단일 임포트 가능한 패키지로 구성됩니다.

이 문서에서는 일반적으로 디스트로의 패키지 관리자가 설치하는 단위(예: .deb 또는 .rpm 파일)를 가리키는 의미로 “package”라는 용어를 사용하지 않습니다. 필요한 경우 “디스트로의 패키지”와 같은 표현을 사용합니다. (다시 말해 많은 경우 Python 패키지는 python- 뒤에 Python 패키지 이름을 붙인 이름의 디스트로 패키지 안에 포함되어 제공됩니다.)

Python 전용 패키지 관리자
Python 패키징 표준(예: PEP 376PEP 427)에 부합하는 방식으로 Python 패키지를 설치, 업그레이드 및/또는 제거하는 도구입니다. 가장 널리 사용되는 Python 전용 패키지 관리자는 pip [1]이며, 그 밖의 예로는 이전의 Easy Install 명령 [2]setup.py 명령의 직접 사용이 있습니다.

(Conda [3]conda 명령이 Python 패키지만이 아니라 훨씬 더 많은 것을 설치할 수 있으므로 어떤 의미에서는 디스트로 패키지 관리자에 더 가까운 특수한 경우입니다. conda 명령은 일반적으로 Conda가 생성한 환경에서만 작동하므로, Python 전용 패키지 관리자로 동작할 때는 이 문서의 대부분의 우려 사항이 conda에 적용되지 않습니다.)

배포판 패키지 관리자
설치된 배포판 인스턴스에서 해당 배포판의 패키지를 설치, 업그레이드 및/또는 제거하는 도구로서, Python 패키지뿐만 아니라 비(非)Python 패키지도 설치할 수 있으므로 일반적으로 PEP 376과 무관한 자체 설치 소프트웨어 데이터베이스를 보유합니다. 예로 apt, dpkg, dnf, rpm, pacmanbrew가 있습니다. 중요한 특징은 패키지 관리자가 패키지를 설치한 경우, Python 전용 패키지 관리자의 요구를 충족하는 방식으로 해당 패키지를 제거하거나 업그레이드하면 일반적으로 배포판 패키지 관리자가 일관되지 않은 상태에 놓인다는 점입니다.

이 문서는 특정 맥락에서 배포판 패키지 관리자를 가리키기 위해 “외부 패키지 관리자” 또는 “시스템의 패키지 관리자”와 같은 표현도 사용합니다.

섀도잉
설치된 Python 패키지를 섀도잉한다는 것은 섀도잉된 패키지의 파일을 제거하지 않고 다른 패키지가 가져오기에 우선되도록 하는 것입니다. 이를 위해서는 sys.path에 여러 항목이 있어야 합니다. 패키지 A 2.0이 하나의 sys.path 항목에 모듈 a.py를 설치하고 패키지 A 1.0이 더 뒤의 sys.path 항목에 모듈 a.py를 설치하면, import a는 앞쪽 항목의 모듈을 반환하며, 이를 A 2.0이 A 1.0을 섀도잉한다고 합니다.

동기

Python의 엄청난 인기에 힘입어 소프트웨어 배포판(여기서 배포판은 Linux 및 기타 OS 배포판뿐만 아니라 Homebrew와 MacPorts 같은 오버레이 배포판도 의미합니다)은 일반적으로 두 가지 목적으로 Python을 제공합니다. 최종 사용자가 그 자체로 사용하는 소프트웨어 패키지로 제공하는 것과, 배포판의 다른 소프트웨어를 위한 언어 종속성으로 제공하는 것입니다.

예를 들어 Fedora와 Debian(그리고 그 파생 배포판 및 기타 많은 배포판)은 최종 사용자가 사용할 수 있는 python3 명령과 배포판에 포함된 Python 언어 소프트웨어를 위한 #!/usr/bin/python3 셰뱅을 제공하는 /usr/bin/python3 바이너리를 제공합니다. Linux/UNIX용 Python의 공식 바이너리 릴리스가 없기 때문에 이러한 OS의 거의 모든 Python 최종 사용자는 배포판이 빌드하고 제공하는 Python 인터프리터를 사용합니다.

배포판 사용자가 사용할 수 있는 python3 실행 파일과 배포판의 다른 소프트웨어를 위한 종속성으로 사용할 수 있는 python3 실행 파일은 일반적으로 동일한 바이너리입니다. 이는 최종 사용자가 가상 환경 외부에서 pip와 같은 도구를 사용하여 Python 패키지를 설치하면 해당 패키지가 배포판에서 제공하는 Python 언어 소프트웨어에 표시된다는 의미입니다. 새로 설치된 패키지(또는 해당 패키지의 종속성 중 하나)가 배포판을 통해 설치된 패키지보다 최신이면서 하위 호환성이 없는 버전인 경우, 배포판에서 제공하는 소프트웨어가 작동하지 않을 수 있습니다.

이는 배포판 자체가 Python으로 작성된 패키지 관리 도구를 사용하는 경우가 많기 때문에 배포판의 무결성에 심각한 문제를 일으킬 수 있습니다. 예를 들어 pip install 명령으로 Fedora의 dnf 명령을 의도치 않게 망가뜨려 복구하기 어렵게 만들 수 있습니다.

이는 시스템 전체 설치(sudo pip install)와 사용자 홈 디렉터리 설치(pip install --user) 모두에 적용됩니다. 두 위치의 패키지 모두 /usr/bin/python3sys.path에 나타나기 때문입니다.

시스템 전체 설치에는 더 심각한 문제가 있습니다. sudo pip uninstall을 사용하여 이 상황에서 복구하려고 하면 시스템의 패키지 관리자가 제공한 패키지를 제거하게 될 수 있습니다. 실제로 패키지를 단순히 업그레이드하는 경우에도 이런 일이 발생할 수 있습니다. pip는 OS가 제공한 패키지의 이전 버전을 제거하려고 하기 때문입니다. 이 시점에는 시스템에 남아 있는 소프트웨어만으로 시스템을 일관된 상태로 복구하지 못할 수도 있습니다.

지난 수년간 배포판의 패키지를 사용하지 않을 때 Python 라이브러리나 애플리케이션을 설치하는 가장 좋은 방법은 가상 환경을 사용하는 것이라는 합의가 형성되었습니다. 이 방식은 PyPA의 virtualenv 프로젝트를 통해 널리 알려졌으며, 이제 이 방식의 간단한 버전을 Python 표준 라이브러리에서 venv로 사용할 수 있습니다. Python 패키지를 virtualenv에 설치하면 정규화되지 않은 /usr/bin/python3 인터프리터에 해당 패키지가 표시되지 않으며 시스템 소프트웨어가 손상되는 것도 방지할 수 있습니다.

그러나 경우에 따라 배포판 외부에서 Python 패키지를 설치하여 배포판이 제공하는 명령의 동작에 영향을 주는 것이 유용하고 의도적인 일일 수 있습니다. 이는 Python 언어 확장을 작성하는 메커니즘을 갖춘 Sphinx나 Ansible과 같은 소프트웨어에서 흔히 발생합니다. 사용자는 유료 지원이나 보안 업데이트를 위해 배포판의 기본 소프트웨어 버전을 사용하면서 PyPI에서 작은 확장을 설치하고 싶을 수 있으며, 해당 확장을 기본 시스템의 소프트웨어에서 가져올 수 있기를 원할 수 있습니다.

이 경우에도 운영 체제가 예상하는 것보다 최신인 종속성을 설치하거나 그 밖의 방식으로 애플리케이션의 동작에 부정적인 영향을 줄 위험은 여전히 있지만, 운영 체제에서 파일을 제거할 위험까지 감수할 필요는 없습니다. pip와 같은 도구는 특별히 요청된 경우 시스템의 패키지 관리자가 소유한 파일을 삭제하지 않고 기본 sys.path의 어떤 디렉터리에든 패키지를 설치할 수 있어야 합니다.

따라서 이 PEP는 두 가지를 제안합니다.

첫째, Python 인터프리터의 배포자가 해당 인터프리터의 패키지가 Python 외부의 수단을 통해 관리되는 것으로 표시하는 방법을 제안합니다. 이에 따라 pip와 같은 Python 전용 도구는 특별히 재정의되지 않는 한 인터프리터의 전역 sys.path에 설치된 패키지를 어떤 방식으로도(추가, 업그레이드/다운그레이드 또는 제거) 변경해서는 안 됩니다. 또한 배포자가 대안으로 가상 환경을 사용하는 방법을 나타낼 수 있는 수단도 제공합니다.

이는 선택적 활성화 메커니즘입니다. 기본적으로 업스트림 소스에서 컴파일된 Python 인터프리터에는 이러한 표시가 없으므로, 자체 컴파일한 인터프리터나 인터프리터에 명시적으로 표시하지 않은 배포판과 함께 pip install을 실행해도 항상 그래 왔던 것처럼 작동합니다.

둘째, 인터프리터의 전역 컨텍스트에 패키지를 설치할 때(표시되지 않은 인터프리터에 설치하거나 표시를 재정의하는 경우) Python 전용 패키지 관리자는 파일을 생성할 sysconfig 스킴의 디렉터리 내에서만 파일을 수정하거나 삭제해야 합니다. 이를 통해 Python 인터프리터 배포자는 두 디렉터리를 설정할 수 있습니다. 하나는 자체적으로 관리하는 패키지용이고, 다른 하나는 최종 사용자가 설치하는 관리되지 않는 패키지용이며, 관리되지 않는 패키지를 설치해도 외부 패키지 관리자가 소유한 파일이 삭제되거나 덮어쓰이지 않도록 보장할 수 있습니다.

근거

다음 절에서 자세히 설명하듯이, 첫 번째 동작 변경에는 EXTERNALLY-MANAGED라는 이름의 마커 파일을 생성하는 과정이 포함됩니다. 이 파일이 존재한다는 것은 가상 환경이 아닌 환경의 패키지 설치가 배포판의 패키지 관리자와 같이 Python 외부의 어떤 수단에 의해 관리된다는 것을 나타냅니다. 이 파일은 기본 sysconfig 스킴의 stdlib 디렉터리에 존재하도록 지정되며, 특정 sys.path상의 위치가 아니라 인터프리터 또는 설치 전체를 표시합니다. 그 이유는 앞서 확인했듯이 외부에서 관리되는 Python을 손상시킬 위험이 있는 서로 관련된 두 가지 문제가 있기 때문입니다. 시스템 전체에 호환되지 않는 새 패키지 버전을 설치할 수 있고(예: sudo pip install을 사용하여), 사용자 계정에만 패키지를 설치할 수도 있지만 표준 Python 명령의 sys.path에 포함된 위치에 설치할 수 있습니다(예: pip install --user를 사용하여). 마커 파일이 시스템 전체의 site-packages 디렉터리에 있었다면 두 번째 경우에도 명확히 적용된다고 보기 어려웠을 것입니다. Alternatives 섹션에서는 가능한 위치에 대해 추가로 논의합니다.

두 번째 동작 변경은 이러한 문제를 이미 경험한 배포판에 존재하는 sysconfig 설정을 활용하며, 특히 Python 전용 패키지 관리자가 외부 패키지 관리자가 소유한 파일을 삭제하거나 덮어쓰는 문제를 다룹니다.

사용 사례

이 PEP에서 변경하는 동작은 가능한 한 많은 사용 사례에서 “올바르게 작동하도록” 의도되었습니다. 이 절에서는 여러 대표적인 사용 사례 및 컨텍스트에 대해 이 PEP에서 지정한 변경 사항을 살펴봅니다. 구체적으로, 이 PEP에 의해 변경될 수 있는 다음 두 가지 동작에 대해 질문합니다.

  1. 이 PEP를 구현한 후에는 pip install과 같은 Python 전용 설치 도구가 기본적으로 설치를 허용합니까?
  2. 그러한 도구를 실행하는 경우, 해당 컨텍스트에서 외부(Python 전용이 아닌) 패키지 관리자가 제공한 패키지(예: 배포판 패키지 관리자)를 삭제할 수 있어야 합니까?

(간단히 하기 위해 이 절에서는 Python 전용 설치 도구로 pip를 다루지만, 분석 결과는 다른 Python 전용 패키지 관리 도구에도 동일하게 적용됩니다.)

다음 표는 아래에서 자세히 논의하는 사용 사례를 요약합니다.

사례 설명 pip install 허용 외부에서 설치한 패키지 삭제 허용
1 패치되지 않은 CPython 현재 예, 계속 예 현재 예, 계속 예
2 배포판 /usr/bin/python3 현재 예, 아니요로 변경됨(배포판이 마커 파일을 추가한다고 가정) 현재 예(Debian 제외), 아니요로 변경됨
3 가상 환경의 배포판 Python 현재는 예이며, 계속 예입니다. 외부에서 설치된 패키지가 없습니다.
4 --system-site-packages를 사용하는 가상 환경의 배포판 Python 현재는 예이며, 계속 예입니다. 현재는 아니며, 계속 아닙니다.
5 Docker의 배포판 Python 현재는 예이며, 아니요가 됩니다(배포판에서 마커 파일을 추가한다고 가정합니다). 현재는 예이며, 아니요가 됩니다.
6 Conda 환경 현재는 예이며, 계속 예입니다. 현재는 예이며, 계속 예입니다.
7 개발자 대상 배포판 현재는 예이며, 아니요가 됩니다(마커 파일을 추가한다고 가정합니다). 현재는 대개 예이며, 아니요가 됩니다(sysconfig를 필요한 대로 구성한다고 가정합니다).
8 패키지를 빌드하는 배포판 현재는 예이며, 계속 예로 유지할 수 있습니다. 현재는 예이며, 아니요가 됩니다.
9 배포판 Python 표준 라이브러리에서 복사한 PYTHONHOME 현재는 예이며, 아니요가 됩니다. 현재는 예이며, 아니요가 됩니다.
10 업스트림 Python 표준 라이브러리에서 복사한 PYTHONHOME 현재는 예이며, 계속 예입니다. 현재는 예이며, 계속 예입니다.

위의 사용 사례를 더 자세히 설명하면 다음과 같습니다.

  1. 특별한 sysconfig 구성이나 패치가 없고 마커 파일도 없는 표준 패치 미적용 CPython입니다. 이 PEP는 해당 동작을 변경하지 않습니다.

    이러한 CPython은 이 PEP와 관계없이 동일한 시스템에 배포판이 설치한 Python과 겹치는 방식으로 설치해서는 안 됩니다. 예를 들어 Python을 /usr/bin에 제공하는 운영 체제에서는 ./configure --prefix=/usr로 빌드한 사용자 지정 CPython을 설치해서는 안 됩니다. 그렇지 않으면 배포판의 일부 파일을 덮어쓰고, 결국 배포판이 설치된 파일 중 일부를 덮어쓰게 됩니다. 대신 설치 위치는 별도의 디렉터리여야 합니다(예를 들어 /usr/local, /opt 또는 홈 디렉터리).

    따라서 이러한 CPython에는 자체 stdlib 디렉터리와 배포판이 설치한 어떤 Python과도 겹치지 않는 자체 sysconfig 스킴이 있다고 가정할 수 있습니다. 그러므로 운영 체제가 설치한 패키지는 여기에서 보이지도 않고 관련도 없습니다.

    이 경우 “외부 설치” 패키지라는 개념이 있다면, 이는 운영 체제 외부에 존재하며 일반적으로 이 CPython을 빌드하고 설치한 사람이 관리하는 것입니다. 설치 프로그램은 마커 파일을 추가하거나 sysconfig 스킴을 수정하지 않기로 선택했으므로 현재 동작을 선택한 것이며, pip install은 이 CPython에서 사용할 수 있는 모든 패키지를 제거할 수 있습니다.

  2. 우리의 Recommendations for distros를 따르는 배포판의 /usr/bin/python3로, root 권한으로 pip install을 실행하거나 pip install --user를 실행하는 경우입니다.

    이러한 권장 사항에는 stdlib 디렉터리에 마커 파일을 제공하여 기본적으로 pip install을 방지하고, 배포판이 제공하는 패키지를 기본 sysconfig 스킴이 아닌 위치에 배치하여 root 권한의 pip가 해당 위치에 쓰지 못하게 하는 내용이 포함됩니다.

    Debian, Fedora 및 그 파생 배포판을 비롯한 많은 배포판은 이미 후자를 시행하고 있습니다.

    Debian 및 그 파생 배포판에서는 현재 pip install이 배포판 설치 패키지를 삭제하지 않습니다. Debian이 이를 방지하는 patch to pip to prevent this를 포함하고 있기 때문입니다. 따라서 이러한 배포판에서는 이 PEP가 동작 변경이 아닙니다. 단지 해당 동작을 더 이상 Debian에 국한되지 않는 방식으로 표준화하여 업스트림 pip에 포함할 수 있도록 할 뿐입니다.

    (Debian 또는 그 파생 배포판에서 외부 설치 패키지가 삭제되었다는 사용자 보고를 본 적이 있습니다. 이는 사용자가 이전에 sudo pip install --upgrade pip를 실행하여 이제 Debian 패치가 적용되지 않은 /usr/bin/pip의 버전을 사용하고 있기 때문이라고 추측합니다. 업스트림 패키지 설치 프로그램에서 이 동작을 표준화하면 이 문제를 해결할 수 있습니다.)

  3. 가상 환경(venv 또는 virtualenv에서 생성된 환경) 내부에서 사용되는 배포판 Python입니다.

    가상 환경 내부에서는 모든 패키지가 해당 환경의 소유입니다. pip, setuptools 등이 환경에 설치된 경우에도 해당 환경에 특화된 도구로 관리되며, 관리되어야 합니다. 시스템에서 관리되는 것이 아닙니다.

  4. --system-site-packages를 사용하는 가상 환경 내부에서 사용되는 배포판 Python입니다. 이는 앞의 경우와 유사하지만 전역 sys.path에 있는 모든 항목이 보인다는 점에서 명시적으로 언급할 가치가 있습니다.

    현재 “pip가 외부에서 설치된 패키지를 삭제합니까?”라는 질문에 대한 답은 아니요입니다. pip는 가상 환경에서 실행되면서 해당 환경 외부의 패키지를 삭제하려고 할 때 적용되는 특수 처리를 갖추고 있기 때문입니다. 이 PEP 이후에도 답은 아니요이지만, 근거는 더 일반적이 됩니다. 시스템 사이트 패키지는 해당 환경의 패키지 관리에 사용되는 어떤 sysconfig 스킴에도 속하지 않게 됩니다.

  5. 단일 애플리케이션 컨테이너 이미지(예: Docker 컨테이너) 내부에서 사용되는 배포판 Python입니다. 이 사용 사례에서는 시스템 소프트웨어를 손상시킬 위험이 더 낮습니다. 일반적으로 컨테이너에서는 단일 애플리케이션만 실행되고, 컨테이너를 다시 빌드할 수 있으며 실행 중인 시스템을 복구하기 위해 애쓸 필요가 없으므로 영향도 더 작기 때문입니다. 또한 정규화되지 않은 RUN pip install ... 문 등을 포함하는 기존 Dockerfile이 매우 많으므로, 이를 손상시키지 않는 것이 좋습니다. 따라서 기본 컨테이너 이미지 빌더는 기반 운영 체제가 기본적으로 마커 파일을 제공하더라도 마커 파일이 존재하지 않도록 해야 할 수 있습니다.

    작은 동작 변경이 있습니다. 현재는 root 권한으로 실행된 pip가 외부 설치 패키지를 삭제하지만, 이 PEP 이후에는 삭제하지 않습니다. 이를 재정의하는 방법은 제안하지 않습니다. 그러나 기본 이미지는 일반적으로 최소한으로 구성되므로 패키지를 단순히 제거할 사용 사례는 많지 않을 것입니다(특히 배포판 자체 도구를 사용하지 않는 경우에는 더욱 그렇습니다). 일반적인 경우는 pip가 패키지를 업그레이드하려는 경우이며, 이전에는 이 과정에서 이전 버전을 삭제했을 것입니다(Debian 제외). 이 변경 이후에도 이전 버전은 디스크에 남지만, pip는 여전히 외부 설치 패키지를 shadow하며, 이것으로 실제 사용 시 호환성을 깨뜨리는 변경이 되지 않기에 충분하다고 생각합니다. Python import 문은 여전히 새로 설치된 패키지를 가져옵니다.

    이를 수행할 방법이 필요해지는 경우, 배포판 자체가 사용하는 sysconfig 스킴에 설치 도구가 접근할 방법을 배포판에서 문서화할 것을 제안합니다. 자세한 논의는 Recommendations for distros 섹션을 참조하십시오.

    이 PEP의 저자들은 단일 애플리케이션 컨테이너 이미지에서도 배포판이 설치한 Python 인터프리터와 함께 가상 환경을 사용하는 것이 여전히 좋은 방법이라고 생각합니다. 단일 애플리케이션을 실행하더라도 해당 애플리케이션은 Python으로 구현된 OS 명령을 실행할 수 있으며, Python 전용 도구를 사용하여 배포판에서 제공한 Python 패키지를 설치하거나 업그레이드했다면 해당 명령이 중단될 수 있습니다.

  6. Conda는 Conda 저장소에서 사용할 수 없는 소프트웨어를 설치하기 위해 pip와 같은 비-conda 도구를 사용하는 것을 특별히 지원합니다. 이 맥락에서 Conda는 외부 패키지 관리자/배포판 역할을 하고, pip는 Python 전용 패키지 관리자 역할을 합니다.

    어떤 의미에서는 Conda가 자체적으로 Python 인터프리터를 설치하므로 첫 번째 경우와 유사합니다.

    이 PEP가 Conda에 어떠한 변경도 요구하지 않는다고 생각하며, 이 PEP의 변경 사항을 구현한 pip 버전은 Conda 환경 내부에서도 현재와 같이 계속 동작합니다. (그렇기는 하지만, 다른 배포판에서도 별도의 스킴을 사용하는 것이 좋은 생각인 이유와 동일한 이유로, pip로 설치한 소프트웨어와 Conda로 설치한 소프트웨어에 별도의 sysconfig 스킴을 사용할지 고려해 볼 가치가 있습니다.)

  7. “개발자 대상 배포판”이란 배포판에서 Python 또는 다른 언어를 직접 사용하는 사용자들이 라이브러리를 추가하려는 경우 배포판 자체를 변경하도록 기대되거나 권장되는 특정 유형의 배포판을 의미합니다. 일반적인 예로는 소프트웨어 개발 회사의 사설 “모노리포”가 있으며, 여기서는 단일 저장소가 서드파티 소프트웨어와 사내 소프트웨어를 모두 빌드하고, 배포판의 Python 인터프리터를 직접 사용하는 사람들은 일반적으로 해당 사내 소프트웨어를 작성하는 소프트웨어 개발자입니다. Nixpkgs_와 같은 사용자 수준 패키지 관리자도 포함될 수 있습니다. 이러한 패키지 관리자는 Python 개발자인 Nix 사용자에게 Nix용으로 소프트웨어를 패키징하도록 권장하기 때문입니다.

    이러한 경우 배포판은 pip install을 시도할 때 새 패키지를 추가하기 위한 배포판 자체의 기능을 사용하도록 안내하고, 관련 문서 링크를 함께 제공할 수 있습니다.

    배포판이 배포판의 Python 인터프리터에서 가상 환경을 생성하는 것을 지원하거나 권장한다면, 가상 환경을 올바르게 설정하는 방법에 대한 사용자 지정 지침이 있을 수도 있습니다(예를 들어 Nixpkgs가 그러합니다).

  8. 배포판 Python용 배포판 Python 패키지를 빌드할 때(사례 2), 배포판의 패키지 빌드 프로세스의 일부로 pip install을 사용할 수 있으면 유용할 수 있습니다. (예를 들어 xyz용 sdist/소스 tarball 내부에서 pip install .을 사용하여 python-xyz RPM을 빌드하는 경우를 생각해 보십시오.) 배포판은 installer_와 같이 더 대상이 명확하면서도 여전히 Python 전용인 설치 도구를 사용하고자 할 수도 있습니다.

    이 경우 빌드 프로세스는 pip install이 작동하도록 마커 파일을 억제할 방법을 찾아야 하며, Python 전용 도구가 제공된 기본값 대신 배포판의 sysconfig 스킴을 사용하도록 지정해야 할 가능성이 높습니다. 이를 구현하는 방법에 관한 자세한 논의는 Recommendations for distros 섹션을 참조하십시오.

    이 PEP의 결과로 pip는 더 이상 시스템에 이미 설치된 패키지를 제거할 수 없습니다. 그러나 패키지 빌드 프로세스에는 시스템의 다른 파일을 삭제하는 지침을 포함해서는 안 되며(일반적으로 포함할 수도 없으며), 자체 파일만 패키징할 수 있으므로 이러한 동작 변경은 문제가 되지 않습니다.

  9. 가상 환경과는 달리 대체 Python 환경을 설정하기 위해 PYTHONHOME을 사용하며, PYTHONHOME이 배포판 Python에서 직접 복사한 일부 디렉터리로 설정된 배포판 Python입니다(예: cp -a /usr/lib/python3.x pyhome/lib).

    수정 사항이 없다고 가정하면 동작은 기본 배포판 Python과 동일합니다(사례 2). 따라서 동작이 변경됩니다. 기본적으로 더 이상 pip install을 할 수 없으며, 이를 재정의하더라도 외부에서 설치된 패키지(즉, OS에서 복사되어 OS가 관리하는 sys.path 항목에 존재하는 Python 패키지)를 더 이상 삭제하지 않습니다.

    PYTHONHOME이 배포판 Python의 단순한 복사본이라면 배포판 Python처럼 동작해야 하므로 이러한 동작 변경은 정당화될 수 있어 보입니다.

  10. 호환되는 수정되지 않은 업스트림 Python에서 가져온 PYTHONHOME과 함께 사용되는 배포판 Python(또는 모든 Python 인터프리터)입니다.

    이 PEP의 동작 변경은 표준 라이브러리의 파일(stdlib의 마커 파일과 sysconfig 모듈의 동작)에 따라 결정되므로, 동작은 수정되지 않은 업스트림 CPython과 동일합니다(사례 1).

사양

외부 패키지 관리자를 사용하는 인터프리터로 표시하기

Python 전용 패키지 설치 도구(즉, apt와 같은 외부 도구가 아닌 pip와 같은 도구)가 특정 Python 컨텍스트에 패키지를 설치하기 전에 기본적으로 다음 검사를 수행해야 합니다.

  1. 가상 환경 외부에서 실행되고 있습니까? sys.prefix == sys.base_prefix 인지 여부로 이를 확인할 수 있습니다(Backwards Compatibility 참조).
  2. sysconfig.get_path("stdlib", sysconfig.get_default_scheme())이 식별하는 디렉터리에 EXTERNALLY-MANAGED 파일이 있습니까?

이 두 조건이 모두 참이면 설치 도구는 가상 환경 외부에서는 이 Python 인터프리터의 디렉터리에 패키지를 설치할 수 없음을 나타내는 오류 메시지와 함께 종료해야 합니다.

설치 프로그램은 --break-system-packages와 같은 명령줄 플래그를 사용하여 사용자가 이러한 규칙을 재정의할 방법을 제공해야 합니다. 이 옵션은 기본적으로 활성화되어서는 안 되며, 사용에 위험이 따른다는 의미를 담아야 합니다.

EXTERNALLY-MANAGED 파일은 표준 라이브러리의 configparser 모듈로 구문 분석할 수 있도록 만들어진 INI 스타일 메타데이터 파일입니다. 파일을 UTF-8 인코딩을 사용하여 configparser.ConfigParser(interpolation=None)으로 구문 분석할 수 있고 [externally-managed] 섹션이 포함되어 있다면, 설치 프로그램은 파일에 지정된 오류 메시지를 찾아 자체 오류의 일부로 출력해야 합니다. locale.getlocale(locale.LC_MESSAGES)가 반환하는 튜플의 첫 번째 요소, 즉 언어 코드가 None이 아니라면, 설치 프로그램은 언어 코드가 뒤따르는 Error-라는 키의 값으로 오류 메시지를 찾아야 합니다. 해당 키가 존재하지 않고 언어 코드에 밑줄 또는 하이픈이 포함되어 있다면, 설치 프로그램은 밑줄 또는 하이픈 앞의 언어 코드 부분이 뒤따르는 Error-라는 키를 찾아야 합니다. 둘 중 어느 것도 찾을 수 없거나 언어 코드가 None이라면, 설치 프로그램은 단순히 Error라는 이름의 키를 찾아야 합니다.

설치 프로그램이 파일에서 오류 메시지를 찾을 수 없다면(파일을 구문 분석할 수 없거나 적절한 오류 키가 존재하지 않기 때문), 자체적으로 미리 정의된 오류 메시지를 사용해야 하며, 이 메시지는 패키지를 설치하기 위해 사용자가 가상 환경을 생성하도록 제안해야 합니다.

Python에 특화되지 않은 패키지 관리자로 Python 패키지의 sys.path에서 라이브러리를 관리하는 소프트웨어 배포자는 일반적으로 표준 라이브러리 디렉터리에 EXTERNALLY-MANAGED 파일을 포함해야 합니다. 예를 들어 Debian은 /usr/lib/python3.9/EXTERNALLY-MANAGED에 다음과 같은 내용으로 구성된 파일을 포함할 수 있습니다.

[externally-managed]
Error=To install Python packages system-wide, try apt install
 python3-xyz, where xyz is the package you are trying to
 install.

 If you wish to install a non-Debian-packaged Python package,
 create a virtual environment using python3 -m venv path/to/venv.
 Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
 sure you have python3-full installed.

 If you wish to install a non-Debian packaged Python application,
 it may be easiest to use pipx install xyz, which will manage a
 virtual environment for you. Make sure you have pipx installed.

 See /usr/share/doc/python3.9/README.venv for more information.

이는 패키지를 설치하려는 사용자에게 유용하고 해당 배포판과 관련된 정보를 제공합니다. 선택적으로 동일한 파일에 번역을 제공할 수도 있습니다.

Error-de_DE=Wenn ist das Nunstück git und Slotermeyer?

 Ja! Beiherhund das Oder die Virtualenvironment gersput!

생성 후 업데이트되지 않는 단일 애플리케이션 컨테이너 이미지와 같은 특정 상황에서는 배포자가 EXTERNALLY-MANAGED 파일을 포함하지 않을 수 있으며, 이렇게 하면 사용자가 이 규칙을 수동으로 재정의하지 않고도 원하는 것을 설치할 수 있습니다(현재도 가능한 것처럼).

대상 sysconfig 체계에만 쓰기

일반적으로 Python 패키지 설치 프로그램은 sysconfig 표준 라이브러리 패키지가 반환하는 체계의 디렉터리에 설치합니다. 보통 이는 sysconfig.get_default_scheme()이 반환하는 체계이지만, 구성에 따라(예: pip install --user) 다른 체계를 사용할 수도 있습니다.

설치 프로그램이 sysconfig 체계에 설치할 때마다, 이 PEP에서는 해당 체계 외부의 파일을 절대로 수정하거나 삭제하지 않도록 규정합니다. 예를 들어 패키지를 업그레이드할 때 해당 패키지가 그 체계 외부의 디렉터리(다른 체계의 디렉터리일 수 있음)에 이미 설치되어 있다면, 기존 파일을 그대로 두어야 합니다.

업그레이드 중에 설치 프로그램이 결국 기존 설치를 가리게 된다면, 실행이 끝날 때 경고를 출력하도록 권장합니다.

설치 프로그램이 sysconfig 체계 외부의 위치에 설치하는 경우(예: pip install --target), 이 하위 절은 적용되지 않습니다.

배포판을 위한 권고 사항

이 절은 규범적인 내용이 아닙니다. 이 절에서는 배포판이 특별한 이유로 달리해야 하는 경우가 아니라면 따라야 한다고 생각하는 모범 사례를 제시합니다.

설치를 외부 관리 대상으로 표시하기

배포판은 stdlib 디렉터리에 EXTERNALLY-MANAGED 파일을 생성해야 합니다.

사용자가 가상 환경을 사용하도록 안내하기

파일에는 배포판의 패키지 관리자를 통해 시스템 전체 패키지를 설치하는 방법과 가상 환경을 설정하는 방법을 모두 알려 주는 유용하고 해당 배포판과 관련된 오류 메시지가 포함되어야 합니다. 배포판이 python3 명령을 사용할 수 있지만 python3 -m venv가 작동하지 않는 상태에서 사용자에게 자주 사용된다면(특히 pip 또는 get-pip을 사용할 수 있는 경우), 메시지에는 python3 -m venv를 올바르게 작동시키는 방법을 명확하게 설명해야 합니다.

Python 언어 애플리케이션을 설치하는 도구인 pipx_를 패키징하고 오류 메시지에서 이를 제안하는 방안을 고려하십시오. pipx는 해당 애플리케이션만을 위한 가상 환경을 자동으로 생성하므로, 배포판에서 제공되지 않는 Python 언어 소프트웨어를 설치하려 하지만 Python 사용자는 아닌 최종 사용자에게는 훨씬 더 나은 기본 선택입니다. 배포판에 pipx를 패키징하면 시스템 패키지를 손상시키는 것을 피하기 위해 사용자에게 pip install --user --break-system-packages pipx를 실행하도록 안내하는 아이러니를 피할 수 있습니다. 최종 사용자를 위한 배포판의 Python 패키지 또는 환경(예: Fedora의 python3 또는 Debian의 python3-full)이 pipx에 의존하도록 구성하는 방안을 고려하십시오.

컨테이너 이미지에 마커 파일을 유지하기

단일 애플리케이션 컨테이너(예: Docker 컨테이너 이미지)용 공식 이미지를 생성하는 배포판은 EXTERNALLY-MANAGED 파일을 유지해야 하며, 해당 이미지를 사용하는 사용자가 이미지 내부에 패키지 업데이트를 설치하더라도 파일이 사라지지 않도록 하는 방식을 사용하는 것이 바람직합니다(예: RUN apt-get dist-upgrade).

배포판 디렉터리와 로컬 디렉터리를 분리하여 생성하십시오.

배포판은 시스템 인터프리터의 sys.path에 서로 분리된 두 경로를 배치해야 합니다. 하나는 배포판이 설치한 패키지용이고 다른 하나는 로컬 시스템 관리자가 설치한 패키지용으로 하며, sysconfig.get_default_scheme()이 후자의 경로를 가리키도록 구성해야 합니다. 이렇게 하면 pip와 같은 도구가 배포판이 설치한 패키지를 수정하지 않습니다. 로컬 시스템 관리자용 경로는 sys.path에서 배포판 경로보다 앞에 와야 로컬 설치가 배포판 패키지보다 우선합니다.

예를 들어 Fedora와 Debian(및 그 파생 배포판)은 모두 로컬 설치 패키지에 /usr/local을 사용하고 배포판 설치 패키지에 /usr을 사용하는 방식으로 이 분리를 구현합니다. Fedora는 /usr/local/lib/python3.x/site-packages/usr/lib/python3.x/site-packages를 사용합니다. (Debian은 로컬에서 컴파일한 Python 인터프리터와 추가로 분리하기 위한 계층으로 /usr/local/lib/python3/dist-packages/usr/lib/python3/dist-packages를 사용합니다. 즉, 업스트림 CPython을 빌드하여 /usr/local/bin에 설치하면 /usr/local/lib/python3/site-packages를 확인하지만, Debian은 로컬에서 빌드한 인터프리터를 통해 설치한 패키지가 배포판 인터프리터의 sys.path에 나타나지 않도록 해야 합니다.)

/usr/local/usr의 분리는 PATH 환경 변수에 일반적으로 /usr/local/bin:/usr/bin이 포함되고 배포판 소프트웨어가 아닌 소프트웨어가 기본적으로 /usr/local에 설치되는 방식과 유사하다는 점에 유의하십시오. 이 분리는 Filesystem Hierarchy Standard에서 권장합니다.

이를 수행하는 방법은 두 가지입니다. 하나는 Python 라이브러리를 직접 빌드하고 패키징하는 경우(예: 패키징 도우미가 PEP 517으로 빌드된 휠의 압축을 풀거나 setup.py install을 호출하는 경우), 해당 도구가 sysconfig 스킴에는 포함되지 않지만 sys.path에는 포함된 디렉터리를 사용하도록 구성하는 것입니다.

다른 하나는 패키지 빌드 내부에서 실행할 때와 설치된 시스템에서 실행할 때 기본 sysconfig 스킴이 변경되도록 구성하는 것입니다. bpo-43976_의 sysconfig 사용자 지정 후크를 사용하면 이를 쉽게 수행할 수 있습니다(승인 및 구현된 후). 패키징 도구가 환경 변수나 감지 가능한 다른 구성을 설정하도록 하고, 패키지 빌드 내부에서 호출될 때 다른 스킴을 반환하는 get_preferred_schemes 함수를 정의하십시오. 그러면 배포판 패키징의 일부로 pip install을 사용할 수 있습니다.

pip가 특정 스킴을 대상으로 실행되도록 지시하는 --scheme=... 옵션을 추가할 것을 제안합니다. (pip가 현재 스킴을 결정하는 방법은 아래의 Implementation Notes를 참조하십시오.) 이 기능을 사용할 수 있게 되면 로컬 테스트 및 실제 패키징에 필요할 경우, pip install --scheme=posix_distro와 같이 실행하여 패키지를 배포판의 위치에 명시적으로 설치할 수 있습니다(get_preferred_schemes를 우회합니다). 또한 꼭 필요한 경우 pip uninstall --scheme=posix_distro를 사용하여 pip로 시스템 관리 디렉터리에서 패키지를 제거할 수도 있으며, 이는 Rationale_의 사용 사례 5에서 발생하는 (바라건대 이론적인) 회귀 문제를 해결합니다.

pip로 패키지를 설치하려면 pip가 실행될 수 있도록 EXTERNALLY-MANAGED 마커 파일을 억제하거나 명령줄에서 해당 파일을 무시하도록 재정의해야 합니다. 컨테이너 이미지에서와 마찬가지로 빌드 chroot에서도 마커 파일을 억제하는 동일한 방법을 사용하고 싶을 수 있습니다.

이를 자동으로 설정하는 것(빌드 환경에서 마커 파일을 억제하고 get_preferred_schemes가 자동으로 배포판의 스킴을 반환하도록 하는 것)의 장점은 별도 옵션이 없는 pip install이 패키지 빌드 내부에서 작동한다는 점입니다. 이는 일반적으로 내부에서 우연히 pip install을 호출하는 수정되지 않은 업스트림 빌드 스크립트도 올바르게 작동한다는 의미입니다. 물론 패키징 프로세스에서 항상 pip install --scheme=posix_distro --break-system-packages를 호출하도록 보장할 수도 있으며, 이 방법도 작동합니다.

여기에서 최선의 접근 방식은 배포판의 패키징 관례와 메커니즘에 크게 좌우됩니다.

마찬가지로 임포트 가능한 Python 코드용이 아닌 sysconfig 경로, 즉 include, platinclude, scriptsdata에도 두 가지 변형이 있어야 합니다. 하나는 배포판 패키지 소프트웨어용이고 다른 하나는 로컬 설치 소프트웨어용이어야 하며, 배포판은 두 변형을 모두 사용할 수 있도록 설정해야 합니다. 예를 들어 일반적인 FHS 준수 배포판은 기본 스킴의 include/usr/local/include를 사용하고 배포판 패키지 헤더에 /usr/include를 사용하며 둘 다 컴파일러의 검색 경로에 배치합니다. 또한 기본 스킴의 scripts에는 /usr/local/bin을 사용하고 배포판 패키지 진입점에는 /usr/bin을 사용하며 둘 다 $PATH에 배치합니다.

하위 호환성

이러한 모든 메커니즘은 새로운 배포판 릴리스와 pip와 같은 도구의 새 버전에만 제안됩니다.

특히 주요 버전 개념이 있는 배포판에는 새 주요 버전에서만 마커 파일을 추가하거나 sysconfig 스킴을 변경할 것을 강력히 권장합니다. 그렇지 않으면 기존 시스템에서 Python 전용 패키지 관리자를 통해 설치한 소프트웨어를 이제 관리할 수 없게 될 위험이 있습니다(재정의 옵션 없이는). 롤링 릴리스 배포판의 경우 가능하다면 새 Python 마이너 버전에서만 마커 파일을 추가하거나 sysconfig 스킴을 변경하십시오.

패키지 설치 도구의 특정 하위 호환성 문제로는 최신 버전의 도구가 설치된, 이전 버전의 virtualenv로 생성한 환경을 관리하는 일이 있을 가능성이 큽니다. 이제 “가상 환경”은 상당히 정확하게 정의됩니다. pyvenv.cfg 메커니즘을 사용하며, 이로 인해 sys.base_prefix != sys.prefix가 됩니다. 그러나 사용자가 이전 버전의 virtualenv로 생성한 오래된 가상 환경을 가지고 있을 가능성은 있습니다. 현재 pip는 Python 3.6 이상을 지원하고, 이는 다시 virtualenv 15.1.0 이상에서 지원되므로 이러한 상황이 가능합니다. 이전 버전의 virtualenv에서는 대신 새 속성인 sys.real_prefix를 설정하는 메커니즘을 사용하며, 가상 환경에 대한 표준 라이브러리 지원을 사용하지 않으므로 sys.base_prefixsys.prefix와 같습니다. 따라서 가상 환경을 안정적으로 감지하는 논리는 대략 다음과 같습니다.:

def is_virtual_environment():
    return sys.base_prefix != sys.prefix or hasattr(sys, "real_prefix")

보안 관련 영향

이 기능의 목적은 보안 경계를 구현하는 것이 아니라, 선의의 변경으로 인해 사용자의 환경이 예기치 않게 손상되는 것을 방지하는 것입니다. 다시 말해, 이 PEP가 가상 환경 외부에서 pip install을 제한하는 이유는 그렇게 할 수 있는 것이 보안 위험이기 때문이 아니라, “이를 수행하는 명백한 방법은 하나여야 하며–가능하면 오직 하나여야 하며–” 그 방법은 가상 환경을 사용하는 것이어야 하기 때문입니다. 가상 환경 외부에서 pip install을 사용하는 것은 거의 언제나 잘못된 방법임에도 지나치게 명백합니다.

사용자가 보안상의 이유로 sudo pip install이나 pip install --user를 사용하여 sys.path에 파일을 추가할 수 없어야 하는 경우에는, 사용자가 쓸 수 있는 파일에 대한 접근 제어 규칙이나 해당 프로그램을 위해 명시적으로 보안이 설정된 sys.path를 통해 구현해야 합니다. 이 PEP의 메커니즘 중 어느 것도 그러한 시나리오를 해결하는 방법으로 해석해서는 안 됩니다.

이러한 이유로, 마커 파일이 있는 상태에서 설치를 시도하는 것은 보안 사고가 아니며, 이에 대해 감사 이벤트를 발생시킬 필요도 없습니다. 호출한 사용자에게 sudo pip install이나 pip install --user를 사용할 정당한 권한이 있다면 Python 외부에서 동일한 설치를 완전히 수행할 수 있으며, 그러한 권한이 정당하게 없다면 이는 이 PEP의 범위를 벗어나는 문제입니다.

마커 파일 자체는 표준 라이브러리 디렉터리에 있으며, 이곳은 신뢰할 수 있는 위치입니다(즉, 특정 설치 관리자가 사용하는 마커 파일에 쓸 수 있는 사람이라면 누구든 설치 관리자 내부에서 임의의 코드를 실행할 수 있다고 추정할 수 있습니다). 따라서 일반적으로 오류 메시지에서 터미널 이스케이프 시퀀스나 기타 잠재적으로 악의적인 콘텐츠를 필터링할 필요는 없습니다.

대안

이와 유사한 제안 중 이 PEP가 거부하거나 보류하는 제안이 여러 가지 있으며, 이는 대체로 Rationale 문서의 사례별 분석에서의 동작을 유지하기 위한 것입니다.

마커 파일

마커 파일을 sys.path에 두어 특정 디렉터리를 Python 전용 패키지 관리자가 쓰지 못하도록 표시해야 합니까? 이렇게 하면 이 PEP가 다루는 두 번째 문제(배포판 소유 파일을 덮어쓰거나 삭제하지 않는 것)에는 도움이 되지만, 첫 번째 문제(호환되지 않는 설치)에는 도움이 되지 않습니다. /usr/lib/python3.x/site-packages에 있는 디렉터리별 마커는 /usr/local/lib/python3.x/site-packages~/.local/lib/python3.x/site-packages로의 설치를 모두 억제하지 못하며, 이 둘은 모두 /usr/bin/python3sys.path에 포함되어 있습니다. 다시 말해, 마커 파일은 단일 디렉터리를 외부에서 관리되는 것으로 표시하는 것으로 해석해서는 안 됩니다(해당 디렉터리가 우연히 sys.path에 있는 디렉터리일지라도 그렇습니다). 마커 파일은 전체 Python 설치를 외부에서 관리되는 것으로 표시합니다.

위 제안의 또 다른 변형으로, 마커 파일을 sys.path에 두고 sys.path의 어느 디렉터리에서든 찾을 수 있으면 설치를 외부에서 관리되는 것으로 표시해야 합니까? 이 접근 방식의 명백한 장점은 가상 환경에서 자동으로 비활성화된다는 점입니다. 안타깝게도 --system-site-packages를 사용하는 가상 환경에서는 시스템 전체의 sys.path가 표시되지만 패키지 설치는 허용되므로 잘못된 동작을 합니다. (가상 환경을 면제하는 규칙을 유지한다면 작동할 수 있지만, 현재 방식보다 이점이 없어 보입니다.)

마커를 단순히 sysconfig스킴의 새 속성으로 만들어야 합니까? 재정의하기 어렵다는 점을 제외하면 개념적으로는 어느 정도 깔끔합니다. 컨테이너 이미지, 패키지 빌드 환경 등이 마커 파일을 쉽게 억제할 수 있도록 만들고자 합니다. 제거할 수 있는 파일은 쉽게 처리할 수 있지만, sysconfig의 코드는 수정하기가 훨씬 어렵습니다.

파일을 /etc에 두어야 합니까? 아닙니다. 다시 말해 이 파일은 특정 Python 설치를 가리키기 때문입니다. 직접 Python을 설치한 사용자는 해당 인터프리터의 전역 컨텍스트 내에 패키지를 설치하고 싶어 할 수 있습니다.

구성 설정을 pip.confdistutils.cfg에 두어야 합니까? 설치를 표시하는 것에 관한 위의 반대 의견과는 별개로, 이 메커니즘은 어느 도구에도 특정되지 않습니다. (사용자가 어떤 Python 설치에서도 실수로 비가상 환경 설치를 수행하지 않도록 pip가 구성 플래그를 또한 구현하는 것은 합리적으로 보이지만, 이는 이 PEP의 범위를 벗어납니다.)

파일을 TOML로 만들어야 합니까? TOML은 패키징에서 인기를 얻고 있지만(예: PEP 517 참조), 아직 표준 라이브러리에 구현이 없습니다. 엄밀히 말하면 이는 장애물이 아닙니다. 배포판은 파일을 읽을 필요 없이 쓰기만 하면 되므로 TOML 라이브러리가 필요하지 않습니다(형식과 관계없이 파일은 어차피 수작업으로 작성될 가능성이 높습니다). 또한 패키징 도구에는 이미 TOML 리더가 있을 가능성이 높습니다. 그러나 현재 INI 형식은 다양한 다른 형태의 패키징 메타데이터(예: pydistutils.cfgsetup.cfg)에 사용되고 있으며, 우리의 요구 사항을 충족하고 표준 라이브러리로 구문 분석할 수 있습니다. 또한 pip 관리자들은 아직 TOML을 사용하는 것을 피하는 편을 선호한다고 밝혔습니다.

파일은 email.message 스타일이어야 합니까? 이 형식은 패키징 메타데이터(예: sdist 및 wheel 메타데이터)에도 사용되고 표준 라이브러리로 구문 분석할 수도 있지만, 여러 줄 항목을 그다지 명확하게 처리하지 못하며 이것이 우리의 주요 사용 사례입니다.

마커 파일은 설치를 허용할지 여부를 판정하는 실행 가능한 Python 코드여야 합니까? 파일을 sys.path에 두는 것에 대한 위의 우려와는 별개로, 파일을 실행 가능하게 만드는 것은 지나치게 강력한 API를 채택하는 것이며 동작을 이해하기 어렵게 만들 위험이 있다는 우려가 있습니다. (bpo-43976_의 get_default_scheme 후크는 실제로 실행 가능하지만, 해당 코드는 인터프리터를 빌드할 때 제공되어야 하며 빌드 후 제공하도록 의도된 것이 아닙니다.)

마커를 재정의할 때 Python 전용 패키지 관리자가 외부 패키지 관리자에 의해 설치된 패키지를 섀도잉하는 것(즉, 같은 이름의 모듈을 설치하는 것)을 허용하지 않아야 합니까? 이렇게 하면 시스템 소프트웨어를 손상시킬 위험을 최소화할 수 있지만, 추가적인 사용자 경험의 복잡성을 감수할 가치가 있는지는 분명하지 않습니다. 시스템 패키지를 섀도잉해야 하는 정당한 사용 사례가 있으며, 이를 허용하는 추가 명령줄 옵션은 더 혼란스러울 것입니다. 한편, 해당 옵션을 전달하지 않는다고 해서 시스템 소프트웨어를 손상시킬 위험이 없어지는 것은 아닙니다. 시스템 소프트웨어가 try: import xyz가 실패하는 것, 제한된 엔트리 포인트 집합을 찾는 것 등에 의존할 수 있기 때문입니다. 이러한 차이를 전달하기는 어려워 보입니다. Python 전용 패키지 관리자가 패키지를 섀도잉할 경우 경고를 출력하는 것은 좋은 생각이라고 보지만, 기본적으로 이를 비활성화할 가치가 있다고는 생각하지 않습니다.

패키지를 설치한 주체와 해당 패키지를 제거할 수 있는지를 확인하기 위해 PEP 376INSTALLER 파일을 사용하면 안 됩니까? 첫째, 이는 특정 패키지에만 해당합니다(패키지의 dist-info 디렉터리에 있습니다). 따라서 위의 일부 대안과 마찬가지로 전체 환경에 대한 정보나 패키지 설치가 허용되는지 여부를 제공하지 않습니다. PEP 627INSTALLER의 프로그래밍 방식 사용을 방지하도록 PEP 376도 업데이트하면서, 이 파일은 “정보 제공 목적으로만 사용되어야 합니다. […] 우리의 목표는 상호 운용하는 도구를 지원하는 것이며, 어떤 도구가 패키지를 설치했는지에 따라 조치를 취하는 것은 그 목표에 어긋납니다.”라고 명시합니다. 마지막으로, PEP 627이 구상하듯이 한 도구가 다른 도구에 의해 설치된 패키지를 처리하는 방법을 아는 것이 정당한 사용 사례인 경우가 있습니다. 예를 들어, conda는 Conda 환경에 pip이 설치한 패키지를 안전하게 제거할 수 있습니다.

사양은 가상 환경 내부에서 패키지 설치를 비활성화할 방법을 제공하지 않는 이유가 무엇입니까? 이에 대한 특별히 강력한 사용 사례(적어도 이 PEP의 목적과 관련된 사례)는 찾기 어렵습니다. 이것이 필요하다면 해당 환경 안에서 pip uninstall pip을 실행하는 것은 충분히 간단하며, 적어도 의도하지 않은 환경 변경을 억제할 수 있습니다(또한 마커 파일은 쉽게 제거할 수 있으므로 이 사양은 의도적인 변경을 비활성화할 방법을 제공하지 않습니다).

시스템 Python

배포판 소프트웨어는 배포판의 site-packages 디렉터리만 sys.path에 두고, 로컬 시스템 관리자의 site-packages뿐 아니라 사용자별 디렉터리도 무시하여 실행해야 하지 않습니까? 이는 가치 있는 생각이며, “시스템 Python” 또는 “플랫폼 Python”이라는 이름으로 여러 변형이 한동안 제안되어 왔습니다(시스템과 별도로 Python을 작성하거나 Python 소프트웨어를 설치하는 최종 사용자를 위한 별도의 “사용자 Python”과 함께 제공하는 방식입니다). 그러나 이는 훨씬 더 많은 사항이 관련된 변경입니다. 우선, 하위 호환성이 깨지는 변경입니다. Motivation 섹션에서 언급했듯이, Sphinx나 Ansible과 같은 배포판 설치 Python 애플리케이션을 해당 애플리케이션의 sys.path에서 사용할 수 있는 로컬 설치 Python 라이브러리와 함께 실행해야 하는 정당한 사용 사례가 있습니다. 로컬 패키지를 무시하도록 일괄 전환하면 이러한 사용 사례가 깨지며, 배포판은 각 애플리케이션이 로컬 설치 라이브러리를 봐야 하는지 여부를 사례별로 분석해야 합니다.

더욱이, Fedora attempted this change and reverted it는 아이러니하게도 해당 변경의 구현이 broke their package manager라는 사실을 발견했습니다. 이러한 경험을 고려하면 배포판이 해당 접근 방식을 안정적으로 구현하기 전에 해결해야 할 세부 사항이 분명히 있으며, 이를 권장하는 PEP는 시기상조일 것입니다.

이 PEP는 배포자의 “시스템 Python” 또는 유사한 제안에 대한 찬반 결정과 무관한, 완전하고 독립적인 변경을 목적으로 합니다. 앞으로 배포판이 “시스템 Python”을 구현하는 것과 양립할 수 있으며, 두 제안이 같은 종류의 문제를 다루더라도 이 PEP를 구현한 후에도 “시스템 Python”과 유사한 것을 구현하는 편을 지지하는 논거는 여전히 존재합니다. 동시에 이 PEP는 더 대상이 명확하고 최소한인 변경을 추구하므로, “시스템 Python”을 채택할 것으로 예상하지 않는(또는 즉시 구현할 것으로 예상하지 않는) 배포자가 구현할 수 있습니다. 이 PEP의 변경 사항은 그 자체로 타당성을 가지며, 향후 제안을 위한 중간 단계가 아닙니다. 이 PEP는 시스템 소프트웨어를 손상시킬 위험을 줄이는 동시에(완전히 제거하지는 않음) 호환성이 깨지는 변경을 최소화합니다(완전히 피하지는 않음). 따라서 위에서 언급한 단점을 수반하는 완전한 “시스템 Python” 구상보다 훨씬 쉽게 구현할 수 있습니다.

이 PEP의 지침, 즉 사용자는 가능한 한 항상 가상 환경을 사용해야 하며 배포판은 배포판이 관리하는 모듈과 로컬에서 관리하는 모듈을 위해 별도의 sys.path 디렉터리를 두어야 한다는 지침이 향후 추가 실험을 쉽게 만들 것으로 예상합니다. 여기에는 완전히 별도의 “시스템” 및 “사용자” Python 인터프리터를 배포하는 것, 배포판이 소유한 가상 환경 또는 PYTHONHOME에서 시스템 소프트웨어를 실행하면서 단일 인터프리터를 제공하는 것, 또는 특정 소프트웨어(예: 배포판의 패키지 관리자)의 엔트리 포인트를 수정하여 배포판이 관리하는 디렉터리만 볼 수 있는 sys.path를 사용하게 하는 것이 포함될 수 있습니다. 그러나 이러한 아이디어 자체는 이 PEP의 범위 밖에 있습니다.

구현 참고 사항

이 절은 비규범적이며 사양과 잠재적인 구현 모두에 관련된 참고 사항을 포함합니다.

현재 pip는 대상 sysconfig 스킴을 선택하는 방법을 직접 노출하지 않지만, 설치할 때 스킴을 조회하는 세 가지 방법을 제공합니다.

pip install
sysconfig.get_default_scheme()을 호출하며, 일반적으로(업스트림 CPython 및 대부분의 최신 배포판에서) get_preferred_scheme('prefix')와 동일합니다.
pip install --prefix=/some/path
sysconfig.get_preferred_scheme('prefix')를 호출합니다.
pip install --user
sysconfig.get_preferred_scheme('user')를 호출합니다.

마지막으로 pip install --target=/some/path는 어떤 스킴도 조회하지 않고 /some/path에 직접 기록합니다.

Debian은 현재 몇 가지 휴리스틱(VIRTUAL_ENV 환경 변수 확인 포함)을 사용하여 patch to change the default install location inside a virtual environment를 적용하며, 이는 주로 가상 환경에서 사용되는 디렉터리가 site-packages로 유지되고 dist-packages가 되지 않도록 하기 위한 것입니다. 이 패치는 실제로 기본 sysconfig 스킴을 변경하지 않으며 특히 sysconfig.get_path("stdlib")의 결과를 변경하지 않으므로, 이 제안에는 특별히 영향을 미치지 않습니다.

Fedora는 현재 rpmbuild 내부에서 실행되지 않을 때 기본 설치 위치를 변경하는 patch to change the default install location when not running inside rpmbuild를 적용하고 있으며, 이를 두 개의 시스템 전역 디렉터리 접근 방식을 구현하는 데 사용합니다. 이는 개념적으로 bpo-43976_에서 구상한 종류의 훅이지만, 변경된 sysconfig 스킴이 아니라 distutils에 대한 코드 패치로 구현된 것입니다.

위의 is_virtual_environment 구현과 EXTERNALLY-MANAGED 파일을 로드하고 그 파일에서 오류 메시지를 찾는 로직은 구현을 중앙 집중화하기 위해 표준 라이브러리(syssysconfig 각각)에 추가해도 좋지만, 아직 추가할 필요는 없습니다.

참고 자료

이러한 문제와 이를 해결하려 했던 이전 시도에 대한 추가 배경은 2014년의 Debian bug 771794 “pip silently removes/updates system provided python packages”, sudo pip를 /usr/local로 지정하는 방법을 다룬 Fedora의 2018년 기사 Making sudo pip safe(변경 사항이 여전히 sudo pip를 완전히 안전하게 만들지는 않는다고 인정함), 2018년의 pip 이슈 5605 (“Disable upgrades to existing python modules which were not installed via pip”) 및 5722 (“pip should respect /usr/local”), 그리고 PyCon US 2019 이후의 토론 스레드 Playing nice with external package managers를 참조하십시오.