2016-03-02 10 views
6

meine Verzeichnisstruktur Dies ist importiert werden:Wie kann ich eine Python Projektstruktur namens Module zu ermöglichen, von Unterverzeichnissen

Projects 
    + Project_1 
    + Project_2 
    - Project_3 
     - Lib1 
      __init__.py # empty 
      moduleA.py 
     - Tests 
      __init__.py # empty 
      foo_tests.py 
      bar_tests.py 
      setpath.py 
     __init__.py  # empty 
     foo.py 
     bar.py 

Ziele:

  1. eine organisierte Projektstruktur haben
  2. Be in der Lage, jede .py-Datei bei Bedarf unabhängig auszuführen
  3. Sie können beide Geschwister und Cousins ​​ Module beziehen/importieren
  4. Halten Sie alle Import/from-Anweisungen am Anfang jeder Datei.

I Erreicht # 1 durch die obige Struktur mit

ich meistens 2 erreicht haben, 3 und 4 durch folgende Maßnahmen (wie von this excellent guide empfohlen)

In jedem Paket, das benötigt Zugang Eltern oder Cousin Module (wie die Tests Verzeichnis oben) enthalten ich eine Datei setpath.py genannt, die den folgenden Code hat:

import os 
import sys 
sys.path.insert(0, os.path.abspath('..')) 

sys.path.insert(0, os.path.abspath('.')) 
sys.path.insert(0, os.path.abspath('...')) 

in jedem Modul dann die Eltern/Cousin Zugriff benötigt,

import setpath  # Annoyingly, PyCharm warns me that this is an unused import statement 
import foo.py 

Innen setpath.py, die zweite und dritte Einsätze sind nicht unbedingt notwendig, für dieses Beispiel, sondern als Schritt zur Fehlerbehebung enthalten: wie foo_tests.py, kann ich eine schöne, saubere Liste der Importe wie so schreiben .

Mein Problem ist, dass dies nur für Importe funktioniert, die den Modulnamen direkt referenzieren, und nicht für Importe, die das Paket referenzieren. Innerhalb von bar_tests.py funktioniert beispielsweise keine der beiden folgenden Anweisungen, wenn bar_tests.py direkt ausgeführt wird.

import setpath 

import Project_3.foo.py # Error 
from Project_3 import foo # Error 

erhalte ich die Fehlermeldung "Import: Kein Modul mit dem Namen 'Project_3'".

Was ist seltsam ist, dass ich die Datei direkt aus PyCharm ausführen kann und es funktioniert gut. Ich weiß, dass PyCharm mit der Variable Python Path etwas hinter den Kulissen zaubert, damit alles funktioniert, aber ich kann nicht herausfinden, was es ist. Da PyCharm einfach python.exe ausführt und einige Umgebungsvariablen einstellt, sollte es möglich sein, dieses Verhalten innerhalb eines Python-Skripts selbst zu klonen.

Aus Gründen, die für diese Frage nicht relevant sind, muss ich unter Verwendung des Project_3 Qualifiers auf bar verweisen.

Ich bin offen für jede Lösung, die die oben genannten erfüllt und gleichzeitig meine früheren Ziele erfüllt. Ich bin auch offen für eine alternative Verzeichnisstruktur, wenn es eine gibt, die besser funktioniert. Ich habe die Python doc on imports und Pakete gelesen, bin aber immer noch ratlos. Ich denke, ein möglicher Weg könnte sein, die Variable __path__ manuell zu setzen, aber ich bin mir nicht sicher, welche geändert werden muss oder wie sie eingestellt werden soll.

+0

Obwohl ich stimme völlig @Blckknght Antwort (und bevor er veröffentlicht, fing sogar an, ähnlich im gleichen Ton zu schreiben). Es ist vielleicht erwähnenswert, dass, wenn es sich nicht um einen anderen Tippfehler handelt, Ihr aktuelles Modell aufgrund von sys.path.insert (0, os.path.abspath ('...')) möglicherweise nicht funktioniert, da '...' kein a ist Gültiger übergeordneter Pfadwähler Hier sollten Sie die normale '../ ..' Dateisystem-Notation verwenden, nicht Pythons Eltern-Module. – RobertT

+0

@RobertT Unglaublich!So einfach, aber es behebt das Problem und es scheint, dass dies mir erlaubt, relative Wurzeln sowohl für Pakete als auch für Module zu setzen. Ich denke immer noch, dass etwas robusteres mit den Import-Bibliotheken implementiert werden könnte, aber dies beantwortet meine Frage und erreicht die oben genannten Ziele. Mache es bitte zur Antwort, und ich werde es für den Kopfgeldpreis berücksichtigen. – BrianHVB

Antwort

2

