Uv: 의존성이 있는 스크립트 실행하기
(docs.astral.sh)- uv를 사용하면 Python 스크립트 실행 시 의존성 관리를 자동화함
- 별도의 가상환경 관리 없이 스크립트별로 환경이 자동으로 생성 및 유지됨
- 필요한 패키지는 inline metadata 또는 명령행 옵션 등 다양한 방식으로 선언 가능함
- Python 버전과 패키지 관리도 스크립트 단위로 선언 및 자동 조정 가능함
- Lock 파일과 의존성 버전 제한 옵션 등으로 재현성과 유지관리성을 높임
개요
- uv는 Python 스크립트 실행 시 해당 스크립트가 필요로 하는 패키지 의존성을 자동으로 관리해주는 도구임
- 사용자는 번거로운 가상환경 생성, 패키지 설치를 직접 하지 않아도 됨
- 여러 실행 옵션과 inline metadata 활용법, 다양한 의존성 선언 방식, 각종 제어 기능을 제공함
Python 환경과 uv의 역할
- Python은 각각의 설치마다 고유한 환경을 가짐
- 일반적으로 가상환경을 만들고 관리하는 것이 권장됨
- uv는 가상환경을 자동으로 관리하며, 선언적 방식으로 의존성을 다룸
- 단순한 스크립트는
uv run example.py
만으로 즉시 실행 가능함 - 표준 라이브러리 사용 시 추가 설정 없이 작동함
인자 전달 및 입력 방식
- 스크립트에 명령행 인자를 넘길 수 있음
- 표준 입력에서 직접 스크립트 코드를 받아 실행하거나, here-document 기능도 지원함
프로젝트 환경과 --no-project
옵션
- 스크립트가 프로젝트 폴더(예:
pyproject.toml
이 있는 곳)에서 실행될 경우, 프로젝트 의존성도 설치됨 - 필요 없다면
--no-project
플래그를 스크립트 이름 앞에 넣어 프로젝트 환경을 무시할 수 있음
스크립트 의존성 선언과 관리
- 외부 패키지가 필요한 경우, 명령행 옵션
--with
를 이용해 의존성을 지정해서 실행 가능함 - 특정 버전 제약 조건도 지원하며, 여러 의존성도 반복해서 옵션으로 지정 가능함
- 프로젝트 환경에서 추가 의존성을 더할 수 있고, 원하지 않으면
--no-project
로 제어 가능함
Inline Script Metadata(PEP 723 방식)
- Python에서는 이제 스크립트 자체에 의존성이나 Python 버전을 선언하는 표준 포맷을 지원함
-
uv init --script
로 인라인 메타데이터가 포함된 스크립트를 손쉽게 생성 가능함 -
uv add --script
로 스크립트에 필요한 의존성을 TOML 포맷으로 추가 관리할 수 있음 - 인라인 메타데이터가 있으면, 프로젝트의 의존성은 무시되고 오직 스크립트 의존성만 적용됨
Python 버전 선언 및 관리
- 스크립트 내 또는 실행 시 원하는 Python 버전을 지정할 수 있음
- 지정된 버전이 없다면 자동으로 다운로드 및 설정됨
Shebang으로 바로 실행 가능한 스크립트 작성
- shebang(@#!...)을 이용해
uv run --script
방식으로 직접 실행 파일 제작 가능함 - 이때도 의존성 선언 및 Python 버전 등도 스크립트 상단에서 가능함
패키지 인덱스 및 인증 지원
-
--index
옵션으로 커스텀 패키지 인덱스 사용 가능 - index 정보도 메타데이터에 포함됨
- 인증이 필요한 경우 별도 문서 참고 가능
의존성 고정(Lock)과 재현성 향상
-
uv lock --script
로 스크립트 단위의 Lock 파일 작성 및 관리 가능 - 이후 실행/의존성 추가 시 lock 파일 재사용 및 필요시 갱신됨
- 버전 재현성을 위한
exclude-newer
(특정 날짜 이후 릴리즈 제외) 옵션 제공 - RFC 3339 타임스탬프로 날짜 지정
Python 버전 유연성
- 각 실행 시 명령행 옵션으로 任의의 Python 버전 사용 지정 가능
- 예시:
uv run --python 3.10 example.py
Windows 지원
-
.pyw
확장자를 가진 스크립트는 윈도우에서 pythonw로 실행됨 - GUI 기반의 스크립트도 의존성 함께 실행 가능함
참고 문서
- 더 자세한 명령 사용법은 CLI 참조 문서 및 툴 실행/설치 가이드 참고 가능
결론
- uv는 Python 스크립트의 실행 환경, 의존성, 버전, 패키지 인덱스, 재현성 등을 자동으로 간편하게 관리해 생산성과 신뢰성을 동시에 높여주는 도구임
다른 기능은 제쳐두고, 단순히 속도만으로도 사용 할 이유가 충분합니다.
다시 pip 쓰라고 하면 절대 못할 정도입니다.
conda의 시스템 패키지 관리는 flake.nix로 대체해서 사용하고 있는데, 공동 작업이나 기존 conda+pip로 유지 관리되던 프로젝트 이외에 개인적으론 앞으로 uv+nix를 사용할 것 같습니다.
최근 대부분의 python 실행을 uv로 대체했는데 정말 빠릅니다.
완벽히 호환되지 않는 몇몇 고급 기능이 있긴 하지만 대부분의 경우 거의 똑같이 동작합니다.
Hacker News 의견
-
"스크립트 의존성 선언" 기능이 정말 유용함을 경험함
공식 가이드 문서에서 소개하듯, 다음과 같이 Python 코드 최상단에 주석으로 의존성을 명시할 수 있음# /// script # dependencies = [ # "requests<3", # "rich", # ] # /// import requests, rich # ... script
이 파일을 script.py로 저장한 뒤 "uv run script.py"로 실행하면 명시된 의존성이 마법처럼 임시 가상 환경에 설치되어 곧바로 실행할 수 있음
이는 Python의 PEP 723를 구현한 것이며, Claude 4도 이 트릭을 알고 있으므로 “인라인 스크립트 의존성이 포함된 Python 스크립트”를 작성해달라고 프롬프트하면 올바르게 만들어줌
예제로 httpx와 click을 사용해 대용량 파일을 다운로드하고 진행률 바를 보여주는 코드 작성을 요청할 수 있음
Claude 4 이전엔 이런 기능을 위해 맞춤 프로젝트와 별도 안내가 필요했지만, 이제는 그렇지 않음
자세한 사용 사례에서도 참고할 수 있음-
shebang 모드도 정말 유용함을 느낌
아래와 같이 스크립트 첫 줄에 shebang을 추가하면 ./script.sh처럼 실행 가능함#!/usr/bin/env -S uv run --script # /// script # dependencies = [ # "requests<3", # "rich", # ] # /// import requests, rich # ... script
-
requirements 파일과 같은 형식이면 좋겠다는 바람이 있음
만약 그렇게 되면 uv가 없는 사용자를 위해 간단한 주석으로 pip로 동일하게 설치할 수 있는 원라이너도 제공할 수 있기 때문임
예시로pip install -r <(head myscript.py)
와 같은 접근이 가능할 듯함 -
실제로 PEP723은 요즘 주목받는 uv뿐 아니라 pipx, hatch에서도 지원 중임
그리고 pip-tools 등도 지원 로드맵에 포함되어 있음
(관련 이슈 참고) -
처음 봤을 때 requests 옆에 하트 이모지인 줄 알았던 적이 있음
-
이 방식이 정말 멋지다고 생각함
하지만 언젠가는 매직 주석이 아닌 내장 언어 문법으로 채택되었으면 좋겠음
주석은 약간 지저분해 보임
물론 도구 입장에선 매직 주석이 파싱엔 더 쉽고, Python 코어가 패키징 지식이 많지 않다는 등 구조적 고민이 있다는 것도 알지만, 언젠가는 내장 문법이 생기면 좋겠음
-
-
이러한 방식에 공감함
Python이 requirements.txt 파일이 필수는 아니지만, 관리를 소홀히 하면 기능이 깨지는 불편이 자주 생김이 아쉬움
관련 트윗 참고 -
이 방식에서 겪은 함정을 공유하고 싶음
인터넷이 끊겼을 때 라우터를 재시작하는 스크립트에 활용했는데, 의존성 설치 동작이 인터넷 연결에 의존하므로 네트워크가 안 되면 스크립트 자체가 작동 불가해지는 문제가 있음
미리 발견하고 의존성 사전 설치로 해결했지만, 나처럼 실수하지 말고 실제 airgapped 환경(네트워크가 완전 차단된 환경)에서는 사용하지 않기를 권장
uv 캐시가 있어도 캐시 미스가 날 수 있음-
uv run --offline
옵션 사용 시 캐시된 의존성을 활용해서 새 버전 체크 없이 실행할 수 있음
같은 기능이uvx
에도 동작함 (uvx --offline ...
) -
의존성이나 venv를 사용할 일이 있다면 최소 한 번은 인터넷 연결에서 실행해야 그 후 오프라인에서도 쓸 수 있는 구조인 것으로 이해함
-
-
최근 Python 생태계에서 여러 기능들이 점점 잘 맞물려 동작하고 있다는 느낌을 받음
Marimo와 uv 스크립트 의존성 조합으로, 다른 팀에서 사용하기 좋은 재현 가능한 리포팅/진단 도구를 만들기 시작함 -
uv의 이 기능이 가장 마음에 들어 이 때문에 uv로 갈아타게 됨
여러 git-hooks 스크립트들이 각각 별도의 의존성을 가지는데, 이를 메인 venv에 설치하고 싶지 않았음
#!/usr/bin/env -S uv run --script --python 3.13
한 줄만 추가하면 dev들에게는 brew install uv만 안내하면 됐고, venv를 따로 만들 필요 없이 스크립트 안에서 바로 사용할 수 있게 됨-
혹시
-S
플래그가 왜 필요한지 아는 사람 있는지 궁금함
내 BSD 환경에서는/usr/bin/env -S uv run --python 3.11 python
과/usr/bin/env uv run --python 3.11 python
모두 Python shell을 실행해 결과가 똑같다 느꼈음
env 매뉴얼을 봐도 명확히 해석되지 않아 유용한 정보라면 듣고 싶음
(여기서 -S는 인자를 공백으로 나눠주는 역할임) -
UV 덕에 원래는 파이썬 대규모 이관 작업을 golang으로 할 계획이었는데, UV 덕분에 이 이관 범위를 줄일 수 있었음
특히 작은 스크립트 형태의 작업은 더 이상 이동할 필요가 없어짐 -
이 기능은 정말 ‘킬러(feature)’라고 확신함
-
-
의존성 중 Pytorch가 있는 경우엔 이 방식이 조금 제한이 있을 수 있음
Uv가 Pytorch 용 통합 지원을 잘 제공하고 있지만, 스크립트 헤더만으로는 (CPU, CUDA, ROCm 등) 가장 적합한 wheel 인덱스를 명확히 선택하는 방법이 없어 아쉬움 -
uv가 자동으로 만들어주는 venv을 VS Code가 쉽게 인식할 수 있었으면 하는 바람이 있음
지금은 Python extension이 써드파티 import를 모두 빨간 줄로 표시함
임시 해법으로 uv의 Cache 디렉터리에서 수동으로 venv 경로를 찾아 등록하는데, 자주 venv가 재생성되면 또 반복해야 해서 번거로움-
uv python find --script "${filePath}"
명령어로 env 경로를 찾을 수 있음
해당 기능을 VS Code에서 자동 감지해 활성화해주는 확장 프로그램을 개발 중임
-
-
UV의 이 기능이 너무 마음에 듦
jupyter notebook도 별도 설치 없이 다음과 같이 원라인으로 실행할 수 있음uv run --with jupyter jupyter notebook
모든 것이 임시 가상환경에 설치되고, 이후에는 깨끗이 정리됨
프로젝트 내에서 실행할 경우 해당 프로젝트의 의존성도 자동 인식함-
다만 완전히 ‘깨끗하게’ 정리되는 것은 아니며, uv 캐시 폴더가 계속 커질 수 있음
-
나 역시
uv run --with ipython --with boto3 ipython
식으로 자주 활용 중이며, 정말 시간 절약에 큰 도움이 됨
-
-
최근
uv run
관련해서 발견한 소소한 이슈가 있음
스크립트를 프로젝트 폴더 밖에서 실행하면, 실제 스크립트 파일 위치가 아닌 현재 작업 디렉터리에서 pyproject.toml을 찾는 동작이 있음
그래서 의존성을 pyproject.toml에 저장한 스크립트는 “uv run path/to/my/script.py” 식으로 외부에서 실행 시 제대로 동작하지 않을 수 있음
이 현상은 항상 인라인 의존성을 쓰거나,--project
인자를 쓰면 해결할 수 있지만, script 경로를 두 번 입력해야 해서 불편함
uv 자체는 매우 훌륭하지만, 이 작은 특성은 꽤 불편하게 느껴짐 -
uv 전용 shebang과 인스크립트 의존성 방식을 만족스럽게 사용 중에 있었음
여기에 더해uv lock --script example.py
명령어로 스크립트 한 개 전용 lock 파일까지 만들 수 있다는 점이 더욱 인상적임
Python 패키징이 20년 넘게 이어졌는데 이렇게 자연스러운 경험이 이제야 등장한 것에 놀라움- 단일 스크립트용 lock을 생성하는 사용 사례가 궁금함
우리 조직에선 lockfile의 의존성을trivy fs uv.lock
등으로 스캔해, 알려진 CVE가 있는 코드 실행을 예방하는 데에도 활용하고 있음
- 단일 스크립트용 lock을 생성하는 사용 사례가 궁금함