Dependências do seu pacote Python#

Na página de visão geral do pyproject.toml, você aprendeu como configurar um arquivo pyproject.toml com os metadados básicos do seu pacote. Nesta página, você aprenderá como especificar diferentes tipos de dependências no seu pyproject.toml.

O que é uma dependência de pacote?#

A Python package dependency refers to an external package or a tool that is needed when using or working on your Python project. Declare your dependencies in your pyproject.toml file. This keeps all package metadata in one place, making it simpler for users and contributors to understand your package.

Formas antigas de declarar dependências

Embora pyproject.toml agora seja o padrão, às vezes você ainda pode encontrar abordagens mais antigas para armazenar dependências «por aí»:

  • requirements.txt: Antes era comum para dependências, e ainda é usado por alguns projetos para desenvolvimento local

  • setup.py ou setup.cfg: Pode ser necessário para pacotes com extensões em outras linguagens

Saiba mais na documentação do setuptools

Por que especificar dependências#

Specifying dependencies in the project.dependencies array of your pyproject.toml file ensures that libraries needed to run your package are correctly installed into a user’s environment. For instance, if your package requires pandas to run properly, and you add pandas to the project.dependencies array, pandas will be installed into the user’s environment when they install your package using uv, pip, or conda.

[project]
...
...
...
dependencies = [
    "dependency-package-name-1",
    "dependency-package-name-2",
    "pandas",
    "xarray",
    "geopandas",
    "matplotlib"
]

Dependências de desenvolvimento facilitam o trabalho dos contribuidores no seu pacote. Você pode configurar instruções para executar workflows específicos, como testes, linting e até tipagem, que instalam automaticamente grupos de dependências de desenvolvimento. Essas dependências podem ser armazenadas em arrays (listas de dependências) dentro de uma tabela [dependency-groups].

[dependency-groups]
test = [
    "pytest",
    "pytest-cov"
]

Tipos de dependências#

Existem três tipos diferentes de dependências que você aprenderá nesta página:

  1. Dependências obrigatórias: São dependências que precisam ser instaladas para que seu pacote funcione corretamente no ambiente do usuário. Você adiciona essas dependências na tabela project.dependencies do seu arquivo pyproject.toml.

  2. Feature Dependencies: These are dependencies that are required if a user wants to access additional functionality (that is not core) to your package. Store these in the [project.optional-dependencies] of your pyproject.toml file.

  3. Development Dependencies: These dependencies are required if someone wants to develop or work on your package. Linters and testing tools such as pytest and mypy are examples of development dependencies. Store these in the [dependency-groups] table of your pyproject.toml file.

Dica

Uma dependência não faz parte da base de código do seu projeto. Ela é um pacote ou software chamado dentro do código do seu projeto ou usado durante o desenvolvimento do seu pacote.

1. Required dependencies#

Dependências obrigatórias são importadas e chamadas diretamente dentro do código do seu pacote. Elas são necessárias para que seu pacote funcione.

Você pode adicionar suas dependências obrigatórias ao array dependencies na tabela [project] do seu arquivo pyproject.toml. Quando usuários instalarem seu pacote com uv, pip ou conda, essas dependências serão instaladas automaticamente junto com seu pacote no ambiente deles.

[project]
...
...
...
dependencies = [
    "dependency-package-name-1",
    "dependency-package-name-2",
    "pandas",
    "xarray",
    "geopandas",
    "matplotlib"
]

Dica

Tente ao máximo minimizar dependências sempre que possível. Lembre-se de que menos dependências reduzem a possibilidade de conflitos de versão nos ambientes dos usuários.

Como adicionar dependências obrigatórias com UV

Você pode usar uv para adicionar dependências ao seu arquivo pyproject.toml:

Adicionar uma dependência obrigatória:

uv add numpy

Irá adicionar numpy como dependência ao seu array project.dependencies:

[project]

dependencies = [
    "numpy>=2.2.6",
]

Dependências instaladas a partir do GitHub/GitLab

Se você tiver dependências que precisam ser instaladas diretamente do GitHub, pode especificá-las no seu arquivo pyproject.toml desta forma:

dependencies = [
"my_dependency >= 1.0.1 @ git+https://git.server.example.com/mydependency.git@commitHashHere"
]

