Tornado Klasse importieren und starten

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
tenor
User
Beiträge: 24
Registriert: Samstag 2. Januar 2016, 19:32

Hallo,
mittlerweile bin ich mit meinem alten Ansatz den Raspi über Websocket zu steuern weiter gekommen, in dem ich den Websocket in einem extra Thread ausgelagert hatte.

Mein Vorhaben ist jetzt die Websocket Klasse aus meinem Main Skript zu importieren und von dort zu starten.
Da ich nicht der OOP Meister bin, hoffe ich auf ein paar konstruktive Ratschläge :)

In der Datei svtor.py habe ich die Klasse WSHandler erstellt und von der Tornado Websocket Klasse geerbt.
In dieser Klasse wollte ich den Startvorgang des Websockets in eine Funktion packen.
(Bei der letzten Version habe ich noch eine weitere Klasse erstellt und die starten() Funktion dort rein gepackt, diesen Schritt kann man ggf. wieder rückgängig machen, wenn ich erstmal meinen Fehler erkannt habe).

Code: Alles auswählen

import tornado.ioloop
import tornado.web
import tornado.httpserver
import tornado.websocket
import time
import os

class WSHandler(tornado.websocket.WebSocketHandler):

  def init(self, application, request, **kwargs):
      tornado.websocket.WebSocketHandler.__init__(self, application, request, *$
      WSHandler.__init__(self, **kwargs)

  def check_origin(self, origin):
    return True

  def open(self):
    print 'New connection was opened'
    self.write_message("Welcome to my websocket!")

  def on_message(self, message):
    print 'Incoming message:', message
    self.write_message("You said: " + message)

  def on_close(self):
    print 'Connection was closed...'

class Websocket(WSHandler):

  def starten(self):
    self.http_server = tornado.httpserver.HTTPServer(application)
    self.http_server.listen(8888)
    self.tornado.ioloop.IOLoop.instance().start()

application = tornado.web.Application([
    (r'/ws', Websocket),
])
Mein Hauptskript sieht wie folgt aus:

Code: Alles auswählen

import RPi.GPIO as GPIO
import time
import os
import svtor

PIN_OUT_1 = 29
PIN_OUT_2 = 31
PIN_RECHTS = 28
PIN_LINKS = 30

GPIO.setmode(GPIO.BCM)
GPIO.setup(PIN_OUT_1, GPIO.OUT)
GPIO.setup(PIN_OUT_2, GPIO.OUT)
GPIO.setup(PIN_RECHTS, GPIO.OUT)
GPIO.setup(PIN_LINKS, GPIO.OUT)

svt = svtor.Websocket()
svt.starten()

while True:
        Eingabe = raw_input("fahren oder halten: ")

        if Eingabe == "fahren":
            GPIO.output(PIN_OUT_2, False)
            Servo = GPIO.PWM(PIN_OUT_1, 50)
            Servo.start(50)
           # time.sleep(5)

        if Eingabe == "halten":
            Servo.stop()

        elif(Eingabe == "q"):
                print "Programm wird beendet......"
                os._exit(1)
                Servo.stop()
                GPIO.cleanup()

        # Ungueltige Eingabe
        else:
                 print "Ungueltige Eingabe!"
Wahrscheinlich würde der Websocket auch hier die weitere Eingabe verhindern, den kann ich dann aber in einen extra Thread packen.

Ich komme leider nicht so weit:
Traceback (most recent call last):
File "first.py", line 17, in <module>
svt = svtor.Websocket()
TypeError: __init__() takes exactly 3 arguments (1 given)

Ich verstehe die Aussage mit dem __init__() nicht.
Die Klasse, wenn ich sie direkt aus der svtor.py aufrufe, braucht ja auch keine weiteren Argumente.

Wäre Super wenn mir einer einen Tipp geben könnte!
Viele Grüße
Tenor
Zuletzt geändert von Anonymous am Samstag 16. Januar 2016, 12:38, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@tenor: Konstruktiver Ratschlag: Werde OOP-Meister. :-)

`Websocket` ist ein Untertyp von `WSHandler` ohne eigene `__init__()`, also wird die von `WSHandler` verwendet und die erwartet mindestens die Argumente `application` und `request` die Du nicht übergibst. Genau das führt zu der Fehlermeldung. Die `__init__()` erwartet drei Argumente, das `self` wird automatisch übergeben, fehlen also zwei.

