Layout Python: o teste importante é importar o que você realmente distribui
Flat e src resolvem necessidades diferentes. O contrato de instalação e a presença dos recursos no pacote devem orientar a estrutura.
Um tutorial da Real Python propõe escolher a estrutura de projetos conforme o modo de uso e distribuição, distinguindo script, aplicação e biblioteca.[16] A comparação entre layout flat e src/ destaca uma armadilha: testes podem encontrar o código no diretório local mesmo quando ele não foi incluído corretamente na distribuição.[16] A pasta mais organizada não é necessariamente a que melhor testa o contrato entregue ao usuário.
O que muda para quem desenvolve
No layout flat, o pacote fica na raiz e costuma estar imediatamente disponível para importação.[16] No layout src/, ele fica separado e exige instalação, normalmente editável durante o desenvolvimento, reduzindo a interferência da raiz nos imports.[16]
A análise é de custo operacional, não de preferência estética. Para uma automação pequena, uma etapa extra de instalação pode não resolver um problema relevante. Para uma biblioteca, vale perguntar se os testes estão exercitando o pacote instalado ou apenas aproveitando a conveniência do diretório de desenvolvimento.
O tutorial concentra metadados e configuração moderna em pyproject.toml e sugere pontos de entrada pequenos, direcionados a uma função principal testável.[16] Minha recomendação é manter a lógica longe do mecanismo de inicialização. Isso facilita testar comportamento sem depender de como o programa foi chamado.
Como aplicar
Antes de mover arquivos, descreva como o software chega ao consumidor: script copiado, aplicação implantada ou pacote instalado. Escolha o layout mais simples que permita validar esse caminho. Não introduza src/ como ritual nem descarte seu isolamento quando distribuição é parte central do produto.
Faça um experimento com um ambiente limpo: construa a distribuição, instale-a e execute um caso representativo fora da raiz do repositório. Confira imports, ponto de entrada e leitura de recursos. O objetivo é remover a ajuda acidental do diretório de desenvolvimento. Esse é um procedimento sugerido, não uma execução apresentada aqui.
Ícones, traduções e outros recursos precisam acompanhar o pacote, com declaração no build e leitura por mecanismos próprios de recursos, em vez de depender do diretório de execução.[16] Inclua no teste pelo menos um recurso real usado pela aplicação. Uma função que importa corretamente não comprova que seus arquivos auxiliares foram entregues.
Se houver CLI e execução como módulo, direcione ambos para a mesma função principal quando isso atender ao uso. O tutorial apresenta __main__.py e [project.scripts] como pontos de entrada possíveis.[16]
Cuidados e limites
Frameworks impõem convenções próprias: o texto distingue apps Django, blueprints Flask e routers FastAPI.[16] Mover um app Django altera caminhos de importação e exige ajustar seu registro.[16] Uma reorganização ampla merece testes de inicialização, não apenas substituição de nomes de pasta.
O layout src/ não elimina erros de build, e o flat não obriga testes ruins. A decisão deve reduzir o risco que existe no projeto. Estrutura é ferramenta para entregar e verificar software, não uma competição de árvores de diretórios.
Fonte
[16] Fonte: Real Python