IMPORTANTE: Se sua biblioteca depende de um projeto hospedado no GitHub, você deve apontar para um commit/tag/hash específico desse repositório antes de enviar seu projeto para o PyPI. Você nunca sabe como o projeto pode mudar ao longo do tempo. Hashes de commit são mais confiáveis porque não podem ser alterados

2. Optional dependencies#

Dependências opcionais (também chamadas de dependências de feature) podem ser instaladas pelos usuários conforme necessário. Dependências opcionais adicionam funcionalidades específicas ao seu pacote que nem todos os usuários precisam. Por exemplo, se seu pacote tiver uma funcionalidade opcional de gráficos interativos usando Bokeh, você listaria o Bokeh como um [project.optional-dependencies]. Usuários que quiserem gráficos interativos irão instalá-lo. Usuários que não precisarem de gráficos não precisam instalá-lo.

Coloque essas dependências na tabela [project.optional-dependencies].

[project]
...
...
...
[project.optional-dependencies]
plot = ["bokeh"]

Quando um usuário instala seu pacote, uv, pip ou conda instalam automaticamente todas as dependências obrigatórias. Dependências opcionais só são instaladas se o usuário solicitá-las explicitamente.

How to Add Optional Dependencies Using UV

Você pode usar uv para adicionar dependências ao seu arquivo pyproject.toml:

Adicionar uma dependência opcional:

uv add --optional feature pandas

Will add this to your pyproject.toml file:

[project.optional-dependencies]
feature = [
    "pandas>=2.3.3",
]

3. Dependency groups#

Dependências de desenvolvimento incluem pacotes necessários para trabalhar localmente no seu pacote. Elas são usadas para executar tarefas como:

  • executar sua suíte de testes (pytest, pytest-cov)

  • gerar sua documentação (sphinx, pacotes de sphinx-theme)

  • linting e formatação de código (ruff, black)

  • gerar arquivos de distribuição do pacote (build, twine)

Dependency groups are optional because they are not required for users to install and use your package. However, they will make it easier for contributors to your project to set up development environments locally.

New: PEP 735 dependency groups

[dependency-groups] é uma especificação mais nova introduzida pela PEP 735. Ela foi criada para organizar dependências de desenvolvimento e é intencionalmente separada de [project.optional-dependencies], que podem ser instaladas no ambiente do usuário.

Como declarar grupos de dependências#

Você declara dependências de desenvolvimento no seu arquivo pyproject.toml dentro de uma tabela [dependency-groups].

Assim como em optional-dependencies, você pode criar subgrupos separados ou arrays nomeados usando a sintaxe: group-name = ["dep1", "dep2"]

[dependency-groups]
test = [
    "pytest",
    "pytest-cov"
]

lint = [
    "black",
    "flake8",
    "ruff"
]

docs = [
    "sphinx",
    "pydata-sphinx-theme"
]

dev = [
    {include-group = "test"},
    {include-group = "lint"}
]
Como adicionar [dependency-groups] usando UV

Você pode usar uv para adicionar dependências ao seu arquivo pyproject.toml:

Adicionar uma dependência de grupo de desenvolvimento:

uv add --group tests pytest
uv add --group docs sphinx

Irá adicionar o seguinte ao seu arquivo pyproject.toml:

[dependency-groups]
tests = [
    "pytest>=8.4.2",
]
docs = [
    "sphinx>=8.1.3",
]

Entendendo dependências obrigatórias vs. opcionais#

Diagrama mostrando os dois principais grupos de dependências de pacotes Python: obrigatórias e opcionais. Dependências obrigatórias incluem os pacotes principais necessários para usar seu pacote. Dependências opcionais incluem dependências de desenvolvimento para trabalhar localmente no pacote e dependências de funcionalidades adicionais.

Dependências de pacotes Python se dividem em duas categorias: dependências obrigatórias, que usuários precisam para executar seu pacote, e dependências opcionais para desenvolvimento ou funcionalidades adicionais.#

Instalar grupos de dependências#

Quando alguém instala seu pacote, apenas as dependências principais são instaladas por padrão. Para instalar dependências opcionais, você precisa especificar quais grupos incluir durante a instalação do pacote.

Diagrama mostrando um diagrama de Venn com três seções representando grupos de dependências - docs, feature e tests. No centro ele mostra your-package com as dependências principais seaborn e numpy. Duas setas à direita demonstram: primeiro, `python -m pip install your-package` instala apenas o pacote e as dependências principais. Segundo, `python -m pip install your-package[tests]` instala o pacote, as dependências principais e as dependências de teste, incluindo pytest e pytest-cov.

