Problem mit Variable

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
nyma3
User
Beiträge: 10
Registriert: Montag 12. November 2018, 12:36

Hallo, ich bin neu hier in dem Forum ..
und mein erster Post ist gleich eine Bitte um Hilfe :oops

Es geht um folgendes ..
Ich möchte gerne meinen raspberry dazu bringen Emails die ich an ihn sende auszuwerten und dementsprechend Befehle zu verarbeiten.
Dazu sollen nur Email von meiner email-Addresse berücksichtigt werden.
Auch sollen es nur ungelesene Emails sein.
und zum schluß soll das email dann in ein log-file geschrieben werden und die email vom Server gelöscht werden (aber soweit bin ich noch nicht)
das Script ist noch nicht fertig aber es ist schon ein Großteil geschaffen

Leider hänge ich aber gerade bei einem essentiellen Problem

Ich kann die Variable email_message aus der Funktion checkEmail() in der Funktion loop() nicht einlesen.


Irgendwie sehe ich den Wald vor lauter Bäumen nicht mehr .
kann mir vielleicht jemand kurz helfen ??

Code: Alles auswählen

</s>#!/usr/bin/python
import datetime
import time
import email
import imaplib
#import mailbox

# Variables:
SENDER "BERECHTIGTE_EMAIL@GMAIL.COM"
EMAIL_ACCOUNT = "RASPBERRY_EMAIL@gmail.com"
PASSWORD = "RASPBERRY_PASSWORT"
HOST = "imap.gmail.com"
MAILBOX = "inbox"
MSG_SUBJECT = "CORRECT"
CHECK_FREQ = 3

# SCRIPT:
def checkEmail():
    mail = imaplib.IMAP4_SSL(HOST);
    mail.login(EMAIL_ACCOUNT, PASSWORD);
    mail.list();
    count = 0

    mail.select(MAILBOX);
    result, data = mail.uid('search', None, '(UNSEEN FROM "BERECHTIGTE_EMAIL@gmail.com")'); # (ALL/UNSEEN)
    i = len(data[0].split())

    for x in range(i):
        latest_email_uid = data[0].split()[x]
        result, email_data = mail.uid('fetch', latest_email_uid, '(RFC822)')
        raw_email = email_data[0][1]
        raw_email_string = raw_email.decode('utf-8')
        email_message = email.message_from_string(raw_email_string)

        # Header Details
        date_tuple = email.utils.parsedate_tz(email_message['Date'])
        if date_tuple:
            local_date = datetime.datetime.fromtimestamp(email.utils.mktime_tz(date_tuple))
            local_message_date = "%s" %(str(local_date.strftime("%a, %d %b %Y %H:%M:%S")))
        email_from = str(email.header.make_header(email.header.decode_header(email_message['From'])))
        email_to = str(email.header.make_header(email.header.decode_header(email_message['To'])))
        subject = str(email.header.make_header(email.header.decode_header(email_message['Subject'])))

        # Body details
        for part in email_message.walk():
            if part.get_content_type() == "text/plain":
                body = part.get_payload(decode=True)
                file_name = "email_" + str(x) + ".txt"
                output_file = open(file_name, 'a')  # w=write, a=append
                output_file.write("From: %s\nTo: %s\nDate: %s\nSubject: %s\n\nBody: \n\n%s" %(email_from, email_to,local_message_date, subject, body.decode('utf-8')))
                output_file.close()
                input_file = open(file_name, 'r') # r=read (just temporary until script is finished)
                print input_file.read()
                print('Logging into ' + HOST)
                time.sleep(CHECK_FREQ)
                checkEmail()
            else:
                continue


def loop():
    count = 0
    while count < 10:
    	print('establishing connection to ... ' + HOST)
    	try:
    		checkEmail()
    		time.sleep(CHECK_FREQ)
    		if(email_message['Subject'] == MSG_SUBJECT):
    			print("- - -   S U B J E C T   C O R R E C T, (message accepted)   - - -")
    			loop()
    		else:
    			print("- - -   S U B J E C T   W R O N G, (message declined)   - - -")
    			loop()
    	except IndexError:
    		time.sleep(CHECK_FREQ)
    		if count < 9:
    			count = count +1
    			loop()
    			continue
    		else:
    			print("Something went wrong!")
    			print("N O   C O N N E C T I O N")
    			loop()
    			count = 10
