Skip to content

스크립트 실행하기

파이썬 스크립트는 단독으로 실행할 수 있는 파일이다. 예를 들어 python <script>.py 명령어를 사용해 실행한다. uv를 사용해 스크립트를 실행하면 수동으로 환경을 관리하지 않아도 스크립트의 의존성을 자동으로 관리할 수 있다.

Note

파이썬 환경에 익숙하지 않다면: 모든 파이썬 설치에는 패키지를 설치할 수 있는 환경이 포함된다. 일반적으로 각 스크립트에 필요한 패키지를 격리하기 위해 가상 환경을 만드는 것이 권장된다. uv는 가상 환경을 자동으로 관리하며, 의존성을 선언적 방식으로 처리하는 것을 선호한다.

의존성이 없는 스크립트 실행

스크립트에 의존성이 없다면 uv run 명령어로 실행할 수 있다:

example.py
print("Hello world")
$ uv run example.py
Hello world

마찬가지로, 스크립트가 표준 라이브러리의 모듈에 의존하는 경우에도 별도의 작업이 필요하지 않다:

example.py
import os

print(os.path.expanduser("~"))
$ uv run example.py
/Users/astral

스크립트에 인자를 전달할 수도 있다:

example.py
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 패키지를 필요로 한다.

example.py
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 섹션이 추가된다:

example.py
# /// 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 버전 요구사항도 지원한다:

example.py
# /// 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).

example.py
# /// script
# dependencies = [
#   "requests",
# ]
# [tool.uv]
# exclude-newer = "2023-10-16T00:00:00Z"
# ///

import requests

print(requests.__version__)

다양한 Python 버전 사용하기

uv는 각 스크립트 실행 시 임의의 Python 버전을 지정할 수 있다. 예를 들어:

example.py
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를 사용해 실행한다:

example.pyw
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"}

마찬가지로, 의존성과 함께 동작한다:

example_pyqt.pyw
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를 사용해 도구를 실행하고 설치하는 방법을 계속 읽어본다.