When a user installs your package using pip install your-package, only your package and its core dependencies get installed. When they install with pip install your-package[tests], pip will install your package, core dependencies, and the test dependencies from the [project.optional-dependencies] field.#

Usando uv ou pip para instalação#

O UV simplifica esse processo, permitindo sincronizar uma venv no diretório do seu projeto com uma instalação editável do seu pacote e suas dependências automaticamente. Você também pode usar pip e instalar dependências no ambiente de sua escolha.

Instalar grupos de desenvolvimento:

Você pode usar uv sync para sincronizar grupos de dependências na sua venv gerenciada pelo uv

uv sync --group docs                     # Single group
uv sync --group docs --group test        # Multiple groups
uv sync --all-groups                     # All dependency groups

Dica

use --active with uv sync to prefer the currently active virtual environment over the project’s own managed environment:

$ uv sync --active --group docs

Instalar dependências opcionais:

# uv pip install is not ideal if you are using uv supported venvs for your project
$ uv pip install -e ".[docs]"              # Single group
$ uv pip install -e ".[docs,tests,lint]"   # Multiple groups

Dica

Use the --active flag with uv run to prefer the currently active virtual environment over the project’s own managed environment:

$ uv run --active pip install -e ".[docs]"

This is useful when you have activated a virtual environment and want uv run to use it instead of automatically creating or selecting the project’s environment.

Instalar tudo (pacote + todas as dependências):

uv sync --all-extras --all-groups

uv sync é o comando recomendado para workflows de desenvolvimento. Ele gerencia seu ambiente virtual e mantém seu lockfile atualizado. Use uv pip install quando precisar de compatibilidade com comportamento do pip.

Instalar dependências opcionais:

python -m pip install -e ".[docs]"              # Single group
python -m pip install -e ".[docs,tests,lint]"   # Multiple groups

Instalar grupos de desenvolvimento:

python -m pip install --group test              # Single group
python -m pip install --group docs   # Multiple groups

Sempre execute pip usando python -m pip para garantir que você está usando o pip do seu ambiente Python atualmente ativo. Isso ajuda a evitar conflitos de instalação.

Nota: Alguns shells (como zsh no Mac) exigem aspas ao redor dos colchetes para executar corretamente:

python -m pip install ".[tests]"

Combinando grupos de dependências#

Você também pode criar grupos combinados que referenciam outros grupos:

[project.optional-dependencies]
plot = ["bokeh"]
test = ["pytest", "pytest-cov"]
docs = ["sphinx", "pydata-sphinx-theme"]

Depois instale tudo com pip install ou uv sync conforme necessário:

uv pip install -e ".[dev]"
# or
python -m pip install ".[dev]"

Dica

When you install optional dependencies, pip and uv install your package and its core dependencies automatically.

Especificadores de versão para dependências#

Especificadores de versão controlam quais versões de uma dependência funcionam com seu pacote. Use-os para especificar versões mínimas, excluir releases com bugs ou definir intervalos de versão.

Operadores comuns#

  • >= Versão mínima: numpy>=1.20 (Esta é a abordagem mais comum e recomendada)

  • == Versão exata: requests==2.28.0 (Evite fixar dependências assim, a menos que seja necessário)

  • ~= Release compatível: django~=4.2.0 (Permite patches: >=4.2.0,<4.3.0)

  • < ou > - Limites superior/inferior: pandas>=1.0,<3.0

  • != Excluir versão: scipy>=1.7,!=1.8.0 (Raro, mas permite ignorar uma versão release com bugs)

Dica

Boa prática: Use >= para especificar sua versão mínima testada e evite limites superiores a menos que você saiba em qual versão aquela dependência deixa de ser compatível. O UV faz isso por padrão quando adiciona uma dependência ao seu arquivo pyproject.toml. Isso mantém seu pacote flexível e reduz conflitos de dependências.

dependencies = [
    "numpy>=1.20",              # Good - flexible
    "pandas>=1.0,<3.0",         # OK - known breaking change in 3.0
    "requests==2.28.0",         # Avoid - too restrictive
]