loop()


<e>
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Auf die Schnelle sehe ich,
- dass die Funktion checkEmail sich rekursiv selbst aufruft, das ist mit Sicherheit nicht das was du willst, was bezweckst du damit?
- wie oder woher soll denn die Funktion loop die Variable email_message aus der Funktion checkEmail kennen?
Die Variable email_message ist nur innerhalb der Funktion checkEmail definiert.
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@nyma3: generell, eingerückt werden sollte immer mit 4 Leerzeichen pro Ebene, niemals Tabs und Leerzeichen mischen. Funktionsnamen schreibt man wie variablen klein_mit_unterstrich: check_email.

Sowohl der Rekursive Aufruf von checkEmail noch in loop machen Sinn.
In `loop`: Was Du dann mit dem `count` erreichen willst, ist noch verworrener. `continue` sollte man sparsam einsetzen, in Deinen Fällen macht es überhaupt keinen Unterschied, weil die Schleife sowieso an den Stellen wieder von vorne beginnen würde. Ich sehe keine Stelle, wo ich einen Index-Error erwarten würde. Wenn in checkEmail an einer bestimmten Stelle doch ein IndexError auftreten würde, würde ich ihn in eine aussagekräftigere Exception umwandeln.

Zu `checkEmail`:
`;` ist in Python zum Trennen von Anweisungen da und sollte eigentlich nicht benutzt werden, da pro Zeile eine Anweisung Konvention ist. Bei mail.uid mußt Du wohl oder übel SENDER in den Abfragestring hineinformatieren.
'count' wird nicht verwendet.
`i` ist ein schlechter Name für die Anzahl an Mails, `x` ein schlechter Name für den Mail-Index. Du solltest in der gesamten Funktion auch nur einmal split aufrufen und diese Liste an z.B. email_uids binden, über die Du dann (statt des x-Indices) direkt iterierst. Dieses hin und her-Konvertieren von Email-Headern sieht sehr umständlich aus. Zumindest die ganzen str-Aufrufe dürften überflüssig sein. Auch eine "%s"-Formatierung ist unsinnig. locale_message_date ist unter umständen nicht definiert.
Was ist der Sinn, erst einen Datei zu schreiben, die Du dann sofort wieder einliest? Das sleep und rekursive Aufruf an dieser Stelle ist sicher falsch. Ein `else: continue` überflüssig.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@nyma3: Das kommt nicht am Compiler vorbei weil bei der Definition von `SENDER` das Gleichheitszeichen für die Zuweisung fehlt.

Dann sind bei fast allen ``print``-Anweisungen unnötige und irreführende Klammern. Entweder weg mit den Klammern, oder `print_function` aus `__future__` importieren, damit aus ``print`` tatsächlich eine Funktion wird.

Um Bedingungen bei ``if`` gehören ebenfalls keine Klammern.

Die Semikolons am Zeilenende sind auch kein idiomatisches Python.

Auf den Kommentar ``# Variables:`` folgen Konstantendefinitionen und keine Variablen. Die auf Modulebene sowieso nicht stehen sollten.

``# SCRIPT:`` ist auch kein sinnvoller Kommentar. Kommentare sollten dem Leser einen Mehrwert über den Code bieten. Faustregel: Ein Kommentar beschreibt nicht was der Code macht, denn das beschreibt ja schon der Code selbst, sondern warum er das so macht was er macht. Aber auch nur, wenn das nicht offensichtlich ist.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Also `check_email()` statt `checkEmail()`.

Das Hauptprogramm steckt üblicherweise in einer Funktion die `main()` heisst. Also könnte man `loop()` umbenennen. Und dann auch gleich davor schützen das die Funktion aufgerufen wird wenn das Modul importiert statt als Programm ausgeführt wird.

Die rekursiven Aufrufe machen keinen Sinn. Weder in `check_email()` noch in `loop()`. Zumal, selbst wenn sie sinnvoll wären, währen sie in Python das falsche Mittel einfache Schleifen zu ersetzen. Python ist keine Programmiersprache in der „tail call optimization“ garantiert wird, und damit werden solche ”Schleifen” früher oder später zu einem Programmabruch mit einer Ausnahme führen weil der Aufrufstapel voll bzw. das Rekursionslimit erreicht ist.

