Apa struktur proyek terbaik untuk aplikasi Python? [Tutup]

730

Bayangkan Anda ingin mengembangkan aplikasi desktop pengguna akhir (bukan web) yang tidak sepele dengan Python. Apa cara terbaik untuk menyusun hierarki folder proyek?

Fitur yang diinginkan adalah kemudahan perawatan, keramahan IDE, kesesuaian untuk percabangan / penggabungan kontrol sumber, dan pembuatan paket instalasi yang mudah.

Khususnya:

  1. Di mana Anda meletakkan sumbernya?
  2. Di mana Anda meletakkan skrip startup aplikasi?
  3. Di mana Anda meletakkan proyek IDE cruft?
  4. Di mana Anda menempatkan tes unit / penerimaan?
  5. Di mana Anda meletakkan data non-Python seperti file konfigurasi?
  6. Di mana Anda meletakkan sumber non-Python seperti C ++ untuk modul ekstensi pyd / binary?
kbluck
sumber

Jawaban:

378

Tidak terlalu penting. Apa pun yang membuat Anda bahagia akan berhasil. Tidak ada banyak aturan konyol karena proyek Python bisa sederhana.

  • /scriptsatau /binuntuk hal-hal seperti antarmuka baris perintah
  • /tests untuk tes Anda
  • /lib untuk perpustakaan bahasa-C Anda
  • /doc untuk sebagian besar dokumentasi
  • /apidoc untuk dokumentasi API yang dihasilkan Epydoc.

Dan direktori tingkat atas dapat berisi README, Config's dan yang lainnya.

Pilihan yang sulit adalah apakah menggunakan /srcpohon atau tidak . Python tidak memiliki perbedaan antara /src, /libdan /binseperti Java atau C memiliki.

Karena /srcdirektori tingkat atas dilihat oleh beberapa orang sebagai tidak berarti, direktori tingkat atas Anda dapat menjadi arsitektur tingkat atas aplikasi Anda.

  • /foo
  • /bar
  • /baz

Saya sarankan meletakkan semua ini di bawah direktori "nama-produk-saya". Jadi, jika Anda menulis aplikasi bernama quux, direktori yang berisi semua hal ini dinamai /quux.

Proyek lain PYTHONPATH, kemudian, dapat mencakup /path/to/quux/foountuk menggunakan kembali QUUX.foomodul.

Dalam kasus saya, karena saya menggunakan Komodo Edit, cuft IDE saya adalah file .KPF tunggal. Saya benar-benar meletakkannya di /quuxdirektori tingkat atas , dan mengabaikan menambahkannya ke SVN.

S.Lott
sumber
23
Setiap proyek python open source yang Anda sarankan meniru struktur direktori mereka?
Lance Rushing
4
Lihatlah Django untuk contoh yang bagus.
S.Lott
33
Saya tidak cenderung menganggap Django sebagai contoh yang baik - bermain trik dengan sys.path adalah DQ instan dalam buku saya.
Charles Duffy
18
re "trik": Django menambahkan induk dari folder proyek root ke sys.path, sehingga modul dapat diimpor sebagai "dari project.app.module import klass" atau "from app.module import klass".
Jonathan Hartley
3
Oh, saya suka trik ini dan saya menggunakannya sekarang. Saya ingin meletakkan modul bersama di direktori lain, dan saya tidak ingin menginstal modul di seluruh sistem, saya juga tidak ingin meminta orang untuk memodifikasi PYTHONPATH secara manual. Kecuali orang mengusulkan sesuatu yang lebih baik, saya pikir ini sebenarnya cara paling bersih untuk melakukannya.
Yongwei Wu
242

Menurut struktur Filesystem Jean-Paul Calderone dari proyek Python :

