UV?

  • ํŒŒ์ด์ฌ ํ”„๋กœ์ ํŠธ ๊ด€๋ฆฌ all in one ๋„๊ตฌ
  • rust๊ธฐ๋ฐ˜์œผ๋กœ ๋งค์šฐ ๋น ๋ฆ„
  • pip, virtualenv, venv, conda, pyenv, pipenv, poetry ๋“ฑโ€ฆ ์ˆ˜๋งŽ์€ ๋„๊ตฌ๋“ค ๋Œ€์‹  uv ์‚ฌ์šฉํ•ฉ์‹œ๋‹ค
  • https://docs.astral.sh/uv/

pyenv ํ”„๋กœ์ ํŠธ์—์„œ uv ํ”„๋กœ์ ํŠธ๋กœ ๋ฐ”๊พธ๊ธฐ

  1. pyenv ๊ฐ€์ƒํ™˜๊ฒฝ ํ™œ์„ฑํ™” ์ƒํƒœ์—์„œ pip freeze > requirements.txt
  2. .python-version์ด ๊ฐ€์ƒํ™˜๊ฒฝ ์ด๋ฆ„์œผ๋กœ ๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ ๋ฒ„์ „์œผ๋กœ ์ˆ˜์ •(uv ํ˜ธํ™˜ ๋ชฉ์ )
  3. ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ ๊ฒฝ๋กœ์—์„œ uv init --bare ๋กœ pyproject.toml ์ƒ์„ฑ
  4. uv add -r requirements.txt ๋กœ .venv/, uv.lock ์ƒ์„ฑ

1. ์„ค์น˜

๋‹ค์–‘ํ•œ ํ”Œ๋žซํผ์—์„œ CLI๋กœ ์„ค์น˜๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค

# Mac/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh 
 
# Window
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" 
 
# HomeBrew 
brew install uv
 
# PyPI : pip์œผ๋กœ๋„ ์„ค์น˜ ๊ฐ€๋Šฅ
pip install uv

2. Python ์„ค์น˜

uv๋ฅผ ํ†ตํ•ด ๋‹ค์–‘ํ•œ python ๋ฒ„์ „์„ ์‰ฝ๊ฒŒ ์„ค์น˜ํ•˜๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค

uv python list
# ์„ค์น˜๋œ, ์„ค์น˜ ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ํŒŒ์ด์ฌ ๋ฒ„์ „ ์ตœ์‹  patch ํ™•์ธ๊ฐ€๋Šฅ (pyenv๋“ฑ์œผ๋กœ ์„ค์น˜๋œ ๊ฒƒ๋„ ๋‚˜์˜ด)
uv python list                    
 
uv python list --only-installed   # ์„ค์น˜๋œ ๊ฒƒ๋งŒ
uv python list --only-downloads   # ๋‹ค์šด๋กœ๋“œ ๊ฐ€๋Šฅํ•œ ๊ฒƒ๋งŒ
uv python list --managed-python   # uv๊ฐ€ ์„ค์น˜/๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ๋งŒ
uv python list --all-versions     # ๊ฐ€๋Šฅํ•œ patch ์ „๋ถ€
uv python list --all-platforms    # ๊ฐ€๋Šฅํ•œ patch ์ „๋ถ€
 
uv python list 3.13               # 3.13 ๋ฒ„์ „ ๋ฆฌ์ŠคํŠธ
uv python install
# .python-version(s) ํŒŒ์ผ์— ์žˆ๋Š” ๋ฒ„์ „ (๋ชจ๋‘) ์„ค์น˜, ์—†์œผ๋ฉด ๊ฐ€์žฅ ์ตœ์‹  ๋ฒ„์ „ ์„ค์น˜
uv python install                 
 
uv python install --default       # ์œ„์™€ ๋™์ผ, python/python3 executables ์„ค์น˜
 
uv python install 3.12 --default  # 3.12 ๋ฒ„์ „ ์„ค์น˜
uv python install '>=3.8, <3.10'  # 3.8 ์ด์ƒ 3.10 ๋ฏธ๋งŒ ๋ฒ„์ „ ์„ค์น˜
uv python install 3.9 3.10 3.11   # ์—ฌ๋Ÿฌ ๋ฒ„์ „ ๋™์‹œ ์„ค์น˜
uv python install pypy            # ํŠน์ • implementation ์„ค์น˜
 