Mit ``continue`` sollte man sparsam umgehen. Man kann den Programmfluss fast immer ohne dieses Schlüsselwort beschreiben. Im vorliegenden Code kann man beide Vorkommen auch einfach weglassen, ohne das sich etwas ändert.

`count` könnte man besser bennenen. Der Leser möchte ja gerne wissen *was* da gezählt wird. Die Behandlung dieses Zählers ist auch unnötig kompliziert. Man braucht den im ``else``-Fall nicht auf 10 zu setzen, denn den Wert hat der zu dem Zeitpunkt bereits. Man braucht aber auch gar nicht noch mal auf < 9 zu testen sondern kann den einfach bedingungslos um 1 erhöhen. Und den anderen Code aus dem ``else`` einfach *hinter* die ``while``-Schleife schreiben, denn die wird ja nur dann verlassen wenn der 10. Fehler aufgetreten ist. Da muss man nichts mehr extra prüfen. Das da „no connection“ ausgegeben wird ist falsch, denn es können auch andere Fehler zu dieser Ausgabe führen.

Das Schlafen von `CHECK_FREQ`-Sekunden kann man auch *einmal* im Hauptprogramm machen, statt es sowohl in den ``try``- als auch in den ``except``-Block zu schreiben. Im ``try``-Block steht es auch an einer eigenartigen Stelle. Warum will man zwischen dem Abfragen und Auswerten der Mail warten? Der Name ist falsch, denn es handelt sich gar nicht um eine Frequenz.

In der `check_email()`-Funktion macht das warten dann so überhaupt gar keinen Sinn. Warum‽

Da man den ``try``-Block so kurz wie möglich halten sollte, damit man wirklich nur Ausnahmen aus dem Code behandelt für die die Behandlung Sinn macht, sollte die Auswertung in einen ``else``-Block zu dem ``try`` wandern.

Wenn Du `email_message` aus `check_email()` ausserhalb der Funktion brauchst, dann musst Du den Wert an den Aufrufer zurückgeben. Sonst existiert der Wert nur lokal in der Funktion und nur solange der Funktionsaufruf läuft.

Wobei sich hier die Frage stellt *welchen* Wert Du da eigentlich zurückgeben möchtest, denn `email_message` bekommt in `check_email()` ja in einer Schleife potentiell mehr als einen Wert zugewiesen. Oder auch gar keinen, wenn die Suche keine Message-IDs liefert.

`mail` in `check_email()` hiesse besser `server` oder `mail_server`, denn es handelt sich ja nicht um eine Mail. Der Code meldet sich am Server an, aber nicht wieder ab. Das ist unsauber und es gibt Mailserver die einem das auf Dauer übel nehmen können. `IMAP4_SSL`-Objekte sind Kontextmanager, können also mit der ``with``-Anweisung verwendet werden.

Der `list()`-Aufruf beim Server macht keinen Sinn, weil Du mit dem Ergebnis überhaupt nichts machst.

`count` und `result` werden in der Funktion nie verwendet. `result` sollte vielleicht verwendet werden.

Statt die Konstante `SENDER` zu verwenden, steht der Wert noch mal im Quelltext.

Die ``for``-Schleife ist „unpythonisch“. Man kann in Python direkt über die Elemente von Seqzenztypen iterieren, ohne den Umweg über einen Index. Zudem ist das auch sehr ineffizient in jedem Schleifendurchlauf die Zeichenkette wieder erneut an Leerzeichen aufzuteilen, nur um *einen* Wert davon zu verarbeiten. Die Namen `i` und `x` die man sich hier sparen kann, sind zudem noch schlecht gewählt, da sehr nichtssagend. Wenn man zusätzlich zu den Elementen noch eine laufende Zahl benötigt, gibt es die `enumerate()`-Funktion.

Das `latest_` bei `latest_email_uid` macht keinen Sinn.

