Qual é a melhor estrutura de projeto para um aplicativo Python? [fechadas]

730

Imagine que você deseja desenvolver um aplicativo de desktop do usuário final não trivial (não da Web) em Python. Qual é a melhor maneira de estruturar a hierarquia de pastas do projeto?

Os recursos desejáveis ​​são: facilidade de manutenção, compatibilidade com o IDE, adequação para ramificação / mesclagem de controle de origem e geração fácil de pacotes de instalação.

Em particular:

  1. Onde você coloca a fonte?
  2. Onde você coloca os scripts de inicialização do aplicativo?
  3. Onde você coloca o cruft do projeto IDE?
  4. Onde você coloca os testes de unidade / aceitação?
  5. Onde você coloca dados não-Python, como arquivos de configuração?
  6. Onde você coloca fontes não Python, como C ++, para módulos de extensão binária pyd / so?
kbluck
fonte

Respostas:

378

Não importa muito. O que quer que te faça feliz, funcionará. Não há muitas regras tolas, porque os projetos Python podem ser simples.

  • /scriptsou /binpara esse tipo de interface de linha de comando
  • /tests para seus testes
  • /lib para suas bibliotecas em linguagem C
  • /doc para a maioria da documentação
  • /apidoc para os documentos da API gerados pelo Epydoc.

E o diretório de nível superior pode conter README, Config e outros enfeites.

A escolha difícil é usar ou não uma /srcárvore. Python não têm uma distinção entre /src, /libe, /bincomo Java ou C tem.

Como um /srcdiretório de nível superior é visto por alguns como sem sentido, o diretório de nível superior pode ser a arquitetura de nível superior do seu aplicativo.

  • /foo
  • /bar
  • /baz

Eu recomendo colocar tudo isso no diretório "nome do meu produto". Portanto, se você estiver escrevendo um aplicativo chamado quux, o diretório que contém todo esse material será nomeado /quux.

Outro projeto PYTHONPATH, então, pode incluir /path/to/quux/fooa reutilização do QUUX.foomódulo.

No meu caso, desde que eu uso o Komodo Edit, meu conteúdo IDE é um único arquivo .KPF. Na verdade, eu coloquei isso no /quuxdiretório de nível superior e omito adicioná-lo ao SVN.

S.Lott
fonte
23
Qualquer projeto python de código aberto que você recomendaria emular a estrutura de diretórios?
Lance Rushing
4
Veja o Django para um bom exemplo.
S.Lott 5/10/09
33
Eu não costumo considerar o Django um bom exemplo - jogar truques com o sys.path é um DQ instantâneo no meu livro.
Charles Duffy
18
re "truques": O Django adiciona o pai da pasta do projeto raiz ao sys.path, para que os módulos possam ser importados como "do project.app.module import klass" ou "do app.module import klass".
Jonathan Hartley
3
Ah, eu amo esse truque e estou usando agora. Quero colocar o módulo compartilhado em outro diretório e não quero instalar o módulo em todo o sistema, nem quero pedir às pessoas que modifiquem o PYTHONPATH manualmente. A menos que as pessoas proponham algo melhor, acho que esse é realmente o caminho mais limpo a seguir.
Yongwei Wu
242

De acordo com a estrutura do sistema de arquivos de Jean-Paul Calderone de um projeto Python :

Project/
|-- bin/
|   |-- project
|
|-- project/
|   |-- test/
|   |   |-- __init__.py
|   |   |-- test_main.py
|   |   
|   |-- __init__.py
|   |-- main.py
|
|-- setup.py
|-- README
cmcginty
fonte
23
Project/project/? Ah, o segundo é o nome do pacote.
Cees Timmerman
44
como o arquivo executável na pasta bin faz referência ao módulo do projeto? (Eu não acho que a sintaxe python permite que ../em uma instrução include)
ThorSummoner
8
@ThorSummoner Simple. Você instala o pacote! ( pip install -e /path/to/Project)
Kroltan 19/12/14
22
Seria incrível se alguém compactasse uma amostra desse layout com hello.py e hello-test.py e o disponibilizasse para nós novatos.
precisa saber é o seguinte
8
@Bloke O núcleo é o -esinalizador, que instala o pacote como um pacote editável, ou seja, instala-o como links para a pasta real do projeto. O executável pode apenas import projectter acesso ao módulo.
Kroltan
232

Esta postagem de blog de Jean-Paul Calderone geralmente é dada como resposta em #python no Freenode.

Estrutura do sistema de arquivos de um projeto Python