uv python install -f 3.12         # ์กด์žฌํ•˜๋Š” executables ๋Œ€์ฒด (reinstall)
uv python pin

ํ˜„์žฌ ๊ฒฝ๋กœ์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์€ python ๋ฒ„์ „์„ ๊ณ ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค (pyenv local, global๊ณผ ๋™์ผ)

uv python pin                    # ํ˜„์žฌ .python-version ๋ฒ„์ „ ์ถœ๋ ฅ
uv python pin 3.12.9             # 3.12.9๋กœ .python-version ํŒŒ์ผ์ด ์ƒ์„ฑ (์—†์œผ๋ฉด ์„ค์น˜)
uv python pin --global 3.12.4    # 3.12.4๋ฅผ ์ „์—ญ python์œผ๋กœ ์„ค์ •
uv python uninstall
uv python uninstall 3.12.9       # 3.12.9 ๋ฒ„์ „ uninstall
uv python uninstall --all        # ์„ค์น˜๋œ ๋ชจ๋“  ๋ฒ„์ „ uninstall
uv python upgrade (Experimental)
uv python upgrade 3.12           # 3.12๋ฒ„์ „์„ ๊ฐ€์žฅ ์ตœ์‹  path release๋กœ ์—…๋ฐ์ดํŠธ
uv python upgrade                # ์„ค์น˜๋œ ๋ชจ๋“  ๋ฒ„์ „ ์—…๋ฐ์ดํŠธ
uv python find
uv python find                   # ํ˜„์žฌ ๊ฒฝ๋กœ ๊ธฐ์ค€ ์„ค์ •๋œ python executable
uv python find '>3.12'           # 3.13๋ณด๋‹ค ํฐ ๋ฒ„์ „ python executable 
uv python find 3.13              # 3.13.x์™€ ๋งค์นญ๋˜๋Š” python executable 

3. uv ํ”„๋กœ์ ํŠธ

๊ธฐ์กด์— ์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•˜๋ ค๋ฉด ์›ํ•˜๋Š” python ๋ฒ„์ „์„ ์„ค์น˜ํ•˜๊ณ , ๊ฐ€์ƒํ™˜๊ฒฝ์„ ๋งŒ๋“ค๊ณ , ํ™œ์„ฑํ™” ์‹œํ‚ค๊ณ , ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•˜๋Š” ๊ณผ์ • ๋“ฑ์„ ๊ฑฐ์ณ์•ผ ํ–ˆ์ง€๋งŒ, ์ด์ œ uv๋ฅผ ํ†ตํ•ด ๋ฐ”๋กœ ํ™˜๊ฒฝ์„ ํŽธํ•˜๊ฒŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค

uv์˜ ํ•ต์‹ฌ์ด๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋Š” 5๊ฐœ์˜ ๋ช…๋ น์–ด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค

  1. uv init
  2. uv add
  3. uv lock
  4. uv sync
  5. uv run
uv init

python ๋ฒ„์ „์„ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฒƒ์„ ์ฐพ์œผ๋ฉฐ, ๊ทธ๊ฒƒ๋„ ์—†๋‹ค๋ฉด ์„ค์น˜๊นŒ์ง€ ์ง์ ‘ ์ง„ํ–‰ํ•œ๋‹ค

uv init myproj              # myproj ํด๋” ๋งŒ๋“ค๊ณ  ๊ทธ ์•ˆ์— ํ•„์š”ํ•œ ๊ธฐ๋ณธ ํŒŒ์ผ ์ƒ์„ฑ
uv init                     # ํ˜„์žฌ ํด๋”์—์„œ ๊ธฐ๋ณธ ํŒŒ์ผ ์ƒ์„ฑ
 
uv init --app myapp         # ์•ฑ ๋งŒ๋“œ๋Š” ํ”„๋กœ์ ํŠธ (๊ธฐ๋ณธ๊ฐ’)
uv init --lib mylib         # ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋งŒ๋“œ๋Š” ํ”„๋กœ์ ํŠธ, src/mylib/ ํด๋” ์ƒ์„ฑ
uv init --package pkg       # ๋นŒ๋“œ/๋ฐฐํฌ ๊ฐ€๋Šฅํ•œ ํŒจํ‚ค์ง€ ๊ตฌ์กฐ๋กœ ์„ธํŒ… src/pkg/ ํด๋” ์ƒ์„ฑ
 
