Import Abhängigkeiten grafisch darstellen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
zegru
User
Beiträge: 52
Registriert: Freitag 9. Oktober 2020, 09:22

Folgendes Problem: Ich habe ein Programm, das aus mehreren Sourcecode Dateien besteht. Diese importieren nun diverse andere Dateien. Wenn nun ein zirkulärer Import stattfindet, gibt es eine Fehlermeldung. Das dürfte vermutlich so etwas sein wie "A importiert B, das wiederum C, und das wieder A".
Kann man sich diesen Abhängigkeitsbaum grafisch darstellen lassen, um solchen Fehlern leichter nachzugehen?
Benutzeravatar
sparrow
User
Beiträge: 4202
Registriert: Freitag 17. April 2009, 10:28

Ich weiß nicht, ob es so ein Tool gibt, aber: Wenn du so ein Problem hast, dann stimmt etwas grundsätzliches mit der Strukturierung deines Programms nicht. Du versuchst also das Symptom zu visualisieren, nicht das eigentliche Problem.
zegru
User
Beiträge: 52
Registriert: Freitag 9. Oktober 2020, 09:22

Wie wäre denn eine möglichst zielführende Herangehensweise an so etwas?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ist ohne konkreten Code nicht schlüssig zu beantworten. Wie stellt sich die zirkuläre Abhängigkeit denn dar?
Benutzeravatar
__blackjack__
User
Beiträge: 13144
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@zegru: Das hier wäre vielleicht was für Dich: https://github.com/thebjorn/pydeps
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
zegru
User
Beiträge: 52
Registriert: Freitag 9. Oktober 2020, 09:22

Die Fehlermeldung nach dem Stacktrace lautet 'AttributeError: partially initialized module 'conf' has no attribute 'use_default_configuration_file' (most likely due to a circular import)'. Falls ihr es genauer untersuchen wollt liegt der Sourcecode unter https://gitlab.com/muessjoeh/temperaturverlauf.
Das seltsame ist, dass es schon mal funktioniert hat: Ich musste aber meinen Rechner neu aufsetzen, dabei bekam er eine neuere Version des Betriebssystems, eventuell eine neuere Python-Version, ein anderes virtuelles Environment etc. Also ein typischer "Aber früher gings" Fehler.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Daran kann's schlicht nicht liegen. Nichts in der Python-Import-Semantik hat sich in den letzten 10 Jahren so signifikant veraendert. Weder das OS noch das venv kann da einen Einfluss drauf haben. Wenn eher Dinge wie alte rumliegende .pyc-Dateien, die nicht aufgeraeumt wurden, und jetzt ploetzlich nicht mehr da sind, so das ein import den echten Code anzieht.

Und das du solche Probleme hast, ist auch einfach erklaerbar: wie hier gerade __blackjack__ immer mantrahaft wiederholt: auf die oberste Ebene gehoeren NUR(!) Konstanten, Funktions- oder Klassendeklarationen. Du hast aber sowas da:

https://gitlab.com/muessjoeh/temperatur ... /logger.py

Das heiss schon beim import selbst laeuft Anwendungscode, der sich wiederum darauf verlaesst, dass anderer Code durchgelaufen ist. Und sowas beschert einem dann eine solche Ausnahme.

*Alles* muss sauber in Funktionen, und im Eintrittspunkt des Projektes mit einem __main__-Guard abgesichert sauber die Anwendung hochgezogen werden.