Die Klasse `Websocket` macht auch überhaupt keinen Sinn. Du bindest `http_server` an das Objekt, das wird aber nirgends verwendet. Und ich glaube nicht das es das `tornado`-Attribut auf dem Objekt gibt, auf das Du in der letzten Zeile versuchst zuzugreifen. Diese Klasse sieht irgendwie sehr nach Code-raten aus. So funktioniert das nicht. Wenn man ein objektorientiertes Rahmenwerk verwendet, sollte man OOP drauf haben. Möglichst vorher.
tenor
User
Beiträge: 24
Registriert: Samstag 2. Januar 2016, 19:32

@tenor: Konstruktiver Ratschlag: Werde OOP-Meister. :-)
Das habe ich zumindest vor :)
Habe mir das Buch noch einmal raus gekramt und bin die Beispiele durchgegangen. Mit den Beispielen klappt das auch, nur sieht die Verknüpfung mit vorhandenen Klassen wieder ganz anders aus.. Zumindest für mich.

Code: Alles auswählen

`Websocket` ist ein Untertyp von `WSHandler` ohne eigene `__init__()`, also wird die von `WSHandler` verwendet und die erwartet mindestens die Argumente `application` und `request` die Du nicht übergibst. Genau das führt zu der Fehlermeldung. Die `__init__()` erwartet drei Argumente, das `self` wird automatisch übergeben, fehlen also zwei.
Websocket sollte eine neue Klasse sein, die von WSHandler erbt und somit den Konstruktor auch übernimmt.
Ich verstehe hierbei nicht, das ich weitere Werte übergeben muss, da wenn ich die svtor.py direkt starte, benötige ich sie ja auch nicht.
Was muss ich denn für das application und request Argument übergeben? Ich sehe keine Parameter die ich mit geben muss.
BlackJack

@tenor: Du siehst in der `WSHandler.__init__()` nicht das da die Argumente `application` und `request` übergeben werden müssen‽ *Du* willst doch sowieso kein Exemplar von dieser Klasse erstellen, das macht Tornado für Dich. Das erstellt jedes mal wenn ein Websocket geöffnet wird ein Exemplar um die Verbindung zu bedienen.

Wie gesagt: Aus OOP-Sicht macht Deine Klasse überhaupt keinen Sinn.
tenor
User
Beiträge: 24
Registriert: Samstag 2. Januar 2016, 19:32

Diese Klasse war nur ein Test, ich habe sie bereits wieder entfernt und nutze die WSHandler wieder.

Wenn ich jetzt:

Code: Alles auswählen

svt = svtor.WSHandler(r'/ws', WSHandler)
svt.starten()

Kann er mit WSHandler nichts anfangen.

File "first.py", line 17, in <module>
svt = svtor.WSHandler(r'/ws', WSHandler)
NameError: name 'WSHandler' is not defined

Code: Alles auswählen

import tornado.ioloop
import tornado.web
import tornado.httpserver
import tornado.websocket
import time
import os
 
class WSHandler(tornado.websocket.WebSocketHandler):

  def check_origin(self, origin):
    return True

  def open(self):
    print 'New connection was opened'
    self.write_message("Welcome to my websocket!")
 
  def on_message(self, message):
    print 'Incoming message:', message
    self.write_message("You said: " + message)

  def on_close(self):
    print 'Connection was closed...'
 
  def starten(self):
    self.http_server = tornado.httpserver.HTTPServer(application)
    self.http_server.listen(8888)
    self.tornado.ioloop.IOLoop.instance().start()
    application = tornado.web.Application([
        (r'/ws', WSHandler),
    ])
Da das Thema für dich offensichtlich sehr leicht ist, würde ich dich bitten mir die Lösung zu verraten, dann kann ich nachvollziehen wie das gemeint ist.
Zuletzt geändert von Anonymous am Samstag 16. Januar 2016, 17:36, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@tenor: Das Thema ist nicht sehr leicht, die gemeldeten Fehler sind nur jeweils total offensichtlich wenn man die Grundlagen kann. Ob die Grundlagen leicht sind, kommt halt darauf an wie leicht einem OOP fällt. Auf jeden Fall ist das komplex genug um es nicht einfach so durch ein paar Beispiele ohne längere Erklärungen selber zu sehen. Was Du da mit den Klassen und der `starten()`-Methode veranstaltest ist einfach total schräg so dass ich auch nicht einfach sagen könnte was Du anders machen musst, weil ich nicht verstehe was Du damit erreichen willst.

