Neulingsfrage: Variablen bei Script-Aufruf übergeben

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.
dermaxem
User
Beiträge: 18
Registriert: Montag 30. Mai 2022, 16:53

Hallo,

ich bin neu in der Materie und kenne noch nicht alle Funktionen, habe auch noch keinen Lern-Kursus gemacht.
Für eine Terrarium-Steuerung habe ich mir was überlegt, das aber - wegen fehlender Programmiererfahrung - viel auf der Beispiel-Datei des Sensor-Lieferant beruht.
(AZ-Delivery, GY-21-Sensor am RASPI)
Ist bestimmt auch chicer via Funktionen zu lösen, aber würde für mein Urlaubs-Script erst mal langen.
In dem Script (Script1) wird von einem Sensor die Temperatur und Luftfeuchte abgefragt. Funktioniert auch alles wie es soll.
Das Script habe ich mir angepasst, mit Daum/Uhrzeit versehen, dann einen Tag- und Nachmodus definiert und dann die Werte entsprechend behandelt.
Nun möchte ich für einen bestimmten Fall eine E-Mail senden, bzw noch eine URL aufrufen.
Dafür habe ich ein zweites Script "gebastelt", das mir eine E-Mail sendet. Diesem 2ten Script kann ich 2 Werte mit angeben, die via "sys.argv[x]" mit in der Mail auftauchen.
Wenn ich das so in der Konsole aufrufe "

Code: Alles auswählen

python3 mail.py xx yy
" dann erzeugt mir das die Mail, in der dann die Werte auch erscheinen. Klappt soweit

Wie kann ich beim Aufrufen des 2. Mail-Scriptes, aus dem ersten Script heraus, die 2 Werte mit an das Mail-Script übergeben?
Habe es mit

Code: Alles auswählen

exec(open("mail.py" xx yy).read()) 
versucht, erhalte dafür einen SyntaxError: Invalid Syntax.
Gesucht habe ich dazu, aber leider keine Lösung gefunden, die ich auch verstanden hätte.
Hier was ich gefunden habe, aber nicht weiss, wie ich es umsetzten kann:

Code: Alles auswählen

with open("somefile.py") as f:
    code = compile(f.read(), "somefile.py", 'exec')
    exec(code, global_vars, local_vars)
Benutzeravatar
Kebap
User
Beiträge: 686
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

Ich würde eine andere Lösung wählen, und endweder die Mailfunktion in das erste Skript mit aufnehmen, oder das zweite Skript per "import" ins erste Skript einbinden. Dann kannst du dessen Funktionen auch dort benutzen. Du hast doch Funktionen benutzt, oder? :mrgreen:
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@dermaxem: Vergiss `exec()`, schreib das `mail`-Modul so, dass da eine Funktion drin ist, die diese zwei Argumente erwartet und eine Mail verschickt wenn man sie aufruft. Und die Funktion rufst Du dann halt einfach auf.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
dermaxem
User
Beiträge: 18
Registriert: Montag 30. Mai 2022, 16:53

habe es nu so versucht, aber einen Fehler erhalten.
Den hab ich gegooglet, versucht als "str" zu setzten, aber bekomme immer noch den gleichen Fehler.
Was mache ich falsch?

Code: Alles auswählen

Traceback (most recent call last):
  File "/home/pi/test2.py", line 35, in <module>
    exec(open(str("mail.py", temp, humidity)).read())
TypeError: str() argument 2 must be str, not float
Ziel: das externe "mail.py"-Script aufzurufen und diesem die 2 Parameter zu übergeben.
die Werte der Parameter sind jedoch Zahlen.
Wandel ich die auch um, dann kommt:

Code: Alles auswählen

Traceback (most recent call last):
  File "/home/pi/test2.py", line 35, in <module>
    exec(open(str("mail.py", str(temp), str(humidity))).read())
TypeError: decoding str is not supported
und versuche ich es so:

Code: Alles auswählen

Traceback (most recent call last):
  File "/home/pi/test2.py", line 35, in <module>
    exec(open("mail.py", str(temp), str(humidity)).read())
TypeError: an integer is required (got type str)
kommt es auch zum Fehler.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@dermaxem: Falsch daran ist wie schon gesagt `exec()`. Vergiss das.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Das richtige Vorgehen ist es, ein Module `mail` zu haben, in dem es z.B. eine Funktion `send_mail` gibt:

Code: Alles auswählen

from mail import send_mail

send_mail(temperature, humidity)
dermaxem
User
Beiträge: 18
Registriert: Montag 30. Mai 2022, 16:53