uv init myapp --python 3.11 # ์ด ํ”„๋กœ์ ํŠธ๋Š” ์ตœ์†Œ 3.11 ์ด์ƒ์œผ๋กœ ์‹œ์ž‘ (.python-version๋„ ๋งž์ถฐ ์ƒ์„ฑ)
uv init myapp --no-pin-python # .python-verseion ์•„์˜ˆ ์•ˆ๋งŒ๋“ค๊ณ  ์‹œ์ž‘
 
uv init --bare              # pyproject.toml๋งŒ ๋งŒ๋“ค๊ธฐ (๊ธฐ์กด repo์— ์ตœ์†Œํ•œ์˜ ๋ณ€ํ™”๋งŒ ์ค˜์„œ uv ๋„์ž…)
์˜ˆ์‹œ

uv init์œผ๋กœ ์ƒ์„ฑํ•œ ํ”„๋กœ์ ํŠธ ๋‚ด๋ถ€๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค (--app ์˜ต์…˜๊ธฐ์ค€)

git ์ดˆ๊ธฐํ™”, main.py ๋ฐ README.md ๊นŒ์ง€ ์ƒ์„ฑํ•ด์ฃผ๋ฉฐ,
python ๋ฒ„์ „ ๋ฐ ์˜์กด์„ฑ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ .python-version, pyproject.toml ํŒŒ์ผ๋„ ๋ณด์ธ๋‹ค

  • .python-version : ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉํ•  python ๋ฒ„์ „ ์„ ์–ธ
  • pyproject.toml : ํ”„๋กœ์ ํŠธ ํ‘œ์ค€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ + ์˜์กด์„ฑ ๋ฒ”์œ„ ์„ ์–ธ (requests>=2, <3 ๋“ฑ)
    [project]
    name = "myproj"
    version = "0.1.0"
    description = "Add your description here"
    readme = "README.md"
    requires-python = ">=3.12"
    dependencies = []
    • project์˜ ์ด๋ฆ„, ๋ฒ„์ „, ์„ค๋ช…์ด ํฌํ•จ
    • requires-python : ์ตœ์†Œ ์š”๊ตฌ ํŒŒ์ด์ฌ ๋ฒ„์ „ ๋ช…์‹œ
    • dependencies : ์ถ”ํ›„ uv add๋กœ ํ•„์š”ํ•œ ํŒจํ‚ค์ง€ ์ถ”๊ฐ€
uv add

python ํŒจํ‚ค์ง€ ์„ค์น˜๋ฅผ ์œ„ํ•ด์„œ๋Š” pip install ๋Œ€์‹  ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค
pyproject.toml์— dependency๊ฐ€ ์ถ”๊ฐ€๋˜๋ฉฐ ๋™์‹œ์— ๊ฐ€์ƒํ™˜๊ฒฝ์ด .venv/ํด๋”๋กœ ์ƒ์„ฑ๋œ๋‹ค

uv add pandas numpy
 
uv add 'pandas>=2'
uv add "requests>=2.5; python_version >= '3.10'"   # marker ์ถ”๊ฐ€. ํŠน์ • ์กฐ๊ฑด์—์„œ ํŠน์ • ๋ฒ„์ „ ์‚ฌ์šฉ ์ง€์ •
 
uv add --dev pytest                                # ๊ฐœ๋ฐœ์šฉ ์˜์กด์„ฑ dev๊ทธ๋ฃน์œผ๋กœ ์ถ”๊ฐ€
 
# ์ž„์˜์˜ ๊ทธ๋ฃน๋งŒ๋“ค์–ด์„œ ์˜์กด์„ฑ ์ถ”๊ฐ€. uv run, syncํ•  ๋•Œ ์ง€์ • ๊ฐ€๋Šฅ
uv add --group lint ruff                           
 
# pyproject.toml๋งŒ ์ˆ˜์ •ํ•˜๊ณ  .venv์—๋Š” ์„ค์น˜ X. ๋‚˜์ค‘์— uv sync๋กœ ํ•œ๋ฒˆ์— ๋ฐ˜์˜
uv add --no-sync pandas                            
์˜ˆ์‹œ

uv add pandas๋ฅผ ํ•œ ํ›„, pyproject.toml์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณ€ํ•œ๋‹ค

[project]
name = "myproj"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
    "pandas>=3.0.0",
]