Der Fehler ist diesmal wieder offensichtlich weil Du `svtor.WSHandler` ein Argument `WSHandler` übergeben willst was nicht existiert. Es gibt nur `svtor.WSHandler`. Du willst aber sicher nicht diese Klasse selber beim erstellen eines Exemplars übergeben und vor allem willst *Du* kein Exemplar von `WSHandler` erstellen. Das ist nicht Deine Aufgabe. Dafür ist dieser Datentyp nicht vorgesehen. LASS DAS SEIN!
tenor
User
Beiträge: 24
Registriert: Samstag 2. Januar 2016, 19:32

So ziemlich jedes Beispiel das ich zum Thema Tornado finde, sieht ähnlich diesem hier aus http://lowpowerlab.com/blog/2013/01/17/ ... n-tornado/

Mein Vorhaben ist nun, da ich nicht Chatten möchte, sonder mit dem PI weiter arbeiten, den Tornado Websocket von einem Hauptskript aus zu starten.
Vom Hauptskript aus, soll der Handler in einem eigenen Thread weiter laufen um die Hauptschleife nicht zu stören.

starten() soll einfach den Websocket Server starten, inkl. der MainLoop, da im ursprünglichem Skript der Server direkt gestartet wird wenn ich die python Datei ausführe.
In dem Fall muss ich doch das starten des Servers als Funktion definieren, oder nicht?
BlackJack

@tenor: Ja die drei Zeilen könnte man einfach in eine Funktion stecken. Dann mach das doch und hör auf irgendwelche Klassen zu schreiben oder `WSHandler`-Exemplare selber erstellen zu wollen. Das hätte sowieso in einer Funktion stehen sollen die dann im ``if __name__ …``-Block aufgerufen wird, statt `http_server` in den Modulnamensraum zu packen.
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

@tenor: warum muß starten eine Methode des WebSocket-Handlers sein? DAS macht doch überhaupt keinen Sinn. Für das Starten braucht man weder eine Methode noch eine eigene Klasse sondern nur eine Funktion.
tenor
User
Beiträge: 24
Registriert: Samstag 2. Januar 2016, 19:32

ok, ich habe in der first.py

Code: Alles auswählen

import svtor    #damit stehen die Funktionen im Namensraum zur Verfügung

svtor.starten() # Starten des Tornado Websocket Servers
Dann bekomme ich:

File "first.py", line 22, in <module>
svtor.starten()
AttributeError: 'module' object has no attribute 'starten'

Mein Gedanke beim letzten mal war, das ich nicht einfach die Funktion hinzufügen könnte.
Daher dachte ich die Klasse ableiten und erweitern. (Vererbung).
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

@tenor: wie kommst Du zu Deinen Gedanken? Wie man eine Funktion definiert, sollte Dir eigentlich klar sein.
tenor
User
Beiträge: 24
Registriert: Samstag 2. Januar 2016, 19:32

Ich habe das natürlich nachgeschlagen :)
http://www.python-kurs.eu/modularisierung.php
http://www.python-kurs.eu/funktionen.php

Wie in dem Beispiel mit der Mathe Bibliothek. Ich habe die Komplette Datei, nicht nur einzelne Funktionen importiert.
dann wie im Beipsiel : math.sin(x) ausgeführt.

Die Funktion habe ich genau so wie die anderen erstellt und anhand des anderen Links noch einmal überprüft.
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

@tenor: laut Fehlermeldung ist die Funktion nicht definiert. Und wie überprüft man eine Funktion per Link?
tenor
User
Beiträge: 24
Registriert: Samstag 2. Januar 2016, 19:32

mit

Code: Alles auswählen

dir(svtor)
Die Klasse WSHandler wird dann auch angezeigt.

Zum testen habe ich die Funktion starten einmal entfernt und den Code am Ende belassen.
Direkt nach dem Import wird der "frei" stehende Code ja ausgeführt und dann funktioniert dies auch.
Ich kann mich dann mit dem Server via Websocket verbinden.
Ich muss ihn jedoch in einen extra Thread packen, dazu muss ich jedoch die funktion starten verwenden.

Wenn ich den Code zum aufrufen der Funktion abänder, so dass die Klasse die per dir Befehl erkannt vor der Funktion noch schreibe, dann findet er sie, dann habe ich wieder das Problem das nichts übergeben wird.
svtor.WSHandler.starten()
TypeError: unbound method starten() must be called with WSHandler instance as first argument (got nothing instead)

