Jaka jest najlepsza struktura projektu dla aplikacji Python? [Zamknięte]


730

Wyobraź sobie, że chcesz opracować w Pythonie nietrywialną aplikację komputerową dla użytkowników końcowych (nie WWW). Jaki jest najlepszy sposób na uporządkowanie hierarchii folderów projektu?

Pożądanymi cechami są łatwość konserwacji, łatwość obsługi IDE, przydatność do rozgałęziania / łączenia kontroli źródła oraz łatwe generowanie pakietów instalacyjnych.

W szczególności:

  1. Gdzie umieszczasz źródło?
  2. Gdzie umieszczasz skrypty uruchamiania aplikacji?
  3. Gdzie kładziesz cruft projektu IDE?
  4. Gdzie umieszczasz testy jednostkowe / akceptacyjne?
  5. Gdzie umieszczasz dane inne niż Python, takie jak pliki konfiguracyjne?
  6. Gdzie umieszczasz źródła inne niż Python, takie jak C ++ dla binarnych modułów rozszerzeń pyd / so?

Odpowiedzi:


376

To nie ma większego znaczenia. Cokolwiek cię uszczęśliwia, będzie działać. Nie ma wielu głupich reguł, ponieważ projekty w języku Python mogą być proste.

  • /scriptslub /bindla tego rodzaju interfejsu wiersza poleceń
  • /tests do twoich testów
  • /lib dla twoich bibliotek języka C.
  • /doc dla większości dokumentacji
  • /apidoc dla dokumentów API generowanych przez Epydoc.

A katalog najwyższego poziomu może zawierać pliki README, Config i tak dalej.

Trudnym wyborem jest to, czy użyć /srcdrzewa. Python nie ma rozróżnienia pomiędzy /src, /libi /binjak Java lub C ma.

Ponieważ /srckatalog najwyższego poziomu jest przez niektórych postrzegany jako nieistotny, katalog najwyższego poziomu może być architekturą najwyższego poziomu aplikacji.

  • /foo
  • /bar
  • /baz

Zalecam umieszczenie tego wszystkiego w katalogu „name-of-my-product”. Jeśli więc piszesz aplikację o nazwie quux, katalog, który zawiera wszystkie te rzeczy, nazywa się /quux.

Kolejny projekt PYTHONPATHmoże zatem obejmować /path/to/quux/fooponowne użycie QUUX.foomodułu.

W moim przypadku, ponieważ korzystam z Edycji Komodo, mój kufel IDE to pojedynczy plik .KPF. Właściwie umieszczam to w katalogu najwyższego poziomu /quuxi pomijam dodawanie go do SVN.


23
Jakieś projekty pythonowe typu open source, które poleciłbyś emulować ich strukturę katalogów?
Lance Rushing

4
Spójrz na Django jako dobry przykład.
S.Lott,

33
Nie uważam Django za dobry przykład - granie sztuczkami z sys.path to natychmiastowe DQ w mojej książce.
Charles Duffy

18
re „tricks”: Django dodaje element nadrzędny głównego folderu projektu do sys.path, dzięki czemu moduły można importować jako „z projektu.app.module import klass” lub „z app.module import klass”.
Jonathan Hartley,

3
Och, uwielbiam tę sztuczkę i używam jej teraz. Chcę umieścić moduł współdzielony w innym katalogu i nie chcę instalować modułu w całym systemie, ani też nie chcę prosić ludzi o ręczne modyfikowanie PYTHONPATH. Chyba że ludzie zaproponują coś lepszego, myślę, że jest to najczystszy sposób.
Yongwei Wu

242

Według struktury systemu plików Jeana-Paula Calderone'a projektu Python :

Project/
|-- bin/
|   |-- project
|
|-- project/
|   |-- test/
|   |   |-- __init__.py
|   |   |-- test_main.py
|   |   
|   |-- __init__.py
|   |-- main.py
|
|-- setup.py
|-- README

23
Project/project/? Ach, drugi to nazwa pakietu.
Cees Timmerman