Das Dekodieren der kompletten RFC822-Nachricht als Text ist meiner Meinung nach falsch, denn die kann ja aus mehreren MIME-Teilen bestehen von denen jeder anders kodiert sein kann, oder sogar Teile enthalten die gar kein Text sondern beliebige Binärdaten sind. Ich würde an der Stelle die `imapclient`-Bibliothek empfehlen, die nicht so „low level“ wie `imaplib` ist.

Das ``if date_tuple:`` sorgt dafür das `local_message_date` nicht oder falsch definiert ist wenn die Bedingung nicht zutrifft.

`local_message_date` wird sehr komisch erstellt. `strftime()` liefert eine Zeichenkette. Die wandelst Du mit `str` in eine Zeichenkette um. Und die wird dann mit ``'%s' % …`` noch mal zur selben Zeichenkette formatiert. Was soll der Unsinn? Bei `strftime()` muss man bei Python 2 übrigens aufpassen, das dort Umlaute, zum Beispiel in Monatsnamen wie `März` ”irgendwie” kodiert sind in der Bytezeichenkette. Wie, hängt von den Systemeinstellungen ab.

Was soll diese `decode_header()`/`make_header()`-Orgie bewirken?

Den Index der UID für den Dateinamen zu verwenden ist kaputt. Der fängt ja jedes mal bei 0 an, das heisst das Programm wird die unterschiedlichsten E-Mail-Texte an die gleiche Datei anhängen. Besser man nimmt die UID selbst. (Sofern garantiert ist, das die vom IMAP-Server nicht wiederverwendet werden dürfen, das weiss ich im Moment nicht.)

Zeichenketten und Werte mit ``+`` und `str()` zusammenstückeln ist eher BASIC als Python. In Python gibt es dafür Zeichenkettenformatierung.

Dateien sollte man mit der ``with``-Anweisung verwenden um das schliessen auch in Ausnahmefällen zu garantieren.

Das Dateiformat ist komisch. Da wird für jeden Plaintext-MIME-Teil immer wieder Empfänger, Sender, Datum, und Betreff geschrieben. Das der Textteil dekodiert wird, ist ebenfalls falsch, denn a) muss der nicht UTF-8 kodiert sein und b) fällt das schreiben in die Datei auf die Nase wenn da überhaupt etwas anderes als ASCII enthalten ist. Denn dann müsste man die Unicode-Zeichenkette in Python 2 *selbst* explizit kodieren. Ich würde ja einfach die komplette RFC822-Nachricht speichern und gut ist. Die lässt sich dann später ganz normal mit verschiedensten Werkzeugen die mit E-Mails umgehen können, weiterverarbeiten.

Für jeden gespeicherten E-Mail-Teil wieder und wieder auszugeben das man sich jetzt beim `HOST` anmeldet ist unsinnig/falsch.

Ich komme dann ungefähr hierbei als Zwischenstand heraus:

Code: Alles auswählen

#!/usr/bin/python
from __future__ import absolute_import, division, print_function
import datetime
import email
import imaplib
import time

SENDER = 'BERECHTIGTE_EMAIL@GMAIL.COM'
EMAIL_ACCOUNT = 'RASPBERRY_EMAIL@gmail.com'
PASSWORD = 'RASPBERRY_PASSWORT'
HOST = 'imap.gmail.com'
MAILBOX = 'inbox'
EXPECTED_SUBJECT = 'CORRECT'
CHECK_DELAY = 3  # in seconds.
MAX_ERROR_COUNT = 10


