Crash?

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
Odin
User
Beiträge: 20
Registriert: Samstag 5. September 2015, 10:11

Hallo zusammen,
ich versuche aktuelle einen kleinen Bot für telegram zu basteln und verwende folgende API https://python-telegram-bot.org/.
Mein Problem ist das die Ausführung von Funktionen mitten drin ohne Exception oder sonstigen Output abbricht. Auch die Excption-CallBack-Funktion meldet nichts, der Bot an sich läuft aber weiter. Ich poste das trotzdem hier weil es für mich trotzdem nicht nach einem API speziefischen Problem aussieht, sondern ich vermutlich nur mit einer Eigenart von Python nicht vertraut bin.

In folgendem Code tritt das Problem auf:

Code: Alles auswählen

def __handleLightTrigger(bot, update):
	global cmdCallback
	print("cmd")
	print(cmdCallback)
	if update.message.from_user.id is None:
		print("NONE")
	print(update.message.from_user.id) //wird noch ausgegeben
	print(update.message.from_user.id + " | " + type(update.message.from_user.id)) //wird nicht mehr ausgegeben
	print(update.message.text + " | " + type(update.message.text))
	cmdCallback(update.message.from_user.id , update.message.text)
	print("##################")
Ausgabe:

Code: Alles auswählen

cmd
<function fcommandHandler at 0x0........>
XXXXXXXXXXXXX //Meine nicht geschwärzte Telegram ID 
Ich kann den den wert von update.message.from_user.id ausgeben, aber wenn ich versuche den type festzustellen Crashed es?
Bevor ich die type()-Funktion eingebaut habe wurde cmdCallback aufgerufen, dort ein print() ausgeführt und dann ist es beim versuch die Parameter auszugeben gecrashed.
Deshalb habe ich erst ein sichtbarkeits Problem vermutet deepCopy() hat aber auch nicht geholfen.
Sollte diese Frage in diesem Forum schon beantwortet worden sein tut es mir leid, aber ich steh gerade derart auf dem schlauch das ich nicht mal weiß nach was ich suchen sollte.

Gruß
Odin
Sirius3
User
Beiträge: 18332
Registriert: Sonntag 21. Oktober 2012, 17:20

Wenn etwas `crashed`, wäre es gut ,genau den Code zu sehen, und auch die Fehlermeldung mit komplettem Traceback. Sonst kann man schlecht helfen.
Odin
User
Beiträge: 20
Registriert: Samstag 5. September 2015, 10:11

Wie erwähnt gibt es keinerlei Output.

Code: Alles auswählen

import _thread
import copy

import telegram
from telegram.ext import Updater
from telegram.ext import CommandHandler
from telegram.ext import MessageHandler, Filters

__BOT_TOKEN =“XXX"


#bot = telegram.Bot(token=-“”)

def __placeholder():
	pass

cmdCallback = __placeholder
__contactCallback = __placeholder
__updater = None

def __showKeyboard(bot, update):
	print("new Keyboard")
	custom_keyboard = [['Licht']['Options']]
	reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard)

	bot.send_message(chat_id=update.message.chat_id, text="", reply_markup=reply_markup)

def __handleLightTrigger(bot, update):
	global cmdCallback
	print("cmd")
	print(cmdCallback)
	if update.message.from_user.id is None:
		print("NONE")
	print(update.message.from_user.id)
	print(update.message.from_user.id + " | " + type(update.message.from_user.id))
	print(update.message.text + " | " + type(update.message.text))
	cmdCallback(123456789 , "TestText")
	print("##################")

def __handleContactTrigger(bot, update):
	global __contactCallback
	print("contact")
	print(__contactCallback)
	__contactCallback(copy.deepcopy(update.message.from_user.id) ,copy.deepcopy(update.message.contact.user_id))
	print("##################")