44
w jaki sposób plik wykonywalny w folderze bin odwołuje się do modułu projektu? (Nie sądzę, aby składnia python dopuszczała ../w instrukcji include)
ThorSummoner

8
@ThorSummoner Simple. Instalujesz pakiet! ( pip install -e /path/to/Project)
Kroltan,

22
Byłoby wspaniale, gdyby ktoś spakował próbkę tego układu za pomocą hello.py i hello-test.py i udostępnił ją nam, newbs.
jeremyjjbrown

8
@Bloke Rdzeń jest -eflagą, która instaluje pakiet jako pakiet edytowalny, to znaczy instaluje go jako łącza do rzeczywistego folderu projektu. Plik wykonywalny może wtedy import projectmieć tylko dostęp do modułu.
Kroltan

231

Ten post na blogu autorstwa Jeana-Paula Calderone'a jest często podawany jako odpowiedź w #python na Freenode.

Struktura systemu plików projektu Python

Zrobić:

  • nazwij katalog coś związanego z twoim projektem. Na przykład, jeśli projekt ma nazwę „Twisted”, nazwij katalog najwyższego poziomu dla plików źródłowych Twisted. Kiedy robisz komunikaty, należy zawierać numer wersji przyrostka: Twisted-2.5.
  • utwórz katalog Twisted/bini umieść tam pliki wykonywalne, jeśli takie masz. Nie podawaj im .pyrozszerzenia, nawet jeśli są to pliki źródłowe Pythona. Nie umieszczaj w nich żadnego kodu oprócz importu i wywołania funkcji głównej zdefiniowanej gdzie indziej w twoich projektach. (Nieznaczne zmarszczki: ponieważ w systemie Windows interpreter jest wybierany przez rozszerzenie pliku, Twoi użytkownicy systemu Windows naprawdę chcą rozszerzenia .py. Więc, kiedy pakujesz dla systemu Windows, możesz chcieć je dodać. Niestety nie ma łatwego distutils sztuczki, że Wiem, że mogę zautomatyzować ten proces. Biorąc pod uwagę, że w POSIX rozszerzenie .py jest tylko brodawką, podczas gdy w systemie Windows brak jest prawdziwym błędem, jeśli twoja baza użytkowników obejmuje użytkowników systemu Windows, możesz zdecydować się na posiadanie .py rozszerzenie wszędzie.)
  • Jeśli twój projekt można wyrazić jako pojedynczy plik źródłowy Pythona, to umieść go w katalogu i nazwij coś związanego z projektem. Na przykład Twisted/twisted.py. Jeśli potrzebujesz wielu plików źródłowych, utwórz zamiast tego pakiet ( Twisted/twisted/z pustym Twisted/twisted/__init__.py) i umieść w nim pliki źródłowe. Na przykład Twisted/twisted/internet.py.
  • umieść testy jednostkowe w sub-pakiecie pakietu (uwaga - oznacza to, że powyższa opcja pojedynczego pliku źródłowego Pythona była podstępem - zawsze potrzebujesz przynajmniej jednego innego pliku do testów jednostkowych). Na przykład Twisted/twisted/test/. Oczywiście, zrób z tego pakiet Twisted/twisted/test/__init__.py. Umieść testy w plikach takich jak Twisted/twisted/test/test_internet.py.
  • dodaj Twisted/READMEi Twisted/setup.pywyjaśnij i zainstaluj odpowiednio swoje oprogramowanie, jeśli czujesz się dobrze.

Nie:

  • umieść swoje źródło w katalogu o nazwie srclub lib. Utrudnia to uruchomienie bez instalacji.
  • umieść swoje testy poza pakietem Python. Utrudnia to uruchomienie testów dla zainstalowanej wersji.
  • stwórz pakiet, który ma tylko__init__.py a następnie włóż cały swój kod __init__.py. Po prostu stwórz moduł zamiast pakietu, to jest prostsze.
  • spróbuj wymyślić magiczne hacki, aby Python mógł zaimportować moduł lub pakiet bez konieczności dodawania katalogu zawierającego go do ścieżki importu (za pomocą PYTHONPATH lub innego mechanizmu). Będziesz nie prawidłowo obsłużyć wszystkie przypadki, a użytkownicy będą się gniewać na ciebie, gdy oprogramowanie nie działa w ich otoczeniu.