da ich es nicht anders kann, habe ich die Imports des Mail-Scripts mit in den oberen Bereich des Haupt-Scripts gepackt.
Dann eine funktion
"def mailen(grund, now, temp, humidity, spray)"
angelegt und dann da eingerückt alles, was in dem Mail.py-cript drinn war, auisser eben die Import-Zeilen am Anfang.
dann habe ich eine Funktion
"messen(channel)" im Script, da sind paar if-Abfragen. Darauf hin werden werte fü die Variablen zum vermailen gesetzt
und dann mit "mailen(grund, ...) " aufgerufen.
Das gibt dann einen Fehler:
"Fehler: <built-in function exec>"
HIer mal mein Script als Basis:

Code: Alles auswählen

# -*- coding: utf-8 -*-
import thingspeak
import time
import board
import busio
import smtplib
import sys

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from adafruit_htu21d import HTU21D

def messe(channel):
	try:
		#das ganzemess-gedöhns		
		if dies
			grund = "das"
			mailen(grund, now, temp, humidity, spary)
		if wasanderes
			grund "wasanderes"
			spray =1
			mailen(...)
	except:
		print ('\nFehler: ', exec )

def mailen(grund, now, temp, humidity, spray):
	#das ganze mail-gedöns (das Mail-Script ansich, also "solo" geht!)
	smtpServer ......
	smtpObj.sendmail(,....)

if __name__ == "__main__":
        channel = thingspeak.Channel(id=channel_id, api_key=write_key)
        while True:
                messe(channel)
                time.sleep(30)
habe mit (...) abgekürzt
soweit in der Struktur mein Code, den ich aber nur aus den von mir gegoogelten Beispielen zusammen gebaut habe.
Kann ja gut sein, das ich was Grundsätzliche falsch verstanden habe oder falsch anwende.
Die Daten werden wie gewünscht an ThingSpeak übertragen.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Eingerückt wird mit 4 Leerzeichen pro Ebene, nicht Tabs.
Um zu wissen, ob Du das Programm verstanden hast, ist es sehr hinderlich, dass Du da kein Python geschrieben hast, sondern irgendwas fragmentarisches.
Nakte excepts darf man nicht benutzten, außer man gibt wirklich den gesamten Traceback aus (z.B. per logging.exception).
Und schließlich: in Deinem ganzen Code kommt kein `exec` vor, so dass man Deinen Fehler auch nicht nachvollziehen kann.
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

Sirius3 hat geschrieben: Mittwoch 22. Juni 2022, 11:07 Und schließlich: in Deinem ganzen Code kommt kein `exec` vor, so dass man Deinen Fehler auch nicht nachvollziehen kann.
Doch, im print, deshalb wird das ja auch ausgegeben.
Benutzeravatar
Kebap
User
Beiträge: 686
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

Was erwartest du von diesen zwei Zeilen?

Code: Alles auswählen

	except:
		print ('\nFehler: ', exec )
Ich würde die einfach löschen.
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Na nicht nur die, sondern auch das try dazu ;)
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

In diesem Fall kann es schon sinnvoll sein, alle Exceptions abzufangen, weil es eine Endloschleife geben soll, die bei gelegentlichen Fehlern nicht abgebrochen wird.
Aber dann muß man die Fehlermeldung richtig loggen, und nicht einfach wegwerfen.

So dass das ganze dann so aussieht:

Code: Alles auswählen

import thingspeak
import time
import board
import busio
import smtplib
import sys
import logging

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from adafruit_htu21d import HTU21D

logger = logging.getLogger(__name__)

def messe(channel):
    try:
        #das ganzemess-gedöhns      
        if dies:
            grund = "das"
            mailen(grund, now, temperature, humidity, spray)
        if wasanderes:
            grund = "wasanderes"
            spray = 1
            mailen(...)
    except:
        logger.exception(f'error at messe of channel {channel}')


def mailen(grund, now, temperature, humidity, spray):
	#das ganze mail-gedöns (das Mail-Script ansich, also "solo" geht!)
	smtp_server = ...
	smtp_server.sendmail(...)

def main():
    logging.basicConfig()
    channel = thingspeak.Channel(id=channel_id, api_key=write_key)
    while True:
        messe(channel)
        time.sleep(30)


if __name__ == "__main__":
    main()
Und dann hat man auch alle Informationen, um auf die Fehlersuche gehen zu können.
dermaxem
User
Beiträge: 18
Registriert: Montag 30. Mai 2022, 16:53