def check_email():
    with imaplib.IMAP4_SSL(HOST) as server:
        print('Logging into', HOST)
        server.login(EMAIL_ACCOUNT, PASSWORD)
        server.select(MAILBOX)
        _, data = server.uid(
            'search', None, '(UNSEEN FROM "{0}")'.format(SENDER)
        )
        for uid in data[0].split():
            _, data = server.uid('fetch', uid, '(RFC822)')
            # 
            # FIXME Decoding seems to be dangerous as the complete
            #   RFC822 message could contain message parts in different
            #   encodings or even binary data that's not supposed to be
            #   decoded as text at all.
            # 
            email_message = email.message_from_string(
                data[0][1].decode('utf-8')
            )
            date_tuple = email.utils.parsedate_tz(email_message['Date'])
            if date_tuple:
                local_message_date = (
                    datetime.datetime
                        .fromtimestamp(email.utils.mktime_tz(date_tuple))
                        .strftime('%a, %d %b %Y %H:%M:%S')
                )
            else:
                local_message_date = '-/-'
            
            for part in email_message.walk():
                if part.get_content_type() == 'text/plain':
                    file_name = 'email_{}.txt'.format(uid)
                    with open(file_name, 'a') as output_file:
                        #
                        # FIXME Writing a unicode string without
                        #   encoding it will blow up if there is
                        #   anything outside ASCII in it.
                        # 
                        output_file.write(
                            u'From: {}\n'
                            u'To: {}\n'
                            u'Date: {}\n'
                            u'Subject: {}\n\n'
                            u'Body:\n\n{}'.format(
                                email_message['From'],
                                email_message['To'],
                                local_message_date,
                                email_message['Subject'],
                                #
                                # FIXME Message payload can be any
                                #   other encoding.  Hard coding UTF-8
                                #   is wrong.
                                # 
                                part.get_payload(decode=True).decode('utf-8'),
                            )
                        )


def main():
    error_count = 0
    while error_count < MAX_ERROR_COUNT:
        print('establishing connection to ...', HOST)
        try:
            # 
            # FIXME `check_email()` currently doesn't return anything
            #   and also processes potentially no message at all or
            #   even more than one e-mail message.
            # 
            email_message = check_email()
        except IndexError:
            error_count += 1
        else:
            if email_message['Subject'] == EXPECTED_SUBJECT:
                print(
                    '- - -   S U B J E C T   C O R R E C T,'
                    ' (message accepted)   - - -'
                )
            else:
                print(
                    '- - -   S U B J E C T   W R O N G,'
                    ' (message declined)   - - -'
                )

        time.sleep(CHECK_DELAY)
    
    print('Something went wrong!', MAX_ERROR_COUNT, 'times.')


if __name__ == '__main__':
    main()
@Sirius: Der `IndexError` kommt bei ``data[0]`` in `checkEmail()` wenn der IMAP-Server ein leeres Ergebnis liefert, weil beispielsweise keine ungesehenen Mails von dem Absender gefunden werden konnten. Schön/leicht ersichtlich ist dieser ”Test” allerdings wirklich nicht.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
nyma3
User
Beiträge: 10
Registriert: Montag 12. November 2018, 12:36

Wow .. ok,
ich habe ja gewusst, dass ich kein "toller" Programmierer bin (ist bei mir mehr ein Hobby - das mir aber enorm viel Freude bereitet),
aber dass ich so extrem schlecht bin .. ok .. ich glaube ich muss mir echt mal ein gutes Programmier-kompendium kaufen ...

Auf jeden Fall mal ein großes Danke für die Ehrlichkeit und die Direktheit .. ich werde mir das mal zu Herzen nehmen und in Zukunft hoffentlich besser machen.
Auch ein großes Danke an _blackjack_ ich werde es gleich mal durchnehmen und ausprobieren
nyma3
User
Beiträge: 10
Registriert: Montag 12. November 2018, 12:36

hi,
Ich glaube ich brauche doch noch ein bisschen Hilfe ..
Irgendwie komme ich leider nicht weiter ..

zu dem decoding ..
es würde nur text geschickt werden (weil ja nur ein bestimmter string als befehl umgesetzt werden soll)
dafür wurde ja auch bei dem EXPECTED_SUBJECT eine Kontrollschnittstelle angedacht, dass nur bestimmte Email ausgewertet werden
die zweite Kontrollschnittstelle: SENDER (nur emails von Sender sollen verarbeitet werden wenn der richtige Betreff angegeben wurde)


Ich weiß nur gerade nicht wo der Fehler liegt, bzw was ich machen müsste, damit das script läuft
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ohne Code und konkrete Fehlermeldung oder Hinweis, was da wie wo nicht so wie erwartet tut ist aus diesem Post keine Hilfestellung die man geben koennte ableitbar.
nyma3
User
Beiträge: 10
Registriert: Montag 12. November 2018, 12:36

