Chegou o momento de dar o próximo passo na sua jornada com Python: estruturar um projeto de forma profissional. Até agora, você provavelmente criou scripts soltos ou notebooks, mas em projetos reais — seja open source ou no trabalho — a organização é crucial. Uma boa estrutura facilita a colaboração, a manutenção, a testabilidade e a distribuição do seu código. Nesta aula, vamos explorar os principais elementos de um projeto Python moderno, incluindo o layout de pastas, o arquivo pyproject.toml e o chamado src layout, além de boas práticas que farão seu código brilhar.

Vamos começar com uma visão geral do que compõe um projeto Python típico, depois mergulharemos em cada parte com exemplos práticos. Ao final, você terá um modelo mental claro para iniciar qualquer novo projeto com o pé direito.

Layout

O layout de um projeto Python é a organização de pastas e arquivos. Uma boa estrutura não é apenas estética: ela define como o código será importado, testado e distribuído. O Python é flexível, mas seguir convenções ajuda a evitar dores de cabeça. Vamos ver um exemplo comum de layout para um projeto chamado meu_projeto:

meu_projeto/
├── pyproject.toml
├── README.md
├── LICENSE
├── src/
│   └── meu_projeto/
│       ├── __init__.py
│       ├── core.py
│       └── utils.py
├── tests/
│   ├── __init__.py
│   ├── test_core.py
│   └── test_utils.py
├── docs/
├── scripts/
└── .gitignore

Esse é um exemplo de src layout, que discutiremos em detalhe adiante. Mas note a presença de pastas como tests, docs e scripts. Cada uma tem um propósito: tests para os testes, docs para documentação e scripts para utilitários de automação. O .gitignore é essencial para não versionar arquivos desnecessários, como __pycache__ e ambientes virtuais.

Outro ponto importante: o nome do pacote (a pasta dentro de src) deve ser único e descritivo. Evite nomes genéricos como utils ou main. Se o projeto for distribuído, o nome do pacote deve coincidir com o nome no PyPI (em minúsculas, com underscores se necessário).

pyproject.toml (visão geral)

O arquivo pyproject.toml é o coração da configuração de um projeto Python moderno. Ele substitui os antigos setup.py, setup.cfg e requirements.txt (em muitos casos). O formato TOML é simples e legível, e o arquivo é lido por ferramentas como pip, build e poetry. Vamos ver um exemplo básico:

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

[project]
name = "meu_projeto"
version = "0.1.0"
description = "Um projeto exemplo"
readme = "README.md"
authors = [{ name = "Seu Nome", email = "voce@exemplo.com" }]
license = { text = "MIT" }
dependencies = [
    "requests>=2.28",
]

[project.optional-dependencies]
dev = [
    "pytest>=7.0",
    "black",
    "ruff",
]

[tool.pytest.ini_options]
addopts = "-q"

O pyproject.toml define metadados do projeto (nome, versão, autor), dependências e configurações de ferramentas. A seção [build-system] indica como o pacote deve ser construído. A seção [project] contém as informações principais, e [project.optional-dependencies] permite agrupar dependências extras (por exemplo, para desenvolvimento). Ferramentas como pytest, black e ruff podem ser configuradas aqui, centralizando tudo.

Uma grande vantagem é que você pode instalar o projeto em modo editável com pip install -e ., o que atualiza automaticamente seu ambiente com as mudanças no código. Isso é essencial durante o desenvolvimento.

src layout

O src layout é uma convenção que coloca o código do pacote dentro de uma pasta src. Em vez de ter o pacote na raiz, você tem:

meu_projeto/
├── pyproject.toml
├── src/
│   └── meu_projeto/
│       ├── __init__.py
│       ├── core.py
│       └── utils.py
└── tests/

Por que usar essa estrutura? Primeiro, ela evita que você importe acidentalmente o código local em vez do instalado. Sem o src, se você rodar os testes a partir da raiz, o Python pode usar o diretório atual e acabar testando uma versão não instalada, o que pode mascarar erros de empacotamento. Com o src, você é forçado a instalar o pacote (mesmo em modo editável) para importá-lo, garantindo que os testes rodem contra o pacote como ele seria instalado.

Além disso, o src layout separa claramente o código do pacote de outros arquivos (como testes, scripts e configurações), tornando a estrutura mais limpa e profissional. É o padrão recomendado por muitos guias e ferramentas, como o Python Packaging User Guide.

Para configurar o projeto com src layout no pyproject.toml, você precisa indicar onde o pacote está. Com setuptools, você pode usar:

[tool.setuptools.packages.find]
where = ["src"]

