Endlosprogramm - Ausführung beenden lassen

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
tlosert
User
Beiträge: 18
Registriert: Donnerstag 6. Mai 2021, 13:05

Donnerstag 6. Mai 2021, 15:06

Hallo zusammen,

zunächst muss mich erst mal als ein Python-Neuling auten :D
Ich habe ein kleines Programm angepasst, in dem ich diverse Aktionen ausführen lassen möchte, sobald ich bei Telegram im Bot gewisse "Befehle" schreibe.
Also gebe ich z.B. Test ein, erscheint im Bot (testweise) die Nachricht "oh du verstehst mich". Soweit so gut.
Nun möchte ich per Befehl das "Endlos-Programm" beenden lassen.
Bei der Eingabe von "Ende" soll das Programm stoppen. Habe schon diverse Sachen wie sys.exit(), raise SystemExit ausprobiert, aber es läuft immer weiter.

Anbei das tolle Programm:

Code: Alles auswählen

import sys
import time
import telepot
import os
from telepot.loop import MessageLoop

def handle(msg):
    content_type, chat_type, chat_id = telepot.glance(msg)
    print(content_type, chat_type, chat_id)

    if content_type == 'text':
        bot.sendMessage(chat_id, msg['text'])
        if msg['text'] == 'Test':
          bot.sendMessage(chat_id, 'oh du verstehst mich')
        elif msg['text'] == 'Start':
          bot.sendMessage(chat_id, 'was soll denn gestartet werden?')
       [b][color=#FF0000] elif msg['text'] == 'Ende':
          bot.sendMessage(chat_id, 'Programm wird beendet')
          exit()[/color][/b]
        else: bot.sendMessage(chat_id, 'keine Ahnung was du möchtest')
          
    print (chat_id)
    print (bot)


bot = telepot.Bot('X64xxxxxxx:AxxxxxPMQexxxxxr5xxxGjxxxxTtixxxxhhxxx')
MessageLoop(bot, handle).run_as_thread()
print ('Listening ...')

# Keep the program running.
while 1:
    time.sleep(10)
Was muss ich ändern?

Danke schon mal und Grüße
Thomas
Benutzeravatar
__blackjack__
User
Beiträge: 8721
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Donnerstag 6. Mai 2021, 22:07

@tlosert: Wenn man in den Code schaut startet der diverse Threads und in jedem läuft eine Endlosschleife die alle Ausnahmen schluckt und weiterläuft. Yeah. Wer sich das ausgedacht hat, für den gibt es einen speziellen Platz in der Hölle. Da muss man ziemlich hart aussteigen, weil sauber kommt man aus der Nummer nicht raus.

Anmerkungen zum Quelltext: Eingerückt wird vier Leerzeichen pro Ebene.

`exit()` gibt es eigentlich nicht einfach so, das muss man aus `sys` importieren. Einfach so existiert das nur zufällig und ist undokumentiert.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Das hat zur Folge das `bot` nicht mehr so einfach magisch überall vorhanden ist. Funktionen und Methoden bekommen alles was sie ausser Konstanten benötigen, als Argument(e) übergeben.

Statt die `MessageLoop` in einem eigenen Thread zu starten und im Hauptthread dann in einer sinnlosen Warteschleife zu ”idlen”, sollte man die `MessageLoop` einfach im Hauptthread laufen lassen.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import os
import signal
from functools import partial

import telepot
from telepot.loop import MessageLoop


def handle(bot, message):
    content_type, chat_type, chat_id = telepot.glance(message)
    print(content_type, chat_type, chat_id)

    if content_type == "text":
        text = message["text"]
        bot.sendMessage(chat_id, text)

        if text == "Test":
            bot.sendMessage(chat_id, "oh du verstehst mich")

        elif text == "Start":
            bot.sendMessage(chat_id, "was soll denn gestartet werden?")

        elif text == "Ende":
            bot.sendMessage(chat_id, "Programm wird beendet")
            os.kill(os.getpid(), signal.SIGTERM)

        else:
            bot.sendMessage(chat_id, "keine Ahnung was du möchtest")


def main():
    bot = telepot.Bot("X64xxxxxxx:AxxxxxPMQexxxxxr5xxxGjxxxxTtixxxxhhxxx")
    print("Listening ...")
    MessageLoop(bot, partial(handle, bot)).run_forever()


if __name__ == "__main__":
    main()
long long ago; /* in a galaxy far far away */
tlosert
User
Beiträge: 18
Registriert: Donnerstag 6. Mai 2021, 13:05

Montag 17. Mai 2021, 22:23

__blackjack__ hat geschrieben:
Donnerstag 6. Mai 2021, 22:07
@tlosert: Wenn man in den Code schaut startet der diverse Threads und in jedem läuft eine Endlosschleife die alle Ausnahmen schluckt und weiterläuft. Yeah. Wer sich das ausgedacht hat, für den gibt es einen speziellen Platz in der Hölle. Da muss man ziemlich hart aussteigen, weil sauber kommt man aus der Nummer nicht raus.

Anmerkungen zum Quelltext: Eingerückt wird vier Leerzeichen pro Ebene.

`exit()` gibt es eigentlich nicht einfach so, das muss man aus `sys` importieren. Einfach so existiert das nur zufällig und ist undokumentiert.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Das hat zur Folge das `bot` nicht mehr so einfach magisch überall vorhanden ist. Funktionen und Methoden bekommen alles was sie ausser Konstanten benötigen, als Argument(e) übergeben.

Statt die `MessageLoop` in einem eigenen Thread zu starten und im Hauptthread dann in einer sinnlosen Warteschleife zu ”idlen”, sollte man die `MessageLoop` einfach im Hauptthread laufen lassen.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import os
import signal
from functools import partial

import telepot
from telepot.loop import MessageLoop


def handle(bot, message):
    content_type, chat_type, chat_id = telepot.glance(message)
    print(content_type, chat_type, chat_id)

    if content_type == "text":
        text = message["text"]
        bot.sendMessage(chat_id, text)

        if text == "Test":
            bot.sendMessage(chat_id, "oh du verstehst mich")

        elif text == "Start":
            bot.sendMessage(chat_id, "was soll denn gestartet werden?")

        elif text == "Ende":
            bot.sendMessage(chat_id, "Programm wird beendet")
            os.kill(os.getpid(), signal.SIGTERM)

        else:
            bot.sendMessage(chat_id, "keine Ahnung was du möchtest")


def main():
    bot = telepot.Bot("X64xxxxxxx:AxxxxxPMQexxxxxr5xxxGjxxxxTtixxxxhhxxx")
    print("Listening ...")
    MessageLoop(bot, partial(handle, bot)).run_forever()


if __name__ == "__main__":
    main()
Guten Abend,

vielen Dank für die angepasste Version. Ich hatte diese schon ausprobiert, das Programm lässt sich beenden.
ABER beim erneuten Start bricht es gleich wieder ab, als ob "Ende" in den Bot als Befehl eingegeben worden war.

Es erscheint folgendes bei jedem neuen Programmstart:

Code: Alles auswählen

Backend terminated or disconnected. Use 'Stop/Restart' to restart.
Grüße
Thomas
Benutzeravatar
Dennis89
User
Beiträge: 201
Registriert: Freitag 11. Dezember 2020, 15:13

Dienstag 18. Mai 2021, 06:25

Guten Morgen,

@__blackjack__ ich habe zu deinem Programm auch eine kleine Zwischenfrage. Ich kann nicht erkennen wie/wo 'message' in die Funktion 'handle' kommt? Nach meinem Verständnis wird nur 'bot' beim Aufruf übergeben.
Ich habe allerdings noch nie etwas mit 'telepot' gemacht, eventuell verstehe ich es auch deswegen nicht.

Danke und Grüße
Dennis
“A ship is always safe at the shore, but that is not what it is built for.”
Sirius3
User
Beiträge: 14609
Registriert: Sonntag 21. Oktober 2012, 17:20

Dienstag 18. Mai 2021, 06:29

Wie startest Du welches Programm? Etwa aus einer IDE heraus, die den Python-Prozess nicht sauber trennt?

@Dennis86: `handle` ist ein Callback, der vom MessageLoop mit dem Argument message aufgerufen wird. Das partial braucht man, weil man zusätzlich noch weitere Argumente braucht, die MessageLoop beim Aufruf nicht übergibt.
Benutzeravatar
Dennis89
User
Beiträge: 201
Registriert: Freitag 11. Dezember 2020, 15:13

Dienstag 18. Mai 2021, 07:44

Danke @Sirius3 🙂

Grüße Dennis
“A ship is always safe at the shore, but that is not what it is built for.”
tlosert
User
Beiträge: 18
Registriert: Donnerstag 6. Mai 2021, 13:05

Dienstag 18. Mai 2021, 09:03

Sirius3 hat geschrieben:
Dienstag 18. Mai 2021, 06:29
Wie startest Du welches Programm? Etwa aus einer IDE heraus, die den Python-Prozess nicht sauber trennt?

@Dennis86: `handle` ist ein Callback, der vom MessageLoop mit dem Argument message aufgerufen wird. Das partial braucht man, weil man zusätzlich noch weitere Argumente braucht, die MessageLoop beim Aufruf nicht übergibt.
Also ich habe einmal in der crontab "@reboot python3 /home/pi/telegram/bot.py" stehen. Nach Beendigung wollte ich es einfach per Smartphone App mit ssh Zugriff auf den pi mit "python3 /home/pi/telegram/bot.py &" erneut starten.
Wenn es keine andere Lösung gibt würde ich das Programm so umstellen, dass es nur einmal durchläuft (kein loop) und das dann je Minute. Ist ja nur Bastelei aber macht Spaß...
Grüße Thomas
tlosert
User
Beiträge: 18
Registriert: Donnerstag 6. Mai 2021, 13:05

Mittwoch 19. Mai 2021, 07:39

Guten Morgen,
habe einiges probiert, aber leider nicht es hinbekommen mit dem Beenden des Programmes. Bzw es wird ja beendet aber beim nächsten Start geht es sofort in das neue ende. Besteht denn die Möglichkeit diese Schleife 10 Sekunden laufen zu lassen und dann das Programm automatisch beenden? So könnte ich es einfach jede zwei Minuten einmal starten und dann wird auch die Telegramm Queue abgearbeitet? Grüße Thomas
__deets__
User
Beiträge: 9881
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mittwoch 19. Mai 2021, 08:03

Warum muss das beendet werden? Dafür ist Telegram nicht gemacht. Und im Zweifel verlierst du Nachrichten.
tlosert
User
Beiträge: 18
Registriert: Donnerstag 6. Mai 2021, 13:05

Mittwoch 19. Mai 2021, 13:40

__deets__ hat geschrieben:
Mittwoch 19. Mai 2021, 08:03
Warum muss das beendet werden? Dafür ist Telegram nicht gemacht. Und im Zweifel verlierst du Nachrichten.
Na ja das aktuelle Beenden beendet auch gleich jeden Neustart mit, warum auch immer. Zur Not, da es nur eine kleine bot Spielerei von mir ist kann das Programm schauen welche Nachrichten geschickt worden sind und dann entsprechend 10 Sekunden lang reagieren.
Sirius3
User
Beiträge: 14609
Registriert: Sonntag 21. Oktober 2012, 17:20

Mittwoch 19. Mai 2021, 14:00

@tlosert: das beantwortet noch nicht die Frage, warum Du im ersten Schritt überhaupt Deinen Bot beenden möchtest.
tlosert
User
Beiträge: 18
Registriert: Donnerstag 6. Mai 2021, 13:05

Donnerstag 20. Mai 2021, 08:56

Sirius3 hat geschrieben:
Mittwoch 19. Mai 2021, 14:00
@tlosert: das beantwortet noch nicht die Frage, warum Du im ersten Schritt überhaupt Deinen Bot beenden möchtest.
Ja stimmt, eigentlich ist es egal. Dachte halt wenn ich was starte, kann ich es auch beenden. Aber im Prinzip kann es ja durchlaufen.
Damit bin ich aber auf ein neues Problem gestoßen, beschreibe es gleich mal hier.
tlosert
User
Beiträge: 18
Registriert: Donnerstag 6. Mai 2021, 13:05

Donnerstag 20. Mai 2021, 09:12

Also, folgende Tatsache besteht:
Starte ich das Programm in Thonny, läuft es reibungslos. Die bisher in Telegramm eingegeben Kommandos, etc. werden der Reihe nach abgearbeitet, es sieht wie folgt aus (Text in der Shell):

Code: Alles auswählen

Python 3.7.3 (/usr/bin/python3)
>>> %Run bot.py
Listening ...
>>> 635555555
<telepot.Bot object at 0xb5fffff0>
635555555
<telepot.Bot object at 0xb5fffff0>
635555555
<telepot.Bot object at 0xb5fffff0>
635555555
<telepot.Bot object at 0xb5fffff0>
635555555
<telepot.Bot object at 0xb5fffff0>
Aber, starte ich das Programm im Terminal oder via crontab mit: "

Code: Alles auswählen

pi@raspberrypi:~/thomas/telegram $ /usr/bin/python3 bot.py
" wird mir zwar "Listening ..." angezeigt, was im Programm via

Code: Alles auswählen

print 
ausgegeben wird. Es tut sich aber nichts weiteres, d.h. gebe ich im Bot auf dem Smartphone Befehle ein, passiert nichts. Also läuft das Programm bisher nur, wenn ich es unter Thonny starte. Das verstehe ich nicht.

Starte ich python3 mit sudo, bekomme ich sogar folgenden Fehler angezeigt:

Code: Alles auswählen

pi@raspberrypi:~/thomas/telegram $ sudo python3 bot.py
Traceback (most recent call last):
  File "bot.py", line 4, in <module>
    import telepot
ModuleNotFoundError: No module named 'telepot'
Ist das Modul "telepot" dann ggf. nur für den user pi installiert? k.a.?

Danke für eure Unterstützung. Alles wird irgendwann gut :-)
__deets__
User
Beiträge: 9881
Registriert: Mittwoch 14. Oktober 2015, 14:29

Donnerstag 20. Mai 2021, 09:44

Wie sieht dein Programm *jetzt* aus? Hast du Sirius Anregungen übernommen? Ich vermute mal nicht, und du erlebst jetzt, was du schon immer erleben wolltest: dein Programm beendet sich. Und der Thread im Hintergrund, der sonst von Thonny am Leben gehalten wurde, beendet sich.

Benutz Sirius3’s Grundlagen.
Sirius3
User
Beiträge: 14609
Registriert: Sonntag 21. Oktober 2012, 17:20

Donnerstag 20. Mai 2021, 10:20

Warum startest Du jetzt mit sudo? Wenn Du telebot lokal installiert hast, dann kennt das root nicht.
Antworten