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?
Import Abhängigkeiten grafisch darstellen
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.
- __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
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.
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.
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).
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).
- __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``.
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
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?
Jetzt habe ich das Problem, dass nur eine einzige Instanz des Loggers vorhanden sein sollte. Gibt es hierfür eine standardmäßige Herangehensweise?
Das Standardrezept dafuer (auf Moduleben, und ja, das ist natuerlich eine Ausnahme zur Vermeidung globalen Zustands):
Das war's. Dadurch kann der von ueberall im Modul genutzt werden.
Code: Alles auswählen
logger = logging.getLogger(__name__)
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?
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?
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?
Deine zweite Frage ist mir unklar. Geht’s da immer noch um die zirkularen Importe?
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.
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.
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.
So was ist immer noch falsch: https://gitlab.com/muessjoeh/temperatur ... ils.py#L32
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.