uv와 PEP 723으로 Python 스크립트 활용하기
(cottongeeks.com)- uv 패키지 매니저와 PEP 723을 통해 의존성 문제 없이 Python 스크립트 실행이 가능해짐
- uvx 기능은 Disposable 가상환경을 자동 생성하여 환경 설정의 불편함을 해결함
- PEP 723 메타데이터를 Python 파일에 포함하면 스크립트 자동 실행 및 패키지 관리가 편리해짐
- 실행 스크립트 예시로 YouTube 자막 추출 프로그램을 빠르게 구현 및 배포할 수 있음
- 이를 통해 이제 Python도 간결한 단일 실행 파일 작성이 가능해져 스크립트 활용성이 크게 향상됨
개요
- Python에서 "일회성(one-off) 스크립트" 실행 시 매번 환경 설정과 패키지 설치 과정을 거쳐야 했던 불편함이 uv와 PEP 723 도입으로 사라짐
- uv는 Rust로 개발된 고속의 Python 패키지 및 프로젝트 매니저이며, 신규 기능 uvx를 포함하여 Disposable 가상 환경 설정, 패키지 자동 설치, Python 버전 연동을 매우 빠르고 간편하게 처리함
uv와 uvx 장점
- uvx 기능은 Nodejs 생태계의 npx와 유사하게 동작, 지정된 Python 패키지(예: ruff) 실행 환경을 신속하게 생성함
- Disposable virtual environment를 캐시로 활용하여 오버헤드 없이 빠른 실행 제공
- 환경 세팅과 의존성 설치가 명령 한줄로 이루어져, 개발자가 별도 환경 설정 관리 필요 없음
PEP 723 소개 및 활용
- PEP 723은 Python 스크립트 상단에 의존성 및 환경 메타데이터를 포함할 수 있도록 표준을 정의함
- 예시: 코드 상단에 requires-python, dependencies 등 명시 가능함
- 이를 인식한 외부 도구(uv 등)는 스크립트 파일 내 작성된 정보로 자동 설치, 환경 세팅, 실행을 처리함
uv와 PEP 723의 결합
- 실제 Python 예제 파일 상단에 해당 메타데이터를 작성하고, uv의 run 명령어로 실행 시 즉시 필요한 모든 패키지 자동 설치와 지정 Python 버전 세팅 후 코드 실행이 이루어짐
- 예시 코드는 인터넷 API(PEP 목록)를 호출하고 결과를 출력함. 추가 패키지 설치나 환경설정 필요 없이 한줄 실행 가능
실용 예: YouTube 자막 스크립트
- shebang(
#!/usr/bin/env -S uv run --script
)과 PEP 723 메타데이터를 추가한 Python 스크립트 예시 제공 - youtube-transcript-api 등 외부 패키지 자동 설치 및 가상환경 자동 세팅 처리
- 사용자는 실행 파일(
ytt
)로 Youtube 동영상 URL이나 ID를 인자로 주면, 자막을 추출하여 결과를 즉시 제공 받음 - chmod로 실행 권한 부여 후, 터미널에서 간단하게 실행 가능
개발자 경험 개선 및 가능성 확장
- 과거에는 단순한 스크립트 실행에도 Go 등 단일 실행 파일 바이너리 언어를 더 선호했으나, 이제 Python도 동등한 수준의 간편함 제공
- uv와 PEP 723 조합으로 Python 스크립트의 공유 및 배포, 실행 자동화가 크게 수월해짐
- Github 예제(cottongeeks/ytt-mcp)를 통해 좀 더 복잡한 사용 사례 개발 가능
Hacker News 의견
-
글 작성자처럼 요즘은 Go 대신 크로스 플랫폼 파이썬 원오프(one-off)와 개인 스크립트에 손이 더 가는 편이지만, 파이썬 타입 체크 시스템이 혼돈 그 자체인 상황에 불만족 느낌 ty, pyrefly 같은 툴이 조금이나마 개선해주길 기대하는 마음
-
이젠 파이썬 스크립트가 virtualenv 때문에 고생하지 않고 바로 잘 작동하는 느낌 쉘 스크립트 쪽도 이런 경험이 생기면 좋겠다는 생각 패키징, 의존성 관리, 재현성은 여전히 석기시대 상황 지금은 여전히 curl | bash로 운에 맡기거나, 누락된 의존성 3개와 수동 단계 12개가 들어간 README 파일뿐인 현실 Nix? 이미 시간과 공간, 그리고 Nix 매뉴얼을 초월한 사람에게만 쓸만한 선택지 느낌 Docker? sed 명령 한번 쓰려고 리눅스 배포판을 다운로드하는 게 합리적으로 보인다면야 괜찮은 선택 누구나 쉽게 쓸 수 있는 단순하고 선언적인 중간지점이 꼭 필요하다는 생각
- 나는 꼭 대상 OS에 기본 내장된 바이너리와 라이브러리만 사용하는 쉘 스크립트만 작성하는 방식 선호 쉘 스크립트를 이식성 있게 만든다는 목표는 실질적으로 어울리지 않는 선택 macOS 전용 명령을 쓰는 쉘 스크립트를 리눅스에서 써야 한다면, 어떤 패키지 매니저도 해결 못하는 문제
- Nix는 이 목적엔 그렇게 어렵지 않은 사용성 느낌 다른 배포판에 Nix 설치하면 다음과 같은 식으로 간단히 쓸 수 있다는 예시 공유 NixOS나 Nix 패키징 전체는 꽤 도전적이지만, 쉘스크립트에만 한정하면 진입장벽이 낮은 편
- 굳이 새로운 쉘 스크립트를 작성할 필요를 못 느끼는 상황 모든 의존성 설치가 허용된 환경이라면 uv 같은 툴이 다 처리해줌 Clojure를 좋아하면 babashka도 추천
- 쉘스크립트에서의 패키징과 의존성 관리, 재현성이 구시대 같은 이유는, 그런 것들이 필요할 정도면 쉘이 적합하지 않은 상황이라는 생각 쉘은 20줄 내외의 작은 스크립트일 때만 어울린다는 입장 언어 자체가 그 이상의 규모에 어울릴 정도로 품질이 뛰어나지 않음
- mise 추천 회사에서 개발 환경 관리에 활용 중이며, Docker와 Nix보다 훨씬 간단하게 환경 구성 가능 병렬 설치 지원이라 일반 Dockerfile보다 큰 장점 체감
-
정말 멋진 트렌드이고 점점 더 대중화되는 느낌 처음 simonw 블로그에서 접했고, simonwillison의 블로그 포스트에서 관련 내용을 확인 올해 3월에 다른 블로그 게시글로 Hacker News 토론도 있었음 이런 트렌드가 메인 페이지에 오래 머물러 더 많은 사람들이 인지하면 좋겠다는 바람
-
uv를 작은 프로젝트에 써 보니 정말 뛰어난 경험 uv run과 uv tool run(uvx) 조합으로 GitHub의 파이썬 스크립트를 vm에서 바로 설치하고 실행하는 과정이 초간단 git clone도, venv 생성이나 진입, pip install도 필요 없음 무엇보다 uv 속도가 너무 빨라서 처음엔 뭔가 잘못된 줄로 느껴질 정도, 실제론 pip보다 10배 빠른 결과물 다만 도구와 문서가 아직은 조금 미완, 하지만 그 정도 혁신성과 실용성으로 충분히 쓸 만하다는 입장
- 진심으로 uv가 pyenv에서 --help 출력하는 시간보다 의존성 설치 속도가 더 빠름에 감탄
-
러스트도 이와 비슷하게 싱글 파일 타입 쉘 스크립트 아이디어를 발전시키는 중 원래 러스트에서 이런 방식(의존성 관리 포함된 단일 파일 실행 지원)을 처음 봤음 이 패턴이 더 많은 언어에서 자리 잡길 바라는 입장, gist로 주고받거나 빠른 소규모 툴 작성 등에 매우 유용 cargo-script RFC 문서도 참고
- C#도 해당 기능 지원: dotnet-run-app 공식 안내
-
uv run --script
를 사용할 때 메타데이터를 스크립트에 포함시키면, 스크립트에서 바로 파이썬 REPL을 띄워 수정 테스트를 하기가 약간 불편함 예를 들어 아래처럼 해야 하는데,$ uv run --python=3.13 --with-requirements <(uv export --script script.py) -- python >>> from script import X
좀 더 간결한 방식이 있으면 좋겠다는 희망 예를 들어
$ uv run --with-script script.py python
처럼 가능하면 베스트일 것 같은데, 실제로는 아래와 같이 실행하면 스크립트에 맞는 파이썬 및 venv 환경 바로 진입 가능
$ "$(uv python find --script script.py)" >>> from script import X
다만 한 번은 스크립트를 실행해 환경 생성이 필요
- 셋업 후 REPL 호출하는 기능이 필요하다면 이 gist 예시 참고 추천
스크립트에
--interactive
같은 플래그를 넣어 CLI 옵션화할 수 있음 Typer 기반의 작은 CLI를 보통 이런 식으로 자주 작성 dev 스크립트에서는--sql
플래그로 DuckDB SQL REPL 진입했던 경험도 있음 - 간단한 방법 한 가지 소개
cat ~/.local/bin/uve #!/bin/bash temp=$(mktemp) uv export --script $1 --no-hashes > $temp uv run --with-requirements $temp vim $1 unlink $temp
- 셋업 후 REPL 호출하는 기능이 필요하다면 이 gist 예시 참고 추천
스크립트에
-
conda를 쓴다면, 파이썬 스크립트용 shell 래퍼에서 직접 환경 활성화하는 방식 가능 아래와 같이 작성
#!/usr/bin/env bash eval "$(conda shell.bash hook)" conda activate myenv python myscript
다만, PEP 723 스타일처럼 독립적이진 않은 접근이라는 점
-
어제와 오늘 HN 스레드를 보고 처음 uv 체험 결정, 빠른 속도와 쉬운 의존성 관리에 깊은 인상 공식 문서가 개선된다면 더 좋을 듯, 특히 requirements.txt 워크플로에서 uv로 전환하는 가이드가 있으면 편리할 것 같음 프로젝트별 파이썬 버전 지정이 혼란스러운 부분( .python-version과 pyproject.toml 두 곳에서 정의)
- 파이썬 개발자 도구 익스플로러 전자책 작성 경험 있음 공식 문서가 부족한 부분을 안내한 적 있는데, requirements.txt에서 마이그레이션 방법 uv 프로젝트의 파이썬 버전 변경 방법도 참고 추천 추가로 다루었으면 하는 주제 있으면 언제든 제안 요청
-
pyproject.toml
의requires-version
필드는 호환 보장 버전 범위를 의미,.python-version
은 개발에 쓸 특정 버전을 지정 uv init으로 만들면 초기엔 같아 보이지만, 시간이 지나면 requires-version은.python-version
보다 더 낮은 최소 지원 버전을 지정하게 됨 requires-version은 패키지 메타데이터에도 들어가고, 배포한 패키지를 사용할 타인의 의존성 해석에도 영향을 미침 예를 들어 v1은 Python 3.구버전도 지원하지만 v2는 안 할 경우 등 - 비슷하게 느끼는 점이 있는데, 나만의 워크플로(동일 파일을 모든 컴퓨터에 드롭박스로 동기화하며 플랫폼에 관계없이 사용)를 고집 중 npm이나 dotnet처럼 플랫폼 변경할 때 npm update나 dotnet restore는 필요하지만, venv는 문제없이 잘 작동 반면 uv는 이렇게 플랫폼 스위치할 때 일 처리가 더 복잡하고 수동정리가 필요한 느낌
- pyproject.toml은 (uv 자체와는 상관없이) 외부 개발자와 사용자에게 코드를 공유할 때 필요한 환경 정의 목적 PyPI용 패키지 빌드 시 어떤 환경이 필요한지 명시하는 것이며, 버전 범위도 여러 사람이 코드 재사용 가능한 폭을 넓히기 위해 설정 .python-version은 uv에게만, 내 개발 환경을 셋업할 때만 참고하는 용도 미리 만들어 놓은 환경이 있다면 굳이 새로 설정 안 해도 무방 uv가 아직 공식 빌드 백엔드는 아니지만 해당 기능을 준비 중(이슈 #3957)
- 자세히 알아본 적은 없지만, .python-version 파일의 역할은 TOML 파서가 없는 다른 툴을 위한 호환 목적 정도로 추정
-
예전에 파이썬 스크립트가 알아서 의존성을 설치하도록 만드는 도구를 만들어보고 싶었던 경험 있음 (uvx처럼 동작하는 도구를 목표, 단 파이썬만 있어도 동작하는 방식) 다만 스크립트 맨 앞에 이상하게 보일 만한 라인을 여러 줄 붙여야 하는 점이 단점 궁금하다면 pypi에 pysolate라는 이름으로 공개 중
- 비슷하지만 널리 쓰이지는 않은 isolate 프로젝트도 있음 방식은 조금 다르지만 흥미로운 접근 사례
-
COBOL에서 영감을 받은 Grace Hopper 스타일 메시지 모든 파이썬 프로그램에는 ENVIRONMENT division을 정의해 컴파일 및 실행 환경(하드웨어, 소프트웨어 요구사항 포함)을 명확히 기재하는 문화 필요 주장 이런 구조는 다양한 시스템 간 프로그램 이식성 개선에 결정적 영향