Faz:

  • nomeie o diretório como algo relacionado ao seu projeto. Por exemplo, se o seu projeto tiver o nome "Torcido", nomeie o diretório de nível superior para seus arquivos de origem Twisted. Ao fazer lançamentos, você deve incluir um sufixo número de versão: Twisted-2.5.
  • crie um diretório Twisted/bine coloque seus executáveis ​​lá, se houver algum. Não dê uma .pyextensão a eles , mesmo que sejam arquivos de origem Python. Não coloque nenhum código neles, exceto a importação e a chamada para uma função principal definida em outro lugar em seus projetos. (Ligeiro enrugamento: como no Windows, o intérprete é selecionado pela extensão do arquivo, os usuários do Windows realmente desejam a extensão .py. Portanto, quando você empacota o Windows, pode querer adicioná-la. Infelizmente, não há truques fáceis de desmontar que Sei que para automatizar esse processo, considerando que no POSIX a extensão .py é apenas uma verruga, enquanto no Windows a falta é um bug real, se sua base de usuários inclui usuários do Windows, você pode optar por apenas usar o .py extensão em todos os lugares.)
  • Se o seu projeto for expressável como um único arquivo de origem Python, coloque-o no diretório e atribua um nome relacionado ao seu projeto. Por exemplo Twisted/twisted.py,. Se você precisar de vários arquivos de origem, crie um pacote ( Twisted/twisted/com um vazio Twisted/twisted/__init__.py) e coloque seus arquivos de origem. Por exemplo Twisted/twisted/internet.py,.
  • coloque seus testes de unidade em um subpacote do seu pacote (observe - isso significa que a única opção de arquivo fonte do Python acima foi um truque - você sempre precisa de pelo menos um outro arquivo para seus testes de unidade). Por exemplo Twisted/twisted/test/,. Claro, faça um pacote com Twisted/twisted/test/__init__.py. Coloque testes em arquivos como Twisted/twisted/test/test_internet.py.
  • adicione Twisted/READMEe Twisted/setup.pyexplique e instale seu software, respectivamente, se você estiver se sentindo bem.

Não faça:

  • coloque sua fonte em um diretório chamado srcou lib. Isso dificulta a execução sem a instalação.
  • coloque seus testes fora do seu pacote Python. Isso dificulta a execução dos testes em uma versão instalada.
  • crie um pacote que tenha apenas um __init__.pye depois coloque todo o seu código __init__.py. Basta fazer um módulo em vez de um pacote, é mais simples.
  • tente criar hacks mágicos para tornar o Python capaz de importar seu módulo ou pacote sem que o usuário adicione o diretório que o contém ao caminho de importação (via PYTHONPATH ou algum outro mecanismo). Você não lidará corretamente com todos os casos e os usuários ficarão com raiva de você quando o software não funcionar no ambiente deles.
Adrian
fonte
25
Era exatamente disso que eu precisava. "NÃO tente criar hacks mágicos para tornar o Python capaz de importar seu módulo ou pacote sem que o usuário adicione o diretório que o contém ao caminho de importação". Bom saber!
Jack O'Connor
1
O fato é que isso não menciona a parte importante do documento de um projeto onde colocá-lo.
lpapp 02/09
14
Confuso sobre "coloque sua fonte em um diretório chamado src ou lib. Isso dificulta a execução sem a instalação.". O que seria instalado? É o nome do diretório que causa o problema ou o fato de ser um subdiretório?
Peter Ehrlich
4
"Algumas pessoas afirmam que você deve distribuir seus testes dentro do próprio módulo - discordo. Isso geralmente aumenta a complexidade dos usuários; muitos conjuntos de testes geralmente exigem dependências adicionais e contextos de tempo de execução". python-guide-pt-br.readthedocs.io/en/latest/writing/structure/...
endolith
2
"Isso dificulta a execução sem a instalação." - esse é o ponto
Nick T
123

Confira Open Sourcing a Python Project the Way Right .

Deixe-me extrair a parte do layout do projeto desse excelente artigo:

Ao configurar um projeto, é importante acertar o layout (ou estrutura de diretório). Um layout sensato significa que os colaboradores em potencial não precisam gastar uma eternidade procurando um pedaço de código; os locais dos arquivos são intuitivos. Como estamos lidando com um projeto existente, isso significa que você provavelmente precisará mudar algumas coisas.