๊ทธ๋ฆฌ๊ณ  uv.lock์ด๋ผ๋Š” ํŒŒ์ผ๋„ ์ƒ๊ธฐ๋Š”๋ฐ ๊ทธ ์•ˆ์„ ๋ณด๋ฉด pandas๊ฐ€ โ€˜3.0.0โ€™ ๋ฒ„์ „์œผ๋กœ ๊ณ ์ •๋˜์–ด ์žˆ๊ณ  numpy ๋“ฑ ๋‹ค๋ฅธ ํŒจํ‚ค์ง€๋„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค

version = 1
revision = 3
requires-python = ">=3.12"
resolution-markers = [...]
 
[[package]]
name = "numpy"
version = "2.4.1"
source = {...}
sdist = {...}
wheels = [...]
 
[[package]]
name = "pandas"
version = "3.0.0"
...
 
[[package]]
name = "python-dateutil"
...
 
[[package]]
name = "six"
...
 
[[package]]
name = "tzdata"
...

uv tree๋ฅผ ์‹คํ–‰ํ•˜๋ฉด pandas์— ํ•„์š”ํ•œ ๋‹ค๋ฅธ ์˜์กด์„ฑ ์žˆ๋Š” ํŒจํ‚ค์ง€๋“ค์ด ํ•จ๊ป˜ ์„ค์น˜๋œ ๊ฒƒ์ž„์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค

์ฆ‰, uv๋Š” pyproject.toml์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์‹ค์ œ ์„ค์น˜๋˜๋Š” ํŒจํ‚ค์ง€ ๋ชฉ๋ก๊ณผ ๋ฒ„์ „์„ uv.lock์— ๊ธฐ๋ก(resolve)ํ•˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค

Resolve์™€ Install

  • resolve : pyproject.toml โ†’ uv.lock
    • ๊ฐ ํŒจํ‚ค์ง€์˜ ์˜์กด์„ฑ ํŠธ๋ฆฌ๋ฅผ ์ „๋ถ€ ํ™•์ธํ•ด์„œ ๋ชจ๋“  ์ œ์•ฝ์„ ๋™์‹œ์— ๋งŒ์กฑํ•˜๋Š” ์กฐํ•ฉ ํƒ์ƒ‰ํ•˜์—ฌ ์ €์žฅ
  • install : uv.lock โ†’ ํŒจํ‚ค์ง€ ์„ค์น˜
    • ์‹ค์ œ ๊ฐ€์ƒํ™˜๊ฒฝ์— ํŒจํ‚ค์ง€ ์„ค์น˜
  • ๊ด€๋ จ ๋ช…๋ น์–ด
    • uv lock : resolve๋งŒ ์ˆ˜ํ–‰
    • uv add : ์˜์กด์„ฑ์ถ”๊ฐ€ โ†’ resolve โ†’ install
    • uv sync : (ํ•„์š”์‹œ) resolve โ†’ install
      • uv sync --locked : install๋งŒ ํ—ˆ์šฉ (pyproject์™€ ๋‹ค๋ฅด๋ฉด ์‹คํ–‰์•ˆ๋จ)
    • uv run : (ํ•„์š”์‹œ) resolve โ†’ (ํ•„์š”์‹œ) install โ†’ ์‹คํ–‰
      • uv run --locked : install ๋ฐ ์‹คํ–‰๋งŒ ํ—ˆ์šฉ (pyproject์™€ ๋‹ค๋ฅด๋ฉด ์‹คํ–‰์•ˆ๋จ)
uv lock

์ง์ ‘ uv lock์œผ๋กœ resolve๋งŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค
์•ž์„œ uv add --no-sync๋กœ pyproject.toml์—๋งŒ dependency๋ฅผ ์ถ”๊ฐ€ํ–ˆ๊ฑฐ๋‚˜ ํŠน์ • ๋ชฉ์ ์„ ์œ„ํ•ด resolve๋ฅผ ๋ถ„๋ฆฌ์‹œํ‚ค๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค

uv lock                  # pyproject.toml ๊ธฐ์ค€์œผ๋กœ uv.lock๋งŒ ์ƒ์„ฑ
uv sync

ํ˜„์žฌ uv.lock ๋‚ด์šฉ์„ ๊ธฐ์ค€์œผ๋กœ ๋ชจ๋“  ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ด์„œ ํ”„๋กœ์ ํŠธ environment๋ฅผ ์—…๋ฐ์ดํŠธํ•œ๋‹ค
uv.lock๋งŒ ์žˆ์œผ๋ฉด uv sync๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ํ”„๋กœ์ ํŠธ์˜ ํ™˜๊ฒฝ์„ ๋™์ผํ•˜๊ฒŒ ๋ณต์ œํ•  ์ˆ˜ ์žˆ๋‹ค