Project/
|-- bin/
|   |-- project
|
|-- project/
|   |-- test/
|   |   |-- __init__.py
|   |   |-- test_main.py
|   |   
|   |-- __init__.py
|   |-- main.py
|
|-- setup.py
|-- README
cmcginty
sumber
23
Project/project/? Ah, yang kedua adalah nama paketnya.
Cees Timmerman
44
bagaimana file yang dapat dieksekusi di folder bin referensi modul proyek? (Saya tidak berpikir sintaks python memungkinkan ../dalam pernyataan include)
ThorSummoner
8
@ThorSummoner Sederhana. Anda menginstal paket! ( pip install -e /path/to/Project)
Kroltan
22
Akan luar biasa jika seseorang membuat sampel layout ini dengan hello.py dan hello-test.py dan membuatnya tersedia bagi kita yang baru.
jeremyjjbrown
8
@Bloke Inti adalah -ebendera, yang menginstal paket sebagai paket yang dapat diedit, yaitu menginstalnya sebagai tautan ke folder proyek yang sebenarnya. Dapat dieksekusi kemudian hanya import projectmemiliki akses ke modul.
Kroltan
232

Posting blog ini oleh Jean-Paul Calderone umumnya diberikan sebagai jawaban dalam #python di Freenode.

Struktur sistem file proyek Python

Melakukan:

  • beri nama direktori yang terkait dengan proyek Anda. Misalnya, jika proyek Anda bernama "Twisted", beri nama direktori tingkat atas untuk file sumbernya Twisted. Ketika Anda melakukan rilis, Anda harus menyertakan nomor versi akhiran: Twisted-2.5.
  • buat direktori Twisted/bindan letakkan executable Anda di sana, jika Anda punya. Jangan beri mereka .pyekstensi, bahkan jika itu adalah file sumber Python. Jangan memasukkan kode apa pun di dalamnya kecuali impor dan panggil ke fungsi utama yang ditentukan di tempat lain di proyek Anda. (Sedikit kerutan: karena pada Windows, juru bahasa dipilih oleh ekstensi file, pengguna Windows Anda sebenarnya menginginkan ekstensi .py. Jadi, ketika Anda mengemas untuk Windows, Anda mungkin ingin menambahkannya. Sayangnya tidak ada trik distutil yang mudah yang Saya tahu untuk mengotomatiskan proses ini. Menimbang bahwa pada POSIX ekstensi .py hanya merupakan kutil, sedangkan pada Windows kekurangannya adalah bug yang sebenarnya, jika basis pengguna Anda mencakup pengguna Windows, Anda mungkin ingin memilih untuk hanya memiliki .py ekstensi di mana-mana.)
  • Jika proyek Anda dapat diekspresikan sebagai file sumber Python tunggal, maka masukkan ke direktori dan beri nama sesuatu yang terkait dengan proyek Anda. Sebagai contoh Twisted/twisted.py,. Jika Anda memerlukan banyak file sumber, buat paket sebagai gantinya ( Twisted/twisted/, dengan kosong Twisted/twisted/__init__.py) dan tempatkan file sumber Anda di dalamnya. Sebagai contoh Twisted/twisted/internet.py,.
  • letakkan tes unit Anda dalam sub-paket paket Anda (catatan - ini berarti bahwa opsi file sumber Python tunggal di atas adalah trik - Anda selalu memerlukan setidaknya satu file lain untuk pengujian unit Anda). Sebagai contoh Twisted/twisted/test/,. Tentu saja, buatlah paket dengan Twisted/twisted/test/__init__.py. Tempatkan tes di file seperti Twisted/twisted/test/test_internet.py.
  • tambahkan Twisted/READMEdan Twisted/setup.pyjelaskan dan instal perangkat lunak Anda, jika Anda merasa senang.

Jangan:

  • letakkan sumber Anda di direktori bernama srcatau lib. Ini membuatnya sulit dijalankan tanpa menginstal.
  • letakkan tes Anda di luar paket Python Anda. Ini membuatnya sulit untuk menjalankan tes terhadap versi yang diinstal.
  • buat paket yang hanya memiliki __init__.pydan kemudian masukkan semua kode Anda __init__.py. Buat saja modul alih-alih paket, itu lebih sederhana.
  • cobalah untuk membuat hack ajaib untuk membuat Python dapat mengimpor modul atau paket Anda tanpa meminta pengguna menambahkan direktori yang berisi itu ke jalur impor mereka (baik melalui PYTHONPATH atau mekanisme lain). Anda tidak akan menangani semua kasus dengan benar dan pengguna akan marah kepada Anda ketika perangkat lunak Anda tidak berfungsi di lingkungan mereka.
