O uv é um gerenciador de pacotes para o Python escrito em Rust, mantido pela Astral. Ele se destaca por sua velocidade e versatilidade, se propondo a substituir ferramentas como pyenv, poetry e pipx.

Curiosidade: uv vem de “ultravioleta”, que remete a uma coisa muito rápida. Os próprios autores do uv não se preocuparam muito em dar um significado para o nome, mas o objetivo era encontrar um nome fácil, rápido de digitar e que estivesse disponível no PyPI e em outros lugares sem conflitar com comandos já existentes do linux e outros projetos

Embora o Poetry ainda tenha muita relevância no ecossistema do python, o uv tem se tornado a preferida rapidamente devido sua performance, sua completude e uma boa DX em comparação com sua rival. Se você ainda não conhece o Poetry e quiser saber mais sobre essa ferramenta, leia este outro artigo que eu escrevi.

Instalação

A instalação do uv pode ser feita através do pip (gerenciador de pacotes padrão do python), porém é mais recomendado usar seu instalador:

# macOS / Linux
# requer o curl, mas você pode usar o wget
$ curl -LsSf https://astral.sh/uv/install.sh | sh
# windows
PS> powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

Nota: o instalador faz a instalação apenas para o usuário, sendo necessário algumas alterações na execução do script de instalação se desejar uma instalação global

Não é necessário ter o python instalado previamente para usar o uv, já que ele também faz o gerenciamento do python permitindo instalar múltiplas versões do interpretador e alternar entre as versões instaladas facilmente.

Usando o uv em um projeto

Iniciando um novo projeto

uv init new-project

O comando init cria uma pasta com o nome passado como argumento (new-project no exemplo acima), adiciona alguns arquivos iniciais para o uv e inicializa um repositório git.

Para usar o uv para gerenciar um projeto já existente é só executar o comando uv init no diretório do projeto. Caso já exista um arquivo pyproject.toml, o uv vai usar este arquivo para gerenciar dependências e outras configurações da ferramenta.

Nota: o uv cria automaticamente um ambiente virtual para o projeto, não sendo necessário que você faça essa etapa manualmente com python -m venv .venv por exemplo.

Gerenciando dependências

Tipicamente quando clonamos um repositório, é necessário instalar as dependências do projeto antes de executá-lo. Com o uv podemos fazer isso usando o comando uv sync. Porém, não é necessário rodar esse comando na maioria das vezes porque o uv faz o sync automaticamente quando é necessário.

Sempre que o comando uv sync é executado, ele instala bibliotecas que estão faltando no ambiente virtual do projeto e também remove bibliotecas instaladas que não são necessárias nas dependências do projeto.

O comando uv add <package> serve para adicionar pacotes (bibliotecas) como dependências do projeto. Exemplo de uso adicionando o httpx:

uv add httpx

Você também pode adicionar múltiplos pacotes em um único comando:

uv add httpx sqlalchemy pydantic

Adicionando dependências de desenvolvimento:

uv add pytest ruff --dev

Dependências de desenvolvimento são adicionadas dentro de um grupo de dependências especial chamado dev.

Os grupos de dependências possibilitam segregar as dependências de acordo com o ambiente (ex: dev, ci, prod). Isso é útil quando diferentes ambientes requerem dependências adicionais, como ferramentas de execução de testes que são usadas nos ambientes de desenvolvimento e nas pipelines de CI.

Para remover pacotes:

uv remove pydantic

Atualizar a versão de um pacote específico:

uv lock --upgrade-package httpx

Para fazer a atualização de todos os pacotes:

uv sync --upgrade

Nota: o uv nem sempre vai trazer as versões mais atuais dos pacotes, mas vai fazer o melhor pra trazer a maior versão que atenda as restrições de versões impostas pelos pacotes do projeto sem causar quebra de compatibilidade entre eles.

Executando comandos e scripts

O uv cria um ambiente virtual para o gerenciamento do projeto com o objetivo de gerar uma camada de isolamento, por isso é preciso que comandos e scripts sejam executados dentro deste ambiente virtual para ter acesso as dependências do projeto. Caso seus scripts não sejam executados neste ambiente, pode ser que você se depare com erros como ModuleNotFoundError: No module named....

Para executar scripts python pelo ambiente virtual, você pode passar o arquivo python como argumento para o comando uv run. Exemplo:

uv run main.py

O uv vai rodar o script dentro do ambiente virtual, sem que você precise ativar o ambiente manualmente.

O mesmo se aplica para CLIs que estejam instaladas no seu ambiente virtual:

# executando testes com o pytest no ambiente virtual
uv run pytest

Você também pode passar um arquivo de variáveis de ambiente para ser carregado na execução do uv run:

# executa main.py com as variáveis de ambiente do arquivo .env
uv run --env-file .env main.py 

Apesar dos comandos serem executados dentro do ambiente virtual com o uv run, ele não habilita o ambiente virtual no terminal em que o comando foi executado. Isso significa que após rodar uv run main.py e o programa terminar, seu terminal continuará fora do ambiente virtual e qualquer comando subsequente ainda precisará ser executado com uv run para rodar dentro do venv.

