Use um arquivo pyproject.toml para a configuração e os metadados do seu pacote#

Conclusões sobre o pyproject.toml

  1. Há apenas duas tabelas que são obrigatórias para um pacote Python instalável: [build-system] e [project]. A tabela [project] armazena os metadados do seu pacote.

  2. Há dois campos obrigatórios na tabela [project]: name= e version=.

  3. Adicione metadados à seção classifiers do seu arquivo pyproject.toml para facilitar que os usuários encontrem o seu projeto no PyPI.

  4. Ao adicionar classifiers à tabela [project], use apenas valores válidos da página de classifiers do PyPI. Um valor inválido aqui gerará um erro quando você fizer a build do seu pacote ou publicar no PyPI.

  5. There is no specific order for tables in the pyproject.toml file. However, fields need to be placed within the correct table sections. For example requires = always needs to be associated with the [build-system] table.

Sobre o arquivo pyproject.toml#

Todo pacote Python moderno deveria incluir um arquivo pyproject.toml. Para pacotes Python puros, esse arquivo substitui o arquivo setup.py e/ou setup.cfg para descrever os metadados do projeto.

Se o seu projeto não for Python puro, você ainda pode precisar de um arquivo setup.py para fazer a build das extensões não Python. No entanto, um arquivo pyproject.toml ainda deve ser usado para armazenar os metadados do seu projeto.

Tutorial

Se você está migrando de um arquivo setup.py ou setup.cfg e quer ajuda, confira este tutorial. especificar requisitos de build e metadados é chamado de pyproject.toml

Sobre o formato .toml#

O arquivo pyproject.toml é escrito no formato TOML (Tom’s Obvious, Minimal Language). O TOML é uma estrutura fácil de ler baseada em pares chave/valor. Cada seção no arquivo pyproject.toml contém um [identificador de tabela]. Abaixo desse identificador de tabela estão os pares chave/valor que dão suporte à configuração daquela tabela específica.

  • Abaixo, [build-system] é considerado uma tabela na linguagem toml.

  • Dentro da tabela build-system, requires = é uma chave.

  • O valor associado a requires é um array contendo o valor "hatchling".

[build-system]
requires = ["hatchling"]

Como o pyproject.toml é usado quando você faz a build de um pacote#

Quando você publica no PyPI, vai notar que cada pacote tem metadados listados. Vamos dar uma olhada no xclim, um dos nossos pacotes do pyOpenSci. Note que na página inicial do PyPI você vê alguns metadados sobre o pacote, incluindo python, informações do mantenedor e mais. O PyPI consegue preencher esses metadados porque eles foram definidos usando a sintaxe e os classifiers corretos pelos mantenedores do Xclim, arquivo pyproject.toml. Esses metadados, quando o pacote xclim é construído, são traduzidos em um arquivo de distribuição que permite ao PyPI ler os metadados e exibi-los no site deles.

Imagem mostrando a barra lateral esquerda do PyPI para o pacote xclim. A seção no topo diz Classifier. Abaixo há uma lista de itens incluindo Status de desenvolvimento, público-alvo, Licença, idioma natural, sistema operacional, linguagem de programação e tópico. Abaixo de cada uma dessas seções estão várias opções de classifier." width="300px">

Quando você adiciona a seção classifier ao seu pyproject.toml e o seu pacote é construído, a ferramenta de build organiza os metadados em um formato que o PyPI consegue entender e representar na página inicial do seu pacote no PyPI. Esses classifiers também permitem que os usuários filtrem os pacotes pela versão do python que eles suportam, categorias e mais.#

Benefícios de usar um arquivo pyproject.toml#

Incluir os metadados do seu pacote em um formato pyproject.toml separado e legível por humanos também permite que alguém visualize os metadados do projeto em um repositório do GitHub.

O Setup.py ainda é útil para builds de pacotes complexos

Usar o setup.py para gerenciar builds e metadados de pacotes pode causar problemas no desenvolvimento de pacotes. Em alguns casos em que a build de um pacote Python é complexa, um arquivo setup.py pode ser necessário. Embora este guia não cubra builds complexas, forneceremos recursos para trabalhar com builds complexas no futuro.

Campos opcionais vs. obrigatórios do arquivo pyproject.toml#

Quando você cria o seu arquivo pyproject.toml, há diversos campos de metadados que você pode usar. Abaixo, sugerimos campos específicos para você começar, que dão suporte à publicação no PyPI e ajudam os usuários a encontrar o seu pacote.