hallo,
wenn ich von einer berechtigten email ein email an die raspberry-pi-email-adresse schicke und der richtige betreff in der email steht, dann spuckt das skript diese Antwort aus:

Code: Alles auswählen

establishing connection to ... imap.gmail.com
Traceback (most recent call last):
  File "./test3.py", line 107, in <module>
    main()
  File "./test3.py", line 86, in main
    email_message = check_email()
  File "./test3.py", line 19, in check_email
    with imaplib.IMAP4_SSL(HOST) as server:
  File "/usr/lib/python2.7/imaplib.py", line 224, in __getattr__
    raise AttributeError("Unknown IMAP4 command: '%s'" % attr)
AttributeError: Unknown IMAP4 command: '__exit__'
leider hänge ich hier mit meinem Wissen hinterher um das zu lösen
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich habe das natürlich nicht getestet und bin anscheinend irrtümlich davon ausgegangen das `IMAP4_SSL` ein Kontextmanager ist weil das in Python 3 der Fall ist. Man muss das ``with`` also mit einem ``try``/``finally`` ersetzen, das erstellen von `server` davor schreiben, und im ``finally``-Zweig den `logout()`-Aufruf explizit machen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
nyma3
User
Beiträge: 10
Registriert: Montag 12. November 2018, 12:36

ähm ok..
ich habe jetzt

Code: Alles auswählen

try imaplib.IMAP4_SSL(HOST) as server
bei den anderen beiden bin ich mir gerade nicht sicher wie ich es genau umsetzen soll
kannst du mir vielleicht nur noch mit diesen 2 sachen helfen?
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@nyma3: mit Ersetzen hat __blackjack__ nicht "wörtlich ersetzen" gemeint, sondern das, was das with-Statement intern schon macht, über das Konstrukt eines try-finally-Blocks simulieren.

Code: Alles auswählen

server = imaplib.IMAP4_SSL(HOST)
try:
    #alles was vorher im with-Block stand
finally:
    server.logout()
nyma3
User
Beiträge: 10
Registriert: Montag 12. November 2018, 12:36

Hallo,
habe ich es richtig verstanden?
sorry falls die frage blöd erscheint:

Code: Alles auswählen

#!/usr/bin/python
from __future__ import absolute_import, division, print_function
import datetime
import email
import imaplib
import time

SENDER = 'BERECHTIGTE_EMAIL@GMAIL.COM'
EMAIL_ACCOUNT = 'RASPBERRY_EMAIL@gmail.com'
PASSWORD = 'RASPBERRY_PASSWORT'
HOST = 'imap.gmail.com'
MAILBOX = 'inbox'
EXPECTED_SUBJECT = 'CORRECT'
CHECK_DELAY = 3  # in seconds.
MAX_ERROR_COUNT = 10

def check_email():
    server = imaplib.IMAP4_SSL(HOST)
        print('Logging into', HOST)
        server.login(EMAIL_ACCOUNT, PASSWORD)
        server.select(MAILBOX)
        _, data = server.uid(
            'search', None, '(UNSEEN FROM "{0}")'.format(SENDER)
        )
        for uid in data[0].split():
            _, data = server.uid('fetch', uid, '(RFC822)')
            # 
            # FIXME Decoding seems to be dangerous as the complete
            #   RFC822 message could contain message parts in different
            #   encodings or even binary data that's not supposed to be
            #   decoded as text at all.
            # 
            email_message = email.message_from_string(
                data[0][1].decode('utf-8')
            )
            date_tuple = email.utils.parsedate_tz(email_message['Date'])
            try:
            if date_tuple:
                local_message_date = (
                    datetime.datetime
                        .fromtimestamp(email.utils.mktime_tz(date_tuple))
                        .strftime('%a, %d %b %Y %H:%M:%S')
                )
            else:
                local_message_date = '-/-'
            
            for part in email_message.walk():
                if part.get_content_type() == 'text/plain':
                    file_name = 'email_{}.txt'.format(uid)
                    with open(file_name, 'a') as output_file:
                        #
                        # FIXME Writing a unicode string without
                        #   encoding it will blow up if there is
                        #   anything outside ASCII in it.
                        # 
                        output_file.write(
                            u'From: {}\n'
                            u'To: {}\n'
                            u'Date: {}\n'
                            u'Subject: {}\n\n'
                            u'Body:\n\n{}'.format(
                                email_message['From'],
                                email_message['To'],
                                local_message_date,
                                email_message['Subject'],
                                #
                                # FIXME Message payload can be any
                                #   other encoding.  Hard coding UTF-8
                                #   is wrong.
                                # 
                                part.get_payload(decode=True).decode('utf-8'),
                            )
                server.logout()
                        )