O arquivo pyproject.toml funciona muito bem para pacotes Python puros. Porém, alguns pacotes (principalmente no ecossistema científico Python) precisam de dependências escritas em outras linguagens como C ou Fortran. O Conda foi criado para suportar a distribuição de ferramentas com dependências não-Python.

Para usuários de conda:

Você pode manter um arquivo environment.yml para ajudar usuários e contribuidores a configurarem ambientes conda. Isso é especialmente útil para pacotes com dependências de sistema como GDAL.

Considere o Pixi para workflows focados em pacotes conda:

Pixi é um gerenciador de pacotes moderno construído em cima dos ecossistemas conda e de pacotes Python. O Pixi consegue tratar requisitos de pacotes conda e Python de forma equivalente ao resolver ambientes, mas usa uma abordagem «conda-first», utilizando pacotes conda já resolvidos sempre que possível ao resolver dependências Python. O Pixi também pode usar pyproject.toml para configuração. Se seu projeto depende fortemente de pacotes conda, o Pixi oferece um workflow simplificado com resolução de dependências mais rápida e suporte automático a lock files para reprodutibilidade completa do ambiente. Se você já possui um arquivo de definição de ambiente conda, como um environment.yml, pode importar o ambiente para um novo workspace Pixi com

pixi init --import environment.yml

Uma observação para usuários de conda

Se você usa um ambiente conda para desenvolvimento e instala seu pacote com python -m pip install -e ., as dependências serão instaladas a partir do PyPI, potencialmente sobrescrevendo pacotes conda que já haviam sido instalados. Isso pode causar conflitos, especialmente em pacotes com dependências de sistema.

Para evitar isso, instale seu pacote sem dependências:

python -m pip install -e . --no-deps

Depois instale as dependências através do seu arquivo conda environment.yml.

Dependências no Read the Docs#

Depois de especificar dependências no seu pyproject.toml, você pode usá-las em outros workflows, como gerar documentação no Read the Docs.

Read the Docs é uma plataforma de documentação que gera e publica automaticamente sua documentação. Para instalar suas dependências durante o processo de build, configure-as em um arquivo readthedocs.yaml.

Aqui está um exemplo que instala suas dependências opcionais docs:

python:
  install:
    - method: pip
      path: .
      extra_requirements:
        - docs

Dependency Locking#

In addition to declaring dependencies in pyproject.toml, it is common for packages to lock down exact versions of all their dependencies in a separate lock file. A lock file provides benefits of reproducibility, security, and potentially faster installs, among other things. Pinning the exact dependency versions used in a project eliminates «works on my machine» bugs and gives CI a reproducible baseline. For applications meant to be run rather than imported, lock files also ensure anyone installing the project gets a known-good set of dependencies instead of whatever happens to be latest.

pyproject.toml vs lock file#

  • pyproject.toml: defines all environments you intend to support for users importing your package into their project.

  • lock file: defines a specific environment used for development

pyproject.toml should be permissive, erring on the side of allowing too much even if it may allow untested environments. In most cases, it is better that users install your package but encounter an issue rather than being restricted from installing your package by the pyproject.toml when it would otherwise work.

A lock file is the opposite. If it installs, the resulting environment should work even if this means some valid environments are excluded.

Standardized Lock File

As of March 2025, PEP 751 defined a standard pylock.toml format to unify the various lock file formats in use by other package managers (e.g. uv.lock, poetry.lock, pdm.lock). Most package managers provide ways to generate a PEP 751 compatible file. See PyPA specification for up-to-date formatting info on pylock.toml

How to work with lock files?#

Lock files are not written by hand. Package managers and IDEs provide tools to create, update, and reformat lock files as needed.

  1. Create - Package managers often do this automatically though it can be done manually. For example, calling uv add numpy will automatically create a uv.lock file, setup the environment, and install numpy.

  2. Update - This is not done automatically by package managers. Maintainers can choose to do this manually or setup their own automated workflow. Updates can be for specific packages or all dependencies.

  3. Reformat - Package managers currently use native formats (e.g. uv uses uv.lock) and provide tools for converting into pylock.toml and other formats (e.g. requirements.txt) when needed

Below is the uv CLI workflow for lock files:

# Create a uv.lock file based on pyproject.toml
> uv lock

# Update uv.lock
> uv lock --upgrade
> uv lock --upgrade-package pandas

# Install packages into environment based on uv.lock
> uv sync