Se preferir, você pode ativar o ambiente virtual gerenciado pelo uv para executar scripts e comandos dentro do ambiente virtual sem precisar usar o uv run:

# linux e mac
source .venv/bin/activate
# windows
.\.venv\Scripts\activate

Caso o ambiente virtual tenha sido ativado com sucesso, você deve ver o nome do projeto entre parênteses no seu terminal: (meu-projeto) user@host:path/project$ .

Agora você pode executar comandos sem o uv run:

# este comando terá o mesmo efeito que o uv run pytest
# porque está rodando dentro do ambiente virtual
pytest

Para executar scripts você precisa passá-los como argumento para o python:

python main.py

O comando acima, quando executado de dentro do ambiente virtual, produz resultado semelhante ao uv run main.py executado de fora do ambiente virtual.

Nota: veja que não é necessário usar o comando python quando é utilizado o uv run para executar scripts. Ou seja, você pode rodar uv run main.py diretamente ao invés de uv run python main.py.

Gerenciando Instalações do Python

O uv também pode ser usado para gerenciar instalações do python, permitindo que seja instaladas várias versões diferentes e permutar entre elas de acordo com o projeto facilmente. O seguinte comando instala as versões 3.11 e 3.12:

uv python install 3.11 3.12

Para checar quais versões do python você tem:

uv python list

Por trás dos panos o uv já faz a instalação automaticamente da versão adequada do python caso não encontre uma versão que satisfaça o requisito do projeto onde o comando está sendo executado. Por isso nem é necessário usar o uv python install para adicionar alguma versão específica do python normalmente.

Nota: O comportamento padrão de instalação automática pode ser desabilitado com a flag --no-python-downloads em qualquer comando do uv.

A versão do python a ser utilizada no projeto pode ser selecionada na inicialização do projeto com uv init usando a flag --python:

uv init --python 3.13

A maioria dos comandos do uv permitem selecionar a versão do python a ser utilizada com a flag --python, porém, cada comando é influenciado de uma forma diferente por essa flag. No comando init por exemplo, a flag configura a versão do interpretador a ser utilizada pelo projeto nos arquivos .python-version e pyproject.toml (propriedade requires-python), enquanto no comando uv run a flag é utilizada para direcionar a versão do python a ser usada para executar o script ou comando.

# este comando será executado no python 3.10
echo 'print("hello world!")' | uv run --python 3.10 - 

O uv sempre vai tentar buscar uma instalação do python que atenda os requisitos de versão do comando, e caso não encontre, ele vai instalar uma nova versão.

Se você quiser conferir este comportamento do uv, faça o seguinte:

  1. Cheque quais são as versões do python não instaladas para você com uv python list
  2. Execute echo 'print("hello world!")' | uv run --python $PYTHON_VERSION - substituindo $PYTHON_VERSION por uma versão que você não tenha instalado
  3. Você vai ver o uv fazendo a instalação do interpretador antes da execução do comando em si, mas pra confirmar você pode checar que a versão passada agora está instalada ao rodar uv python list de novo.

Executando CLIs

O uv também possui utilitários para executar CLIs sem a necessidade de instalação, semelhante ao pipx. Apesar de não precisar instalar a ferramenta a ser executada explicitamente, o uv cuida de fazer uma instalação isolada e descartável em um ambiente virtual separado de qualquer projeto. Exemplo de uso:

uv tool run ruff format

O comando uv tool run também pode ser invocado através de seu alias uvx:

uvx ruff check

A vantagem dessa abordagem é que a ferramenta pode ser utilizada facilmente em um único comando sem precisar rodar comandos de instalação, além de possuir um ambiente virtual exclusivo que garante o isolamento da ferramenta e suas dependências.

Também é possível instalar CLIs de maneira permanente, global e mantendo o isolamento oferecido do uvx com o comando uv tool install:

# Instala o Databricks CLI
uv tool install databricks-cli

# Verificando instalação
databricks --version

Além do comando ser diferente, o uv tool install difere do uvx por instalar a ferramenta em um local permanente, enquanto o uvx salva em um diretório de cache gerenciado pelo uv.

Ambientes Virtuais

Os ambientes virtuais são a chave para trabalhar em multiplos projetos sem causar conflitos entre suas dependencias. O uv pode ser usado para criar ambientes virtuais através do comando uv venv:

# cria um ambiente virtual em .venv dentro do diretório atual
uv venv
# cria um ambiente virtual em ~/.venvs/my_project
uv venv ~/.venvs/my_project
# cria um ambiente virtual com python 3.10 como interpretador
uv venv --python 3.10

Entretanto, o uv, por padrão, cria automaticamente um ambiente virtual para seu projeto caso não exista e usa ele para instalação das dependências. Dessa forma não é necessário usar o comando venv no seu fluxo de trabalho normalmente.

Se você precisar mudar a versão do python do seu venv, você pode recriar o ambiente com a versão desejada:

uv venv --python $PYTHON_VERSION

