跳到主要内容

python在uv的基础使用

· 阅读需 15 分钟

最近在进行MCP server 的开发,目前主流的 mcp server 主要以node 和python 为主,其中python 主要以uv 来启动服务的,我也是由此才开始接触uv,uv 工具可以管理本地的python 版本,虚拟环境以及打包发布,还可以运行脚本,作为mcp 开发的前序基础,本文主要记录以下使用uv 开发管理python 应用的各个环节。之后在进行mcp 开发时会一直使用uv 来管理环境。

uv 的安装​

# On macOS and Linux.
curl -LsSf https://astral.sh/uv/install.sh | sh

# On Windows.
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

# With pip.
pip install uv

我习惯使用 pip 安装,如果本地电脑中有多个python 环境,如 python3.11, python3.12 等,需要确认一下用的是哪个版本的pip, 使用 pip --version 来查看一下具体的pip 位置

pip --version
pip 23.0.1 from C:\Python310\lib\site-packages\pip (python 3.10)

上面显示我使用的是3.10 的python, 安装完uv 以后,就可以在 C:\Python310\Scripts 目录中找到 uv.exe 和 uvx.exe, 这两个可执行文件后面都用的到。

uv 基础使用​

在没有uv 之前,创建项目的流程大概是这样

  1. 先使用python 创建一个虚拟环境 python -m venv venv
  2. 安装项目所需要的第三方包 pip install xxxx
  3. 导出依赖到 requirements.txt 中, pip freeze>requirements.txt
  4. 之后上传到git,如果要上传到pypi, 需要在配置打包脚本

总体来讲,上面的流程没有什么问题,但是每一步都有很多注意的问题

  1. 创建虚拟环境时,需要确认哪个版本的python? 众所周知,不同版本的python差异还是蛮大的,且不会向后兼容。
  2. 使用pip 安装包的时候,缺乏依赖管理,如使用pip 安装了A,A又依赖了B,B又依赖了C,当环境中不在依赖A时,如果卸载了A,则不会卸载B和C。

使用uv 管理python 环境​

传统的python 安装需要到官网或者镜像网站下载python 安装包安装,并配置好环境变量,创建虚拟环境的时候需要指明对应的python版本,如系统中有两个python 环境

C:\Python310\python.exe
C:\Python311\python.exe

如果在环境变量 path 中,311的优先级更高,则使用 python -m venv venv 创建出来的python 环境是3.11 的,如果想要创建一个3.10 的,需要将具体的路径打全了

C:\Python310\python.exe -m venv venv

使用 uv 安装python​

运行 uv python list 查看系统中的python 版本

C:\Users\yangyanxing>uv python list
cpython-3.13.2-windows-x86_64-none C:\Python313\python.exe
cpython-3.13.1+freethreaded-windows-x86_64-none <download available>
cpython-3.13.1-windows-x86_64-none <download available>
cpython-3.12.9-windows-x86_64-none C:\Python312\python.exe
cpython-3.12.8-windows-x86_64-none AppData\Roaming\uv\python\cpython-3.12.8-windows-x86_64-none\python.exe
cpython-3.11.11-windows-x86_64-none <download available>
cpython-3.11.9-windows-x86_64-none C:\Python311\python.exe
cpython-3.10.16-windows-x86_64-none <download available>
cpython-3.10.11-windows-x86_64-none C:\Python310\python.exe
cpython-3.9.21-windows-x86_64-none <download available>
cpython-3.8.20-windows-x86_64-none <download available>
cpython-3.8.10-windows-x86_64-none C:\Python38\python.exe
cpython-3.7.9-windows-x86_64-none <download available>
pypy-3.10.14-windows-x86_64-none <download available>
pypy-3.9.19-windows-x86_64-none <download available>
pypy-3.8.16-windows-x86_64-none <download available>
pypy-3.7.13-windows-x86_64-none <download available>

image.png

如果要安装 3.7.9 的python