def startTelegramBot(pcmdCallback, contactCallback):
	global cmdCallback
	global __contactCallback
	cmdCallback = pcmdCallback
	__contactCallback = contactCallback


	global __updater
	__updater = Updater(__BOT_TOKEN)
	dispatcher = __updater.dispatcher

	#Show custom keyboard command
	keyboard_handler = CommandHandler('lichtschalter', __showKeyboard)
	dispatcher.add_handler(keyboard_handler)

	#Register button press
	lighttrigger_handler = MessageHandler(Filters.text, __handleLightTrigger)
	dispatcher.add_handler(lighttrigger_handler)

	#Register contacts send
	contacttrigger_handler = MessageHandler(Filters.contact, __handleContactTrigger)
	dispatcher.add_handler(contacttrigger_handler)

	__updater.start_polling()
	print("Idle")
	#updater.idle()

def stopTelegramBot():
	__updater.stop()
Das ist das restliche File.

Code: Alles auswählen

from telegramBot import startTelegramBot
from telegramBot import stopTelegramBot

def init():
	startTelegramBot(fcommandHandler, contactHandler)

def fcommandHandler(sender, cmd):
	print("commandHandler")
	if sender is None:
		print("PARAMETER 1 VERLOREN")
	else:
		print("User: "+sender)
	if cmd is None:
		print("PARAMETER 2 VERLOREN")
	else:
		print("CMD: "+cmd)
	print("#######################################")

def contactHandler(sender, userid):
	pritn("contactHandler")
	print(send)
	pritn(userid)

Code: Alles auswählen

from commandHandler2 import init

init()

input("Ende ...")
Das ist der restliche Code.
Benutzeravatar
__blackjack__
User
Beiträge: 14250
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Odin: Werd mal diese ganzen führenden doppelten Unterstriche los. Das sollte jeweils nur einer sein, nicht zwei. Wenn überhaupt, man könnte die auch ganz weglassen.

`_thread` wird importiert, aber nicht verwendet. Was gut ist, denn der führende Unterstrich sagt ja, dass das kein Modul ist das man verwenden soll, weil es nicht zur öffentlichen API gehört.

Als nächstes solltest Du ``global`` loswerden. Globale Variablen machen Programme sehr unübersichtlich und Python hat Klassen, so dass man die auch gar nicht braucht.

`__placeholder()` macht keinen Sinn. An den Stellen wo diese Funktion aufgerufen würde, wenn Du sie nicht vorher ersetzt, fällt der Aufruf auf die Nase weil Du Argumente übergibst, die diese Funktion gar nicht erwartet. Da kannst Du auch einfach mit `None` initialisieren, das hat letztlich einen gleichwertigen Effekt. Auch das wird zu einer Ausnahme führen, der Code ist aber simpler.

Wobei die Variablen gar nicht uninitialisiert sein sollten. Das passiert ja auch nur weil es globale Variablen sind, und keine Attribute die in einer `__init__()`-Methode garantiert vor der Benutzung sinnvoll belegt werden.

Für das Importiern aus dem `telegram.ext`-Modul würde ich nur eine ``import``-Anweisung verwenden.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

`custom_keyboard` ist ein Laufzeitfehler:

Code: Alles auswählen

In [14]: [['Licht']['Options']]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-14-b99d9c166308> in <module>()
----> 1 [['Licht']['Options']]

TypeError: list indices must be integers or slices, not str
Etwas aufgeräumter sähe der Bot dann so aus:

Code: Alles auswählen

#!/usr/bin/env python3
from copy import deepcopy

from telegram import ReplyKeyboardMarkup
from telegram.ext import CommandHandler, Filters, MessageHandler, Updater