Bevor der uebliche Vergleich mit anderen Sprachen kommt: auch da hast du das Problem, mit allem, was in Java oder C/C++ static ist. Auch dieser Code wird vor dem einsprung in main ausgefuehrt, und die Reihenfolge ist wahlweise beliebig (C/C++, abhaengig von der Link-Order), oder bestimmt durch implizite Abhaengigkeiten (https://stackoverflow.com/questions/641 ... cy-in-java).
Benutzeravatar
__blackjack__
User
Beiträge: 13144
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Was auch ein ziemlicher clusterfuck ist, ist das in dem Verzeichnis eine `__init__.py` liegt, das also ein Package ist, lauf Anleitung dann aber die Module mit dem aktuellen Arbeitsverzeichnis *in* dem Package ausgeführt werden als wären es einzelne Module die gar nicht in einem Package sind. "." ist im Package/Modulsuchpfad, wodurch die gleichen Module nun über zwei unterschiedliche „full qualified names“ verfügbar sind, womit jedes Modul potentiell zweimal unabhängig im laufenden Programm existieren kann, je nach dem über welchen Weg man es importiert. Das kann auch zu lustigen Effekten führen.

Module in Packages führt man niemals direkt aus, sondern immer über den voll qualifizierten Namen und die `-m`-Option und *nicht* wenn das aktuelle Arbeitsverzeichnis irgendwo *in* einerm Package steht. Also beispielsweise aus einem Verzeichnis höher ``python3 -m temperaturverlauf.fetch`` statt in dem Verzeichnis ``./fetch.py``.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
zegru
User
Beiträge: 52
Registriert: Freitag 9. Oktober 2020, 09:22

Ich denke, dass die Lernkurve von Python steiler ist als vermutet und noch ziemlich am Anfang bei mir ist.
Jetzt habe ich das Problem, dass nur eine einzige Instanz des Loggers vorhanden sein sollte. Gibt es hierfür eine standardmäßige Herangehensweise?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das Standardrezept dafuer (auf Moduleben, und ja, das ist natuerlich eine Ausnahme zur Vermeidung globalen Zustands):

Code: Alles auswählen

logger = logging.getLogger(__name__)
Das war's. Dadurch kann der von ueberall im Modul genutzt werden.
zegru
User
Beiträge: 52
Registriert: Freitag 9. Oktober 2020, 09:22

Das heißt also, dass der Logger pro __name__ automatisch ein Singleton ist?
Und ich habe meinen Code entsprechend angepasst: Abgesichert über den __name__ und die Libraries in ein eigenes Verzeichnis getan, leider ohne Erfolg. Habt ihr noch eine Idee?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Na nicht wirklich ein singleton. Aber pro Modul hat man dann nur einen. Ob logging selbst ggf sicherstellt, dass gleich benamte Logger dann auch immer dasselbe Objekt sind - kA. Aber das ist das empfohlene pattern.

Deine zweite Frage ist mir unklar. Geht’s da immer noch um die zirkularen Importe?
zegru
User
Beiträge: 52
Registriert: Freitag 9. Oktober 2020, 09:22

Zumindest das Symptom ist immer noch das selbe: Das Starten eines Tests (und übrigens aller sonstigen ausführbaren Bestandteile) wird mit der Fehlermeldung abgebrochen, dass die zugrunde liegende Ursache eventuell ein zirkulärer Import sein könnte.
Sirius3
User
Beiträge: 17768
Registriert: Sonntag 21. Oktober 2012, 17:20

Das Problem mit zirkulären Importen ist, dass Du keine klare Hierarchie in Deinen Modulen hast.
Bei Dir lib.conf importiert lib.utils importiert lib.logger das lib.conf importiert.
Das Problem läßt sich simple lösen, in dem die die unsinnigen Aufrufe von lib.logger.init aus jedem Modul rauslöschst, denn der Logger darf nur einmal im Hauptprogramm initialisiert werden.
Damit hast Du eine klare Hierarchie:
utils: hat, als Sammlung primitiver Funktionen keine weiteren Abhängigkeiten
conf: liefert Configurationen für viele Module, hat aber nur die Abhängigkeit zu utils.
und schließlich logger, was ja eh nur im Hauptprogramm gebraucht wird.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

zegru
User
Beiträge: 52
Registriert: Freitag 9. Oktober 2020, 09:22

Hm. Wie logge ich dann etwas in den Libraries?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ganz normal. Warum denkst du, du musst da zur Zeit des Importes etwas konfigurieren? Musst du nicht. Kann Stunden später erfolgen.
zegru
User
Beiträge: 52
Registriert: Freitag 9. Oktober 2020, 09:22

Stimmt, jetzt geht es. Danke!
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Um das mal ein bisschen einzuordnen: generell darf man in libraries niemals eine logging-Konfiguration vornehmen. Nur logger benutzen. Der Hintergrund ist ganz einfach: man weiss ja gar nicht, wo die library zum Einsatz kommt, und welche Mechanismen da dann zur Konfiguration vorgesehen sind. Im Gegenteil, man macht Integratoren das Leben extra schwer, weil man dann muehselig nachtraeglich konfigurierte Logger etc. wieder "saeubern" muss.
Antworten