使用 uv python install 3.7.9

uv python install 3.7.9
cpython-3.7.9-windows-x86_64-none ------------------------------ 5.70 MiB/29.97 MiB

image.png

也可以指定安装 pypy 版本的python(也可能安装失败)

image.png

使用 uv 创建虚拟环境​

先使用uv 创建一个项目,这步骤不是必须的,但是推荐使用uv 创建项目,因为会默认创建pyproject.toml 文件以及初始化git 和README 文件,算是比较正规的操作,否则需要自己手工创建。

uv init firstuv

uv 工具会创建一个 firstuv 的目录,并且创建好相应的文件

image.png

此时,还没有创建虚拟环境,但是我们注意到,pyproject.toml 文件中有配置 requires-python 为>=3.12, 如果想修改python 版本,如使用3.8 的python, 则先修改pyproject.toml的配置, 再修改 .python-version 中的python 版本。

注意,如果 pyproject.toml 文件中配置的是 requires-python = ">=3.11", 而 .python-version 写的是 3.10, 则之后运行 uv sync 命令进行安装会报错。

使用 uv sync 命令可以让uv 工具根据pyproject.toml 文件中的配置进行虚拟环境的创建和依赖的下载,当我们从github 上下载一个别人开发的基于uv的python 项目时,这个命令可以很方便的创建好虚拟环境和安装好依赖,这里由于是自己创建的干净的项目,我先不使用sync 命令,手动的去创建虚拟环境, 使用 uv venv 命令创建虚拟环境

cd firstuv
uv venv --python 3.11
Using CPython 3.11.9 interpreter at: C:\Python311\python.exe
warning: The requested interpreter resolved to Python 3.11.9, which is incompatible with the project's Python requirement: `>=3.12`
Creating virtual environment at: .venv
Activate with: .venv\Scripts\activate

我使用python 3.11 创建了这个虚拟环境,但注意到得到了一个警告,由于pyproject 中指定python 为 >=3.12, 而我这里安装的是3.11, 会出现不兼容的问题。

这里还贴心的提示出激活虚拟环境命令。

使用 uv 安装第三方包​

以往会使用pip install xxxx 来安装包,这已经成为了python 开发者必知必会的操作了,有了uv工具,可以使用 uv add xxxx 来安装第三方库

uv add requests fastapi

image.png

安装结束以后,uv.lock 文件会添加很多内容,这里的内容不要手工修改,让uv 自己来管理,同时 pyproject.toml 文件也添加上了刚才的依赖。

image.png

运行 uv tree 命令查看项目依赖

image.png

这个命令可以很清晰的展示项目中各个包的依赖关系。

使用 uv 卸载第三方包​

之前有提过,使用pip 安装了A,A又依赖了B,B又依赖了C,那么卸载A时时不会卸载B和C的,这样就会造成一种site-packages 越来越臃肿的问题。

上面的依赖关系

fastapi->pydantic->pydantic-core->typing-extensions

那么我们来看一下卸载 fastapi 会不会同时卸载掉对应的依赖包

image.png

生成 pip 的依赖文件​

uv 是一个新兴的 python 管理工具,并不是所有的开发者都会习惯使用它,更多的还是使用pip 来管理python 环境, 所以uv 工具也支持使用pip 来管理环境。

导出当前项目依赖到 requirements.txt 文件

通过 pyproject.toml 中的配置信息导出依赖文件,此时的pyproject.toml 中记录为

[project]

name = "firstuv"

version = "0.1.0"

description = "Add your description here"

readme = "README.md"

requires-python = ">=3.11"

dependencies = [

    "fastapi>=0.115.12",

    "requests>=2.32.3",

]

执行以下命令

uv pip compile pyproject.toml -o requirements.txt

即可将项目依赖导出为 requirements.txt 文件。

也可以通过 uv 工具运行 pip 导出requirements.in 文件,

uv pip freeze>requirements.in