class Bot:
    
    TOKEN = 'XXXXXX-I'
    
    def __init__(self, command_callback, contact_callback):
        self.command_callback = command_callback
        self.contact_callback = contact_callback
        self.updater = Updater(self.TOKEN)
        
        dispatcher = self.updater.dispatcher
        dispatcher.add_handler(
            CommandHandler('lichtschalter', self.show_keyboard)
        )
        dispatcher.add_handler(
            MessageHandler(Filters.text, self.handle_light_trigger)
        )
        dispatcher.add_handler(
            MessageHandler(Filters.contact, self.handle_contact_trigger)
        )

    def start(self):
        self.updater.start_polling()
        print('Idle')
    
    def stop(self):
        self.updater.stop()
    
    @staticmethod
    def show_keyboard(bot, update):
        print('new Keyboard')
        bot.send_message(
            chat_id=update.message.chat_id,
            text='',
            # 
            # FIXME Trying to index a list with a string doesn't work.
            # 
            reply_markup=ReplyKeyboardMarkup([['Licht']['Options']]),
        )

    def handle_light_trigger(self, _bot, update):
        print('cmd')
        print(self.command_callback)
        message = update.message
        if message.from_user.id is None:
            print('NONE')
        print(message.from_user.id)
        print(message.from_user.id, '|', type(message.from_user.id))
        print(message.text, '|', type(message.text))
        self.command_callback(123456789, 'TestText')
        print('##################')

    def handle_contact_trigger(self, _bot, update):
        print('contact')
        print(self.contact_callback)
        message = update.message
        self.contact_callback(
            deepcopy(message.from_user.id), deepcopy(message.contact.user_id)
        )
        print('##################')
Und ich sehe da keinen Grund warum das ausgerechnet an der von Dir im ersten Beitrag angegebenen Stelle aufhören sollte etwas auszugeben.

Bei den Importen fällt übrigens auf, dass das entweder keine absolut angegebenen Importe sind, oder das Du mehrere Module hast die nicht in einem Package stecken. Das sollte so nicht sein. Wenn man ein Programm oder eine Bibliothek hat, die aus mehr als einem Modul besteht, sollte man die Gefahr von Namenskollisionen möglichst klein halten, in dem man sie in einem Package bündelt, so das nur dieser Packagename auf oberster Ebene mit allen anderen Bibliotheken auf dem System konkurriert.

Und es ist in Python nicht üblich eine Klasse pro Modul zu haben. Also ist ein eigenes Modul für die `Bot`-Klasse eventuell keine gute Aufteilung.
“All tribal myths are true, for a given value of 'true'.” — Terry Pratchett, The Last Continent
Odin
User
Beiträge: 20
Registriert: Samstag 5. September 2015, 10:11

