python之uv使用詳解
uv:
conda create -n xxx python=3.11 -y
conda activate xxx
pip install -r requirements.txt
安裝與更新
這里只講兩種筆者認為最多涉及到的安裝與更新方式,分別是命令行standard安裝更新和pip安裝更新
standalone
本地開發(fā)機器上安裝以及更新的方式,筆者是ubuntu系統(tǒng),所以安裝使用如下指令
curl -LsSf https://astral.sh/uv/install.sh | sh
參考官網(wǎng)uv安裝
更新的指令即是:
UV_NO_MODIFY_PATH=1 uv self update
pip 安裝
pip 安裝適合在構建docker鏡像
的時候選擇的安裝方式,因為一般構建docker鏡像選擇的基礎鏡像都是一個最小化的python basic image
.
它可能只包含基本的python環(huán)境和pip,所以在Dockerfile
中使用pip安裝uv,安裝指令如下:
pip install uv
既然是pip安裝的包,更新指令就是
pip install --upgrade uv
不過這個更新指令基本使用不到,構建鏡像過程中只需要在基礎鏡像安裝uv,不需要更新uv.
創(chuàng)建以及初始化項目
關鍵指令為
uv init [OPTIONS] [PATH]
比如在python-uv
目錄下執(zhí)行uv init --python=3.11 test
命令會創(chuàng)建一個名字為test的子目錄
且子目錄作為項目根目錄,命令指定了項目運行所需的python版本為3.11
,同時在test
目錄下創(chuàng)建項目配置文件pyproject.toml
,配置文件中設置項目名稱為test
,和項目根目錄同名,同時還有一些git repo必需的文件以及文件夾,可見init指令也將項目初始化成一個git repo.先看一下目錄結構:
test ├── .git ├── .gitignore ├── main.py ├── pyproject.toml ├── .python-version └── README.md
如果init指令不指定參數(shù)PATH
則是將運行uv init所在目錄
作為項目根目錄去創(chuàng)建上面這些內(nèi)容.
此時在項目內(nèi)運行uv sync
才能看到虛擬環(huán)境目錄.venv
和uv.lock
被創(chuàng)建.
所以完整的項目初始化之后結構如下:
└── test ├── .git ├── .gitignore ├── main.py ├── pyproject.toml ├── .python-version ├── README.md ├── uv.lock └── .venv
有幾個重要文件和文件夾需要提前說明:
- .venv: 項目python虛擬環(huán)境和依賴包相關文件夾. 可以在項目目錄上下文執(zhí)行指令
source .venv/bin/activate
激活虛擬環(huán)境.但是由于使用uv來構建以及管理的項目使用uv指令居多,所以一般情況下不需要顯式的激活這個虛擬環(huán)境.同時.venv
也不應該上傳到版本控制系統(tǒng),每一次項目repo同步下來只需要運行uv sync
則可以更新本地的虛擬環(huán)境中l(wèi)ib更新到最新,繼續(xù)開發(fā)項目或者本地調(diào)試. - pyproject.toml: 存放整個項目的元數(shù)據(jù),視作項目配置文件,其內(nèi)部包含項目名稱,描述,依賴,構建,腳本以及工具等等,一般執(zhí)行uv相關命令都會涉及到更改此配置文件,官方關于此配置文件編寫教程鏈接pyproject.如果從遠端同步下來uv項目,可以執(zhí)行
uv sync
即是根據(jù)此配置文件去構建本地的開發(fā)虛擬環(huán)境和依賴包. - uv.lock 文件:項目依賴的精確版本信息,和
pyproject.toml
配置不同,配置文件一般是描述依賴的版本邊界約束
,比如某個依賴包最低版本或者最高版本.lock文件中是真正虛擬環(huán)境內(nèi)安裝的精確版本,任何涉及到更新依賴的命令比如uv sync
,uv add {package}
等操作在依賴的版本邊界約束
內(nèi)可能拉取最新的版本,就會更新此lock文件.此文件真正起作用的地方是在CI構建過程,因為虛擬環(huán)境文件不會提交到版本管理系統(tǒng),每次構建都需要拉取這些依賴,涉及到使用指令uv sync
.由于前面所說這個指令可能會拉取新的依賴版本可能引入不兼容問題,因此使用指令uv sync --frozen
強制使用uv.lock文件中的精確版本來完成構建過程,保證構建前后的版本一致,降低構建過程引入新版本帶來的風險.uv.lock文件要求必須上傳到版本控制系統(tǒng)且不可以手動更改.
這里可以看到init
甚至可以將項目初始化成一個git repo,實際可能不需要這個功能,多半情況下本地已經(jīng)有git repo了只需要創(chuàng)建以及初始化uv 項目即可,這個情況下,可以稍微調(diào)整一下init指令相關的optional參數(shù)關閉git repo相關feature即可.另外--name
也可以顯示指定項目的名稱,這樣就不是默認的以項目文件夾名字作為項目名稱了,刪除當前的test文件夾,使用如下命令創(chuàng)建新的非git repo的uv項目:
uv init test --description="a test uv project" --vcs=none --no-readme --python=3.11 --managed-python
上面指令還會生成一個main.py
的入口文件,然后在test
目錄下執(zhí)行如下命令可以自動創(chuàng)建虛擬環(huán)境
uv run main.py
輸出:
Using CPython 3.11.13 Creating virtual environment at: .venv Hello from test!
可以看到虛擬環(huán)境文件夾.venv
和uv.lock
文件都已經(jīng)創(chuàng)建好了,至此項目初始化結束.
這里簡要說明一下剛創(chuàng)建好的項目是沒有虛擬環(huán)境文件夾.venv
的,除了使用uv run main.py
之外還可以使用uv sync
命令.
本質(zhì)上uv會查看本地是否有項目指定運行的python版本,比如本項目初始化的時候指定的是3.11版本的python,沒有的話會先下載此版本python到~/.local/share/uv/python
目錄,然后再將此版本python"拷貝"到當前項目.venv
文件夾中.
可以在項目文件夾為上下文的命令行窗口中執(zhí)行指令source .venv/bin/activate
來激活此(使用deactivate
則退出)虛擬環(huán)境.只是使用uv指令的時候是完全不需要顯式激活虛擬環(huán)境這個操作.
依賴管理
uv 添加依賴有uv add
命令,它會將依賴包安裝到當前虛擬環(huán)境.venv
中同時更新pyproject.toml
中的dependencies配置.
比如需要把最新的fastapi
添加到項目中可以使用uv add "fastapi[standard]==0.116"
添加一個固定版本的fastapi
依賴包.當依賴安裝好后查看pyproject.toml
可以看到依賴已被添加
dependencies = [ "fastapi[standard]==0.116.0", ]
實際開發(fā)過程中不會這么嚴格限制一個版本,需要支持能夠獲取最新的bugfix修訂版本,所以一般依賴都會給定一個版本范圍比如fastapi
版本是0.116到0.117版本的最新修訂版本
,那么添加依賴指令變?yōu)?code>uv add "fastapi[standard]>=0.116,<0.117",此命令會覆蓋pyproject.toml
中的版本約束,同時升級虛擬環(huán)境中安裝的fastapi到0.116.x最新版本修.
當然也可以直接修改pyproject.toml
里面的fastapi依賴,如下
dependencies = [ "fastapi[standard]>=0.116,<0.117", ]
然后再運行uv sync
也能達到同樣效果.但是最佳做法還是使用uv add
增加或者修改現(xiàn)有的依賴.
刪除依賴則是uv remove
指令.
默認情況下如果安裝依賴不指定版本約束,當前會安裝最新版本,且dependencies會寫入 dep >= latest version
,比如執(zhí)行如下指令
uv add httpx
查看dependencies
dependencies = [ "httpx>=0.28.1", ]
此時安裝的是最新版本,當讓可以修改依賴的版本約束uv add "httpx>=0.28,<0.29"
限制依賴版本為0.28.x
的最新修訂版本.
uv中的依賴主要是三類,第一類是project.dependencies
項目依賴,默認情況下使用指令uv add xxx
的依賴都屬于項目依賴,這些依賴在配置文件里面回添加到[project]
配置段下的dependencies
里面.這些依賴說白了都是代碼中引入的包,代碼運行時必不可少的包.
第二種是dependency groups.就是開發(fā)所需依賴,不會被大包到項目中去,所以這類依賴也不會出現(xiàn)在[project]
配置段中,只會出現(xiàn)在[dependency-groups]
配置段中.
有兩種使用dependency groups的方法,第一種是uv add --dev xxx
把依賴放入dev這個group,可以理解為dev是內(nèi)置的dependency groups.比如將pytest
這個包放入dev
group用于項目測試.
uv add pytest --dev
配置文件
[dependency-groups] dev = [ "pytest>=8.4.1", ]
當然要刪除在dev group中的這個包也需要加上–dev flag uv remove pytest --dev
. 注意dev
這個group在uv sync
的時候也會拉取相應的依賴包.
除了dev
這個官方定義的組之外,還可以自定義組,使用指令uv add --group {group_name} {package}
實現(xiàn),比如:
uv add --group lint ruff
會創(chuàng)建一個lint
的自定義組,且添加ruff依賴到此組.
[dependency-groups] dev = [ "pytest>=8.4.1", ] lint = [ "ruff>=0.12.8", ]
刪除的話也需要添加flaguv remove ruff --group lint
自定義組在使用uv sync
時是不會拉取依賴的,需要配置[tool.uv]
下面的default-groups
追加自定義的group
[tool.uv] default-groups = ["dev", "lint"]
uv run
uv run 指令是非常強大的指令. 下面全面說明它的用法:
直接在命令行運行python代碼片段
它可以在命令行運行一段python代碼片段,比如:
uv run python -c "import sys;print(sys.executable)"
輸出當前解釋器的位置
~/.local/share/uv/python/cpython-3.11.13-linux-x86_64-gnu/bin/python3.11
直接運行項目中可執(zhí)行腳本文件
它還可以直接運行項目中的可執(zhí)行腳本文件,比如項目中存在如下python腳本文件
#!/usr/bin/env python #-*- coding:utf-8 -*- import httpx if __name__ == '__main__': print(httpx.__version__) pass
輸出結果:
0.28.1
這里用到了項目依賴httpx
,但是可以在不需要顯式激活當前虛擬環(huán)境
的情況下直接打印當前虛擬環(huán)境中的依賴包信息.
除了python腳本外,uv run還可以直接運行shell腳本,比如有如下foo.sh的shell腳本:
python cli.py
可以使用uv run bash foo.sh
也能正常運行腳本獲取打印的依賴包版本信息.
再看一個細節(jié),比如下面沒有shebang行的python腳本sample.py
if __name__ == '__main__': print('hello')
如果直接命令行運行./sample.py
肯定報錯,大家都知道需要指定python解釋器運行,比如python sample.py
使用默認的解釋器運行就不會報錯.
使用uv 運行此腳本則是命令uv run sample.py
也能正確輸出結果.uv運行python腳本的整個過程可以等價于如下操作:
uv sync source .venv/bin/activate python sample.py
當我們知道當前要運行的腳本是python時候,甚至可以直接用指令uv run -- python sample.py
運行python腳本.
運行python包中快捷指令
我們知道有些包安裝后是有在命令行運行的快捷指令的,比如環(huán)境中安裝了fastapi[standard]
,那么在命令行里面就可以使用指令fastapi dev xxx.py
以develop模式快速啟動fastapi web app. 那么在uv管理的項目里面依然可以運行這種命令行指令.
比如先在uv項目里面添加fastapi[standard]
包
uv add "fastapi[standard]"
編寫一個fastapi web app 的 main.py文件:
#-*- coding:utf-8 -*- from fastapi import FastAPI app = FastAPI(title='test') @app.get("/") async def index(): return 'hello'
uv 命令 develop 模式啟動此app:
uv run -- fastapi dev main.py --port 8090
這里 uv run --
中的--
保證此字符后面都是fastapi的命令行指令與其運行參數(shù),而不會被解析成uv run
參數(shù),非常關鍵。
curl測試以及輸出結果:
curl http://localhost:8090 "hello"
因此基本上使用uv在命令行運行這些包中的快捷指令
基本就是 uv run -- {package cli cmd} {cli params}
這樣的形式.
uv項目本地運行調(diào)試細節(jié)
上一部分完全探討了uv run這個指令,這一部分詳細探討uv項目本地運行以及調(diào)試的細節(jié).
這里涉及到兩種運行:
- 1. vscode 中運行調(diào)試
- 2. 本地命令行中調(diào)試運行細節(jié)
vscode 中運行調(diào)試uv項目
和之前筆者用conda創(chuàng)建虛擬環(huán)境,在vscode中調(diào)試項目那一套基本操作類似.
這里首先需要創(chuàng)建項目vscode debug所需的launch.json文件,文件最基本內(nèi)容如下,可以根據(jù)需求進行更改.
{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "uv test single file", "type": "debugpy", "request": "launch", "program": "${file}", "console": "integratedTerminal", "env": { "OPENAI_BASE_URL": "https://api.deepseek.com", "OPENAI_API_KEY": "xxxx" } }, { "name": "uv test fastapi dev", "type": "debugpy", "request": "launch", "module": "fastapi", "console": "integratedTerminal", "args": [ "dev", "main.py", "--port", "8090" ], "env": { "OPENAI_BASE_URL": "https://api.deepseek.com", "OPENAI_API_KEY": "xxxx" } } ] }
筆者這里創(chuàng)建了兩個相關配置uv test single file
是項目單文件的debug配置,另一個uv test fastapi dev
是整個fastapi項目develop模式啟動調(diào)試的配置.其中比較關鍵的配置字段是module
,需要指定為fastapi
指令,其本質(zhì)上還是相當于如下uv指令
OPENAI_BASE_URL='https://api.deepseek.com' OPENAI_API_KEY=xxx uv run -- fastapi dev main.py --port 8090
啟動應用.
vscode中需要給項目指定python解釋器,這一步其實就是將當前虛擬環(huán)境中的python解釋器配置為項目的python解釋器,具體在vscode中做法為:
View-->Command Pattle...-->Python:Select Interpreter-->{選擇你的項目文件夾}-->解釋器選擇本項目虛擬環(huán)境中的python(./.venv/bin/python)
這樣vscode中debug選擇Python Debugger: Debug using launch.json
選擇uv test fastapi dev
則是以develop模式啟動整個fastapi項目本地調(diào)試,選擇uv test single file
則是調(diào)試項目中的單個文件.總之這是筆者總結下來的比較舒服的在vscode中開發(fā)uv項目的相關配置.
命令行運行
如果不使用vscode直接在命令行啟動運行,那更簡單了,直接參考前面講到的uv運行python包中的快捷指令那一塊使用uv run即可,具體指令如下:
OPENAI_BASE_URL='https://api.deepseek.com' OPENAI_API_KEY=xxx uv run -- fastapi dev main.py --port 8090
深入理解 uv lock, uv sync, uv lock
uv lock 行為解析
如果項目中沒有uv.lock
文件,那么會根據(jù)pyproject.toml
解析出的精確依賴版本
生成uv.lock文件;如果項目中存在uv.lock
文件,根據(jù)解析出的精確依賴版本
更新情況決定是否更新當前uv.lock文件.
其中有一個指令uv lock --check
則是解析pyproject.toml檢查是否更行uv.lock.
uv sync 行為解析
uv sync首先需要去檢查uv.lock文件是否是最新的,如果不是,則解析pyproject.toml更新uv.lock中依賴的精確版本,最后再根據(jù)uv.lock文件同步下載依賴包到虛擬環(huán)境.
uv sync有兩個關鍵參數(shù)--locked
和--frozen
,他倆區(qū)別如下:
參數(shù) | 說明 |
---|---|
–locked | 檢查uv.lock是否是最新,如果要更新則報錯,如果本身就是最新的則繼續(xù)同步過程 |
–frozen | 不進行uv.lock的更新檢測,直接以uv.lock中依賴包版本同步虛擬環(huán)境中的依賴 |
使用較多的指令是uv sync --locked
,主要用于CI過程,保證提交的uv.lock文件中的依賴一定是最新版本,如果報錯,證明uv.lock不是最新版本,需要開發(fā)提交最新的uv.lock到repo再觸發(fā)ci過程.必須保證uv.lock一致性.
uv run 行為解析
可以簡單理解默認的uv run之前要進行uv sync操作.
其實–frozen參數(shù)其實是是在實際部署運行時候使用,uv run --frozen xxx
表示不再check uv.lock是否更新,直接運行程序.因為前面CI過程已經(jīng)保證uv.lock一致且依賴包已經(jīng)最新.如果是容器部署的uv應用,基本上--frozen
參數(shù)會出現(xiàn)在容器入口程序中.
uv項目的docker file
根據(jù)上面的所有信息,基本上可以總結出一個uv項目的Dockerfile模板:
FROM python:3.11.13-slim WORKDIR /app COPY ./ ./ RUN pip install uv RUN uv sync --locked EXPOSE 8090 ENTRYPOINT ["uv", "run", "--frozen", "--", "fastapi", "run", "main.py", "--host", "0.0.0.0", "--port", "8090"]
項目相關文件:
- main.py
#-*- coding:utf-8 -*- from fastapi import FastAPI app = FastAPI(title='test') @app.get("/") async def index(): return 'hello'
- pyproject.toml
[project] name = "test" version = "0.1.0" description = "a test uv project" readme = "README.md" requires-python = ">=3.11" dependencies = [ "click>=8.2.1", "fastapi[standard]>=0.116.1", "httpx>=0.28,<0.29", "openai>=1.99.6", ] [dependency-groups] dev = [ "pytest>=8.4.1", ] lint = [ "ruff>=0.12.8", ]
構建鏡像
docker build -t uv-test:latest . --no-cache
并且運行
docker run --rm -p 8090:8090 --name uv-test uv-test:latest
- 測試:
curl http://localhost:8090
- 輸出:
hello
總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Python基礎教程之循環(huán)語句(for、while和嵌套循環(huán))
這篇文章主要給大家介紹了關于Python基礎教程之循環(huán)語句(for、while和嵌套循環(huán))的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-03-03python的數(shù)據(jù)與matlab互通問題:SciPy
這篇文章主要介紹了python的數(shù)據(jù)與matlab互通問題SciPy,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12