uv sync                     # uv.lock ๊ธฐ๋ฐ˜์œผ๋กœ ์„ค์น˜ (defalut ๊ทธ๋ฃน๋งŒ)
uv sync --group lint        # ํŠน์ • ๊ทธ๋ฃน ํฌํ•จํ•ด์„œ ์„ค์น˜
uv sync --all-groups        # ๋ชจ๋“  ๊ทธ๋ฃน ์„ค์น˜
 
uv sync --locked            # uv.lock ์—…๋ฐ์ดํŠธ ์—†์ด ํ˜„์žฌ ๊ทธ๋Œ€๋กœ ์„ค์น˜ (์ตœ์‹  ์•„๋‹ˆ๋ฉด ์—๋Ÿฌ)
uv run

uv ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ํ”„๋กœ์ ํŠธ์—์„œ ํŒŒ์ด์ฌ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ๋Š” ์•ž์— uv run์„ ๋ถ™์—ฌ์ฃผ๋ฉด ๋œ๋‹ค
๊ทธ๋Ÿผ ๊ฐ€์ƒํ™˜๊ฒฝ(uv add๋กœ ์ƒ๊ธด)์„ ํ™œ์„ฑํ™”ํ•  ํ•„์š”๋„ ์—†๊ณ  ์•Œ์•„์„œ ๊ฒฝ๋กœ๋ฅผ ์žก์•„์„œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค

๊ทธ๋ฆฌ๊ณ  ์ด๋•Œ pyproject.toml๊ณผ uv.lock๊ณผ ์‹ค์ œ ์„ค์น˜๋œ ํŒจํ‚ค์ง€๊ฐ€ ์ผ์น˜๋˜๋„๋ก ๋™๊ธฐํ™”์ฒ˜๋ฆฌ๋ฅผ ์•Œ์•„์„œ ํ•ด์ค€๋‹ค

uv run python main.py                   # resolve -> install -> ์‹คํ–‰
 
uv run --locked python main.py          # ํ˜„์žฌ uv.lock ๊ธฐ์ค€ ์‹คํ–‰ (์ตœ์‹  ์•„๋‹ˆ๋ฉด ์—๋Ÿฌ)
๊ธฐํƒ€
uv export : uv.lock์„ ๋‹ค๋ฅธ lockfile ํ˜•ํƒœ๋กœ ์ถœ๋ ฅ
uv export --format requirements.txt    # ๊ธฐ๋ณธ๊ฐ’
uv export --format pylock.toml
 
uv export > requirements.txt           # ํŒŒ์ผ๋กœ ์ €์žฅ
 
uv export --locked --format requirments.txt  # uv.lock์ด ์ตœ์‹ ์ธ ๊ฒฝ์šฐ์—๋งŒ export (์•„๋‹ˆ๋ฉด ์—๋Ÿฌ)
uv export --frozen --format requirments.txt  # uv.lock ์ˆ˜์ •์•ˆํ•˜๊ณ  ๊ทธ๋Œ€๋กœ export
uv remove : ์„ค์น˜ํ•œ ํŒจํ‚ค์ง€ ์ œ๊ฑฐ ๋ฐ pyproject.toml์—์„œ๋„ ์ œ๊ฑฐ
uv remove pandas
uv version : pyproject.toml์— ์žˆ๋Š” ํ”„๋กœ์ ํŠธ์˜ ๋ฒ„์ „์„ ์ฝ๊ธฐ ๋ฐ ์—…๋ฐ์ดํŠธ
uv version                    # ์˜ˆ์‹œ : uv-test 0.1.0
 