Adrian
sumber
25
Inilah yang saya butuhkan. "JANGAN mencoba membuat hack ajaib untuk membuat Python dapat mengimpor modul atau paket Anda tanpa meminta pengguna menambahkan direktori yang berisi itu ke jalur impor mereka." Senang mendengarnya!
Jack O'Connor
1
Masalahnya, ini tidak menyebutkan bagian dokumen penting dari suatu proyek di mana menempatkannya.
lpapp
14
Bingung tentang "taruh sumber Anda di direktori yang disebut src atau lib. Ini membuatnya sulit dijalankan tanpa menginstal.". Apa yang akan dipasang? Apakah itu nama dir yang menyebabkan masalah, atau fakta bahwa itu adalah subdirr?
Peter Ehrlich
4
"Beberapa orang akan menyatakan bahwa Anda harus mendistribusikan tes Anda di dalam modul Anda sendiri - saya tidak setuju. Ini sering meningkatkan kompleksitas bagi pengguna Anda; banyak suite tes sering memerlukan dependensi tambahan dan konteks runtime." python-guide-pt-br.readthedocs.io/en/latest/writing/structure/…
endolith
2
"Ini membuatnya sulit dijalankan tanpa menginstal." - itu intinya
Nick T
123

Lihat Sumber Terbuka Proyek Python dengan Cara yang Benar .

Biarkan saya kutip bagian tata letak proyek dari artikel yang sangat baik:

Saat menyiapkan proyek, tata letak (atau struktur direktori) penting untuk dikerjakan dengan benar. Tata letak yang masuk akal berarti bahwa kontributor potensial tidak perlu menghabiskan selamanya mencari sepotong kode; lokasi file intuitif. Karena kita sedang berurusan dengan proyek yang sudah ada, itu berarti Anda mungkin perlu memindahkan beberapa barang.

Mari kita mulai dari atas. Sebagian besar proyek memiliki sejumlah file tingkat atas (seperti setup.py, README.md, requirement.txt, dll). Lalu ada tiga direktori yang harus dimiliki setiap proyek:

  • Direktori dokumen yang berisi dokumentasi proyek
  • Direktori yang dinamai dengan nama proyek yang menyimpan paket Python yang sebenarnya
  • Direktori uji di salah satu dari dua tempat
    • Di bawah direktori paket yang berisi kode uji dan sumber daya
    • Sebagai direktori tingkat atas yang berdiri sendiri Untuk mendapatkan pemahaman yang lebih baik tentang bagaimana file Anda harus diatur, berikut ini snapshot sederhana dari tata letak untuk salah satu proyek saya, 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

Seperti yang Anda lihat, ada beberapa file level atas, direktori docs (dihasilkan adalah direktori kosong tempat sphinx akan meletakkan dokumentasi yang dihasilkan), direktori sandman, dan direktori uji di bawah sandman.

David C. Bishop
sumber
4
Saya melakukan ini, tetapi lebih dari itu: Saya memiliki Makefile tingkat atas dengan target 'env' yang mengotomatiskan 'virtualenv env; ./env/bin/pip instal -r requirement.txt; ./env/bin/python setup.py mengembangkan ', dan juga biasanya target' tes 'yang bergantung pada env dan juga menginstal dependensi pengujian dan kemudian menjalankan py.test.
pjz
@ pjz Bisakah Anda memperluas ide Anda? Apakah Anda berbicara tentang menempatkan Makefilepada tingkat yang sama setup.py? Jadi jika saya mengerti Anda make envmengotomatiskan pembuatan yang baru venvdan instal paket ke dalamnya ...?
St.Antario
@ St.Antario tepatnya. Seperti yang disebutkan pada umumnya saya juga memiliki target 'test' untuk menjalankan tes, dan kadang-kadang target 'release' yang melihat tag saat ini dan membangun roda dan mengirimkannya ke pypi.
pjz
19