Wenn ich den Aufruf dann abänder:
svtor.WSHandler.starten(r'/ws', WSHandler)

Dann findet er den WSHandler wieder nicht.
tenor
User
Beiträge: 24
Registriert: Samstag 2. Januar 2016, 19:32

from svtor import WSHandler

x = WSHandler(r'/ws', WSHandler)
WSHandler.starten(x)

Ich habe den Aufruf entsprechend geändert, bekomme aber den folgenden Fehler:

Traceback (most recent call last):
File "first.py", line 28, in <module>
x = WSHandler(r'/ws', WSHandler)
File "/usr/local/lib/python2.7/dist-packages/tornado/websocket.py", line 132, in __init__
**kwargs)
File "/usr/local/lib/python2.7/dist-packages/tornado/web.py", line 172, in __init__
application.ui_methods.items())
AttributeError: 'str' object has no attribute 'ui_methods'
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

@tenor: zu Deinem Problem ist hier alles schon gesagt. Wenn zum Erzeugen einer Instanz einer Klasse Parameter erwartet werden, die Du nicht hast, könnte es ja sein, dass es gar nicht vorgesehen ist, das DU eine Instanz erzeugst. Denk mal über das Wort »Handler« nach.
tenor
User
Beiträge: 24
Registriert: Samstag 2. Januar 2016, 19:32

Ich denke nicht das zu dem Problem alles gesagt wurde. Sonst würde es ja laufen.
So weit wie jetzt war ich auch vorher noch gar nicht. (auch wenn du jetzt keinen Fortschritt erkennen kannst)

Ich denke jede Klasse kann abgeleitet und erweitert werden. So lese ich es zumindest in allen Tutorials und Büchern die ich habe.
Warum dann nicht bei dieser?

Beim Import der gesamten Datei, ohne starten() Funktion, tut er letzten Endes ja auch nicht mehr.

Wie ich BlackJack verstehe, sollte es auch möglich sein, dies in eine extra Funktion zu packen:
@tenor: Ja die drei Zeilen könnte man einfach in eine Funktion stecken.
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

@tenor: jede Klasse kann abgeleitet werden, nur oft macht es keinen Sinn, eine Klasse abzuleiten. Daher beantworte einfach mal die Fragen:
- Was macht ein Handler?
- Warum leitest Du Tornados WebSocketHandler ab?
- Was sind Methoden?
- Trifft die Definition von Methode auch auf »starten« zu?
tenor
User
Beiträge: 24
Registriert: Samstag 2. Januar 2016, 19:32

- Was macht ein Handler?
Ich sehe ihn als eine Art Service der Daten entgegen nimmt und ggf. zurück sendet.

- Warum leitest Du Tornados WebSocketHandler ab?
Du meinst im Aufruf aus der first.py?
Weil ich die Klasse erst instanzieren muss, bevor ich die Funktionen nutzen kann.

- Was sind Methoden?
Methoden sind an die Klasse gebunden und sind für Aktionen des Objekts verantwortlich.
Diese geben ggf. einen Wert zurück.
(Aus dem Kurs: Sie ist eine Funktion, die innerhalb einer class-Definition definiert ist)

- Trifft die Definition von Methode auch auf »starten« zu?
Aus meiner Sicht ja. Abgesehen davon das sie nicht beendet wird, da in ihr eine Dauerschleife läuft.
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

@tenor:

- Was macht ein Handler?
Die Abgrenzung zu einem Server, der ja auch Daten entgegennimmt, ist, dass ein Handler für genau eine Verbindung zuständig ist, also eine Anfrage bearbeitet und von einem Server, der ja viele Verbindungen verwaltet, für diesen Zweck erzeugt wird.

- Warum leitest Du Tornados WebSocketHandler ab?
Ableiten hat nichts mit Instanzieren zu tun. Wenn man ableitet, will man die Funktionalität einer Klasse erweitern. Hier: Du willst die allgemeine WebSocketHandler-Funktionalität so erweitern, dass Dein WebSocket-Handler konkrete Aufgaben erledigen kann.

- Was sind Methoden?
Methoden verändern oder fragen den internen Zustand eines Objekts ab.

- Trifft die Definition von Methode auch auf »starten« zu?
Nein. Der interne Zustand des Objekts vom Typ WSHandler ist »starten« völlig egal.

Was bedeutet dies also für »starten«?
Antworten