25
Właśnie tego potrzebowałem. „NIE próbuj wymyślać magicznych hacków, aby Python mógł zaimportować moduł lub pakiet bez konieczności dodawania katalogu zawierającego go do ścieżki importu”. Dobrze wiedzieć!
Jack O'Connor

1
Rzecz w tym, że nie wspomina o ważnej części projektu dotyczącej tego, gdzie ją umieścić.
lpapp,

14
Mylić o „umieść swoje źródło w katalogu o nazwie src lub lib. Utrudnia to uruchomienie bez instalacji.”. Co by było zainstalowane? Czy to nazwa katalogu powoduje problem, czy fakt, że jest to katalog podrzędny?
Peter Ehrlich

3
„Niektóre osoby twierdzą, że należy rozpowszechniać testy w obrębie samego modułu - nie zgadzam się. Często zwiększa to złożoność użytkowników; wiele pakietów testowych często wymaga dodatkowych zależności i kontekstów uruchomieniowych”. python-guide-pt-br.readthedocs.io/en/latest/writing/structure/...
endolit

2
„Utrudnia to uruchomienie bez instalacji”. - to punkt
Nick T

123

Sprawdź Open Sourcing projektu Python we właściwy sposób .

Pozwólcie mi wyciągnąć część dotyczącą układu tego znakomitego artykułu:

Podczas konfigurowania projektu ważny jest układ (lub struktura katalogów). Rozsądny układ oznacza, że ​​potencjalni współpracownicy nie muszą spędzać wiecznie na poszukiwaniu kodu; lokalizacje plików są intuicyjne. Ponieważ mamy do czynienia z istniejącym projektem, oznacza to, że prawdopodobnie będziesz musiał coś poruszyć.

Zacznijmy od góry. Większość projektów ma wiele plików najwyższego poziomu (takich jak setup.py, README.md, wymagania.txt itp.). Istnieją wtedy trzy katalogi, które powinien mieć każdy projekt:

  • Katalog docs zawierający dokumentację projektu
  • Katalog o nazwie z nazwą projektu, który przechowuje rzeczywisty pakiet Python
  • Katalog testowy w jednym z dwóch miejsc
    • W katalogu pakietu zawierającym kod testowy i zasoby
    • Jako samodzielny katalog najwyższego poziomu Aby lepiej zrozumieć, w jaki sposób należy uporządkować pliki, oto uproszczona migawka układu dla jednego z moich projektów, 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

Jak widać, istnieją pewne pliki najwyższego poziomu, katalog docs (wygenerowany jest pusty katalog, w którym sfinks umieści wygenerowaną dokumentację), katalog sandman i katalog testowy pod sandman.


4
Robię to, ale co więcej: mam plik Makefile najwyższego poziomu z celem „env”, który automatyzuje „virtualenv env; ./env/bin/pip install -r wymagania.txt; ./env/bin/python setup.py develop ”, a także zwykle cel„ testowy ”, który zależy od env, a także instaluje zależności testowe, a następnie uruchamia py.test.
pjz

@pjz Czy możesz rozwinąć swój pomysł? Czy mówisz o postawieniu Makefilena tym samym poziomie co setup.py? Więc jeśli rozumiem, że poprawnie make envautomatyzujesz tworzenie nowego venvi instalujesz w nim pakiety ...?
St.Antario

@ St.Antario dokładnie. Jak wspomniano, ogólnie mam też cel „testowy” do uruchomienia testów, a czasem cel „wydania”, który patrzy na bieżący tag i buduje koło i wysyła je do pypi.
pjz


19