Coba mulai proyek menggunakan templat python_boilerplate . Ini sebagian besar mengikuti praktik terbaik (misalnya yang ada di sini ), tetapi lebih cocok jika Anda merasa ingin membagi proyek Anda menjadi lebih dari satu telur di beberapa titik (dan percayalah, dengan apa pun kecuali proyek paling sederhana, Anda akan melakukannya.) situasi umum adalah ketika Anda harus menggunakan versi perpustakaan orang lain yang dimodifikasi secara lokal).

  • Di mana Anda meletakkan sumbernya?

    • Untuk proyek besar yang layak, masuk akal untuk membagi sumber menjadi beberapa telur. Setiap telur akan diletakkan sebagai layouttools-layout terpisah di bawahnya PROJECT_ROOT/src/<egg_name>.
  • Di mana Anda meletakkan skrip startup aplikasi?

    • Pilihan yang ideal adalah membuat skrip startup aplikasi terdaftar sebagai entry_pointsalah satu dari sel telur.
  • Di mana Anda meletakkan proyek IDE cruft?

    • Tergantung pada IDE. Banyak dari mereka menyimpan barang-barang mereka PROJECT_ROOT/.<something>di root proyek, dan ini baik-baik saja.
  • Di mana Anda menempatkan tes unit / penerimaan?

    • Setiap telur memiliki serangkaian tes terpisah, disimpan dalam PROJECT_ROOT/src/<egg_name>/testsdirektori. Saya pribadi lebih suka menggunakan py.testuntuk menjalankannya.
  • Di mana Anda meletakkan data non-Python seperti file konfigurasi?

    • Tergantung. Mungkin ada berbagai jenis data non-Python.
      • "Sumberdaya" , yaitu data yang harus dikemas dalam sebuah telur. Data ini masuk ke direktori telur yang sesuai, di suatu tempat di dalam namespace paket. Ini dapat digunakan melalui pkg_resourcespaket dari setuptools, atau karena Python 3.7 melalui importlib.resourcesmodul dari pustaka standar.
      • "File config" , yaitu file non-Python yang dianggap eksternal dari file sumber proyek, tetapi harus diinisialisasi dengan beberapa nilai ketika aplikasi mulai berjalan. Selama pengembangan saya lebih suka menyimpan file seperti itu di PROJECT_ROOT/config. Untuk penyebaran bisa ada berbagai opsi. Di Windows seseorang dapat menggunakan %APP_DATA%/<app-name>/config, di Linux, /etc/<app-name>atau /opt/<app-name>/config.
      • File yang dihasilkan , yaitu file yang dapat dibuat atau dimodifikasi oleh aplikasi selama eksekusi. Saya lebih suka menyimpannya PROJECT_ROOT/varselama pengembangan, dan di bawah /varselama penyebaran Linux.
  • Di mana Anda meletakkan sumber non-Python seperti C ++ untuk modul ekstensi pyd / binary?
    • Ke PROJECT_ROOT/src/<egg_name>/native

Dokumentasi biasanya akan masuk PROJECT_ROOT/docatau PROJECT_ROOT/src/<egg_name>/doc(ini tergantung pada apakah Anda menganggap beberapa telur sebagai proyek besar yang terpisah). Beberapa konfigurasi tambahan akan ada di file seperti PROJECT_ROOT/buildout.cfgdan PROJECT_ROOT/setup.cfg.