此时requirements.in 文件中已经记录了第三方依赖包,

annotated-types==0.7.0

anyio==4.9.0

certifi==2025.1.31

charset-normalizer==3.4.1

fastapi==0.115.12

idna==3.10

pydantic==2.10.6

pydantic-core==2.27.2

requests==2.32.3

sniffio==1.3.1

starlette==0.46.1

typing-extensions==4.12.2

urllib3==2.3.0

这时使用pip 的开发者可以使用 pip install -r requirements.in 来安装包了。

但是这里推荐使用uv pip compile requirements.in -o requirements.txt 命令来重新编译一下, 生成的requirements.txt 会更加详细的记录包的依赖缘由

# This file was autogenerated by uv via the following command:

#    uv pip compile requirements.in -o requirements.txt

certifi==2025.1.31

    # via

    #   -r requirements.in

    #   requests

charset-normalizer==3.4.1

    # via

    #   -r requirements.in

    #   requests


requests==2.32.3

    # via -r requirements.in

......

之后对于习惯使用 pip 管理环境的用户就可以正常的使用 pip install 了。

uv 运行脚本​

使用 uv init 创建的项目会自动创建一个hello.py 的文件,未来在实际项目中,创建完项目以后大概率第一件事就是删除掉这个文件,不过作为示例,我们先来看一下这个hello.py 中的内容

def main():

    print("Hello from firstuv!")

if __name__ == "__main__":

    main()

很简单,就是一个hello world。 在没有uv 工具之前,要运行这个脚本,使用 python hello.py 即可,有了uv 工具,可以使用 uv run hellor.py 命令也可运行脚本

使用 uv 打包发布​

我们会使用pip 从pypi 中安装第三方的包,但是很少向pypi 提交自己编写的包,这节使用uv 打包并上传到pypi。

在打包之前,我们来看一下的uv init 初始化项目有哪些不同,官方解释如下 https://docs.astral.sh/uv/concepts/projects/init/

几个常用的选项如下

  • uv init --app 创建一个普通的app, 这个也是默认的选项,这种就是最基础的项目,这类型的项目不能直接打包,由于pyproject.toml 中没有配置build system
  • uv init --package 创建一个“包”, 这类型的项目可以被打包发布,pyproject.toml 中会自动配置 build system,并且可以发布为工具(tool),使用 uv tool run (或者 uvx )来直接运行(本节重点介绍此种方式创建的包)
  • uv init --lib 创建一个"库", 这种和package 的区别在于没有project.scripts, 不能当工具来使用

上一节使用 uv init xxxx 来初始化一个普通的项目,普通的项目没有打包编译选项,这节我们使用 uv init --package 来初始化项目。

uv init --package --python 3.11 mcp-gaodeweather-server

Initialized project `mcp-gaodeweather-server` at `C:\Users\yangyanxing\Desktop\uvtest\mcp-gaodeweather-server`

cd mcp-gaodeweather-server

uv sync

此时就创建好了一个package 类型的项目。

我们来看一下目录结构

tree mcp-gaodeweather-server
mcp-gaodeweather-server
├── .python-version
├── README.md
├── pyproject.toml
└── src
└── mcp-gaodeweather-server
└── __init__.py

这里比之前多了一个scr 目录,包的代码都放到这里。

再看一下 pyproject.toml 文件

[project]
name = "mcp-gaodeweather-server"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
authors = [
    { name = "yangyanxing", email = "yanxingyang@gmail.cn" }
]
requires-python = ">=3.11"
dependencies = []
[project.scripts]
mcp-gaodeweather-server = "mcp_gaodeweather_server:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

这个配置文件比之前多了 project.scripts 和 build-system 选项。build-system 选项为打包时用到的配置,流行的打包方式有多种,可以参考 https://hellowac.github.io/pypug-zh-cn/guides/writing-pyproject-toml.html#pyproject-guide-build-system-table