uv version --bump minor       # ์˜ˆ์‹œ : uv-test 0.2.0
uv version --bump major       # ์˜ˆ์‹œ : uv-test 1.0.0
uv version --bump patch       # ์˜ˆ์‹œ : uv-test 1.0.1
uv tree : ํ˜„์žฌ ์„ค์น˜๋œ ํŒจํ‚ค์ง€๋“ค์˜ ์˜์กด์„ฑ์„ tree ํ˜•ํƒœ๋กœ ์ถœ๋ ฅ
uv-test v0.1.0
โ”œโ”€โ”€ matplotlib v3.10.8
โ”‚   โ”œโ”€โ”€ contourpy v1.3.3
โ”‚   โ”‚   โ””โ”€โ”€ numpy v2.4.1
โ”‚   โ”œโ”€โ”€ cycler v0.12.1
โ”‚   โ”œโ”€โ”€ fonttools v4.61.1
โ”‚   โ”œโ”€โ”€ kiwisolver v1.4.9
โ”‚   โ”œโ”€โ”€ numpy v2.4.1
โ”‚   โ”œโ”€โ”€ packaging v26.0
โ”‚   โ”œโ”€โ”€ pillow v12.1.0
โ”‚   โ”œโ”€โ”€ pyparsing v3.3.2
โ”‚   โ””โ”€โ”€ python-dateutil v2.9.0.post0
โ”‚       โ””โ”€โ”€ six v1.17.0
โ”œโ”€โ”€ numpy v2.4.1
โ”œโ”€โ”€ pandas v3.0.0
โ”‚   โ”œโ”€โ”€ numpy v2.4.1
โ”‚   โ””โ”€โ”€ python-dateutil v2.9.0.post0 (*)
โ””โ”€โ”€ pytest v9.0.2 (group: dev)
    โ”œโ”€โ”€ iniconfig v2.3.0
    โ”œโ”€โ”€ packaging v26.0
    โ”œโ”€โ”€ pluggy v1.6.0
    โ””โ”€โ”€ pygments v2.19.2
(*) Package tree already displayed

4. ๊ธฐ์กด ๋ฐฉ์‹๊ณผ ํ˜ธํ™˜์„ ์œ„ํ•œ ๋ช…๋ น์–ด

uv์˜ ๋ฉ”์ธ ๋ช…๋ น์–ด๋Š”

  • uv init
  • uv add
  • uv lock
  • uv sync
  • uv run
    ์ด์ง€๋งŒ, ๊ธฐ์กด ๋ฐฉ์‹๊ณผ ํ˜ธํ™˜์ด ๋  ์ˆ˜ ์žˆ๋„๋ก ์ข€ ๋” ์ต์ˆ™ํ•œ ํ˜•ํƒœ์˜ ๋ช…๋ น์–ด๋„ ์ œ๊ณตํ•œ๋‹ค
uv venv
uv venv --python 3.11.6               # .venv ํด๋”์— 3.11.6 ๋ฒ„์ „์œผ๋กœ ๊ฐ€์ƒํ™˜๊ฒฝ ์„ค์น˜ (์—†์œผ๋ฉด ๋‹ค์šด๋กœ๋“œ)
uv venv --no-project --python 3.11.6  # .python-version ์žˆ์–ด๋„ 3.11.6์œผ๋กœ ๊ฐ€์ƒํ™˜๊ฒฝ ์ƒ์„ฑ
uv venv --prompt myproj               # ๊ฐ€์ƒํ™˜๊ฒฝ ํ™œ์„ฑํ™”ํ•˜๋ฉด ํ”„๋กฌํ”„ํŠธ๊ฐ€ (myproj)๋กœ ๋ณด์ž„
uv venv --seed                        # venv๋งŒ๋“ค ๋•Œ, ๊ธฐ๋ณธ ํŒจํ‚ค์ง€(pip, setuptools, wheel) ์ดˆ๊ธฐ ์ฃผ์ž…
uv venv /path/to/venv --python 3.11.6 # ๊ธฐ๋ณธ .venv๋Œ€์‹  ๋‚ด๊ฐ€ ์ง€์ •ํ•œ ๊ฒฝ๋กœ์— ๊ฐ€์ƒํ™˜๊ฒฝ ์ƒ์„ฑ
uv pip

uv pip ๋ช…๋ น์–ด vs uv ๋ช…๋ น์–ด

  • uv pip compile โ†’ uv lock
  • uv pip sync โ†’ uv sync
  • uv pip install โ†’ uv add
  • uv pip freeze โ†’ uv export
