uv — 빠른 Python 패키지 및 프로젝트 관리자은(는) 무엇을 하나요?
개요
uv는 Astral 사의 단일 바이너리로, Python 워크플로 전반을 다룹니다: 플랫폼 간 잠금 파일(uv.lock)을 사용하는 프로젝트, 인라인 의존성이 있는 단일 파일 스크립트, 커맨드라인 도구(uvx), Python 인터프리터 설치, 그리고 드롭인 방식의 uv pip 인터페이스. 필요 시 Python 자체를 다운로드하므로 머신에는 uv만 있으면 됩니다.
사용 방법
설치
# 패키지 관리자
pipx install uv
brew install uv
winget install --id=astral-sh.uv -e
Astral은 패키지 관리자가 없는 머신을 위한 독립 설치 프로그램도 제공합니다; 설치 페이지(https://docs.astral.sh/uv/getting-started/installation/)에 최신 명령어와 체크섬이 있습니다.
uv --version
uv self update # 독립 설치 프로그램만 해당; 그 외에는 패키지 관리자로 업그레이드
릴리스를 고정하려면 설치 프로그램 URL에 버전을 넣으세요: https://astral.sh/uv/0.12.21/install.sh.
프로젝트 생성
uv init dispatch-api # 패키징된 앱: src/dispatch_api/, uv_build 백엔드, 콘솔 스크립트
uv init --no-package cron-jobs # main.py와 빌드 시스템 없는 플랫 레이아웃
uv init --lib geo-utils # py.typed 마커가 있는 라이브러리
uv init --bare --python 3.12 # 현재 디렉토리에 pyproject.toml만 생성
uv init은 또한 .python-version, README.md, .gitignore를 작성하고 git을 초기화합니다. --bare는 pyproject.toml만 작성하므로, 기존 저장소 내부에서 사용하기에 적합합니다.
의존성 관리
uv add 'httpx>=0.27' 'pydantic>=2.7,<3' # 런타임 의존성
uv add --dev pytest ruff # dev 그룹, 프로덕션에 배포되지 않음
uv add --group docs mkdocs # 모든 이름 있는 의존성 그룹
uv add --optional postgres 'psycopg[binary]' # 엑스트라: pip install dispatch-api[postgres]
uv add -r requirements.txt # 기존 requirements 파일 가져오기
uv remove httpx
uv lock --upgrade-package pydantic # 제약 조건 내에서 하나의 패키지만 업그레이드
uv lock --upgrade # 모두 업그레이드
uv tree --outdated --depth 1 # 최신 릴리스가 있는 패키지 확인
첫 번째 uv add는 .venv/와 uv.lock을 생성합니다. 모든 변경 사항은 pyproject.toml, 잠금 파일, 그리고 환경을 함께 업데이트합니다.
실행 및 동기화
uv run pytest # 필요 시 잠그고 동기화한 후 .venv 내에서 실행
uv run python -m dispatch_api
uv run --with rich python -c "import rich" # 이 명령에만 추가 패키지 사용
uv sync # 모든 항목 설치, dev 그룹 포함
uv sync --locked # CI: uv.lock이 pyproject.toml과 일치하지 않으면 실패
uv sync --frozen --no-dev # 프로덕션: uv.lock을 그대로 사용, dev 그룹 건너뜀
uv sync --extra postgres
uv lock --check # 잠금 파일이 오래되었을 때 0이 아닌 종료 코드 반환
활성화가 필요 없습니다. uv run은 자체적으로 프로젝트 환경을 찾습니다.
인라인 의존성이 있는 스크립트
uv init --script fetch-rates.py --python 3.12
uv add --script fetch-rates.py 'httpx<1' rich
uv run fetch-rates.py
uv lock --script fetch-rates.py # 재현 가능한 실행을 위해 fetch-rates.py.lock 작성
uv add --script는 파일 상단에 메타데이터 블록을 유지 관리합니다:
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "httpx<1",
# "rich>=15.0.0",
# ]
# ///
첫 번째 줄에 #!/usr/bin/env -S uv run --script를 추가하고 chmod +x하여 파일을 직접 실행할 수 있습니다.
도구
uvx ruff check . # 설치 없이 실행; uvx = uv tool run
uvx [email protected] check . # 정확한 버전 지정
uvx --from 'markitdown[pdf]' markitdown q3-board-report.pdf # 명령어 이름이 스펙과 다른 경우
uv tool install ruff # PATH에 영구적으로 격리 설치
uv tool upgrade --all
uv tool list
uv tool uninstall ruff
설치된 도구는 uv tool dir --bin이 출력하는 디렉토리에 저장됩니다. uv는 해당 디렉토리가 PATH에 없을 때 경고합니다.
Python 버전
uv python install 3.12 3.13
uv python list --only-installed
uv python pin 3.12 # .python-version 파일 작성
uv run --python 3.13 pytest # 다른 인터프리터로 일회성 실행
uv python upgrade 3.12 # 3.12의 최신 패치 릴리스로 업그레이드
uv venv --python 3.12 # .venv에 일반 가상 환경 생성
요청은 3.12, 3.12.3, >=3.11,<3.13, [email protected], 3.13t(프리-스레드)를 허용합니다. uv는 누락된 인터프리터를 자동으로 다운로드합니다. 이를 금지하려면 --no-python-downloads를 전달하세요.
pip 호환 인터페이스
uv venv
uv pip install -r requirements.txt
uv pip compile requirements.in -o requirements.txt --universal
uv pip sync requirements.txt # 목록에 없는 항목은 모두 제거
uv pip install --python .venv/bin/python 'rich>=13'
CI 및 Docker
name: ci
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
version: "0.12.21"
enable-cache: true
- run: uv python install
- run: uv sync --locked --all-extras --dev
- run: uv run pytest
FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:0.12.21 /uv /uvx /bin/
WORKDIR /app
ENV UV_NO_DEV=1 UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
# 의존성을 먼저 처리하여 uv.lock이 변경될 때까지 이 레이어를 재사용
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --locked --no-install-project
COPY . /app
RUN --mount=type=cache,target=/root/.cache/uv uv sync --locked
ENV PATH="/app/.venv/bin:$PATH"
CMD ["python", "-m", "dispatch_api"]
.dockerignore에 .venv를 추가하세요. 호스트 환경은 이미지 내에서 작동하지 않습니다.
예제
예제 1: requirements.txt 서비스를 잠금 파일이 있는 uv 프로젝트로 마이그레이션
요청: "dispatch-api 저장소에 requirements.txt와 requirements-dev.txt가 있습니다. uv로 이동하고 잠금 파일을 만들어 테스트를 실행하세요."
cd dispatch-api
uv init --bare --python 3.12
uv python pin 3.12
uv add -r requirements.txt
sed '/^-r /d' requirements-dev.txt | uv add --dev -r -
uv run python -m pytest -q
uv run ruff check .
결과: pyproject.toml에 5개의 런타임 패키지와 dev 그룹이 나열되고, uv.lock이 30개의 패키지를 고정하며, 모든 검사가 통과합니다. 요약 출력:
Initialized project `dispatch-api`
Pinned `.python-version` to `3.12`
+ uvicorn==0.34.0
+ pytest==8.3.4
+ ruff==0.8.6
1 passed, 1 warning in 0.12s
All checks passed!
예제 2: 자체 포함 스크립트
요청: "환경 설정 없이 모든 서버에 복사해서 실행할 수 있는 디스크 사용량 보고서 스크립트를 작성해 주세요."
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.12"
# dependencies = ["rich>=13"]
# ///
import shutil
from rich.console import Console
from rich.table import Table
usage = shutil.disk_usage("/")
table = Table(title="디스크 사용량")
table.add_column("총 GB")
table.add_column("사용 가능 GB")
table.add_row(f"{usage.total / 1e9:.0f}", f"{usage.free / 1e9:.0f}")
Console().print(table)
chmod +x disk-report.py
./disk-report.py
결과: uv는 첫 번째 실행 시 rich로 캐시된 환경을 구축하고 테이블을 출력합니다. 이후 실행은 즉시 시작됩니다.
디스크 사용량
┏━━━━━━━━━━┳━━━━━━━━━┓
┃ 총 GB ┃ 사용 가능 GB ┃
┡━━━━━━━━━━╇━━━━━━━━━┩
│ 3936 │ 2211 │
└──────────┴─────────┘
지침
uv.lock과.python-version을 커밋하세요.uv.lock을 수동으로 편집하지 마세요.pyproject.toml을 변경하거나uv add,uv remove,uv lock을 사용하세요.--locked또는--frozen:--locked는 잠금 파일을 확인하고 오래되었을 때 실패하므로 CI에 적합합니다.--frozen은 확인을 건너뛰고 파일에 명시된 대로 정확히 설치합니다.uv sync는 정확하고,uv run은 그렇지 않습니다.uv sync는 잠금 파일에 없는 패키지(uv pip install로 추가된 패키지 포함)를 제거합니다.uv run은 누락된 항목만 추가합니다.uv pip는 탐색을 통해 대상을 선택합니다: 활성화된VIRTUAL_ENV, 그다음 현재 디렉토리 또는 가장 가까운 상위 디렉토리의.venv. 잘못된 폴더에서 실행하면 다른 사람의 환경에 설치됩니다. 스크립트에서는 환경 경로와 함께--python을 전달하세요.- 플랫 프로젝트와 pytest:
--bare또는--no-package를 사용하면 프로젝트 자체가 설치되지 않으므로uv run pytest가ModuleNotFoundError로 실패할 수 있습니다.uv run python -m pytest를 사용하거나,[tool.pytest.ini_options]아래에pythonpath = ["."]를 설정하세요. uv init은 기본적으로 패키징된src/레이아웃을 사용합니다.main.py가 있는 스크립트 스타일 프로젝트에는--no-package를 전달하세요.- 실험적 명령어:
uv format,uv check,uv audit은 변경될 수 있다는 경고를 출력합니다. 아직 이들을 기반으로 파이프라인을 구축하지 마세요. - 비공개 인덱스:
[[tool.uv.index]]아래에 선언하고 인덱스 이름을 딴 환경 변수를 통해 자격 증명을 전달하세요.internal이라는 인덱스의 경우UV_INDEX_INTERNAL_USERNAME과UV_INDEX_INTERNAL_PASSWORD를 사용합니다. 절대 커밋하지 마세요. 기본first-index전략은 의존성 혼동을 방지합니다.unsafe-best-match는 피하세요. - 공급망:
uv run,uvx및 인라인 메타데이터가 있는 스크립트는 패키지를 다운로드하고 실행합니다. 신뢰할 수 없는 출처의 스크립트를 실행하기 전에 의존성 목록을 검토하세요. uv self update는 독립 설치 프로그램으로 설치한 경우에만 작동합니다.- CI 캐시: 캐시를 저장하기 전에
uv cache prune --ci를 실행하여 작게 유지하세요. - 사용하지 말아야 할 경우: uv는 Python 패키지 인덱스, git, 로컬 경로에서 설치합니다. conda 패키지나 시스템 라이브러리를 설치하지 않으므로, 이들에 의존하는 스택에는 conda를 유지하세요.