Arquivos importantes

Os arquivos pyproject.toml, .python_version e uv.lock são usados pelo uv para resolver as versões do python e dependencias do projeto.

.python_version

O arquivo .python_version especificamente é usado apenas para dizer qual versão do python é usada no projeto em desenvolvimento. Este arquivo, além de guiar o uv na escolha da versão do interpretador a ser usada no contexto do projeto, também ajuda na reprodutibilidade do ambiente de desenvolvimento.

O conteúdo deste arquivo é meramente uma versão do python. Exemplo:

3.12

pyproject.toml

O pyproject.toml carrega os metadados do projeto, incluindo suas dependencias. Muitas ferramentas do python se baseiam nesse arquivo para fazer suas configurações, como é o caso do uv. As principais propriedades extraídas do pyproject.toml para o uv são:

  • project.version: diz qual é a versão do projeto em si. Afeta o comando uv bump que faz a alteração da versão do projeto
  • project.requires-python: restringe as versões do python suportadas pelo projeto. Diferente do arquivo .python_version, o requires-python costuma ser um range de versões e é voltado para consumidores do pacote gerado pelo projeto ao invés dos produtores do código fonte. O pacote gerado com o comando uv build dá erro ao ser instalado em um ambiente que não atenda a restrição de versão do python declarada no requires-python.
  • project.dependencies: é uma lista de pacotes que precisam ser instalados para que o seu código funcione corretamente. Este Campo é modificado pelos comandos uv add e uv remove, além de ser essencial para o uv sync fazer a instalação das dependências e também determina sua árvore de dependências para instalação do pacote gerado com o uv build.
  • dependency-groups: aqui é onde os grupos de dependências extras como “dev” são agrupados. Nessa seção pode ter vários grupos de dependências e cada grupo tem uma estrutura e função muito semelhante ao do project.dependencies.
# Exemplo de de um arquivo pyproject.toml
[project]
name = "my_project"
version = "0.1.2"
description = "My cool project"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
    "alembic>=1.16.5",
    "fastapi[standard]>=0.117.1",
    "openai[aiohttp]>=1.109.1",
    "psycopg2>=2.9.10",
    "pydantic-settings>=2.11.0",
    "sqlalchemy>=2.0.43",
    "uvicorn>=0.36.0",
]

[dependency-groups]
dev = [
  "pytest"
]
lint = [
  "ruff"
]

Nota: Não é recomendado atualizar as dependências do projeto manualmente editando o arquivo pyproject.toml. Use o uv add e uv remove para isso.

uv.lock

O uv.lock é gerado para fixar as versões das dependências e manter o ambiente de desenvolvimento mais reprodutível em diferentes lugares. É esse arquivo que garante que as mesmas versões que estão sendo usadas no seu computador também serão usadas no computador de um colega ou em alguma pipeline de CI.

Apesar do project.dependencies no arquivo pyproject.toml ter uma finalidade semelhante, nem sempre a melhor estratégia é pinar versões específicas neste arquivo. Então o que acontece muitas vezes é que as versões das dependências no pyproject possuem uma faixa de versões aceitas ao invés de versões específicas. Além disso, o uv.lock também cobre as versões de dependências das suas dependências, que passam a ser conhecidas após a resolução da árvore de dependências do seu projeto pelo uv. E por fim, o uv.lock é utilizado como referência para execução do código fonte em comandos como uv sync, enquanto as dependências declaradas no pyproject.toml são utilizadas para instalar seu projeto como uma biblioteca (neste caso o arquivo de lock não é utilizado).

O uv faz atualizações do arquivo uv.lock automaticamente em alguns comandos como uv add, uv remove, uv run, por isso você não precisa se preocupar com ele. Para garantir a reprodutibilidade do ambiente, o uv.lock deve ser enviado para o repositório git e ele nunca deve ser alterado manualmente. Se por algum motivo você precisar gerar um novo uv.lock, você pode executar o comando uv lock.

Extras

  • Você pode atualizar a versão do uv com uv self update
  • Para checar a versão do uv: uv --version ou uv self version
  • Você pode inserir configurações adicionais ao seu uv no arquivo pyproject.toml sob a propriedade tool.uv ou no arquivo uv.toml
  • O uv também lê algumas variáveis de ambiente de configuração para definir alguns comportamentos (Ex: UV_ENV_FILE, UV_INDEX_URL, UV_PYTHON).
  • Coisas que o uv faz automaticamente de forma inteligente:
    • Criação de ambiente virtual (uv venv)
    • Atualização do arquivo lock (uv lock)
    • Instalação e remoção de pacotes de acordo com os requisitos do projeto (uv sync)
    • Endereçamento e instalação do python (uv python find e uv python install se necessário)
  • O uv tem uma ótima performance na instalação de pacotes
  • Outros comandos bem úteis:
    • uv version --bump [patch|minor|major]: atualiza a versão do seu projeto
    • uv build: gera um pacote instalável do seu projeto
    • uv publish: publica o instalável do seu pacote em algum index (PyPI por padrão) - requer login

Veja mais