uv pip compile : ์˜์กด์„ฑํŒŒ์ผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž ๊ธˆํŒŒ์ผ ์ƒ์„ฑ
uv pip compile requirements.in -o requirements.txt  # requirements.in -> requirements.txt (๊ณ ์ • ๋ฒ„์ „ ์ƒ์„ฑ)
uv pip compile pyproject.toml -o requirements.txt   # pyproject.toml -> requirements.txt
uv pip compile requirements.in requirements-dev.in -o requirements-dev.txt # dev/test ๊ฐ™์€ ์ถ”๊ฐ€ ์š”๊ตฌ๋ฅผ ํ•ฉ์ณ์„œ ๋ณ„๋„ ์ž ๊ธˆ ์ƒ์„ฑ
uv pip compile requirements.in -o requirements.txt --upgrade-package ruff # ํŠน์ • ํŒจํ‚ค์ง€๋งŒ ๋ฒ„์ „ ์˜ฌ๋ ค์„œ ์žฌ์ž ๊ธˆ (๋‚˜๋จธ์ง€๋Š” ์ตœ๋Œ€ํ•œ ์œ ์ง€)
uv pip compile requirements.in --format pylock.toml -o pylock.toml # ํ‘œ์ค€ lockfile(pylock.toml)๋กœ ์ƒ์„ฑ (ํˆด ํ˜ธํ™˜ / ํ‘œ์ค€ ํฌ๋งท ํ•„์š”ํ•  ๋•Œ)
uv pip sync : ์ž ๊ธˆ ํŒŒ์ผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ™˜๊ฒฝ ์ƒ์„ฑ
uv pip sync requirements.txt
uv pip sync pyloc.toml
uv pip sync --python /path/to/venv/bin/python requirements.txt
uv pip install: ํŒจํ‚ค์ง€ ์ถ”๊ฐ€/์—…๋ฐ์ดํŠธ ์„ค์น˜
uv pip install flask                    # ๋‹จ์ผ ํŒจํ‚ค์ง€ ์„ค์น˜
uv pip install "flask[dotenv]"          # extras ํฌํ•จ ํŒจํ‚ค์ง€ ์„ค์น˜
 
uv pip install -e .                     # editable ์„ค์น˜
 
uv pip install -r requirements.txt      # ํŒŒ์ผ์—์„œ ์ผ๊ด„์„ค์น˜
 
uv pip install --python /path/to/python ruff   # ํŠน์ • ํŒŒ์ด์ฌ ์ง€์ •ํ•ด์„œ ์„ค์น˜
uv pip install --system ruff                   # ์‹œ์Šคํ…œ ํŒŒ์ด์ฌ์— ์„ค์น˜(CI/์ปจํ…Œ์ด๋„ˆ์—์„œ ์ข…์ข… ์‚ฌ์šฉ)
uv pip uninstall: ํŒจํ‚ค์ง€ ์ œ๊ฑฐ
uv pip uninstall flask
uv pip freeze : ํ˜„์žฌ ํ™˜๊ฒฝ์˜ ์„ค์น˜ ํŒจํ‚ค์ง€๋ฅผ requirements.txt ์Šคํƒ€์ผ๋กœ ์ถœ๋ ฅ
uv pip freeze                           # ํ™”๋ฉด ์ถœ๋ ฅ
uv pip freeze > requirements.txt        # requirements.txt๋กœ ์ €์žฅ
๊ธฐํƒ€
uv pip list                    # ์„ค์น˜๋œ ํŒจํ‚ค์ง€ ๋ชฉ๋ก
uv pip list --format json      # json ํ˜•ํƒœ๋กœ ์ถœ๋ ฅ
 
uv pip show numpy              # ํŒจํ‚ค์ง€ ์„ค์น˜ ๊ฒฝ๋กœ
uv pip show --system numpy     # ์‹œ์Šคํ…œ ํŒŒ์ด์ฌ์—์„œ ํŒจํ‚ค์ง€ ์„ค์น˜ ๊ฒฝ๋กœ 
 
uv pip tree                    # ํŒจํ‚ค์ง€๊ฐ„ ์˜์กด์„ฑ tree๋กœ ์ถœ๋ ฅ
uv pip tree --depth 2          # tree ๊นŠ์ด ์ œํ•œ
 
uv pip check                   # ํ˜„์žฌ ํ™˜๊ฒฝ์— ์ถฉ๋Œ/๋ˆ„๋ฝ๋œ ์˜์กด์„ฑ ์ ๊ฒ€

5. ํฌ๋งคํŒ…

ํŒŒ์ด์ฌ ํŒจํ‚ค์ง€ ๊ด€๋ จ๋œ ๊ฒƒ์€ ์•„๋‹ˆ์ง€๋งŒ, uv๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์œ ์šฉํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๋กœ ํฌ๋งคํŒ…์ด ์žˆ๋‹ค

