@myoggradio: Anmerkungen zum Quelltext:
Die Importe sollten vor dem Code stehen.
Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.
Namen sollten keine kryptischen Prä- oder Suffixe haben oder abgekürzt werden. Die ganze `p_` haben da nichts zu suchen, und wenn man `parameter` meint, sollte man nicht nur `parm` schreiben.
`anhang` sollte eigentlich `anhaenge` heissen, denn die Liste enthält nicht einen, sondern potentiell mehrere Anhänge.
Beim öffnen von Textdateien sollte man immer die Kodierung mit angeben. Da hier keinerlei Vorkehrungen beim Versenden für Texte gemacht wird die etwas anderes als ASCII enthalten, sollte man die Kodieurng der INI-Datei auch darauf beschränken.
Man muss nicht jedes kleine Zwischenergebnis an einen Namen binden. `inhalt` und `saetze` braucht es beispielsweise nicht, das wäre einfach:
Code: Alles auswählen
with open(ini_file_path, encoding="ASCII") as datei:
for satz in datei.read().splitlines():
...
Allerdings würde man eher nicht die ganze Datei auf einmal in den Speicher laden, sondern über die Datei iterieren — das Dateiobjekt liefert dann die einzelnen Zeilen.
`satz` und `worte` ist aber auch komisch, weil das nicht das beschreibt was die Werte sind. `satz` ist eigentlich `zeile`, und `worte` ist als Wert problematisch weil einfach an allen "="-Zeichen aufgeteilt wird, wo man eigentlich nur zwei Ergebnisse haben will: alles *vor* dem ersten "=" und alles *nach* dem ersten "=". Da bietet sich die `partition()`-Methode statt `split()` an.
`parm` kann nur einen der vielen Werte annehmen die da geprüft werden, also sollten das nicht alles eigenständige ``if``\s sein, sondern ein ``if`` und dann ``elif``\s.
Wie ist denn bitte der Code zum Anhängen von einzelnen Werten an eine Liste zustande gekommen? Unsinnig umständlicher geht es ja kaum. Statt erst eine Liste mit einem willkürlichen Dummy-Wert zu erzeugen, den dann durch den Wert zu ersetzen, und dann die Liste an die angehängt werden soll um den Inhalt der einelementigen Liste zu erweitern, sollte man einfach das eine Element an die Liste anhängen.
Der Name `text` ist mal der Dateiname/-pfad und mal der Inhalt der Datei. Das ist verwirrend.
Das was da `file` heisst ist gar keine Datei sondern ein Datei*name*, was zur Folge hat, dass man in der Schleife dann das tatsächliche Dateiobjekt nicht mehr `file` nennen kann und es den schlechten Namen, weil kryptischen und falschen Namen `fp` bekommt.
Pfade sollte man nicht mit Zeichenkettenoperationen bearbeiten. Dafür gibt es das `pathlib`-Modul. `Path`-Objekte haben auch eine sehr praktische Methode eine Datei komplett einzulesen.
Es ist unschön das Zeilen in denen irgendetwas steht, aber kein "=" vorkommt, und Zeilen in denen vor dem "=" etwas unbekanntes steht, einfach ignoriert werden. Da würde es Sinn machen mindestens eine Warnung auszugeben, weil sich der Benutzer da vielleicht einfach nur vertippt hat, und deshalb dann etwas aus der Datei ignoriert wird.
Zwischenstand (ungetestet):
Code: Alles auswählen
#!/usr/bin/python3
import smtplib
import sys
from email.message import EmailMessage
from pathlib import Path
def main():
ini_file_path = Path(sys.argv[1])
subject = ""
from_ = ""
to = []
cc = []
bcc = []
text_filename = ""
anhaenge = []
with ini_file_path.open(encoding="ASCII") as datei:
for zeilennummer, zeile in enumerate(datei, 1):
schluessel, trenner, wert = zeile.rstrip().partition("=")
if trenner:
if schluessel == "subject":
subject = wert
elif schluessel == "from":
from_ = wert
elif schluessel == "to":
to.append(wert)
elif schluessel == "cc":
cc.append(wert)
elif schluessel == "bcc":
bcc.append(wert)
elif schluessel == "txt":
text_filename = wert
elif schluessel == "anhang":
anhaenge.append(wert)
else:
print(
f"{ini_file_path}:{zeilennummer}:"
f" unbekannter Schlüssel {schluessel!r}"
)
else:
print(f"{ini_file_path}:{zeilennummer}: kein Trennzeichen")
message = EmailMessage()
message["Subject"] = subject
message["From"] = from_
message["To"] = to
message["Cc"] = cc
message["Bcc"] = bcc
message.set_content(Path(text_filename).read_text(encoding="ASCII"))
for file_path in map(Path, anhaenge):
message.add_attachment(
file_path.read_bytes(),
maintype="application",
subtype="octet",
filename=file_path.name,
)
with smtplib.SMTP("smtps.udag.de", 587) as server:
server.starttls()
server.login("christian@myoggradio.org", "???")
print(server.send_message(message))
if __name__ == "__main__":
main()
Man könnte da noch ein bisschen verallgemeinern und statt der vielen einzelnen Variablen für die Ini-Datei ein Wörterbuch erstellen. Ungetestet:
Code: Alles auswählen
#!/usr/bin/python3
import smtplib
import sys
from email.message import EmailMessage
from pathlib import Path
def main():
ini_file_path = Path(sys.argv[1])
config = {
"subject": "",
"from": "",
"to": [],
"cc": [],
"bcc": [],
"txt": "", # Filename to load the mail body text from.
"anhang": [],
}
with ini_file_path.open(encoding="ASCII") as datei:
for zeilennummer, zeile in enumerate(datei, 1):
schluessel, trenner, wert = zeile.rstrip().partition("=")
if trenner:
try:
if isinstance(config[schluessel], str):
config[schluessel] = wert
else:
config[schluessel].append(wert)
except KeyError:
print(
f"{ini_file_path}:{zeilennummer}:"
f" unbekannter Schlüssel {schluessel!r}"
)
else:
print(f"{ini_file_path}:{zeilennummer}: kein Trennzeichen")
message = EmailMessage()
message.set_content(Path(config.pop("txt")).read_text(encoding="ASCII"))
for file_path in map(Path, config.pop("anhang")):
message.add_attachment(
file_path.read_bytes(),
maintype="application",
subtype="octet",
filename=file_path.name,
)
for key, value in config.items():
message[key.capitalize()] = value
with smtplib.SMTP("smtps.udag.de", 587) as server:
server.starttls()
server.login("christian@myoggradio.org", "???")
print(server.send_message(message))
if __name__ == "__main__":
main()