不过如果不熟悉的话就使用默认的 hatchling.build

还有一个project.scripts 配置,mcp-gaodeweather-server = "mcp_gaodeweather_server:main",这个是配置了一个脚本的启动命令。

uv run mcp_gaodeweather_server

这行命令将会输出 Hello from mcp-gaodeweather-server!, 这个输出是 src/mcp-gaodeweather-server/__init__.py 中的main 方法定义的输出

def main() -> None:
    print("Hello from mcp-gaodeweather-server!")

也就是当运行 uv run mcp_gaodeweather_server 时,实际上是执行了mcp_gaodeweather_server 模块中的main方法。

以上只有演示代码,并没有做什么, 之后会创建一个用于查询天气的 mcp 服务。

使用 uv 打包​

使用 uv build 命令即可将项目进行打包

C:\Users\yangyanxing\Desktop\uvtest\mcp-gaodeweather-server>uv build
Building source distribution...
Building wheel from source distribution...
Successfully built dist\mcp_gaodeweather_server-0.1.0.tar.gz
Successfully built dist\mcp_gaodeweather_server-0.1.0-py3-none-any.whl

编译好的二进制whl文件还有源文件.tar.gz包文件将保存在dist 目录下。在打包之前,也可以编辑一下pyproject.toml 文件中的name,version,description 等元信息。这里就先不改了。

uv tool 的使用​

uv 中有个tool 的概念,在 pyproject.toml 文件中定义的脚本

[project.scripts]
mcp-gaodeweather-server = "mcp_gaodeweather_server:main"

我们在之后是可以用 uv tool run mcp-gaodeweather-server 来运行的,我们也可以使用 uv tool install xxxx 来安装tool,如 uv tool install arxiv-mcp-server ,执行过程如下

当您运行 uv tool install arxiv-mcp-server 时,uv 会执行以下操作:

  • 解析包名: 确定要安装的包是 arxiv-mcp-server。​

  • 创建或使用现有的虚拟环境: uv 会在全局工具目录中创建一个隔离的虚拟环境,以确保安装的工具不会影响全局 Python 环境。​

  • 安装包及其依赖项: 在上述虚拟环境中,uv 使用 pip 安装 arxiv-mcp-server 及其所有必要的依赖项。​

  • 生成可执行文件: 安装完成后,uv 会在虚拟环境的 bin 目录下创建与 arxiv-mcp-server 相关的可执行文件,通常与包名相同。

上面我们使用了 uv run mcp-gaodeweather-server 来运行脚本,此时的脚本是在本地的,别人是无法使用的,或者我们换了一个虚拟环境也是运行不了的,我们期望使用 uv tool run mcp-gaodeweather-server 命令在任何地方运行我们的脚本, 也可以使用uvx mcp-gaodeweather-server 来运行, uvx 是 uv tool run 的缩写,uvx 可以自动从pypi 上下载安装包。

一般情况下,安装包会从pypi 下载,当然也可以从本地whl 文件安装,上面执行打包步骤以后,生产的whl 文件由于还没有上传到pypi,所以无法从pypi中下载安装的,此时我们可以使用本地文件进行安装。