Spróbuj uruchomić projekt przy użyciu szablonu python_boilerplate . W dużej mierze postępuje zgodnie z najlepszymi praktykami (np. Tymi tutaj ), ale lepiej nadaje się w przypadku, gdy w pewnym momencie okaże się, że chcesz podzielić swój projekt na więcej niż jedno jajko (i wierz mi, jeśli chodzi o coś innego niż najprostsze projekty, zrobisz to. często zdarza się, że musisz użyć lokalnie zmodyfikowanej wersji biblioteki innej osoby).

  • Gdzie umieszczasz źródło?

    • W przypadku dość dużych projektów sensowne jest podzielenie źródła na kilka jaj. Każde jajko przejdzie jako osobny układ setuptools PROJECT_ROOT/src/<egg_name>.
  • Gdzie umieszczasz skrypty uruchamiania aplikacji?

    • Idealną opcją jest zarejestrowanie skryptu uruchamiania aplikacji jako entry_pointjednego z jaj.
  • Gdzie kładziesz cruft projektu IDE?

    • Zależy od IDE. Wielu z nich trzyma swoje rzeczy w PROJECT_ROOT/.<something>katalogu głównym projektu i jest w porządku.
  • Gdzie umieszczasz testy jednostkowe / akceptacyjne?

    • Każde jajko ma osobny zestaw testów, przechowywany w swoim PROJECT_ROOT/src/<egg_name>/testskatalogu. Osobiście wolę używać ich py.testdo uruchamiania.
  • Gdzie umieszczasz dane inne niż Python, takie jak pliki konfiguracyjne?

    • To zależy. Mogą istnieć różne typy danych spoza Pythona.
      • „Zasoby” , tj. Dane, które muszą być spakowane w jajku. Dane te trafiają do odpowiedniego katalogu jaj, gdzieś w przestrzeni nazw pakietów. Może być używany przez pkg_resourcespakiet z setuptoolslub od Pythona 3.7 przez importlib.resourcesmoduł ze standardowej biblioteki.
      • „Pliki konfiguracyjne” , tj. Pliki inne niż Python, które należy traktować jako zewnętrzne w stosunku do plików źródłowych projektu, ale należy je zainicjować z pewnymi wartościami, gdy aplikacja zacznie działać. Podczas programowania wolę przechowywać takie pliki PROJECT_ROOT/config. W przypadku wdrożenia mogą istnieć różne opcje. W systemie Windows można użyć %APP_DATA%/<app-name>/config, w systemie Linux /etc/<app-name>lub /opt/<app-name>/config.
      • Wygenerowane pliki , tj. Pliki, które mogą być tworzone lub modyfikowane przez aplikację podczas wykonywania. Wolałbym zachować je PROJECT_ROOT/varpodczas programowania i /varpodczas wdrażania systemu Linux.
  • Gdzie umieszczasz źródła inne niż Python, takie jak C ++ dla binarnych modułów rozszerzeń pyd / so?
    • W PROJECT_ROOT/src/<egg_name>/native

Dokumentacja zwykle przechodzi do PROJECT_ROOT/doclub PROJECT_ROOT/src/<egg_name>/doc(zależy to od tego, czy traktujesz niektóre jajka jako osobne duże projekty). Niektóre dodatkowe konfiguracje będą w plikach takich jak PROJECT_ROOT/buildout.cfgi PROJECT_ROOT/setup.cfg.


Dzięki za świetną odpowiedź! Wyjaśniłeś mi wiele rzeczy! Mam tylko jedno pytanie: czy jaja można zagnieżdżać?
Shookie,

Nie, nie możesz „zagnieżdżać” jaj w sensie przechowywania plików .egg w innych plikach .egg, mając nadzieję, że przyda się to [chyba, że ​​masz coś naprawdę dziwnego]. Możesz jednak stworzyć „wirtualne” jajka - puste pakiety, które nie dostarczają żadnego przydatnego kodu, ale umieszczają inne pakiety na swoich listach zależności. W ten sposób, gdy użytkownik spróbuje zainstalować taki pakiet, rekurencyjnie zainstaluje wiele zależnych jaj.
KT.