๊ธฐ์กด์—๋Š” ์ฝ”๋“œ ์Šคํƒ€์ผ, import ์Šคํƒ€์ผ์„ ์ •๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด isort, black, flake8 ๋“ฑ์„ ์‚ฌ์šฉํ–ˆ์—ˆ๋‹ค

  • isort : import ๋ฌธ ์ „์šฉ ์ •๋ฆฌ ๋„๊ตฌ
    • ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ / ์„œ๋“œํŒŒํ‹ฐ / ๋กœ์ปฌ import ๊ตฌ๋ถ„
    • ์•ŒํŒŒ๋ฒณ ์ˆœ์„œ ์ •๋ ฌ
    • unused import ์ œ๊ฑฐ(์ œํ•œ์ )
  • black : ์ฝ”๋“œ ํฌ๋งทํ„ฐ
    • ์ค„ ๊ธธ์ด, ๋“ค์—ฌ์“ฐ๊ธฐ, ๋”ฐ์˜ดํ‘œ, ์ค„๋ฐ”๊ฟˆ ๋“ฑ ๊ฐ•์ œ
  • flake8 : ๋ฆฐํ„ฐ(linter)
    • ๋ฌธ๋ฒ• ์˜ค๋ฅ˜, ์Šคํƒ€์ผ ์œ„๋ฐ˜, ์•ˆ ์“ฐ๋Š” ๋ณ€์ˆ˜ ๋“ฑ ๊ฒ€์‚ฌ
    • ์‹ค์ œ ๊ทœ์น™์€ ์—ฌ๋Ÿฌ ํ”Œ๋Ÿฌ๊ทธ์ธ(pyflakes, pycodestyle ๋“ฑ)์˜ ๋ฌถ์Œ

ํ•˜์ง€๋งŒ ruff๋Š” uv ๋ฅผ ์ œ์ž‘ํ•œ astral์‚ฌ์—์„œ ๋งŒ๋“  formatter๋กœ ์œ„ ๊ธฐ๋Šฅ๋“ค์„ ๋ชจ๋‘ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค (์ฐธ๊ณ )

ruff ๋ฅผ ์ง์ ‘ uv add๋กœ ์„ค์น˜ํ•œ ๋’ค uv run ruff format์„ ์‹คํ–‰ํ•ด๋„ ๋˜์ง€๋งŒ,
uv format๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ๋”ฐ๋กœ ์„ค์น˜ ์—†์ด ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค

uv format
uv format                       # `ruff format`๊ณผ ๋™์ผ
uv format --check               # reformatํ•  ํŒŒ์ผ ์žˆ๋Š”์ง€ ํ™•์ธ
uv format --diff                # reformatํ•˜๋ฉด ๋‹ฌ๋ผ์ง€๋Š” ๋ถ€๋ถ„ ํ™•์ธ

๋˜ํ•œ pyproject.toml์— ๊ด€๋ จ ์„ค์ •์„ ๋„ฃ์–ด ์ปค์Šคํ„ฐ๋งˆ์ด์ง•๋„ ๊ฐ€๋Šฅํ•˜๋‹ค (์ฐธ๊ณ )

[tool.ruff]
# Exclude a variety of commonly ignored directories.
exclude = [
    ".git",
    ".venv",
]
 
# Code Formatting (Black) configuration
line-length = 120
indent-width = 4
 
# Assume Python 3.12
target-version = "py312"
 
[tool.ruff.lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`)  codes by default.
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
select = ["E4", "E7", "E9", "F", "I"]
ignore = []
 
# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []
 
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
 
[tool.ruff.format]
# Like Black, use double quotes for strings.
quote-style = "double"
 
# Like Black, indent with spaces, rather than tabs.
indent-style = "space"
 
# Like Black, respect magic trailing commas.
skip-magic-trailing-comma = false
 
# Like Black, automatically detect the appropriate line ending.
line-ending = "auto"
 
# Enable auto-formatting of code examples in docstrings. Markdown,
# reStructuredText code/literal blocks and doctests are all supported.
#
# This is currently disabled by default, but it is planned for this
# to be opt-out in the future.
docstring-code-format = false
 
# Set the line length limit used when formatting code snippets in
# docstrings.
#
# This only has an effect when the `docstring-code-format` setting is
# enabled.
docstring-code-line-length = "dynamic"