KT.
sumber
Terima kasih atas jawaban yang bagus! Anda mengklarifikasi banyak hal untuk saya! Saya hanya punya satu pertanyaan: Bisakah telur bersarang?
Shookie
Tidak, Anda tidak dapat "bersarang" telur dalam arti menyimpan file .egg dalam file .egg lainnya dan berharap ini akan banyak berguna [kecuali Anda merencanakan sesuatu yang sangat aneh]. Apa yang dapat Anda lakukan, adalah membuat telur "virtual" - paket kosong yang tidak menyediakan kode yang berguna, tetapi daftarkan paket lain dalam daftar ketergantungannya. Dengan cara ini, ketika seorang pengguna mencoba untuk menginstal paket seperti itu ia akan secara rekursif menginstal banyak telur dependen.
KT.
@KT, bisakah Anda menguraikan sedikit tentang bagaimana Anda menangani data yang dihasilkan? Secara khusus, bagaimana Anda (dalam kode) membedakan antara pengembangan dan penyebaran? Saya membayangkan Anda memiliki beberapa base_data_locationvariabel, tetapi bagaimana Anda mengaturnya dengan tepat?
cmyr
1
Saya kira Anda berbicara tentang "data runtime" - sesuatu yang sering orang letakkan di bawah / var / nama paket atau ~ / .packagename / var, atau yang lainnya. Sebagian besar waktu pilihan-pilihan itu sudah cukup sebagai default yang tidak ingin diubah oleh pengguna Anda. Jika Anda ingin membiarkan perilaku ini disetel, opsi agak banyak dan saya tidak berpikir ada satu praktik terbaik yang cocok untuk semua. Pilihan umum: a) ~ / .packagename / configfile, b) ekspor MY_PACKAGE_CONFIG = / path / ke / configfile c) opsi baris perintah atau parameter fungsi d) kombinasi dari keduanya.
KT.
Perhatikan bahwa sangat umum untuk memiliki kelas Config singleton di suatu tempat, yang menangani logika memuat konfigurasi favorit Anda untuk Anda dan mungkin bahkan memungkinkan pengguna memodifikasi pengaturan saat runtime. Secara umum, meskipun, saya pikir ini adalah masalah yang bernilai pertanyaan terpisah (yang mungkin telah ditanyakan sebelumnya di suatu tempat di sini).
KT.
15

Dalam pengalaman saya, itu hanya masalah iterasi. Letakkan data dan kode Anda ke mana pun menurut Anda. Kemungkinannya, toh Anda akan salah. Tetapi begitu Anda mendapatkan ide yang lebih baik tentang bagaimana keadaan akan terbentuk, Anda berada dalam posisi yang jauh lebih baik untuk membuat tebakan semacam ini.

Sejauh sumber ekstensi, kami memiliki direktori Code di bawah trunk yang berisi direktori untuk python dan direktori untuk berbagai bahasa lainnya. Secara pribadi, saya lebih cenderung mencoba memasukkan kode ekstensi apa pun ke dalam repositori sendiri di waktu mendatang.

Dengan mengatakan itu, saya kembali ke titik awal saya: jangan terlalu mempermasalahkan hal itu. Letakkan di suatu tempat yang tampaknya bekerja untuk Anda. Jika Anda menemukan sesuatu yang tidak berhasil, itu dapat (dan harus) diubah.

Jason Baker
sumber
Ya. Saya mencoba untuk menjadi "Pythonic" tentang hal itu: eksplisit lebih baik daripada implisit .. Direktori hirarki dibaca / diperiksa lebih dari yang tertulis. Dll ..
eric
10

Data non-python terbaik dibundel di dalam modul Python Anda menggunakan package_datadukungan di setuptools . Satu hal yang saya sangat sarankan adalah menggunakan paket namespace untuk membuat ruang nama bersama yang dapat digunakan beberapa proyek - mirip dengan konvensi Java untuk memasukkan paket com.yourcompany.yourproject(dan dapat memiliki com.yourcompany.utilsnamespace bersama ).

Bercabang kembali dan menggabungkan, jika Anda menggunakan sistem kontrol sumber yang cukup baik, ia akan menangani penggabungan bahkan melalui penggantian nama; Bazaar sangat pandai dalam hal ini.

Bertentangan dengan beberapa jawaban lain di sini, saya +1 memiliki srcdirektori tingkat atas (dengan docdan testdirektori di samping). Konvensi khusus untuk pohon direktori dokumentasi akan bervariasi tergantung pada apa yang Anda gunakan; Sphinx , misalnya, memiliki konvensi sendiri yang didukung oleh alat cepatnya.

Tolong, tolong manfaatkan setuptools dan pkg_resources; ini membuatnya lebih mudah bagi proyek lain untuk bergantung pada versi spesifik kode Anda (dan untuk beberapa versi diinstal secara bersamaan dengan file non-kode yang berbeda, jika Anda menggunakan package_data).

Charles Duffy
sumber