Isso faz com que o setuptools procure pacotes dentro de src. Agora, ao instalar com pip install -e ., o pacote será instalado corretamente e os testes poderão importá-lo.

Boas práticas

Além da estrutura, algumas boas práticas são fundamentais para manter seu projeto saudável:

  • Use ambientes virtuais: sempre crie um venv para isolar as dependências do projeto. Ex.: python -m venv .venv e ative-o.
  • Versionamento com Git: inicie um repositório Git e faça commits pequenos e descritivos. Nunca versionie .venv, __pycache__ ou arquivos de build.
  • Documentação: mantenha um README.md claro, com instruções de instalação, uso e exemplos. Use docstrings no código.
  • Testes automatizados: escreva testes para as funcionalidades principais. Use pytest e rode-os com frequência.
  • Linters e formatadores: use ferramentas como ruff (ou black + flake8) para manter o código consistente e evitar erros comuns.
  • Gerenciamento de dependências: declare todas as dependências no pyproject.toml e mantenha as versões atualizadas. Evite instalar pacotes diretamente no ambiente sem registrar.

Essas práticas não são obrigatórias, mas tornam seu projeto mais profissional e facilitam a colaboração. Lembre-se: o código é lido por humanos, então priorize a clareza.

Referências

Exercícios

  1. Exercício 1: Crie a estrutura de pastas para um projeto chamado meu_app usando o src layout. Inclua as pastas tests, src, docs e um arquivo pyproject.toml mínimo.
  2. ✓ Resposta: Uma possível estrutura seria:
    meu_app/
    ├── pyproject.toml
    ├── src/
    │   └── meu_app/
    │       └── __init__.py
    ├── tests/
    │   └── __init__.py
    ├── docs/
    └── README.md
    

    E o pyproject.toml mínimo:

    [build-system]
    requires = ["setuptools>=61.0"]
    build-backend = "setuptools.build_meta"
    
    [project]
    name = "meu_app"
    version = "0.1.0"
    description = "Um app exemplo"
    
    [tool.setuptools.packages.find]
    where = ["src"]
    
  3. Exercício 2: Explique a diferença entre um layout tradicional (pacote na raiz) e o src layout. Por que o src layout é recomendado?
  4. ✓ Resposta: No layout tradicional, o pacote fica na raiz do projeto, o que pode causar importações acidentais do código local em vez do instalado. O src layout coloca o pacote dentro de uma pasta src, forçando a instalação do pacote (mesmo em modo editável) para importá-lo. Isso garante que os testes sejam executados contra o pacote como ele será instalado, evitando erros de empacotamento e tornando a estrutura mais limpa.
  5. Exercício 3: No pyproject.toml, o que significa a seção [project.optional-dependencies]? Dê um exemplo de como usá-la para dependências de desenvolvimento.
  6. ✓ Resposta: A seção [project.optional-dependencies] define grupos de dependências extras que podem ser instalados opcionalmente. Por exemplo, para dependências de desenvolvimento:
    [project.optional-dependencies]
    dev = [
        "pytest",
        "ruff",
        "black",
    ]
    
    Para instalar essas dependências, use pip install -e .[dev].
  7. Exercício 4: Liste três boas práticas essenciais para manter um projeto Python organizado e explique cada uma em uma frase.
  8. ✓ Resposta: 1) Usar ambientes virtuais para isolar dependências; 2) Escrever testes automatizados para garantir que o código funcione; 3) Manter a documentação (README e docstrings) atualizada para facilitar o uso e a colaboração.
  9. Exercício 5: Suponha que você tenha um projeto com o arquivo pyproject.toml abaixo. Identifique e explique cada seção principal.
    [build-system]
    requires = ["setuptools>=61.0"]
    build-backend = "setuptools.build_meta"
    
    [project]
    name = "meu_projeto"
    version = "0.1.0"
    dependencies = ["requests"]
    
    [tool.pytest.ini_options]
    addopts = "-q"
    
  10. ✓ Resposta: A seção [build-system] especifica o backend de construção (setuptools) e as dependências necessárias para construir o pacote. A seção [project] define os metadados principais: nome, versão e dependências do projeto. A seção [tool.pytest.ini_options] configura o pytest, neste caso adicionando a opção -q (modo silencioso) por padrão.

Observações finais

Estruturar um projeto é uma habilidade que você usará em todos os projetos sérios. Comece com um modelo simples e vá refinando conforme a necessidade. Lembre-se de que a consistência é mais importante do que a perfeição: escolha uma estrutura e siga-a. Com o tempo, você desenvolverá seu próprio estilo, mas sempre baseado nas boas práticas que vimos aqui.

Na próxima aula, vamos aprofundar no uso de ferramentas de teste e linting. Até lá, mãos à obra!