pip
및 pip-tools
와의 호환성¶
uv는 일반적인 pip
및 pip-tools
워크플로우를 대체할 수 있도록 설계되었다.
간단히 말해, 기존 pip
및 pip-tools
사용자는 패키징 워크플로우를 크게 변경하지 않고도 uv로 전환할 수 있다. 대부분의 경우, pip install
을 uv pip install
로 바꾸는 것만으로도 문제없이 작동한다.
하지만 uv는 pip
의 완벽한 복제본이 아니며, 일반적인 pip
워크플로우에서 벗어날수록 동작 차이를 더 자주 마주칠 수 있다. 어떤 경우에는 이러한 차이가 의도적일 수 있고, 다른 경우에는 구현 세부사항 때문일 수 있으며, 또 다른 경우에는 버그일 수 있다.
이 문서에서는 uv와 pip
간의 알려진 차이점을 설명하고, 그 이유와 해결 방법, 그리고 향후 호환성에 대한 의도를 밝힌다.
설정 파일과 환경 변수¶
uv는 pip.conf
나 PIP_INDEX_URL
처럼 pip
전용 설정 파일이나 환경 변수를 읽지 않는다.
다른 도구를 위한 설정 파일과 환경 변수를 읽는 것은 몇 가지 문제를 야기한다:
- 사용자가 포맷, 파서 등의 버그에 의존하게 되므로, 대상 도구와 버그 수준까지 호환성을 유지해야 한다.
- 대상 도구가 포맷을 변경하면, uv도 동일한 방식으로 변경해야 한다.
- 설정이 특정 버전에 종속된 경우, uv는 사용자가 어떤 버전의 도구를 사용할지 알아야 한다.
- 대상 도구에 없는 설정이나 구성을 uv가 도입하면,
pip.conf
(또는 유사 파일)가pip
에서 사용 불가능해질 수 있다. - uv가 실제 동작에 영향을 미치지 않는 설정을 읽어 사용자를 혼란스럽게 할 수 있다. 많은 사용자가 uv가 다른 도구를 위한 설정 파일을 읽을 것이라고 예상하지 않는다.
대신, uv는 UV_INDEX_URL
같은 자체 환경 변수를 지원한다. 또한 uv.toml
파일이나 pyproject.toml
의 [tool.uv.pip]
섹션을 통해 지속적인 구성을 지원한다. 자세한 내용은 설정 파일을 참고한다.
사전 릴리스 호환성¶
기본적으로 uv는 다음과 같은 두 가지 경우에 의존성 해결 과정에서 사전 릴리스 버전을 허용한다:
- 패키지가 직접 의존성이고, 버전 마커에 사전 릴리스 지정자가 포함된 경우 (예:
flask>=2.0.0rc1
). - 패키지의 모든 게시된 버전이 사전 릴리스인 경우.
전이적 사전 릴리스로 인해 의존성 해결이 실패하면, uv는 사용자에게 --prerelease allow
옵션을 사용해 다시 실행하라는 메시지를 표시한다. 이 옵션을 사용하면 모든 의존성에 대해 사전 릴리스를 허용한다.
또는 전이적 의존성을 requirements.in
파일에 사전 릴리스 지정자와 함께 추가해 특정 의존성에 대한 사전 릴리스 지원을 선택할 수 있다 (예: flask>=2.0.0rc1
).
요약하면, uv는 사전 릴리스를 허용할지 여부를 미리 알아야 한다. 반면 pip
은 해결기가 관련 지정자를 어떤 순서로 만나느냐에 따라 전이적 의존성에서 사전 릴리스 식별자를 일부 존중할 수 있다 (#1641).
사전 릴리스는 모델링하기 매우 까다롭고, 패키징 도구에서 버그의 빈번한 원인이 된다. 참조 구현체로 여겨지는 pip
조차도 사전 릴리스 처리에 대해 여러 가지 미해결 문제가 있다 (#12469, #12470, #40505 등). uv의 사전 릴리스 처리는 의도적으로 제한적이며, 의도적으로 사용자가 사전 릴리스를 명시적으로 선택하도록 요구한다. 이는 정확성을 보장하기 위함이다.
앞으로 uv는 전이적 의존성에서 사전 릴리스 식별자를 지원할 수도 있다. 그러나 이는 Python 패키징 사양의 발전에 달려 있을 가능성이 크다. 기존 PEP는 "의존성 해결"을 다루지 않으며, 대신 단일 버전 지정자에 대한 동작에 초점을 맞추고 있다. 따라서 패키징 생태계에서 사전 릴리스에 대한 올바르고 의도된 동작에 대해 해결되지 않은 문제가 남아 있다.
여러 인덱스에 존재하는 패키지¶
uv와 pip
모두에서 사용자는 특정 패키지의 사용 가능한 버전을 검색할 여러 패키지 인덱스를 지정할 수 있다. 하지만 uv와 pip
은 여러 인덱스에 존재하는 패키지를 처리하는 방식에서 차이가 있다.
예를 들어, 한 회사가 내부용 requests
패키지를 프라이빗 인덱스(--extra-index-url
)에 배포하면서도 기본적으로 PyPI에서 패키지를 설치할 수 있도록 허용한다고 가정해 보자. 이 경우, 프라이빗 requests
는 PyPI의 공개 requests
와 충돌할 수 있다.
uv는 여러 인덱스에서 패키지를 검색할 때, 인덱스를 순서대로 반복하며(--extra-index-url
을 기본 인덱스보다 우선시함), 일치하는 패키지를 찾는 즉시 검색을 중단한다. 이는 패키지가 여러 인덱스에 존재할 경우, uv가 해당 패키지를 포함하는 첫 번째 인덱스에 있는 버전들만 후보로 고려한다는 것을 의미한다.
반면 pip
은 모든 인덱스에서 후보 버전을 결합하고, 결합된 집합에서 최적의 버전을 선택한다. 하지만 pip
은 인덱스 검색 순서에 대해 어떤 보장도 하지 않으며, 패키지가 이름과 버전 기준으로 고유하다고 가정한다. 심지어 이는 인덱스 간에서도 마찬가지다.
uv의 동작 방식은 패키지가 내부 인덱스에 존재할 경우, 항상 내부 인덱스에서 설치하고 PyPI에서는 설치하지 않도록 설계되었다. 이는 "의존성 혼동" 공격을 방지하기 위한 목적이다. 이 공격은 공격자가 내부 패키지와 동일한 이름의 악성 패키지를 PyPI에 배포하여 악성 패키지가 내부 패키지 대신 설치되도록 유도하는 방식이다. 예를 들어, 2022년 12월 발생한 torchtriton
공격이 대표적인 사례다.
v0.1.39 버전부터 사용자는 --index-strategy
커맨드라인 옵션이나 UV_INDEX_STRATEGY
환경 변수를 통해 pip
스타일의 동작을 선택할 수 있다. 이 옵션은 다음과 같은 값을 지원한다:
first-index
(기본값): 모든 인덱스에서 각 패키지를 검색하되, 패키지를 포함하는 첫 번째 인덱스의 버전들만 후보로 고려한다.--extra-index-url
인덱스를 기본 인덱스 URL보다 우선시한다.unsafe-first-match
: 모든 인덱스에서 각 패키지를 검색하되, 다른 인덱스에 더 새로운 버전이 있더라도 첫 번째 인덱스의 호환 가능한 버전을 우선시한다.unsafe-best-match
: 모든 인덱스에서 각 패키지를 검색하고, 후보 버전들의 결합된 집합에서 최적의 버전을 선택한다.
unsafe-best-match
는 pip
의 동작과 가장 유사하지만, "의존성 혼동" 공격의 위험에 노출될 수 있다.
uv는 또한 특정 패키지를 전용 인덱스에 고정하는 기능도 지원한다(Indexes 참조). 이를 통해 특정 패키지는 항상 지정된 인덱스에서 설치된다.
PEP 517 빌드 격리¶
uv는 기본적으로 PEP 517 빌드 격리를 사용한다. 이는 pip install --use-pep517
과 유사하며, pypa/build
를 따르고 향후 pip
이 PEP 517 빌드를 기본으로 전환할 예정임을 고려한 것이다(pypa/pip#9175).
빌드 시 의존성이 누락되어 패키지 설치에 실패하면, 먼저 패키지의 최신 버전을 사용해 보길 권한다. 문제가 지속되면 패키지 관리자에게 이슈를 제기하여 올바른 PEP 517 빌드 시 의존성을 명시하도록 요청하는 것이 좋다.
빌드 격리를 우회하려면, 패키지의 빌드 의존성을 미리 설치한 후 --no-build-isolation
옵션과 함께 uv pip install
을 실행한다. 예를 들면 다음과 같다:
uv pip install wheel && uv pip install --no-build-isolation biopython==1.77
PEP 517 빌드 격리에서 실패하는 것으로 알려진 패키지 목록은 #2252에서 확인할 수 있다.
전이적 URL 의존성¶
uv는 URL 의존성(예: ruff @ https://...
)에 대한 퍼스트클래스 지원을 포함하지만, 전이적 URL 의존성을 처리하는 방식에서 pip와 두 가지 차이점이 있다.
첫째, uv는 URL이 아닌 의존성이 URL 의존성을 해결 과정에 도입하지 않는다고 가정한다. 다시 말해, 레지스트리에서 가져온 의존성 자체가 URL에 의존하지 않는다고 간주한다. 만약 URL이 아닌 의존성이 URL 의존성을 도입하면, uv는 해결 과정에서 해당 URL 의존성을 거부한다. (참고로 PyPI는 공개된 패키지가 URL 의존성을 가지는 것을 허용하지 않는다. 다른 레지스트리는 더 관대할 수 있다.)
둘째, 직접 URL 의존성을 사용하여 제약 조건(--constraint
) 또는 오버라이드(--override
)가 정의되고, 제약된 패키지가 자체적으로 직접 URL 의존성을 가지고 있다면, uv는 해결 과정에서 해당 전이적 직접 URL 의존성을 거부할 수 있다. 단, 이는 URL이 입력 요구 사항 집합의 다른 곳에서 참조되지 않는 경우에 한한다.
uv가 전이적 URL 의존성을 거부하면, 가장 좋은 방법은 관련 pyproject.toml
또는 requirement.in
파일에 URL 의존성을 직접 의존성으로 추가하는 것이다. 위에서 언급한 제약 조건은 직접 의존성에는 적용되지 않기 때문이다.
기본적으로 가상 환경 사용¶
uv pip install
과 uv pip sync
는 기본적으로 가상 환경과 함께 동작하도록 설계되었다.
구체적으로, uv는 항상 현재 활성화된 가상 환경에 패키지를 설치하거나, 현재 디렉터리나 상위 디렉터리에서 .venv
라는 이름의 가상 환경을 찾아 설치한다(활성화되지 않았더라도).
이는 pip
와 다르다. pip는 가상 환경이 활성화되지 않으면 전역 환경에 패키지를 설치하며, 비활성화된 가상 환경을 찾지 않는다.
uv에서는 --python /path/to/python
옵션을 통해 Python 실행 파일 경로를 제공하거나, --system
플래그를 사용해 시스템 Python에 설치할 수 있다. 이 플래그는 pip
처럼 PATH
에서 찾은 첫 번째 Python 인터프리터에 설치한다.
즉, uv는 기본 동작을 뒤집어 시스템 Python에 설치하려면 명시적으로 옵트인해야 한다. 이는 시스템을 손상시킬 수 있고 복잡한 문제를 일으킬 수 있으므로, 제한된 상황에서만 사용해야 한다.
자세한 내용은 "임의의 Python 환경 사용하기"를 참고한다.
의존성 해결 전략¶
주어진 의존성 명세에 대해 설치할 패키지의 '정확한' 집합은 보통 하나로 정해지지 않는다. 대신, 명세를 만족하는 여러 유효한 패키지 집합이 존재한다.
pip
와 uv 모두 설치할 패키지의 정확한 집합에 대해 어떤 보장도 하지 않는다. 단지 해결 결과가 일관적이고 결정적이며 명세를 준수할 것만을 보장한다. 따라서 어떤 경우에는 pip
와 uv가 서로 다른 해결 결과를 내놓을 수 있지만, 두 결과 모두 동등하게 유효하다.
예를 들어, 다음을 살펴보자:
starlette
fastapi
이 글을 쓰는 시점에서 starlette
의 최신 버전은 0.37.2
이고, fastapi
의 최신 버전은 0.110.0
이다. 그러나 fastapi==0.110.0
은 starlette
에 의존하며, 상한을 지정한다: starlette>=0.36.3,<0.37.0
.
만약 해결자가 starlette
의 최신 버전을 우선시한다면, starlette
에 대한 상한을 제외한 이전 버전의 fastapi
를 사용해야 한다. 실제로 이 경우 fastapi==0.1.17
로 되돌아가야 한다:
# This file was autogenerated by uv via the following command:
# uv pip compile requirements.in
annotated-types==0.6.0
# via pydantic
anyio==4.3.0
# via starlette
fastapi==0.1.17
idna==3.6
# via anyio
pydantic==2.6.3
# via fastapi
pydantic-core==2.16.3
# via pydantic
sniffio==1.3.1
# via anyio
starlette==0.37.2
# via fastapi
typing-extensions==4.10.0
# via
# pydantic
# pydantic-core
반대로, 해결자가 fastapi
의 최신 버전을 우선시한다면, starlette
의 상한을 만족하는 이전 버전을 사용해야 한다. 실제로 이 경우 starlette==0.36.3
으로 되돌아가야 한다:
# This file was autogenerated by uv via the following command:
# uv pip compile requirements.in
annotated-types==0.6.0
# via pydantic
anyio==4.3.0
# via starlette
fastapi==0.110.0
idna==3.6
# via anyio
pydantic==2.6.3
# via fastapi
pydantic-core==2.16.3
# via pydantic
sniffio==1.3.1
# via anyio
starlette==0.36.3
# via fastapi
typing-extensions==4.10.0
# via
# fastapi
# pydantic
# pydantic-core
uv의 해결 결과가 pip
와 바람직하지 않은 방식으로 다를 경우, 이는 종종 명세가 너무 느슨하다는 신호이다. 사용자는 명세를 더 엄격하게 조정하는 것을 고려해야 한다. 예를 들어, starlette
와 fastapi
의 경우, 사용자는 fastapi>=0.110.0
을 요구할 수 있다.
pip check
¶
현재 uv pip check
는 다음과 같은 진단 정보를 제공한다:
- 패키지에
METADATA
파일이 없거나,METADATA
파일을 파싱할 수 없는 경우 - 패키지의
Requires-Python
이 현재 실행 중인 인터프리터의 Python 버전과 일치하지 않는 경우 - 패키지가 설치되지 않은 패키지에 의존하는 경우
- 패키지가 설치된 패키지에 의존하지만, 호환되지 않는 버전인 경우
- 가상 환경에 동일한 패키지의 여러 버전이 설치된 경우
어떤 경우에는 uv pip check
가 pip check
에서 제공하지 않는 진단 정보를 표시하기도 하고, 그 반대의 경우도 있다. 예를 들어, pip check
는 현재 환경에 동일한 패키지의 여러 버전이 설치된 경우 경고를 표시하지 않지만, uv pip check
는 이를 경고한다.
--user
옵션과 user
설치 스킴¶
uv는 --user
플래그를 지원하지 않는다. 이 플래그는 user
설치 스킴을 기반으로 패키지를 설치한다. 대신, 패키지 설치를 분리하기 위해 가상 환경 사용을 권장한다.
또한 pip는 사용자가 대상 디렉토리에 쓰기 권한이 없을 때 user
설치 스킴으로 대체한다. 이는 시스템 Python에 설치할 때 일부 시스템에서 발생하는 상황이다. uv는 이러한 대체 동작을 구현하지 않는다.
자세한 내용은 #2077을 참고한다.
--only-binary
옵션 강제 적용¶
--only-binary
인자는 사전 빌드된 바이너리 배포판만 설치하도록 제한한다. --only-binary :all:
을 사용하면 pip와 uv 모두 PyPI 및 다른 레지스트리에서 소스 배포판을 빌드하지 않는다.
그러나 직접 URL로 제공되는 의존성(예: uv pip install https://...
)의 경우, pip는 --only-binary
를 강제로 적용하지 않는다. 따라서 pip는 이러한 모든 패키지에 대해 소스 배포판을 빌드한다.
반면 uv는 직접 URL 의존성에 대해 --only-binary
를 강제로 적용한다. 단, 한 가지 예외가 있다. uv pip install https://... --only-binary flask
와 같은 경우, uv는 패키지 이름을 미리 추론할 수 없을 때 주어진 URL에서 소스 배포판을 빌드한다. 이는 uv가 메타데이터를 빌드하지 않고서는 패키지가 "허용"되는지 여부를 판단할 수 없기 때문이다.
pip와 uv 모두 --only-binary
가 제공되더라도 편집 가능한 요구사항(editables requirements)을 빌드하고 설치하는 것을 허용한다. 예를 들어, uv pip install -e . --only-binary :all:
은 허용된다.
--no-binary
옵션 강제 적용¶
--no-binary
인자는 소스 배포본만 설치하도록 제한한다. --no-binary
를 사용하면 uv는 미리 빌드된 바이너리 배포본의 설치를 거부하지만, 로컬 캐시에 이미 존재하는 바이너리 배포본은 재사용한다.
또한 pip와 달리, uv의 리졸버는 --no-binary
가 적용된 경우에도 미리 빌드된 바이너리 배포본의 메타데이터를 읽는다.
manylinux_compatible
강제 적용¶
PEP 600은 Python 배포자가 _manylinux
표준 라이브러리 모듈에 manylinux_compatible
함수를 정의함으로써 manylinux
호환성을 선택적으로 적용할 수 있는 메커니즘을 설명한다.
uv는 manylinux_compatible
을 존중하지만, 현재 glibc 버전에 대해서만 테스트를 수행하며, manylinux_compatible
의 반환 값을 전역적으로 적용한다.
즉, manylinux_compatible
이 True
를 반환하면 uv는 해당 시스템을 manylinux
호환으로 간주한다. 반대로 False
를 반환하면 manylinux
비호환으로 간주하며, 모든 glibc 버전에 대해 manylinux_compatible
을 호출하지 않는다.
이 접근 방식은 명세의 완전한 구현은 아니지만, no-manylinux
와 같은 일반적인 포괄적인 manylinux_compatible
구현과 호환된다:
from __future__ import annotations
manylinux1_compatible = False
manylinux2010_compatible = False
manylinux2014_compatible = False
def manylinux_compatible(*_, **__): # PEP 600
return False
바이트코드 컴파일¶
pip
와 달리 uv는 기본적으로 설치 과정에서 .py
파일을 .pyc
파일로 컴파일하지 않는다. 즉, uv는 __pycache__
디렉터리를 생성하거나 채우지 않는다. 설치 시 바이트코드 컴파일을 활성화하려면 uv pip install
또는 uv pip sync
명령에 --compile-bytecode
플래그를 추가하거나, UV_COMPILE_BYTECODE
환경 변수를 1
로 설정한다.
바이트코드 컴파일을 건너뛰는 것은 특정 워크플로우에서 바람직하지 않을 수 있다. 예를 들어, Docker 빌드에서는 시작 시간을 단축하기 위해 바이트코드 컴파일을 활성화할 것을 권장한다(빌드 시간이 증가하는 대신).
바이트코드 컴파일은 Python 인터프리터가 발생시키는 다양한 경고를 억제한다. 따라서 드물게 uv로 설치한 Python 코드를 실행할 때 SyntaxWarning
이나 DeprecationWarning
메시지가 표시될 수 있으며, 이는 pip
를 사용할 때는 나타나지 않는다. 이러한 경고는 유효하지만, 일반적으로 바이트코드 컴파일 과정에서 숨겨진다. 이 경고는 무시하거나, 상위 수준에서 수정하거나, uv에서 바이트코드 컴파일을 활성화하여 비슷하게 억제할 수 있다.
엄격성과 스펙 준수¶
uv는 pip
보다 더 엄격한 기준을 적용한다. pip
가 설치를 허용하는 패키지도 uv는 거부하는 경우가 있다. 예를 들어, uv는 잘못된 URL 조각이 포함된 HTML 인덱스를 거부하지만, pip
는 이를 무시한다.
특정 스펙 준수 문제가 알려진 유명 패키지의 경우, uv는 관대하게 처리하기도 한다.
스펙 위반으로 인해 pip
가 설치하는 패키지를 uv가 거부한다면, 먼저 해당 패키지의 최신 버전을 설치해 보는 것이 좋다. 그래도 실패할 경우, 패키지 관리자에게 문제를 보고해야 한다.
pip
커맨드라인 옵션과 하위 명령어¶
uv는 pip
의 모든 커맨드라인 옵션과 하위 명령어를 완벽히 지원하지는 않지만, 상당 부분을 지원한다.
지원되지 않는 옵션과 하위 명령어는 사용자 요구와 구현 복잡도를 기준으로 우선순위가 정해지며, 일반적으로 개별 이슈로 추적된다. 예를 들어:
지원되지 않는 옵션이나 하위 명령어를 발견하면, 해당 이슈가 이미 보고되었는지 이슈 트래커를 검색해 보길 바란다. 아직 보고되지 않았다면 새 이슈를 열어도 좋다. 기존 이슈에 관심을 표시하고 싶다면, 해당 이슈에 '좋아요'를 눌러도 된다.
레지스트리 인증¶
uv는 --keyring-provider
에 대해 pip
의 auto
또는 import
옵션을 지원하지 않는다. 현재는 subprocess
옵션만 사용할 수 있다.
pip
과 달리, uv는 기본적으로 키링 인증을 활성화하지 않는다.
pip
과 달리, uv는 HTTP 401 응답을 받기 전까지 기다리지 않는다. uv는 자격 증명이 있는 호스트에 대해 모든 요청에 인증 정보를 첨부한다.
egg
지원¶
uv는 pip
에서 레거시 또는 더 이상 사용되지 않는 기능을 지원하지 않는다. 예를 들어, uv는 .egg
스타일 배포를 지원하지 않는다.
하지만 uv는 부분적으로 (1) Docker 이미지와 Conda 환경에서 가끔 발견되는 .egg-info
스타일 배포와 (2) 레거시 편집 가능한 .egg-link
스타일 배포를 지원한다.
구체적으로, uv는 새로운 .egg-info
또는 .egg-link
스타일 배포를 설치하지는 않지만, 해결 과정에서 기존에 존재하는 이러한 배포를 인식하고, uv pip list
와 uv pip freeze
로 이를 나열하며, uv pip uninstall
로 제거할 수 있다.
빌드 제약 조건¶
--constraint
(또는 UV_CONSTRAINT
)를 통해 제약 조건을 제공할 때, uv는 빌드 의존성을 해결할 때(예: 소스 배포판을 빌드할 때) 해당 제약 조건을 적용하지 않는다. 대신, 전용 --build-constraint
(또는 UV_BUILD_CONSTRAINT
) 설정을 통해 빌드 제약 조건을 제공해야 한다.
한편 pip은 PIP_CONSTRAINT
를 통해 지정된 경우 빌드 의존성에 제약 조건을 적용하지만, 커맨드라인에서 --constraint
를 통해 제공된 경우에는 적용하지 않는다.
예를 들어, setuptools
에 빌드 의존성이 있는 패키지를 빌드할 때 setuptools 60.0.0
을 사용하도록 보장하려면 --constraint
대신 --build-constraint
를 사용한다.
pip compile
기본 동작¶
pip compile
과 pip-tools
의 기본 동작에는 몇 가지 작지만 주목할 만한 차이가 있다.
기본적으로 uv는 컴파일된 요구사항을 출력 파일에 자동으로 저장하지 않는다. 대신, 사용자가 -o
또는 --output-file
옵션을 통해 출력 파일을 명시적으로 지정해야 한다.
uv는 기본적으로 컴파일된 요구사항을 출력할 때 extras를 제거한다. 즉, uv는 --strip-extras
를 기본값으로 사용하지만, pip-compile
은 --no-strip-extras
를 기본값으로 사용한다. pip-compile
은 다음 주요 릴리스(v8.0.0)에서 이 기본값을 변경할 예정이며, 그때부터 두 도구 모두 --strip-extras
를 기본값으로 사용하게 된다. uv에서 extras를 유지하려면 uv pip compile
명령에 --no-strip-extras
플래그를 추가한다.
기본적으로 uv는 출력 파일에 인덱스 URL을 기록하지 않지만, pip-compile
은 기본값(PyPI)과 일치하지 않는 --index-url
또는 --extra-index-url
을 출력한다. 출력 파일에 인덱스 URL을 포함하려면 uv pip compile
명령에 --emit-index-url
플래그를 추가한다. pip-compile
과 달리, uv는 --emit-index-url
이 전달되면 기본 인덱스 URL을 포함한 모든 인덱스 URL을 출력한다.
requires-python
적용 규칙¶
의존성의 requires-python
범위를 평가할 때, uv는 하한만 고려하고 상한은 완전히 무시한다. 예를 들어, >=3.8, <4
는 >=3.8
로 처리된다. requires-python
의 상한을 존중하면 이론적으로는 정확하지만 실제로는 잘못된 해결책을 도출할 수 있다. 예를 들어, 리졸버는 상한이 없는 처음 발표된 버전으로 되돌아가는 경우가 있다 (참고: Requires-Python
상한).
requires-python
지정자에 대해 Python 버전을 평가할 때, uv는 후보 버전을 메이저, 마이너, 패치 구성 요소로 잘라내고, 프리릴리즈나 포스트릴리즈 식별자 등을 무시한다.
예를 들어, requires-python: >=3.13
을 선언한 프로젝트는 Python 3.13.0b1을 허용한다. 3.13.0b1은 엄밀히 말하면 3.13보다 크지 않지만, 프리릴리즈 식별자를 생략하면 3.13보다 크다고 판단한다.
이 방식은 PEP 440을 엄격히 준수하지는 않지만, pip와 일관된 동작을 보인다.
패키지 우선순위¶
주어진 요구사항에 대해 보통 여러 가지 해결책이 존재하며, 리졸버는 이들 중 하나를 선택해야 한다. uv의 리졸버와 pip의 리졸버는 서로 다른 패키지 우선순위 체계를 가지고 있다. 두 리졸버 모두 사용자가 제공한 순서를 우선순위 중 하나로 사용하지만, pip는 uv에는 없는 추가적인 우선순위를 가지고 있다. 따라서 uv는 pip보다 사용자 순서 변경의 영향을 더 크게 받는다.
예를 들어, uv pip install foo bar
는 foo
의 최신 버전을 bar
보다 우선시하며, 이는 uv pip install bar foo
와 다른 해결 결과를 가져올 수 있다. 마찬가지로, 이 동작은 uv pip compile
의 입력 파일에서 요구사항의 순서에도 적용된다.