# PEP 751 pylock.toml support
> uv export --format pylock.toml -o pylock.toml # export uv.lock -> pylock.toml
> uv pip sync pylock.toml                       # install from pylock.toml

See official docs for more details. See also the relevant docs for Poetry and PDM.

Should I use a lock file?#

Most package managers will generate a lock file automatically for you (e.g. uv, Poetry, PDM). The real question is when you version control the lock file as part of your package.

Recommendation: Versioning a lock file

If your project is an application others use directly, include a lock file as the recommended environment.

If your project is a library to be used in other projects and it is mature enough to have CI, include a lock file for CI and contributors. For a small library only you maintain that is shared amongst people you know, waiting to add a lock file is not an issue. In general, you should version the lock file.

For private libraries shared within a team, a lock file is less important. But if the project is an application or tool that others run directly, instead of importing into their code, committing the lock file is generally the most convenient choice. It gives users a reproducible set of dependencies instead of having each user resolve from pyproject.toml.

Recommendation: Which format to version control

Version control the standard pylock.toml format.

There is some maintenance cost from lock files. Maintainers should aim to update the lock file neither too rarely nor too often.

  • Too rarely means you risk missing updates with bugfixes, security patches, performance improvements, etc.

  • Too often means you may introduce bugs or even security vulnerabilities before maintainers of your dependencies catch them. Package managers are starting to support dependency cooldowns to mitigate this.

Recommendation: Updating a lock file

Update lock files frequently (e.g. weekly) but configure a dependency cooldown of several days to avoid automatically installing the latest packages. Only override the cooldown if a new package has a needed bug fix or security patch.

Dependency cooldowns

Dependency cooldowns are strongly encouraged by security experts to avoid automatically downloading the latest package updates that may have been compromised with malware. Package manager tools are starting to support configurations for cooldowns

> uv lock --exclude-newer "3 days"`

or in pyproject.toml

[tool.uv]
exclude-newer = "3 days"

Integrating cooldown constrained lock files into CI is important since this is where new packages are commonly tested first. Automated testing code that resolves [project.dependencies] every time

> python -m pip install .

can be replaced with lock file based installations

> uv pip sync pylock.toml

after pylock.toml has been added to the project.

> uv lock --exclude-newer "3 days"`
> uv export --format pylock.toml -o pylock.toml

Support for this varies across automated testing frameworks (e.g. hatch, nox) so consult their documentation for how to install dependencies from lock files with dependency cooldowns.

When you decide to update a lock file, make sure to test that the resulting environment works before committing. If it fails because of some dependency update, then it may be necessary to update pyproject.toml to cap the supported versions of that dependency unless/until the code can be updated to support it.

It can also be good, though not necessary, to double check what changed when updating a lock file. The diff can be noisy so the main changes to focus on are

  1. major version updates (e.g. pandas 2.X.X -> pandas 3.X.X)

  2. new transitive dependencies (i.e. not part of your pyproject.toml)

Dica

A lock file captures one environment for CI testing, not the full compatibility range declared in pyproject.toml. Projects that use lock files may want to have CI test other environments such as

  1. the latest packages consistent with your pyproject.toml, subject to dependency cooldowns. This lets you know if a dependency update breaks your package.

  2. older supported versions of Python to let you know if a recent change to your package no longer works with an older Python release.

What about requirements.txt

Older approaches to locking used pip freeze to generate a requirements.txt that got used as a lock file. These are minimal lock files that pin a specific version for the system on which the command was run. They might look like

# requirements.txt
numpy==2.4.6
plotly==6.7.0
pyzmq==27.1.0

However, this minimal level of specificity has several downsides making lock files the preferred format:

  • The versions satisfying pyproject.toml may differ between your Windows laptop and the Linux server your CI runs on. A single lock file contains the information needed to build platform specific and Python version specific environments. In contrast, a separate requirements.txt files is needed to store this information (e.g. requirements.ci.txt, requirements.py313-macos.txt)

  • Packages can get updated without a version update for both legitimate and malicious reasons. Lock files include package hashes to catch this. A hash is a unique signature computed from the code and any change to the code will cause the release to have a different hash even if is given the same release version number.

  • Other metadata determined during resolution of pyproject.toml (e.g. which dependencies are transitive, where the packages were downloaded from, etc.) that can help speed up future installs is lost.