uv tool install dist\mcp_gaodeweather_server-0.1.0-py3-none-any.whl
Resolved 1 package in 3ms
Prepared 1 package in 11ms
Installed 1 package in 22ms
+ mcp-gaodeweather-server==0.1.0 (from file:///C:/Users/yangyanxing/Desktop/uvtest/mcp-gaodeweather-server/dist/mcp_gaodeweather_server-0.1.0-py3-none-any.whl)
Installed 1 executable: mcp-gaodeweather-server.exe

>uv tool list
mcp-gaodeweather-server v0.1.0
- mcp-gaodeweather-server.exe
pycowsay v0.0.0.2
- pycowsay.exe

使用 uv tool install xxxx.whl 即可安装本地工具。使用 uv tool list 查看本地工具显示 mcp-gaodeweather-server.exe 已经安装好了。此时再使用 uvx mcp-gaodeweather-server 或者 uv tool run mcp-gaodeweather-server 即可运行该脚本了。

经过上面的几个步骤,我们基本上已经完成了一个极其简单的工具开发,如果我们将whl 发给别人,本地安装一下就可以使用了,但是在python 的世界里,还是需要将包上传到pypi 中供全世界的python 开发者使用。

使用 uv 上传到pypi 中​

到 pypi 中注册账号,到 Account settings, 创建api token,scope 选择 Entire account(all projects),

image.png

image.png

记录一下这个pypi 开头的token,这个只显示一次。

之后使用 uv publish --token pypi-xxxxx 命令将项目上传到 pypi 中。

终于上传到pypi 了,https://pypi.org/project/mcp-gaodeweather-server/ 这样全世界的python 开发者都可以看到我们的代码了,虽然什么都用都没有。。。

由于我们没有对项目做任何修改,所以看上去什么都没有,这很不符合规范,应该写一些项目介绍来开发者明白项目是做什么的,怎么启动的。

image.png

pyproject.toml 中有很多配置选项,可以参考

https://hellowac.github.io/pypug-zh-cn/specifications/pyproject-toml.html#pyproject-project-table

我挑一些常用的进行编辑

编辑README.md 文件

## 介绍

这是一个使用高德地图的天气接口,获取当前城市天气的demo。

## 使用方法

## MCP 配置

### uvx

```json

{

  "mcpServers": {

    "duckduckgo": {

      "command": "uvx",

      "args": [

        "mcp-gaodeweather-server"

      ]

    }

  }

}

编辑 pyproject.toml 文件,

[project]

name = "mcp-gaodeweather-server"

version = "0.1.1"

description = "这是一个使用高德地图的天气接口,获取当前城市天气的demo"

readme = "README.md"

authors = [

    { name = "yangyanxing", email = "yanxingyang@gmail.com" }

]

keywords= ["mcp", "gaodeweather", "server"]

requires-python = ">=3.11"

dependencies = []



[project.scripts]

mcp-gaodeweather-server = "mcp_gaodeweather_server:main"



[project.urls]

Homepage = "https://github.com/kevinkelin/mcp-gaodeweather-server"

Issues = "https://github.com/kevinkelin/mcp-gaodeweather-server/issues"



[build-system]

requires = ["hatchling"]

build-backend = "hatchling.build"

添加了 project.urls 配置,并写了一些描述,注意要提升一下版本号。

之后再运行 uv build 和 uv publish --token xxxxx 将package 上传到pypi 中。

现在我们就可以在任何地方使用刚才编写的工具了。新创建一个项目。

# 先卸载之前安装的 mcp-gaodeweather-server
uv tool uninstall mcp-gaodeweather-server

# 安装 mcp-gaodeweather-server 工具
uv tool install mcp-gaodeweather-server

# 运行
uv tool run mcp-gaodeweather-server

# 再卸载
uv tool uninstall mcp-gaodeweather-server

# 直接使用 uvx 运行

uvx mcp-gaodeweather-server

直接使用 uvx mcp-gaodeweather-server 运行工具时,uv 如果发现本地没有这个工具,则会先从pypi 中下载,之后再运行这个工具,此时我们再任何地方使用我们编写的工具了。

总结​

本文简单介绍了如何使用uv 来管理python 项目的创建和打包发布

  • uv python install 安装python
  • uv init xxxx 创建项目
  • uv venv 创建虚拟环境
  • uv add 下载第三方包
  • uv remove 卸载第三方包
  • uv pip 执行 pip 命令
  • uv run 执行脚本
  • uv tool run 或者 uvx 执行包中的script 命令,类似于npm run
  • uv build 打包
  • uv publish 发布

熟练使用uv 工具可以很方便的管理python 项目,之后我们再来学习MCP 服务的开发。

wp