OK,

habe das mit dem Logging erweitert und dabei nun festgestellt, das meine Erweiterung des "mailens" um die Variable "now" das Problem darstellt:
Das wollte ich mit dem Umwandeln in "str" beheben, aber irgendwas mache ich falsch:
Hier der Fehler in der "mailen"-Funktion, die u.a. auch "now" mit übergeben bekommet.

Code: Alles auswählen

    body = "\n Datum:"+str(now)+"\n\n\nTemperatur:"+temp+"\n\nLuftfeuchts:"+humidity+"\n\nSpray= "+spray
TypeError: can only concatenate str (not "datetime.datetime") to str
das "now" wird weiter oben im code mit

Code: Alles auswählen

from datetime import datetime
        now = datetime.now()
        spray = 0
bereitgestellt und ist als "global" definiert, so das ich "now" eigentlich von der einen Funktion an die Andere übergeben kann.
-
Nachtrag: Hab es selber gefunden. Ich wandele die VAR vorher selber um mit datetime.now().strftime('%Y-%m-%d %H:%M:%S')
Zuletzt geändert von dermaxem am Sonntag 26. Juni 2022, 13:15, insgesamt 1-mal geändert.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Bis Du sicher, dass das die Zeile ist, die den Fehler verursacht?
Globale Variablen darf man nicht benutzen, und statt Strings per + zusammenzustückeln, nimmt man Format-Strings, dann hat man auch nicht solche Probleme:

Code: Alles auswählen

body = ("\n
    f"Datum: {now}\n\n\n"
    f"Temperatur: {temperature}°C\n\n"
    f"Luftfeuchte: {humidity}%\n\n"
    f"Spray: {spray}"
)
dermaxem
User
Beiträge: 18
Registriert: Montag 30. Mai 2022, 16:53

andere frage dazu:
Wie kann ich die "normale" Ausgabe anzeigen lassen, also die gewollten "print"Ausgaben, die Meldung der Mailen-Funktion aber nicht?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Indem du die print-Funktionen aus dem mailen rausschmeisst? Python kann ja nicht wissen, was es machen soll, wenn du es ihm nicht sagst.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Dafür gibt es logging, dass verschiedene Funktionen verschiedene Informationen in einer Form ausgeben, dass man danach filtern kann, was man denn eigentlich sehen will.
dermaxem
User
Beiträge: 18
Registriert: Montag 30. Mai 2022, 16:53

Das mit dem "Mailen" klappt nun - ausser das es mir die Konsole voll ballert mit Infos, die ich nicht sehen möchte. Aber es geht nun.
Nun sitze ich am Problem: Rechnen mit Zeiten.
Ich würde gerne 2 Zeiten (now) und (now2) vergleichen und auf eine Differenz von 30 Minuten checken.

Code: Alles auswählen

def zeitvergleich(now):
    from datetime import datetime
    now2 = datetime.now()
    lastcall= now2 - now
    if lastcall > 30:
        print ("lastcall > 30!") #und dann darf dies und das passieren
    print ("lastcall < 30!") #und dann darf eben dies und das nicht passieren
Ich bekomme aber eine Meldung, die mir auch klar ist:

Code: Alles auswählen

    if lastspray > 30:
TypeError: '>' not supported between instances of 'datetime.timedelta' and 'int'
da es hier um Zeiten geht, und die "30" ein INT ist, und eben nicht "30 Minuten".
Wie kann man denn mit den Zeiten vergleichen, bzw: wie gebe ich die Vergleichswerte in Minuten an?
Beim googlen finde ich immer nur so Vergleiche mit Time-Delta + 30 Tage und dann die Ausgabe des neuen Datums.
Wie aber kann ich einen Vergleichswert nutzen?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Indem du das mit einem timedelta von 30 Minuten vergleichst.

Code: Alles auswählen

if lastspray > datetime.timedelta(minutes=30):
dermaxem
User
Beiträge: 18
Registriert: Montag 30. Mai 2022, 16:53

Code: Alles auswählen

def zeitvergleich(now):
    from datetime import datetime, timedelta
    now2 = datetime.now()
    lastcall = now2 - now
    if lastcall > datetime.timedelta(minutes=30):
        print ("JA, ist OK") # also > 30 minunten
    print ("neeee.. 30 Minunten sind noch nicht rum"!) #  also < 30
da kommt dann aber :

Code: Alles auswählen

AttributeError: type object 'datetime.datetime' has no attribute 'timedelta'
Antworten