TOML per Email in Klassen senden

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
Holzkopp
User
Beiträge: 12
Registriert: Samstag 24. Juli 2021, 20:49

Hey,

ich benötige eure Hilfe:

Ich habe mich jetzt das erste Mal mit TOML und Email beschäftigt und mir ein Skript zusammengebastelt. Jetzt frage ich mich, ob es nicht übersichtlicher und funktionaler wäre, den Code in Klassen zu trennen. Eine Klasse fürs Importieren der Zugangsdaten (TOML-Import) und eine weitere zum Bestücken + Senden der Email.

In einer dritten Datei könnte dann ein simpler Versand der Email angestoßen werden, indem lediglich Betreff und Inhalt der Email definiert werden müsste.

data.toml:

Code: Alles auswählen

# This is a TOML document

[email]
smtp = "smtp.1und1.de"
port_SSL = "465"
absender = "abender@mail.de"
user = "user@mail.de"
pawo = "Passwort"

send_email.py:

Code: Alles auswählen

#! /usr/bin/python3

import smtplib, ssl, os, toml
from email.message import EmailMessage

def config_laden():
    configfile = os.path.join(SKRIPTPFAD, "data.toml")
    with open(configfile) as file:
        return toml.loads(file.read())

SKRIPTPFAD = os.path.abspath(os.path.dirname(__file__))
CONFIG = config_laden()

smtp     = CONFIG["email"]["smtp"]
port     = CONFIG["email"]["port_SSL"]
absender = CONFIG["email"]["absender"]
user     = CONFIG["email"]["user"]
pawo     = CONFIG["email"]["pawo"]


msg = EmailMessage()
msg["From"] = absender
msg["To"] = "an_mich@mail.de"

msg["Subject"] = "Betreffzeile"
msg.set_content("""\
Inhalt der Nachricht:
2. Zeile
3. Zeile 
""")

try:
    server = smtplib.SMTP_SSL(smtp, port)
    server.login(user, pawo)
    server.send_message(msg)
    print("Email wurde erfolgreich versendet")
except Exception as e:
    print(e)
Benutzeravatar
__blackjack__
User
Beiträge: 14065
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Holzkopp: Erst willst Du was auf Klassen aufteilen und dann scheint bei Dir auch noch jede Klasse in einer eigenen Datei leben zu wollen. 😱 Was soll daran denn bitte ”funktionaler” (was soll das sein?) und übersichtlicher sein die nicht mal 40 Zeilen Code auf drei Dateien zu verteilen und auch noch irgendwie in Klassen zu stecken? Weder Klassen noch Module (das sind Dateien letztlich) sind irgendwie Selbstzweck. Wenn man die nicht braucht, sollte man sie nicht verwenden.

Was man machen könnte/sollte ist den Code auf Modulebene in eine Funktion stecken, damit der bisherige Code ein Modul wird, das man importieren kann, ohne das gleich das Programm abläuft.

Importe sind üblicherweise ein Modul pro ``import``-Anweisung. Das `ssl`-Modul muss gar nicht importiert werden, weil das nirgends verwendet wird.

Konstanten werden per Konvention direkt nach den Importen definiert. Die soll man schnell finden und ändern können, ohne den ganzen Quelltext durchgehen zu müssen.

`CONFIG` ist keine Konstante und damit ist der Name komplett in Grossbuchstaben falsch.

In neuem Code würde man eher `pathlib` verwenden für Operationen auf Pfaden, statt der Funktionen in `os.path`.

Im `toml`-Modul gibt es eine Funktion zum laden über den Dateinamen, da muss man nicht die Datei selbst öffnen (und dabei die Angabe der Kodierung vergessen) und es lohnt sich im Grunde keine eigene Funktion mehr dafür.

Keine Abkürzungen. Nicht `pawo` schreiben wenn `password` gemeint ist. Auch `message` kann man ausschreiben.

Wenn man `pawo` ändert, fällt auf warum es potentiell ungünstig ist Code an Gleicheitszeichen auszurichten: Wenn sich dabei ein neuer längster Name ergibt, muss man plötzlich alle ausgerichteten Zeilen verändern, obwohl sich in denen inhaltlich überhaupt nichts geändert hat. Das macht Arbeit und schafft Diffs mit unnötigen ”Änderungen” in denen man die nicht-kosmetischen suchen muss.

Die Ausnahmebehandlung ist nicht so wirklich sinnvoll. Die unterdrückt Informationen die im Fehlerfall zur Fehlersuche nützlich wären.

Die Verbindung zum Server wird nicht sauber geschlossen. `SMTP_SSL`-Objekte sind Kontextmanager, die man mit ``with`` verwenden kann.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import smtplib
from email.message import EmailMessage
from pathlib import Path

import toml


SKRIPTPFAD = Path(__file__).absolute().parent


def main():
    config = toml.load(SKRIPTPFAD / "data.toml")["email"]
    smtp_server = config["smtp"]
    port = config["port_SSL"]
    absender = config["absender"]
    user = config["user"]
    password = config["password"]

    message = EmailMessage()
    message["From"] = absender
    message["To"] = "an_mich@mail.de"
    message["Subject"] = "Betreffzeile"
    message.set_content(
        """\
    Inhalt der Nachricht:
    2. Zeile
    3. Zeile 
    """
    )

    with smtplib.SMTP_SSL(smtp_server, port) as server:
        server.login(user, password)
        server.send_message(message)
        print("Email wurde erfolgreich versendet")


if __name__ == "__main__":
    main()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Holzkopp
User
Beiträge: 12
Registriert: Samstag 24. Juli 2021, 20:49

Puh. Das war mal ne Ansage!

Ich hatte zwar erwartet (und gehofft), als sehr blutiger Anfänger, die ein oder andere Korrektur zu bekommen. Dass aber fasst das komplette Script "fürn Arsch" ist, hätte ich nicht erwartet. Sehr ernüchternd.

Meine Grundidee war, die drei Variablen: Empfänger, Betreffzeile und Nachrichteninhalt an eine zusätzliche Datei (send_email.py) übergeben zu können, um dadurch den eigentlichen Emailversand zu bewirken. Das formelle Schreiben der Email wollte ich auslagern und so auch für alle anderen Skripte zugänglich machen.


Alle deine Hinweise konnte ich nachvollziehen und erscheinen mir jetzt auch logisch. Diesen einen Punkt verstehe ich aber nicht.
__blackjack__ hat geschrieben: Sonntag 22. August 2021, 19:37 Die Verbindung zum Server wird nicht sauber geschlossen. `SMTP_SSL`-Objekte sind Kontextmanager, die man mit ``with`` verwenden kann.
Sirius3
User
Beiträge: 18274
Registriert: Sonntag 21. Oktober 2012, 17:20

Da war ja schon einiges richtige dabei. Statt Variablen einer "Datei" zu übergeben (was nicht funktioniert), schreibt man dafür eine Funktion, die kann, muß aber nicht in einer eigenen Datei, sprich Modul, stehen.
Um die Verbindung zum Server wieder sauber zu schließen, muß man dieses with benutzen, das ja __blackjack__ in seinem Code benutzt hat.

Code: Alles auswählen

#!/usr/bin/env python3
import smtplib
from email.message import EmailMessage
from pathlib import Path
import toml

SKRIPTPFAD = Path(__file__).absolute().parent

def send_email(config, recipient, subject, content):
    smtp_server = config["smtp"]
    port = config["port_SSL"]
    absender = config["absender"]
    user = config["user"]
    password = config["password"]

    message = EmailMessage()
    message["From"] = absender
    message["To"] = recipient
    message["Subject"] = subject
    message.set_content(content)
    with smtplib.SMTP_SSL(smtp_server, port) as server:
        server.login(user, password)
        server.send_message(message)
        print("Email wurde erfolgreich versendet")


def main():
    config = toml.load(SKRIPTPFAD / "data.toml")["email"]
    send_email(config, "an_mich@mail.de", "Betreff", """\
    Inhalt der Nachricht:
    2. Zeile
    3. Zeile 
    """)

if __name__ == "__main__":
    main()
Holzkopp
User
Beiträge: 12
Registriert: Samstag 24. Juli 2021, 20:49

So bekomme ich es ans Laufen!
Ich habe jetzt zwei Dateien:


text_email.py

Code: Alles auswählen

#!/usr/bin/env python3
import smtplib
from sending_email import text_email # eigenes Modul

def main():
    text_email("an@mich.de", "Betreff Text-Email", 
    """\
    Inhalt der Nachricht:
        2. Zeile
    3. Zeile 
    """)

if __name__ == "__main__":
    main()
Und das Modul sending_email.py

Code: Alles auswählen

#!/usr/bin/env python3
import smtplib
from email.message import EmailMessage

# TOML
from pathlib import Path
import toml
SKRIPTPFAD = Path(__file__).absolute().parent


def text_email(recipient, subject, content):
    config = toml.load(SKRIPTPFAD / "data.toml")["email"]
    smtp_server = config["smtp"]
    port = config["port_SSL"]
    absender = config["absender"]
    user = config["user"]
    password = config["password"]

    message = EmailMessage()
    message["From"] = absender
    message["To"] = recipient
    message["Subject"] = subject
    message.set_content(content)

    with smtplib.SMTP_SSL(smtp_server, port) as server:
        server.login(user, password)
        server.send_message(message)
        print("Text-Email wurde erfolgreich versendet")
Das klappt wunderbar!!!


Jetzt wollte ich es mit HTMLprobieren. Das klappt zwar, aber sobald ich einen Teil in das Modul auslagern will, bekomme ich den Fehler

Code: Alles auswählen

  File "html_email.py"
    html = """\
       ^
SyntaxError: invalid syntax
Meine zwei Dateien sehen jetzt so aus:

html_email.py

Code: Alles auswählen

#!/usr/bin/env python3
import smtplib
from sending_email import html_email # eigenes Modul

def main():
    html_email("an@mich.de", "Betreff HTML-Email", 
    text = """\
    Inhalt der Nachricht:
    2. Zeile
    3. Zeile """
    html = """\
    <html>
    <body>
        <p>Hallo<br>
        Dies ist HTML-Format, falls es denn klappt.
        </p>
    </body>
    </html>
    """

if __name__ == "__main__":
    main()
und habe das Modul wie folgt ergänzt:

Code: Alles auswählen

#!/usr/bin/env python3
import smtplib
from email.message import EmailMessage

# MIME
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

# TOML
from pathlib import Path
import toml
SKRIPTPFAD = Path(__file__).absolute().parent


def text_email(recipient, subject, content):
    config = toml.load(SKRIPTPFAD / "data.toml")["email"]
    smtp_server = config["smtp"]
    port = config["port_SSL"]
    absender = config["absender"]
    user = config["user"]
    password = config["password"]

    message = EmailMessage()
    message["From"] = absender
    message["To"] = recipient
    message["Subject"] = subject
    message.set_content(content)

    with smtplib.SMTP_SSL(smtp_server, port) as server:
        server.login(user, password)
        server.send_message(message)
        print("Text-Email wurde erfolgreich versendet")


def html_email(recipient, subject, content):
    config = toml.load(SKRIPTPFAD / "data.toml")["email"]
    smtp_server = config["smtp"]
    port = config["port_SSL"]
    absender = config["absender"]
    user = config["user"]
    password = config["password"]

    message = MIMEMultipart("alternative")  # MIME
    message["From"] = absender
    message["To"] = recipient
    message["Subject"] = subject
    message.set_content(content)

    part1 = MIMEText(text, "plain")
    part2 = MIMEText(html, "html")

    message.attach(part1)
    message.attach(part2)

    with smtplib.SMTP_SSL(smtp_server, port) as server:
        server.login(user, password)
        server.send_message(message.as_string())
        print("MIME-Email wurde erfolgreich versendet")
Habt ihr eine Idee, wie ich den HTML-Teil in mein Modul übergeben bekomme?
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@Holzkopp,

Code: Alles auswählen

def main():
    html_email("an@mich.de", "Betreff HTML-Email", 
    text = """\
    Inhalt der Nachricht:
    2. Zeile
    3. Zeile """  <--------- Komma fehlt
    html = """\
    <html>
    <body>
        <p>Hallo<br>
        Dies ist HTML-Format, falls es denn klappt.
        </p>
    </body>
    </html>
    """  <---- Klammer ) fehlt
Wie du siehst, ist es recht unübersichtlich einen """ DocString """ ein einen Funktionsaufruf zu schrieben. Vielleicht besser vorher in eine Variable schreiben oder aus einer Textdatei lesen.
Holzkopp
User
Beiträge: 12
Registriert: Samstag 24. Juli 2021, 20:49

Ich habe jetzt vorerst die Änderungen übernommen. Das ergibt leider eine Fehlermeldung:

Code: Alles auswählen

def main():
    html_email("an@mich.de", "Betreff HTML-Email", 
    text = """\
    Inhalt der Nachricht:
    2. Zeile
    3. Zeile """,
    html = """\
    <html>
    <body>
        <p>Hallo<br>
        Dies ist HTML-Format, falls es denn klappt.
        </p>
    </body>
    </html>
    """)

Code: Alles auswählen

Traceback (most recent call last):
  File "html_email.py", line 27, in <module>
    main()
  File "html_email.py", line 23, in main
    """)
TypeError: html_email() got an unexpected keyword argument 'text'
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

Deine Funktion "html_email" erwartet ja drei Argumente: recipient, subject, content.
Daher gibt es den Fehler.
Angepasst für deinen Fall müsste es wahrscheinlich so aussehen (ungetestet)

Code: Alles auswählen

def html_email(recipient, subject, text, html):
    config = toml.load(SKRIPTPFAD / "data.toml")["email"]
    smtp_server = config["smtp"]
    port = config["port_SSL"]
    absender = config["absender"]
    user = config["user"]
    password = config["password"]

    message = MIMEMultipart("alternative")  # MIME
    message["From"] = absender
    message["To"] = recipient
    message["Subject"] = subject

    part1 = MIMEText(text, "plain")
    part2 = MIMEText(html, "html")

    message.attach(part1)
    message.attach(part2)

    with smtplib.SMTP_SSL(smtp_server, port) as server:
        server.login(user, password)
        server.send_message(message.as_string())
        print("MIME-Email wurde erfolgreich versendet")
Antworten