Uma visão geral de todos os elementos de metadados do projeto pode ser encontrada aqui.

Campos obrigatórios para a tabela [project]#

Como mencionado acima, o seu arquivo pyproject.toml precisa ter um campo name e version para fazer a build do seu pacote corretamente:

  • name: Este é o nome do seu projeto fornecido como uma string

  • version: Esta é a versão do seu projeto. Se você está usando uma ferramenta de SCM para versionamento (usando tags do git para determinar as versões), então a versão pode ser dinâmica (mais sobre isso abaixo).

Campos opcionais para incluir na tabela [project]#

Recomendamos fortemente que você também adicione as chaves de metadados abaixo, pois elas ajudarão os usuários a encontrar o seu pacote no PyPI. Esses campos deixarão claro como o seu pacote está estruturado, quais plataformas você suporta e quais dependências o seu pacote requer.

  • Description: esta é uma breve descrição de uma linha do seu pacote.

  • Readme: Um link para o seu arquivo README.md é usado para a descrição longa. Essa informação será publicada na página inicial do seu pacote no PyPI.

  • Requires-python (usado pelo pip): este é um campo usado pelo pip. Aqui você informa ao instalador se está usando Python 2.x ou 3.x. A maioria dos projetos estará usando 3.x.

  • License: a licença que você está usando

  • Authors: estes são os autores originais do pacote. Às vezes os autores são diferentes dos mantenedores. Em outras ocasiões, podem ser os mesmos.

  • Maintainers: você pode escolher preencher isso ou não. Você pode preencher usando uma lista com um subelemento para o nome e e-mail de cada autor ou mantenedor

authors = [
    {name = "Some Maintainer", email = "some-email@pyopensci.org"},
]
  • project.dependencies: O grupo de dependências é opcional porque nem todos os pacotes requerem dependências. No entanto, se o seu projeto tiver dependências específicas, inclua esta seção no seu pyproject.toml. As dependências declaradas no arquivo pyproject.toml serão instaladas pelo uv ou pip quando o seu projeto for instalado.

  • project.optional-dependencies: Dependências opcionais ou de funcionalidade serão instaladas se alguém executar python -m pip install projectname[feature]. Use este array para declarar dependências que adicionam funcionalidades específicas ao seu pacote e que não são instaladas por padrão quando um usuário executa uv sync ou python -m pip install packagename.

  • dependency-groups: Os grupos de dependências organizam os pacotes e ferramentas que um colaborador ou desenvolvedor precisaria para trabalhar no seu pacote. Essas dependências podem incluir ferramentas para construir e executar testes, linters e formatadores de código. Esta é uma forma opcional, mas altamente recomendada, de organizar e instalar dependências. Esta seção pode substituir um arquivo requirements.txt. Saiba mais sobre como adicioná-las ao seu pacote no guia da PyPA aqui.

  • keywords: Estas são as palavras-chave que aparecerão na página inicial do seu pacote no PyPI. Pense nelas como palavras que as pessoas poderiam usar para pesquisar o seu pacote.

  • classifiers: A seção classifiers dos seus metadados também é importante para a página inicial do seu pacote no PyPI e para a filtragem de pacotes no PyPI. Uma lista com todas as opções de classifiers pode ser encontrada aqui. Alguns dos classifiers que você deveria considerar incluir

    • Status de Desenvolvimento

    • Público-alvo

    • Tópico

    • Linguagem de programação

Opções avançadas no arquivo pyproject.toml#

  • [project.scripts] (Pontos de entrada): Os pontos de entrada são opcionais. Se você tem uma ferramenta de linha de comando que executa um script específico hospedado no seu pacote, você pode incluir um ponto de entrada para chamar esse script diretamente na linha de comando (em vez de no shell do Python).

    • Aqui está um exemplo de um pacote que tem script de ponto de entradas. Note que há vários scripts principais definidos nesse pacote que executam conjuntos de tarefas. O pyOpenSci está usando esses scripts para processar seus metadados.

  • Use Campos Dinâmicos se você tem campos que são preenchidos dinamicamente. Por exemplo, você pode querer atualizar automaticamente a versão do seu pacote usando tags do Git (versionamento baseado em SCM/controle de versão). Exemplo: dynamic = [«version»]

Adicione dependências ao seu arquivo pyproject.toml#

Required dependencies#

A requirements.txt file has been traditionally used to specify dependencies, but modern practice puts these in the pyproject.toml file. Required dependencies are specified under the [project] section as a list of strings:

[project]
...
...
...

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

Optional dependencies#

Optional dependencies are specified under the [project.optional-dependencies] section and are intended to give users options for including additional dependencies with their installation. Optional dependencies are collected together as a list of strings and assigned to a name:

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

Named dependency lists can be invoked by users on installation to include those specific dependencies on installation. Here is an example installing the test and docs dependencies using pip:

  • python -m pip install examplePy[test,docs]

Other installation tools tend to follow a similar pattern where optional dependencies are concatenated as a list to the package name.

Dependency Groups#

Dependency groups are a way to group package requirements in the pyproject.toml file but exclude them in the project metadata when it is built. Because it is not included in the project metadadata, users will not be able to invoke dependency groups when installing from a package index such as PyPI, but they can be accessed by developers who have all the project data (e.g. through cloning a repository). Optional dependencies are intended for package consumers and dependency groups are intended for package maintainers and contributors.

To add development dependencies to your build, add a [dependency-groups] array to your pyproject.toml file. Then specify dependency groups as follows:

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

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

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

One of the capabilities that dependency groups have is the ability to make composition groups. For example, dev dependency group could be composed of a test dependency group and a lint dependency group:

[dependency-groups]
...
...
...

dev = [
    {include-group = "test"},
    {include-group = "lint"}
]

To access groups using pip (requires pip 25.1 or higher), you can invoke them during installation like this:

  • python -m pip install --group dev

Dependências recursivas

Você também pode configurar conjuntos de dependências recursivas. Veja este post de blog para saber mais.

Exemplo de pyproject.toml para build usando hatchling#

Abaixo está um exemplo de configuração de build para um projeto Python. Esta configuração de pacote de exemplo usa o hatchling para construir as distribuições sdist e wheel do pacote.

# pyproject.toml example build setup to use hatchling and hatch_vcs
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "examplePy"
authors = [
    {name = "Some Maintainer", email = "some-email@pyopensci.org"},
]
maintainers = [
    {name = "All the contributors"},
]
description = "An example Python package used to support Python packaging tutorials"
keywords = ["pyOpenSci", "python packaging"]
readme = "README.md"
license = "BSD-3-Clause"
classifiers = [
    # How mature is this project? Common values are
    "Development Status :: 4 - Beta",

    # Indicate who your project is intended for
    "Intended Audience :: Developers",
    "Topic :: Software Development :: Build Tools",
    "Programming Language :: Python :: 3 :: Only",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
]
dependencies = [
    "dependency-package-name-1",
    "dependency-package-name-2",
    "pandas",
    "xarray",
    "geopandas",
    "matplotlib"
]

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

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

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

dev = [
    {include-group = "test"},
    {include-group = "lint"}
]

Note que as dependências são especificadas neste arquivo.

Exemplo de pyproject.toml para build usando setuptools#

Os metadados do pacote, incluindo autores, palavras-chave etc., também são fáceis de ler. Abaixo você pode ver o mesmo arquivo TOML que usa um sistema de build diferente (setuptools). Note como é simples trocar as ferramentas necessárias para construir este pacote!

Nesta configuração de pacote de exemplo você usa:

No exemplo abaixo, [build-system] é a primeira tabela de valores. Ela tem duas chaves que especificam a API do backend de build e o pacote que a contém:

  1. requires =

  2. build-back-end =

[project]
name = "examplePy"
authors = [
    {name = "Some Maintainer", email = "some-email@pyopensci.org"},
]
maintainers = [
    {name = "All the contributors"},
]
description = "A setuptools example Python package used to support Python packaging tutorials"
keywords = ["pyOpenSci", "python packaging"]
readme = "README.md"
license = "BSD-3-Clause"
classifiers = [
    "Programming Language :: Python :: 3",
    "Operating System :: OS Independent",
    "Intended Audience :: Science/Research",
]
dependencies = []
dynamic = [
    "version",
]


[build-system]
requires = [
    "setuptools>=80",
    "setuptools-scm",
    "wheel",
]
build-backend = "setuptools.build_meta"


[tool.setuptools]
# Allow the package to be used directly from a zip archive.
zip-safe = true

# Do not automatically include non-Python package data in the wheel.
include-package-data = false

[tool.setuptools_scm]
write_to = "src/examplePy/_version.py"
version_scheme = "release-branch-semver"


[project.optional-dependencies]

test = [
    "pytest",
    "numpy",
    "pandas",
    "ruff",
]

contrib = [
    "pre-commit>=4.1.0",
]