Vamos começar do topo. A maioria dos projetos possui vários arquivos de nível superior (como setup.py, README.md, requirements.txt, etc.). Existem três diretórios que todo projeto deve ter:

  • Um diretório docs contendo a documentação do projeto
  • Um diretório nomeado com o nome do projeto que armazena o pacote Python real
  • Um diretório de teste em um dos dois locais
    • Sob o diretório do pacote que contém o código e os recursos de teste
    • Como um diretório autônomo de nível superior Para ter uma melhor noção de como seus arquivos devem ser organizados, aqui está um instantâneo simplificado do layout de um dos meus projetos, sandman:
$ pwd
~/code/sandman
$ tree
.
|- LICENSE
|- README.md
|- TODO.md
|- docs
|   |-- conf.py
|   |-- generated
|   |-- index.rst
|   |-- installation.rst
|   |-- modules.rst
|   |-- quickstart.rst
|   |-- sandman.rst
|- requirements.txt
|- sandman
|   |-- __init__.py
|   |-- exception.py
|   |-- model.py
|   |-- sandman.py
|   |-- test
|       |-- models.py
|       |-- test_sandman.py
|- setup.py

Como você pode ver, existem alguns arquivos de nível superior, um diretório docs (gerado é um diretório vazio onde o sphinx colocará a documentação gerada), um diretório sandman e um diretório de teste no sandman.

David C. Bishop
fonte
4
Eu faço isso, mas ainda mais: eu tenho um Makefile de nível superior com um destino 'env' que automatiza 'enven virtualenv'; ./env/bin/pip install -r requirements.txt; ./env/bin/python setup.py develop ', e também geralmente um destino' test 'que depende do env e também instala dependências de teste e executa py.test.
pjz
@pjz Você poderia expandir sua ideia? Você está falando em colocar Makefileno mesmo nível que setup.py? Então, se eu entendo que você make envautomatiza corretamente a criação de um novo venve instala os pacotes nele ...?
St.Antario 28/11
@ St.Antario exatamente. Como mencionado, geralmente também tenho um alvo de 'teste' para executar os testes e, às vezes, um alvo de 'liberação' que analisa a tag atual e constrói uma roda e a envia para o pypi.
pjz 9/01
19

Tente iniciar o projeto usando o modelo python_boilerplate . Ele segue amplamente as melhores práticas (por exemplo, as aqui ), mas é mais adequado se você estiver disposto a dividir seu projeto em mais de um ovo em algum momento (e acredite, com qualquer coisa, menos nos projetos mais simples, você o fará. situação comum é onde você deve usar uma versão modificada localmente da biblioteca de outra pessoa).

  • Onde você coloca a fonte?

    • Para projetos decentemente grandes, faz sentido dividir a fonte em vários ovos. Cada ovo iria como um layout de ferramentas de instalação separado abaixo PROJECT_ROOT/src/<egg_name>.
  • Onde você coloca os scripts de inicialização do aplicativo?

    • A opção ideal é ter o script de inicialização do aplicativo registrado como entry_pointum dos ovos.
  • Onde você coloca o cruft do projeto IDE?

    • Depende do IDE. Muitos deles mantêm suas coisas na PROJECT_ROOT/.<something>raiz do projeto, e isso é bom.
  • Onde você coloca os testes de unidade / aceitação?

    • Cada ovo tem um conjunto separado de testes, mantido em seu PROJECT_ROOT/src/<egg_name>/testsdiretório. Eu pessoalmente prefiro usar py.testpara executá-los.
  • Onde você coloca dados não-Python, como arquivos de configuração?

    • Depende. Pode haver diferentes tipos de dados não-Python.
      • "Recursos" , isto é, dados que devem ser empacotados dentro de um ovo. Esses dados vão para o diretório egg correspondente, em algum lugar no namespace do pacote. Ele pode ser usado através do pkg_resourcespacote setuptoolsou desde o Python 3.7 através do importlib.resourcesmódulo da biblioteca padrão.
      • "Arquivos de configuração" , ou seja, arquivos não-Python que devem ser considerados externos aos arquivos de origem do projeto, mas precisam ser inicializados com alguns valores quando o aplicativo iniciar a execução. Durante o desenvolvimento, prefiro manter esses arquivos PROJECT_ROOT/config. Para implantação, pode haver várias opções. No Windows, pode-se usar %APP_DATA%/<app-name>/config, no Linux /etc/<app-name>ou /opt/<app-name>/config.
      • Arquivos gerados , ou seja, arquivos que podem ser criados ou modificados pelo aplicativo durante a execução. Eu preferiria mantê-los PROJECT_ROOT/vardurante o desenvolvimento e /vardurante a implantação do Linux.
  • Onde você coloca fontes não Python, como C ++, para módulos de extensão binária pyd / so?
    • Para dentro PROJECT_ROOT/src/<egg_name>/native