Und ich sehe da keinen Grund warum das ausgerechnet an der von Dir im ersten Beitrag angegebenen Stelle aufhören sollte etwas auszugeben.
Ich auch nicht, deshalb Poste ich hier.
Werd mal diese ganzen führenden doppelten Unterstriche los. Das sollte jeweils nur einer sein, nicht zwei. Wenn überhaupt, man könnte die auch ganz weglassen.
Dachte doppelte Unterstriche stehen für private Variablen/Funktionen.
`_thread` wird importiert, aber nicht verwendet. Was gut ist, denn der führende Unterstrich sagt ja, dass das kein Modul ist das man verwenden soll, weil es nicht zur öffentlichen API gehört.
Du hast recht, vor allem da ich nicht mehr weiß wozu ich das gebraucht habe.
Als nächstes solltest Du ``global`` loswerden. Globale Variablen machen Programme sehr unübersichtlich und Python hat Klassen, so dass man die auch gar nicht braucht.
Ich weiß, das war mal eine Klasse, da ich Sichtbarkeit als Ursache verdächtigt habe ist das dem Debuggen zum opfer gefallen.
`__placeholder()` macht keinen Sinn. An den Stellen wo diese Funktion aufgerufen würde, wenn Du sie nicht vorher ersetzt, fällt der Aufruf auf die Nase weil Du Argumente übergibst, die diese Funktion gar nicht erwartet. Da kannst Du auch einfach mit `None` initialisieren, das hat letztlich einen gleichwertigen Effekt. Auch das wird zu einer Ausnahme führen, der Code ist aber simpler.
Ich inizialisiere es mit __placeholder() damit ich gleich sehe das es ein Funktionszeiger ist.
Für das Importiern aus dem `telegram.ext`-Modul würde ich nur eine ``import``-Anweisung verwenden.
Warum?
Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).
Da hast du recht, ich nutze aber für alle Sprachen die gleiche Konvention und Kamel-Case finde ich nun mal am angenehmsten.
custom_keyboard` ist ein Laufzeitfehler:
Danke.

Edit: Danke dein Code funktioniert, ich wüste aber gern was das Problem an meinem ist, sonst habe ich wenn es wieder auftritt das gleiche Problem nochmal. Den einzigen unterschied den ich sehe ist das du "message" rauskopierst, nach meinem Verständnis dürfte dies aber nichts ändern.
Benutzeravatar
__blackjack__
User
Beiträge: 14250
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Odin: Interna werden mit *einem* führenden Unterstrich gekennzeichnet. Wie zum Beispiel beim `_thread`-Modul. „private“ im Sinne von Zugriffschutz gibt es in Python nicht.

Es gibt keine Zeiger in Python, nur Objekte. Ich sehe da keinen Sinn drin eine Funktion zu definieren die vom Code niemals Fehlerfrei aufgerufen werden kann. Und das es sich bei etwas das `*_callback` heisst, um ein aufrufbares Objekt handelt, erkennt man schon am Namen. Wenn man da aus Dokumentationsgründen etwas aufrufbares dran binden möchte, sollte das auch tatsächlich funktionieren und passende Namen für die Argumente haben. Da reicht dann auch ein ``lambda``-Ausdruck. Aber wie gesagt, da etwas dran binden was nie genutzt wird, ist sowieso keine gute Idee. Und wenn man es dokumentieren will: Man kann das auch tatsächlich dokumentieren.

Ich würde nur einen Import machen, weil das aufteilen auf drei völlig willkürlich ist. Gegenfrage wäre: warum nicht?

Was die Namensschreibweise angeht: Es ist egal was Du am angenehmsten findest, es gibt halt die Konvention. Wenn Du damit brichst ist das für alle anderen ”unangenehm”. Zum Beispiel spuckt das in den meisten Python-IDEs/Editoren dann erst einmal Unmengen von unnötigen Annotationen/Warnungen aus die man aktiv ignorieren muss und über die man vielleicht wichtige Probleme dann übersieht, weil die darin untergehen.

Versuch mal bei einem Java-Projekt oder auch einfach nur in einem Java-Forum `klein_mit_unterstrichen` zu verwenden, mit der Begründung das wäre für Dich aber angenehmer. ;-)

Wenn mein Code funktioniert, dann hätte Deiner auch funktionieren müssen, da ich da in Bezug auf die Codestelle ja nichts geändert habe.
“All tribal myths are true, for a given value of 'true'.” — Terry Pratchett, The Last Continent
Odin
User
Beiträge: 20
Registriert: Samstag 5. September 2015, 10:11

Das der Unterstrich nur eine Kennzeichnung ist war mir bewusst, hatte mich nur in der Anzahl vertan.
Da alles in Python Objekte sind, sind alle Variablen Zeiger. Unabhängig davon wie es die Sprache nennt und ob es Zeigerarithmetik gibt ober nicht.
warum nicht?
ich spar mir den Modul-Name. In kleinst Projekten angenehmer.
Sprachen speziefische Konventionen sind schwachsinn. Die Konventionen muss nur die an einem Projekt beteiligten zufrienden stellen. Ich habe diese Disskussion auch schon zu anderen Sprachen geführt :wink:
Wenn mein Code funktioniert, dann hätte Deiner auch funktionieren müssen, da ich da in Bezug auf die Codestelle ja nichts geändert habe.
Theorie und parxis.
Sirius3
User
Beiträge: 18332
Registriert: Sonntag 21. Oktober 2012, 17:20

Und hier sind die "Projektbeteiligten" die Leute, die Du bittest, Dir zu helfen. Die würde ich also an Deiner Stelle nicht vergraulen.
Odin
User
Beiträge: 20
Registriert: Samstag 5. September 2015, 10:11

Wie viel Prozent meines Codes landet im Internet? Deutlich unter 1%.
Und entschuldige das ich etwas gereizt bin wenn ich um Hilfe frage und Leute statt sich mit meinem Problem zu beschäftigen auf meinem Code rumhacken, der vom Debuggen entstellt ist und in einer Sprache geschrieben ist die ich seit Jahren nicht mehr genutzt habe.
Benutzeravatar
sparrow
User
Beiträge: 4597
Registriert: Freitag 17. April 2009, 10:28

Dann würde ich sagen, solltest du eine Sprache nehmen, die du zu benutzen weißt - oder die an die Konventionen der Sprache halten, die du benutzt.
Versucht man Vorgehen einfach von einer Sprache auf eine andere zu projizieren, wirkt das nicht nur falsch, sondern kann auch Seiteneffekte haben, die man nicht kennt.
Etwas weniger Hybris wirkt da manchmal Wunder.
Benutzeravatar
__blackjack__
User
Beiträge: 14250
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Odin: Es gibt in Python keine Zeiger. Wenn man so etwas behauptet, erwarten die Leute die Zeiger kennen, das man Sachen damit machen kann, die in Python einfach nicht möglich sind. Und das ist nicht nur Zeigerarithmetik, sondern das man tatsächlich mal einen solchen Zeiger als Wert in die Finger bekommt und auf ein anderes Objekt zeigen lassen kann. Wenn eine Programmiersprache Zeiger als Datentyp hat, dann gehören da die Möglichkeit dazu einen Zeiger sowohl als Wert in die Finger zu bekommen, als auch selbst entscheiden zu können wann der dereferenziert wird. Das eine Implementierung einer Programmiersprache unter der Haube Zeiger verwendet, heisst nicht, das Zeiger ein Datentyp in dieser Programmiersprache ist.

Wo sparst Du welchen Modulnamen?

Sprachenspezifische Konventionen sind kein Schwachsinn. Konventionen haben einen Zweck – das die Kommunikation/das lesen/verstehen einfacher ist, weil über die Konvention Informationen übermittelt werden, und das es weniger Variationen gibt, über die man sich Gedanken machen muss. Zum Beispiel das man sich nur den Namen und nicht die Schreibweise merken muss bei verwendeten Bibliotheken. Das fiele nur bei minimalen Sprachen ohne grosse Standardbibliothek, bei Projekten die man komplett isoliert von irgendwelchem anderen Code schreibt.

Konzentriere Dich mal auf das gereizte Gefühl was Du wegen der Hinweise auf die Konventionen hast – dann weisst Du wie angepisst andere sind wenn Du dagegen verstösst. Und wenn Du jetzt sagst „wie kann man wegen so einer Nebensächlichkeit angepisst sein?“ – wieso kann man sich, wenn's so nebensächlich ist, nicht einfach an die Konventionen halten? 😉
“All tribal myths are true, for a given value of 'true'.” — Terry Pratchett, The Last Continent
Benutzeravatar
kbr
User
Beiträge: 1510
Registriert: Mittwoch 15. Oktober 2008, 09:27

Odin hat geschrieben: Freitag 8. März 2019, 15:34Da alles in Python Objekte sind, sind alle Variablen Zeiger. Unabhängig davon wie es die Sprache nennt und ob es Zeigerarithmetik gibt ober nicht.
Es stimmt, dass in Python Labels Referenzen sind. Aber den Datentyp des Zeigers gibt es nicht.
Odin
User
Beiträge: 20
Registriert: Samstag 5. September 2015, 10:11

Ja, die gravierenden Auswirkung von Namenskonventionen auf den Datenfluss ist nicht zu unterschätzen.
Und es zeugt von unglaublicher Bodenhaftung die 5 Zeilen Code dermaßen auseinander zu nehmen ohne auf das Problem einzugehen.
Es mag sein das ich da etwas gereizt regiert habe habe den Hybris-Schuh könnt ihr euch alle mit anziehe.

@__blackjack__
Nur weil Leute die Aussage in den Falschen Hals bekommen ändert das nichts an der Aussage.
Wo sparst Du welchen Modulnamen?
jetzt bin ich mir nicht mehr sicher ob ich mich nicht wieder vertue aber gilt nicht folgende:

Code: Alles auswählen

import X
X.y()

Code: Alles auswählen

import y from X
y()
Das Argument mit den Standardbibliothek ist gut, trotzdem bin ich der Meinung dass die Vorlieben von Projekt beteiligten überwiegen.
Konzentriere Dich mal auf das gereizte Gefühl was Du wegen der Hinweise auf die Konventionen hast
Da ist nichts. Was mich nerft ist ein zwei Seitenaufsatz an dessen Ende Sinngemäß steht "Back to Topic: keine Ahnung"
EDIT: Ich hätte kein Problem damit gehabt wenn es ein "Vielleicht ist das Problem .... Und übrigens zum Style ..." oder ähnliches gewesen wäre.

@kbr
Korrekt.
Benutzeravatar
sparrow
User
Beiträge: 4597
Registriert: Freitag 17. April 2009, 10:28

@Odin: Die Antwort war nicht "keine Ahnung" sondern dass es keinen Grund gibt, aus dem der Code dort stecken bleiben sollte. Das ist ein großer Unterschied. In der Regel deutet das darauf hin, dass du nicht nur fürchterlich unübersichtlichen sondern auch nicht benutzen Code hier gezeigt hat. Ich nehme an, der ist beim Debuggen vielleicht auch irgendwie geändert worden, weil das vorher eine Klasse war. Aber was weiß ich schon ;)
Odin
User
Beiträge: 20
Registriert: Samstag 5. September 2015, 10:11

Das "cmd" in der Ausgabe weißt klar darauf hin das der Code ausgeführt wird, aber wer weiß vielleicht hab ich das auch halluziniert :wink:
Benutzeravatar
__blackjack__
User
Beiträge: 14250
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Odin: Ich glaube bei den Importen reden wir aneinander vorbei. Es geht nicht im ``import …`` vs. ``from … import …`` sondern Du hattest:

Code: Alles auswählen

from telegram.ext import Updater
from telegram.ext import CommandHandler
from telegram.ext import MessageHandler, Filters
Also drei Anweisungen wo eine ausreicht:

Code: Alles auswählen

from telegram.ext import CommandHandler, Filters, MessageHandler, Updater
Dadurch hat sich an der Anzahl der Namen im Modunamensraum ja nichts geändert.

Ich habe aber mittlerweile das eigentlich doch recht offensichtliche Problem entdeckt. Du solltest mal schauen wo Ausnahmen landen, denn die Zeile die zu keiner Ausgabe führt enthält einen `TypeError`. Da muss es doch irgendwo eine Logdatei für den Bot geben oder so, denn sonst kann man echt schwer Fehler finden. Wenn Du Dich mehr an die Konventionen halten würdest, hätte ich das sicher früher entdeckt. 😉
“All tribal myths are true, for a given value of 'true'.” — Terry Pratchett, The Last Continent
Odin
User
Beiträge: 20
Registriert: Samstag 5. September 2015, 10:11

Ok, so sieht der Import besser aus.
Und danke für die Lösung. Wobei mich irritiert das ich trotz des Wissens das es eine Exception gibt den Ausgabekanal nicht finden kann. Da werde ich mal an anderer Stelle nachfragen müssen.

EDIT: MIr ist gerade aufgefallen das ich übersehen habe das der Bot Token noch im Code ist, wäre ein Admin so nett und würde die in meinem Zweiten Post und der dazugehörigen Antwort löschen?
Antworten