Como modernizar um projeto baseado em setup.py?

pyproject.toml deve ser adicionado?

Um arquivo pyproject.toml é fortemente recomendado. A presença de um arquivo pyproject.toml em si não traz muita coisa. [1] O que é realmente recomendado é a tabela [build-system] no pyproject.toml.

Deve o setup.py ser excluído?

Não, setup.py pode existir em um projeto moderno baseado em Setuptools. O arquivo setup.py é um arquivo de configuração válido para setuptools que foi escrito em Python. No entanto, os seguintes comandos for descontinuados e NÃO DEVEM ser mais executados, e seus comandos de substituição recomendados devem ser usados:

Descontinuado

Recomendação

python setup.py install

python -m pip install .

python setup.py develop

python -m pip install --editable .

python setup.py sdist

python -m build

python setup.py bdist_wheel

Para mais detalhes:

Onde começar?

O projeto deve conter um arquivo pyproject.toml na raiz de sua árvore de fontes que contém uma tabela [build-system] como esta:

[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

Este é o método padronizado para permitir que frontends de construção saibam que Setuptools é o backend de construção para este projeto.

Observe que a presença de um arquivo pyproject.toml (mesmo que vazio) aciona pip para alterar seu comportamento padrão para usar isolamento de construção.

Para mais detalhes:

Como lidar com dependências de tempo de construção adicionais?

Além do próprio setuptools, se setup.py depende de outras bibliotecas de terceiros (fora da biblioteca padrão do Python), elas devem ser listadas na lista requires do [build-system ] tabela, para que o frontend de construção saiba instalá-los ao construir as distribuições.

Por exemplo, um arquivo setup.py como este:

import setuptools
import some_build_toolkit  # comes from the `some-build-toolkit` library

def get_version():
    version = some_build_toolkit.compute_version()
    return version

setuptools.setup(
    name="my-project",
    version=get_version(),
)

requer um arquivo pyproject.toml como este (setup.py permanece inalterado):

[build-system]
requires = [
    "setuptools",
    "some-build-toolkit",
]
build-backend = "setuptools.build_meta"

Para mais detalhes:

Qual é o recurso de isolamento de compilação?

Frontends de construção normalmente criam um ambiente virtual efêmero onde instalam apenas as dependências de construção (e suas dependências) listadas em build-system.requires e acionam a construção nesse ambiente.

Para alguns projetos este isolamento é indesejado e pode ser desativado da seguinte forma:

  • python -m build --no-isolation

  • python -m pip install --no-build-isolation

Para mais detalhes:

Como lidar com metadados de empacotamento?

Todos os metadados estáticos podem opcionalmente ser movidos para uma tabela [project] em pyproject.toml.

Por exemplo, um arquivo setup.py como este:

import setuptools

setuptools.setup(
    name="my-project",
    version="1.2.3",
)

pode ser totalmente substituído por um arquivo pyproject.toml como este:

[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "my-project"
version = "1.2.3"

Leia Declaring project metadata: the [project] table para a especificação completa do conteúdo permitido na tabela [project].

Como lidar com metadados dinâmicos?

Se alguns campos de metadados de empacotamento não forem estáticos, eles precisam ser listados como dynamic nesta tabela [project].

Por exemplo, um arquivo setup.py como este:

import setuptools
import some_build_toolkit

def get_version():
    version = some_build_toolkit.compute_version()
    return version

setuptools.setup(
    name="my-project",
    version=get_version(),
)

pode ser modernizado desta forma:

[build-system]
requires = [
    "setuptools",
    "some-build-toolkit",
]
build-backend = "setuptools.build_meta"

[project]
name = "my-project"
dynamic = ["version"]
import setuptools
import some_build_toolkit

def get_version():
    version = some_build_toolkit.compute_version()
    return version

setuptools.setup(
    version=get_version(),
)

Para mais detalhes:

E se alguma coisa que não possa ser alterar esperar um arquivo setup.py?

Por exemplo, existe um processo que não pode ser alterado facilmente e precisa executar um comando como python setup.py --name.

É perfeitamente normal deixar um arquivo setup.py na árvore de fontes do projeto mesmo depois de todo o seu conteúdo ter sido movido para pyproject.toml. Este arquivo pode ser tão minimalista quanto este:

import setuptools

setuptools.setup()

Onde posso ler mais sobre isso?