스크립트 실행하기¶
파이썬 스크립트는 단독으로 실행할 수 있는 파일이다. 예를 들어 python <script>.py
명령어를 사용해 실행한다. uv를 사용해 스크립트를 실행하면 수동으로 환경을 관리하지 않아도 스크립트의 의존성을 자동으로 관리할 수 있다.
Note
파이썬 환경에 익숙하지 않다면: 모든 파이썬 설치에는 패키지를 설치할 수 있는 환경이 포함된다. 일반적으로 각 스크립트에 필요한 패키지를 격리하기 위해 가상 환경을 만드는 것이 권장된다. uv는 가상 환경을 자동으로 관리하며, 의존성을 선언적 방식으로 처리하는 것을 선호한다.
의존성이 없는 스크립트 실행¶
스크립트에 의존성이 없다면 uv run
명령어로 실행할 수 있다:
print("Hello world")
$ uv run example.py
Hello world
마찬가지로, 스크립트가 표준 라이브러리의 모듈에 의존하는 경우에도 별도의 작업이 필요하지 않다:
import os
print(os.path.expanduser("~"))
$ uv run example.py
/Users/astral
스크립트에 인자를 전달할 수도 있다:
import sys
print(" ".join(sys.argv[1:]))
$ uv run example.py test
test
$ uv run example.py hello world!
hello world!
또한, stdin에서 직접 스크립트를 읽어 실행할 수 있다:
$ echo 'print("hello world!")' | uv run -
혹은 쉘이 here-document를 지원한다면 다음과 같이 실행할 수 있다:
uv run - <<EOF
print("hello world!")
EOF
프로젝트(즉, pyproject.toml
파일이 있는 디렉토리) 내에서 uv run
을 사용하면 스크립트를 실행하기 전에 현재 프로젝트를 설치한다. 스크립트가 프로젝트에 의존하지 않는다면 --no-project
플래그를 사용해 이 과정을 건너뛸 수 있다:
$ # 주의: `--no-project` 플래그는 스크립트 이름 앞에 위치해야 한다.
$ uv run --no-project example.py
프로젝트 작업에 대한 자세한 내용은 프로젝트 가이드를 참고한다.
의존성이 있는 스크립트 실행하기¶
스크립트가 다른 패키지를 필요로 할 때, 해당 패키지는 스크립트가 실행되는 환경에 설치되어야 한다. uv는 수동으로 관리되는 장기간의 가상 환경을 사용하는 대신, 필요할 때마다 환경을 생성하는 방식을 선호한다. 이를 위해서는 스크립트에 필요한 의존성을 명시적으로 선언해야 한다. 일반적으로 프로젝트나 인라인 메타데이터를 사용해 의존성을 선언하는 것이 권장되지만, uv는 실행 시마다 의존성을 요청하는 방식도 지원한다.
예를 들어, 다음 스크립트는 rich
패키지를 필요로 한다.
import time
from rich.progress import track
for i in track(range(20), description="For example:"):
time.sleep(0.05)
의존성을 지정하지 않고 이 스크립트를 실행하면 실패한다:
$ uv run --no-project example.py
Traceback (most recent call last):
File "/Users/astral/example.py", line 2, in <module>
from rich.progress import track
ModuleNotFoundError: No module named 'rich'
--with
옵션을 사용해 의존성을 요청할 수 있다:
$ uv run --with rich example.py
For example: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:01
특정 버전이 필요한 경우, 요청한 의존성에 제약 조건을 추가할 수 있다:
$ uv run --with 'rich>12,<13' example.py
--with
옵션을 반복해 여러 의존성을 요청할 수도 있다.
uv run
이 프로젝트 내에서 사용되는 경우, 이러한 의존성은 프로젝트의 의존성에 _추가_로 포함된다. 이 동작을 원하지 않으면 --no-project
플래그를 사용한다.
파이썬 스크립트 생성하기¶
파이썬은 최근에 인라인 스크립트 메타데이터를 위한 표준 형식을 추가했다. 이를 통해 파이썬 버전을 선택하고 의존성을 정의할 수 있다. uv init --script
명령어를 사용해 인라인 메타데이터와 함께 스크립트를 초기화한다:
$ uv init --script example.py --python 3.12
스크립트 의존성 선언¶
인라인 메타데이터 형식을 사용하면 스크립트 내부에서 의존성을 직접 선언할 수 있다.
uv는 인라인 스크립트 메타데이터를 추가하고 업데이트하는 기능을 제공한다. uv add --script
명령어를 사용해 스크립트의 의존성을 선언할 수 있다:
$ uv add --script example.py 'requests<3' 'rich'
이 명령어를 실행하면 스크립트 상단에 TOML 형식으로 의존성을 선언하는 script
섹션이 추가된다:
# /// script
# dependencies = [
# "requests<3",
# "rich",
# ]
# ///
import requests
from rich.pretty import pprint
resp = requests.get("https://peps.python.org/api/peps.json")
data = resp.json()
pprint([(k, v["title"]) for k, v in data.items()][:10])
uv는 스크립트 실행에 필요한 의존성을 자동으로 포함한 환경을 생성한다. 예를 들어 다음과 같이 실행할 수 있다:
$ uv run example.py
[
│ ('1', 'PEP Purpose and Guidelines'),
│ ('2', 'Procedure for Adding New Modules'),
│ ('3', 'Guidelines for Handling Bug Reports'),
│ ('4', 'Deprecation of Standard Modules'),
│ ('5', 'Guidelines for Language Evolution'),
│ ('6', 'Bug Fix Releases'),
│ ('7', 'Style Guide for C Code'),
│ ('8', 'Style Guide for Python Code'),
│ ('9', 'Sample Plaintext PEP Template'),
│ ('10', 'Voting Guidelines')
]
중요
인라인 스크립트 메타데이터를 사용할 때는 uv run
명령어를 프로젝트에서 사용하더라도 프로젝트의 의존성은 무시된다. --no-project
플래그를 사용할 필요가 없다.
uv는 Python 버전 요구사항도 지원한다:
# /// script
# requires-python = ">=3.12"
# dependencies = []
# ///
# Python 3.12에 추가된 문법 사용
type Point = tuple[float, float]
print(Point)
참고
dependencies
필드는 비어 있어도 반드시 제공해야 한다.
uv run
명령어는 필요한 Python 버전을 찾아 사용한다. 해당 버전이 설치되어 있지 않으면 자동으로 다운로드한다. 자세한 내용은 Python 버전 문서를 참고한다.
대체 패키지 인덱스 사용하기¶
의존성을 해결하기 위해 대체 패키지 인덱스를 사용하려면 --index
옵션으로 인덱스를 지정한다:
$ uv add --index "https://example.com/simple" --script example.py 'requests<3' 'rich'
이렇게 하면 인라인 메타데이터에 패키지 데이터가 포함된다:
# [[tool.uv.index]]
# url = "https://example.com/simple"
패키지 인덱스에 접근하려면 인증이 필요한 경우, 패키지 인덱스 문서를 참고한다.
의존성 잠금¶
uv는 PEP 723 스크립트의 의존성을 잠글 수 있다. 이때 uv.lock
파일 형식을 사용한다. 프로젝트와 달리 스크립트는 uv lock
명령어를 통해 명시적으로 잠가야 한다:
$ uv lock --script example.py
uv lock --script
를 실행하면 스크립트 파일 옆에 .lock
파일이 생성된다. 예를 들어, example.py.lock
파일이 만들어진다.
한 번 잠근 후에는 uv run --script
, uv add --script
, uv export --script
, uv tree --script
등의 작업에서 잠긴 의존성을 재사용한다. 필요할 경우 lock 파일을 업데이트한다.
만약 lock 파일이 없다면, uv export --script
같은 명령어는 정상적으로 동작하지만 lock 파일을 생성하지는 않는다.
재현성 개선¶
의존성을 고정하는 것 외에도, uv는 인라인 스크립트 메타데이터의 tool.uv
섹션에 exclude-newer
필드를 지원한다. 이를 통해 특정 날짜 이전에 릴리스된 배포판만 고려하도록 제한할 수 있다. 이 기능은 나중에 스크립트를 실행할 때 재현성을 높이는 데 유용하다.
날짜는 RFC 3339 타임스탬프 형식으로 지정해야 한다 (예: 2006-12-02T02:07:43Z
).
# /// script
# dependencies = [
# "requests",
# ]
# [tool.uv]
# exclude-newer = "2023-10-16T00:00:00Z"
# ///
import requests
print(requests.__version__)
다양한 Python 버전 사용하기¶
uv는 각 스크립트 실행 시 임의의 Python 버전을 지정할 수 있다. 예를 들어:
import sys
print(".".join(map(str, sys.version_info[:3])))
$ # 기본 Python 버전 사용 (시스템마다 다를 수 있음)
$ uv run example.py
3.12.6
$ # 특정 Python 버전 사용
$ uv run --python 3.10 example.py
3.10.15
Python 버전 지정에 대한 자세한 내용은 Python 버전 요청 문서를 참고한다.
GUI 스크립트 사용하기¶
Windows에서 uv
는 .pyw
확장자로 끝나는 스크립트를 pythonw
를 사용해 실행한다:
from tkinter import Tk, ttk
root = Tk()
root.title("uv")
frm = ttk.Frame(root, padding=10)
frm.grid()
ttk.Label(frm, text="Hello World").grid(column=0, row=0)
root.mainloop()
PS> uv run example.pyw
{: style="height:50px;width:150px"}
마찬가지로, 의존성과 함께 동작한다:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QGridLayout
app = QApplication(sys.argv)
widget = QWidget()
grid = QGridLayout()
text_label = QLabel()
text_label.setText("Hello World!")
grid.addWidget(text_label)
widget.setLayout(grid)
widget.setGeometry(100, 100, 200, 50)
widget.setWindowTitle("uv")
widget.show()
sys.exit(app.exec_())
PS> uv run --with PyQt5 example_pyqt.pyw
{: style="height:50px;width:150px"}
다음 단계¶
uv run
에 대해 더 알아보려면 커맨드 레퍼런스를 확인한다.
또는 uv를 사용해 도구를 실행하고 설치하는 방법을 계속 읽어본다.