[Python-de] Projektorganisation revisited

Gerson Kurz Gerson.Kurz at t-online.de
Fre Jul 25 15:50:46 EDT 2003


Ich möchte nochmals auf das Thema Projektorganisation in Python zu sprechen
kommen.

Als Hintergrund: Es geht um eine Geräteemluation, benannt PyEMU. Das ganze
läuft in einem Minipc unter Linux (ohne Tastatur & Bildschirm). Nach "innen"
wird V24 gesprochen, nach aussen V24 und TCP/IP und Named Pipes und (in
Zukunft vielleicht) USB. Anforderungen waren z.B.

- Infrastruktur (Hochfahren, Konfiguration, Logfiles abziehen usw.) soll
unabhängig vom nach aussen exportierten Protokoll sein
- Voll dynamisch (d.h. eine Konfigurationsdatei legt fest, welches Protokoll
über welche SChnittstelle mit welchem anderen Protokoll spricht)
- Soll auf einer Read-Only-Partition laufen (d.h. / wird nach dem Booten ro
geremounted), wg. plötzlichem Stromausfall usw.
- Erweiterbar für weitere Protokolle / Schnittstellen (z.B. USB)

Bisher habe ich folgende Projektorganisation

irgendwas
irgendwas/PyEMU
irgendwas/PyEMU/protokoll_1
irgendwas/PyEMU/protokoll_2
....
irgendwas/PyEMU/protokoll_n

In <irgendwas> ist nur ein Programm: main.py aus Gründen der
Übersichtlichkeit. Das importiert das Modul PyEMU und startet es mit
Parametern nach (Vorteil: PyEMU kann unter Windows als NT-Service
konfiguriert werden); eigentliche Codebasis bleibt identisch.

In <irgendwas/PyEMU> liegt der eigentliche Code, bestehend aus ungefähr 50
Sourcen.

In <irgendwas/PyEMU/protokoll_x> liegen die HW-Protokolle, natürlich mit
sprechenden Namen (also z.B. "Fingerprint" für "Protokoll des
Fingerprint-Sensors"). Das Modul hat für jeden Befehl einen eigenen
Sourcecode, aus folgenden Gründen:

- Man findet die Sourcen zu einem Befehl leichter. Das mag harmlos klingen,
ist aber etwas, was mich immer schon gestört hat: ich habe keine "richtige"
IDE in Python, und bei sagen wir 60 Befehlen ist die Suche nach einem
speziellen rein über grep doch etwas *arg* mühsam. Der Dateiname als
Strukturierung bietet sich da schon an.

- Die Befehle können ziemlich umständlich sein (z.B. Statistikauswertung,
Verschlüsselung usw.) -> die Sourcen sollten nach Möglichkeit nicht >500
Zeilen haben.

- Die Verzeichnisse enthalten je bis zu 60 Sourcen.

Meine Probleme mit dem Ganzen

1) Abhängigkeiten der Module

Beispiel. Ich möchte aus

irgendwas/PyEMU/protokoll_1/blabla.py

eine Funktion in

irgendwas/PyEMU/misc.py

aufrufen. Dann muß ich schreiben (immer vorausgesetzt, daß <irgendwas>
bereits in den PYTHON_PATH aufgenommen wurde)

import PyEMU.misc
PyEMU.misc.funktion()

Wenn ich statt dessen in

irgendwas/PyEMU/blabla.py

bin, kann ich direkt schreiben

import misc
misc.funktion()

Noch schlimmer wird es, wenn protokoll_1 etwas von protokoll_2 braucht (was
möglich ist, wenn es sich um abhängige Hardware handelt; z.B. kann Gerätetyp
A nur betrieben werden, wenn Gerätetyp B (Alarm) möglich ist; also:

in irgendwas/PyEMU/protokoll_1/funktion.py:

import PyEMU.protokoll_2.irgendwas

if PyEMU.protokoll_2.irgendwas.alarmIstDa():
   # ok, weiter gehts

2) Entscheidung, was mache ich als Modul, was nicht.

z.B. habe ich verschiedene Transportsysteme für die Protokolle (V24, TCP/IP,
Dateibasiert für Named Pipes & Testfiles).

Mache ich daraus ein eigenständiges Modul, oder lasse ich die Sourcen im
"main"-Verzeichnis? Bisher ja; eigentlich gehört das aber auch in ein Modul
rein. Aber: Mache ich ein Modul "transportsystems" oder ein Modul pro
Transportsystem?

3) Ferner: In der Konfiguration habe ich z.B. Einträge wie den folgenden
(Real-Life-Beispiel:)

        <transport>
            <protocol>FingerprintProtocol</protocol>
            <type>SerialTransportLayer</type>
            <portname>COM1</portname>
            <baudrate>38400</baudrate>
            <parity>none</parity>
            <stopbits>1</stopbits>
            <databits>8</databits>
            <traceV24>0</traceV24>
        </transport>

Wenn ich die Konfigurationsdatei einlese, kann ich hübsch ein

    requestedType = { hier z.B. "SerialTransportLayer" }
    ...
    module = __import__(requestedType, globals(), locals(), []);
    return getattr( module, requestedType )

machen und habe meine Transportschicht. Wenn ich die aber in ein Modul
gepackt habe, komme ich in Namensraumkonflikteschrott rein.

4) Da habe ich dann so hilfsmodule wie "trace.py". Packe ich die in ein
weiteres Modul? Gehört das nicht in sitepackages, weil es
Projektübergreifend gestaltet werden kann?

Irgendwie befriedigt mich die Projektorganisation nicht: sie funktioniert,
ist aber nicht "schön".

Deshalb nochmal die Frage: Wie strukturiert ihr größere Pythonprojekte
(Source-# > 300)?