@KT Czy możesz trochę wyjaśnić, jak postępujesz z wygenerowanymi danymi? W szczególności, w jaki sposób (w kodzie) rozróżniasz programowanie i wdrażanie? Wyobrażam sobie, że masz jakąś base_data_locationzmienną, ale jak ją odpowiednio ustawić?
cmyr

1
Zakładam, że mówisz o „danych uruchomieniowych” - czymś, co ludzie często umieszczają pod / var / nazwa_pakietu lub ~ /.. Nazwa_pakietu / var, czy coś tam. Zazwyczaj te opcje są wystarczające, aby użytkownicy nie chcieli zmieniać. Jeśli chcesz dopracować to zachowanie, opcje są dość obfite i nie sądzę, że istnieje jedna najlepsza praktyka dla wszystkich. Typowe opcje: a) ~ /. Nazwa_pakietu / plik konfiguracyjny, b) eksport MY_PACKAGE_CONFIG = / ścieżka / do / configfile c) opcje wiersza polecenia lub parametry funkcji d) ich kombinacja.
KT.

Zauważ, że dość często jest gdzieś singletonowa klasa Config, która obsługuje twoją ulubioną logikę ładowania konfiguracji, a może nawet pozwala użytkownikowi modyfikować ustawienia w czasie wykonywania. Ogólnie jednak myślę, że jest to kwestia warta osobnego pytania (które mogłoby być zadane wcześniej gdzieś tutaj).
KT.

15

Z mojego doświadczenia wynika, że ​​to tylko kwestia iteracji. Umieść swoje dane i kod w dowolnym miejscu. Możliwe, że i tak się pomylisz. Ale kiedy już zrozumiesz, jak dokładnie wszystko się ukształtuje, będziesz w znacznie lepszej sytuacji, aby zgadywać tego rodzaju domysły.

Jeśli chodzi o źródła rozszerzeń, pod katalogiem głównym znajduje się katalog Code zawierający katalog dla Pythona i katalog dla różnych innych języków. Osobiście jestem bardziej skłonny, aby następnym razem umieścić kod rozszerzenia we własnym repozytorium.

Powiedziawszy to, wracam do pierwotnego punktu: nie rób z tego zbyt wielkiego interesu. Połóż to w miejscu, które wydaje się działać dla Ciebie. Jeśli znajdziesz coś, co nie działa, można (i należy) to zmienić.


Tak. Staram się być w tym „pytoniczny”: jawne jest lepsze niż niejawne. Dziedziczki katalogów są czytane / sprawdzane bardziej niż są pisane. Itd ..
eric

10

Dane niebędące pythonami najlepiej jest umieszczać w modułach Pythona, korzystając z package_dataobsługi setuptools . Jedną rzeczą, którą zdecydowanie polecam, jest używanie pakietów przestrzeni nazw do tworzenia wspólnych przestrzeni nazw, z których może korzystać wiele projektów - podobnie jak konwencja Java umieszczania pakietów com.yourcompany.yourproject(i możliwość posiadania wspólnej com.yourcompany.utilsprzestrzeni nazw).

Ponownie rozgałęzianie i scalanie, jeśli użyjesz wystarczająco dobrego systemu kontroli źródła, będzie on obsługiwał scalanie nawet poprzez zmianę nazw; Bazar jest w tym szczególnie dobry.

W przeciwieństwie do niektórych innych odpowiedzi tutaj, jestem na +1 o srckatalogu najwyższego poziomu (z doci testkatalogi obok). Konkretne konwencje dotyczące drzew katalogów dokumentacji będą się różnić w zależności od tego, czego używasz; Na przykład Sphinx ma własne konwencje, które obsługuje narzędzie szybkiego startu.

Proszę, skorzystaj z setuptools i pkg_resources; znacznie ułatwia to innym projektom poleganie na określonych wersjach kodu (i jednoczesne instalowanie wielu wersji z różnymi plikami innymi niż kod, jeśli używasz package_data).

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.