Diese Art von Fragen qualifizieren als "in erster Linie auf der Grundlage von Meinungen", also lassen Sie mich meine Meinung teilen, wie ich es tun würde.

Zuerst "kann jede .py-Datei bei Bedarf unabhängig ausführen": Entweder ist die Datei ein Modul, also sollte sie nicht direkt aufgerufen werden, oder sie ist eigenständig ausführbar, dann sollte sie ihre Abhängigkeiten von der obersten Ebene importieren (Sie können es im Code vermeiden oder es stattdessen an den üblichen Ort verschieben, indem Sie setup.py entry_points verwenden, aber Ihre frühere ausführbare Datei wird dann tatsächlich in ein Modul konvertiert). Und ja, es ist einer der Schwachpunkte des Python-Modulmodells, der Missverständnisse verursacht.

Zweitens, verwenden Sie virtualenv (oder venv in Python3) und setzen Sie jedes Ihrer Project_x in separate. Auf diese Weise ist der Name des Projekts nicht Teil des Python-Modulpfads.

Drittens, Link, den Sie angegeben haben, erwähnt setup.py - Sie können davon Gebrauch machen. Setzen Sie Ihren benutzerdefinierten Code in Project_x/src/mylib1, erstellen Sie src/mylib1/setup.py und schließlich Ihre Module in src/mylib1/mylib1/module.py. Dann können Sie Ihren Code per pip wie jedes andere Paket (oder pip -e) installieren, so dass Sie direkt an dem Code arbeiten können, ohne ihn erneut zu installieren, obwohl er leider einige Einschränkungen hat.

Und schließlich, wie Sie in Kommentar bereits bestätigt haben;). Problem mit Ihrem aktuellen Modell war, dass in sys.path.insert(0, os.path.abspath('...')) Sie fälschlicherweise die Python-Modulnotation verwendet haben, die für Systempfade inkorrekt ist und durch '../..' ersetzt werden sollte, um wie erwartet zu funktionieren.

1

Ich denke, Ihre Ziele sind nicht vernünftig.Insbesondere 2 Zielnummer ist ein Problem:

  1. der Lage sein, unabhängig jede Py-Datei ausgeführt wird, wenn nötig

Das ist nicht gut in einem Paket für Module funktioniert . Zumindest nicht, wenn Sie die .py Dateien naiv ausführen (z. B. mit python foo_tests.py in der Befehlszeile). Wenn Sie die Dateien auf diese Weise ausführen, kann Python nicht feststellen, wo die Pakethierarchie starten soll.

Es gibt zwei Alternativen, die funktionieren können. Die erste Option besteht darin, Ihre Skripts vom obersten Ordner (z. B. projects) mit dem Flag -m zum Interpreter auszuführen, um dem Hauptmodul einen gepunkteten Pfad zuzuweisen und explizite relative Importe zum Abrufen der Geschwister- und Cousin-Module zu verwenden. Anstatt direkt auszuführen, führen Sie python -m project_3.tests.foo_tests aus dem Ordner projects (oder python -m tests.foo_tests aus innerhalb project_3 vielleicht), und haben foo_tests.py verwenden from .. import foo. Die andere (weniger gute) Option besteht darin, dem Modulsuchpfad Ihrer Python-Installation auf einer systemweiten Basis einen Ordner der obersten Ebene hinzuzufügen (z. B. den Ordner projects zur Umgebungsvariablen PYTHON_PATH hinzuzufügen) und dann absolute Importe für alle Ihre Module (zB import project3.foo). Dies ist effektiv, was Ihr setpath Modul tut, aber es System weit als Teil der Konfiguration Ihres Systems zu tun, anstatt zur Laufzeit, es ist viel sauberer. Es vermeidet auch die mehreren Namen, die setpath Ihnen erlauben, ein Modul zu importieren (z. B. versuchen import foo_tests, tests.foo_tests und Sie werden zwei separate Kopien des gleichen Moduls erhalten).

+1

Danke für die Antwort, aber beide Lösungen erfordern Schritte außerhalb der Python-Skripte selbst genommen werden. Mit anderen Worten, es gibt keine Möglichkeit, eine der beiden Lösungen innerhalb eines Python-Skripts programmgesteuert zu implementieren. Sie sagen, dass "Wenn Sie die Dateien auf diese Weise ausführen, kann Python nicht sagen, wo die Pakethierarchie starten soll." Aber es sollte möglich sein, genau das zu tun, um Python zu sagen, wo ein Paket innerhalb eines Skripts beginnt. Ich denke, das ist genau das, wofür die Importmodule sind, ich kann einfach nicht herausfinden, wie man sie benutzt, um das gewünschte Verhalten zu erreichen. – BrianHVB