A documentação normalmente entra PROJECT_ROOT/docou PROJECT_ROOT/src/<egg_name>/doc(isso depende se você considera alguns dos ovos como grandes projetos separados). Algumas configurações adicionais estarão em arquivos como PROJECT_ROOT/buildout.cfge PROJECT_ROOT/setup.cfg.

KT.
fonte
Obrigado por uma ótima resposta! Você esclareceu muitas coisas para mim! Só tenho uma pergunta: os ovos podem ser aninhados?
Shookie
Não, você não pode "aninhar" ovos no sentido de armazenar arquivos .egg em outros arquivos .egg e esperar que isso seja de grande utilidade [a menos que você esteja planejando algo realmente estranho]. O que você pode fazer, no entanto, é criar ovos "virtuais" - pacotes vazios que não fornecem nenhum código útil, mas listam outros pacotes em suas listas de dependências. Dessa forma, quando um usuário tenta instalar esse pacote, ele instala recursivamente muitos ovos dependentes.
KT.
@KT, você pode explicar um pouco sobre como você lida com os dados gerados? Em particular, como você (em código) distingue entre desenvolvimento e implantação? Eu imagino que você tenha alguma base_data_locationvariável, mas como você a define adequadamente?
Cmr #
1
Eu presumo que você esteja falando sobre "dados de tempo de execução" - algo que as pessoas geralmente colocam em / var / packagename ou ~ / .packagename / var, ou outros enfeites. Na maioria das vezes, essas opções são suficientes como padrão que seus usuários não desejam mudar. Se você deseja permitir que esse comportamento seja ajustado, as opções são bastante abundantes e não acho que exista uma única prática recomendada para todos. Opções típicas: a) ~ / .packagename / configfile, b) exportam MY_PACKAGE_CONFIG = / caminho / para / configfile c) opções da linha de comando ou parâmetros de função d) combinação desses.
KT.
Observe que é bastante comum ter uma classe Config singleton em algum lugar, que lida com sua lógica de carregamento de configuração favorita para você e talvez até permita que o usuário modifique as configurações em tempo de execução. Em geral, porém, acho que esse é um problema que vale a pena fazer uma pergunta separada (que pode ter sido feita antes em algum lugar aqui).
KT.
15

Na minha experiência, é apenas uma questão de iteração. Coloque seus dados e código onde quer que eles achem. As chances são de que você estará errado de qualquer maneira. Mas uma vez que você tenha uma idéia melhor de como as coisas vão melhorar, você estará em uma posição muito melhor para fazer esse tipo de palpite.

Quanto às fontes de extensão, temos um diretório de código no tronco que contém um diretório para python e um diretório para várias outras linguagens. Pessoalmente, estou mais inclinado a tentar colocar qualquer código de extensão em seu próprio repositório na próxima vez.

Com isso dito, volto ao meu ponto inicial: não faça grandes negócios com isso. Coloque em algum lugar que pareça funcionar para você. Se você encontrar algo que não funciona, ele pode (e deve) ser alterado.

Jason Baker
fonte
Sim. Eu tento ser "pitônico" sobre isso: explícito é melhor que implícito. As hierarquias de diretório são lidas / inspecionadas mais do que são escritas. E-mail
eric
10

Os dados não-python são melhor agrupados em seus módulos Python usando o package_datasuporte em setuptools . Uma coisa que eu recomendo é usar pacotes de namespace para criar namespaces compartilhados que vários projetos podem usar - bem como a convenção Java de colocar pacotes com.yourcompany.yourproject(e poder ter um com.yourcompany.utilsnamespace compartilhado ).

Ramificando e mesclando, se você usar um sistema de controle de fonte suficientemente bom, ele manipulará mesclagens mesmo através de renomeações; Bazar é particularmente bom nisso.

Ao contrário de outras respostas aqui, eu tenho +1 em ter um srcdiretório de nível superior (com doce testdiretórios ao lado). Convenções específicas para árvores de diretórios de documentação variarão dependendo do que você estiver usando; O Sphinx , por exemplo, possui suas próprias convenções, suportadas por sua ferramenta de início rápido.

Por favor, aproveite setuptools e pkg_resources; isso torna muito mais fácil para outros projetos confiar em versões específicas do seu código (e que várias versões sejam instaladas simultaneamente com arquivos diferentes de código, se você estiver usando package_data).

Charles Duffy
fonte