def main():
    error_count = 0
    while error_count < MAX_ERROR_COUNT:
        print('establishing connection to ...', HOST)
        try:
            # 
            # FIXME `check_email()` currently doesn't return anything
            #   and also processes potentially no message at all or
            #   even more than one e-mail message.
            # 
            email_message = check_email()
        except IndexError:
            error_count += 1
        else:
            if email_message['Subject'] == EXPECTED_SUBJECT:
                print(
                    '- - -   S U B J E C T   C O R R E C T,'
                    ' (message accepted)   - - -'
                )
            else:
                print(
                    '- - -   S U B J E C T   W R O N G,'
                    ' (message declined)   - - -'
                )

        time.sleep(CHECK_DELAY)
    
    print('Something went wrong!', MAX_ERROR_COUNT, 'times.')


if __name__ == '__main__':
    main()
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Also ich sehe da diverse Syntaxfehler. Du musst dich schon soweit mit Python beschaeftigen, dass dir bewusst ist, was ein Block ist, und wie sich das zu den diversen Kontrollstrukturen verhaelt.
nyma3
User
Beiträge: 10
Registriert: Montag 12. November 2018, 12:36

Ja ich bin ja auch kein Profi (darum ja auch raspberry pi, sonst wäre es wohl ein server)
es ist ja für mich ein hobby für das ich mich interessiere.

Deshalb ja auch meine Bitte nach Hilfe, wenn ich es alleine könnte, dann würde ich ja nicht hier um Hilfe ansuchen
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Was ist an »#alles was vorher im with-Block stand« unverständlich?
nyma3
User
Beiträge: 10
Registriert: Montag 12. November 2018, 12:36

Ich bin mir jetzt nicht sicher ob es so gehört:

Code: Alles auswählen

def check_email():
    server = imaplib.IMAP4_SSL(HOST)
    try imaplib.IMAP4_SSL(HOST) as server:
        print('Logging into', HOST)
        ...
oder so:

Code: Alles auswählen

    
    def check_email():
    server = imaplib.IMAP4_SSL(HOST)
    try:
    with imaplib.IMAP4_SSL(HOST) as server:
        print('Logging into', HOST)
        ...
        
Das finally habe ich jetzt an dieser stelle stehen:

Code: Alles auswählen

            if date_tuple:
                local_message_date = (
                    datetime.datetime
                        .fromtimestamp(email.utils.mktime_tz(date_tuple))
                        .strftime('%a, %d %b %Y %H:%M:%S')
                )
    finally:
    server.logout()
            else:
                local_message_date = '-/-'
            for part in email_message.walk():
                if part.get_content_type() == 'text/plain':
            ...
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@nyma3: Weder noch. Wie es korrekt ist habe ich in Worten beschrieben und Sirius3 hat es als Code gezeigt.

Wo in der Dokumentation von ``try`` und ``finally`` und ``with`` bist Du denn auf diese beiden falschen Varianten gestossen? Lies doch einfach mal nach was diese Sprachkonstrukte machen, statt zu raten wie der Code vielleicht aussehen müsste, den Du offenbar nicht verstehst.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
nyma3
User
Beiträge: 10
Registriert: Montag 12. November 2018, 12:36

Nach einigen anderen Foren und Beschreibungen in anderen Foren habe ich nun mehr oder weniger eine Ahnung was try, finally und with machen sollen ..

ich habe jetzt auch with ersetzt und es dahingehend geändert.
Was ich aber leider noch nicht herausgefunden habe ist wo es geschlossen wird und wo ich demnach das finally platzieren soll..

Selbst Versuche haben mich da nicht weitergebracht ..

hier mein derzeitiger Stand:

Code: Alles auswählen

#!/usr/bin/python
from __future__ import absolute_import, division, print_function
import datetime
import email
import imaplib
import time

SENDER = 'xxxx@gmail.com'
EMAIL_ACCOUNT = 'xxxxxx@gmail.com'
PASSWORD = 'xxxxxx'
HOST = 'imap.gmail.com'
MAILBOX = 'inbox'
EXPECTED_SUBJECT = 'CORRECT'
CHECK_DELAY = 3  # in seconds.
MAX_ERROR_COUNT = 10

def check_email():
    server = imaplib.IMAP4_SSL(HOST)
    try:
        print('Logging into', HOST)
        server.login(EMAIL_ACCOUNT, PASSWORD)
        server.select(MAILBOX)
        _, data = server.uid(
            'search', None, '(UNSEEN FROM "{0}")'.format(SENDER)
        )
        for uid in data[0].split():
            _, data = server.uid('fetch', uid, '(RFC822)')
            # 
            # FIXME Decoding seems to be dangerous as the complete
            #   RFC822 message could contain message parts in different
            #   encodings or even binary data that's not supposed to be
            #   decoded as text at all.
            # 
            email_message = email.message_from_string(
                data[0][1].decode('utf-8')
            )
            date_tuple = email.utils.parsedate_tz(email_message['Date'])
            if date_tuple:
                local_message_date = (
                    datetime.datetime
                        .fromtimestamp(email.utils.mktime_tz(date_tuple))
                        .strftime('%a, %d %b %Y %H:%M:%S')
                )
            else:
                local_message_date = '-/-'
            for part in email_message.walk():
                if part.get_content_type() == 'text/plain':
                    file_name = 'email_{}.txt'.format(uid)
                    with open(file_name, 'a') as output_file:
                        #
                        # FIXME Writing a unicode string without
                        #   encoding it will blow up if there is
                        #   anything outside ASCII in it.
                        # 
                        output_file.write(
                            u'From: {}\n'
                            u'To: {}\n'
                            u'Date: {}\n'
                            u'Subject: {}\n\n'
                            u'Body:\n\n{}'.format(
                                email_message['From'],
                                email_message['To'],
                                local_message_date,
                                email_message['Subject'],
                                #
                                # FIXME Message payload can be any
                                #   other encoding.  Hard coding UTF-8
                                #   is wrong.
                                # 
                                part.get_payload(decode=True).decode('utf-8'),
                            )
                        )


def main():
    error_count = 0
    while error_count < MAX_ERROR_COUNT:
        print('establishing connection to ...', HOST)
        try:
            # 
            # FIXME `check_email()` currently doesn't return anything
            #   and also processes potentially no message at all or
            #   even more than one e-mail message.
            # 
            email_message = check_email()
        except IndexError:
            error_count += 1
        else:
            if email_message['Subject'] == EXPECTED_SUBJECT:
                print(
                    '- - -   S U B J E C T   C O R R E C T,'
                    ' (message accepted)   - - -'
                )
            else:
                print(
                    '- - -   S U B J E C T   W R O N G,'
                    ' (message declined)   - - -'
                )
        time.sleep(CHECK_DELAY)
    print('Something went wrong!', MAX_ERROR_COUNT, 'times.')
if __name__ == '__main__':
    main()

Ich wäre für jeden Ratschlag dankbar wie ich das Problem lösen kann ..

im Augeblick bekomme ich folgende Fehlermeldung beim Ausführen des Skripts wenn eine entsprechende Email im Postfach liegt

Code: Alles auswählen

  File "./test3.py", line 75
    def main():
    ^
IndentationError: unexpected unindent
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

In Zeile 19 steht ein try. Zu einem try-Statement gehört mindestens ein except-Block. Wo ist der?
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Naja, mindestens ein ``except`` *oder* ``finally``. Und um letzteres geht's. Wie das generelle Muster von ``with`` nach ``try``/``finally`` aussehen muss hat Sirius3 ja bereits auf der vorherigen Seite des Themas gezeigt. Die Dokumentation zu ``with`` verweist auf das entsprechende PEP und da steht auch beschrieben wie man das ``with`` durch ”herkömmliche” Sprachkonstrukte ersetzen kann und wie es funktioniert.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten