SocketServer und epoll

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
der_Angler
User
Beiträge: 25
Registriert: Montag 28. Januar 2013, 00:48

Nabend,

ich bin ein kompletter Python-Neuling und komme zu euch weil ich ein wenig mit meinem Raspberry Pi rumspielen möchte.

Jetzt habe ich ein Problem damit in Python einen SocketServer ans laufen zu bekommen, der per epoll auf Daten wartet.
Aber am besten ich erkläre es an Beispielen.

Folgendes Skript habe ich geschrieben:

Code: Alles auswählen

import socket
from quick2wire.gpio import GPIOPin, In, Out, Pin, exported
import select

pin_bed = 0
pin_bed_switch = 4

pin_nightlight = 3

#light_bed = GPIOPin(pin_bed, direction=Out)
#light_bed_switch = GPIOPin(pin_bed_switch, Pin.In, Pin.Rising)

#light_nightlight = GPIOPin(pin_nightlight, direction=Out)

HOST = ''
PORT = 54321
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()

with exported(GPIOPin(pin_bed, direction=Out)) as light_bed, exported(GPIOPin(pin_bed_switch, Pin.In, Pin.Rising)) as light_bed_switch, exported(GPIOPin(pin$

    epoll = select.epoll()
    epoll.register(light_bed_switch, select.EPOLLIN | select.EPOLLET)
    epoll.register(conn, select.EPOLLIN | select.EPOLLET)

    while True:
        events = epoll.poll()
        for fileno, event in events:

            if fileno == light_bed_switch.fileno():
                if light_bed.value:
                    light_bed.value = 0
                else:
                    light_bed.value = 1

            if fileno == conn.fileno():
                data = conn.recv(1024)
                if data == 'Lampe Bett on\n':
                    conn.send('on')
                    light_bed.value = 1
                elif data == 'Lampe Bett off\n':
                    conn.send('off')
                    light_bed.value = 0
                elif data == 'Lampe Nachtlicht on\n':
                    conn.send('off')
                    light_nightlight.value = 1
                elif data == 'Lampe Nachtlicht off\n':
                    conn.send('off')
                    light_nightlight.value = 0
                elif data == 'get status_lampe_bett\n':
                    if light_bed.value:
                        conn.send('on')
                    else:
                        conn.send('off')
                elif data == 'get status_lampe_nachtlicht\n':
                    if light_nightlight.value:
                        conn.send('on')
                    else:
                        conn.send('off')
                else:
                    conn.send('unknown command')

conn.close()
Das funktioniert grundsätzlich auch wie vorgesehen, hat aber zwei gravierende Nachteile:
1. Die Hardware-Tasten funktionieren nur wenn das Script mit der AndroidApp (oder sonst einer Gegenstelle) eine Verbindung hergestellt hat.
2. Sowie ich die App schliesse (oder clientseitig die Verbindung ändere) beendet sich auch das Script.

Deshalb die Lösung -> ein SocketServer muss her.
Dazu habe ich auch schon etwas gefunden

Code: Alles auswählen

import SocketServer
class MyTCPHandler(SocketServer.BaseRequestHandler):

    def handle(self):
        while 1:
            self.data = self.request.recv(1024).strip()
            if not self.data: break

            print "{} wrote:".format(self.client_address[0])
            print self.data
            self.request.sendall('100')

if __name__ == "__main__":
    HOST, PORT = "", 54321
    server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
    server.serve_forever()
Damit hat zumindest die Kommunikation mit der App geklappt.
Also habe ich mich dran gesetzt und versucht beide Scripts zu verbinden.
Dabei ist dann folgendes herausgekommen

Code: Alles auswählen

from quick2wire.gpio import GPIOPin, In, Out, Pin, exported
import select
import SocketServer

pin_bed = 0
pin_bed_switch = 4
pin_nightlight = 3


class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):

        while True:

            events = epoll.poll()

            for fileno, event in events:

                if fileno == light_bed_switch.fileno():
                    if light_bed.value:
                        light_bed.value = 0
                    else:
                        light_bed.value = 1

                if fileno == server.fileno():
                    self.data = self.request.recv(1024).strip()
                    print self.data
                    if data == 'Lampe Bett on\n':
                        self.send('on')
                        light_bed.value = 1
                    elif data == 'Lampe Bett off\n':
                        self.send('off')
                        light_bed.value = 0
                    elif data == 'Lampe Nachtlicht on\n':
                        self.send('off')
                        light_nightlight.value = 1
                    elif data == 'Lampe Nachtlicht off\n':
                        self.send('off')
                        light_nightlight.value = 0
                    elif data == 'get status_lampe_bett\n':
                        if light_bed.value:
                            self.send('on')
                        else:
                            self.send('off')
                    elif data == 'get status_lampe_nachtlicht\n':
                        if light_nightlight.value:
                            self.send('on')
                        else:
                            self.send('off')
                    else:
                        self.send('unknown command')


if __name__ == "__main__":
    HOST, PORT = "", 54321
    server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)

    with exported(GPIOPin(pin_bed, direction=Out)) as light_bed, exported(GPIOPin(pin_bed_switch, Pin.In, Pin.Rising)) as light_bed_switch, exported(GPIOPin(pin_nightlight, direction=Out)) as light_nightlight:

        light_bed.value = 0
        light_nightlight.value = 0

        epoll = select.epoll()
        epoll.register(light_bed_switch, select.EPOLLIN | select.EPOLLET)
        epoll.register(server, select.EPOLLIN | select.EPOLLET)

        server.serve_forever()
Der erste Erfolg, das Script startet ohne Fehler. Der zweite Erfolg, auch wenn ich die App beende läuft die App weiter.
Aber, leider reagiert das Script nun gar nicht mehr auf die Eingaben die übers Netzwerk kommen.
Ich denke das Problem liegt hier

Code: Alles auswählen

if fileno == server.fileno():
oder schon irgendwo vorher wo das Ereignis registriert wird.

Und versuche ich direkt nach dem Start des Script die Hardware-Taste funktioniert sie nicht. Erst wenn ich die App gestartet habe (und einmal Verbindung hatte) geht die Taste, dann allerdings auch wenn die App wie geschlossen ist.
Anscheinend muss zur Initialisierung erst 1 x eine Verbindung aufgebaut sein.

Und nun meine Bitte an Euch:
Kann mir irgendwer sagen was ich wie ändern muss damit das Programm wieder auf die Netzwerkbefehle reagiert?
Und kann sich jemand die Geschichte mit dem Hardware-Button erklären?
Zuletzt geändert von Anonymous am Montag 28. Januar 2013, 01:20, insgesamt 1-mal geändert.
Grund: Code in Python-Code-Tags gesetzt.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Es wäre sicherlich hilfreich wenn du dein Skript soweit vereinfachen könntest, dass etwaige Hilfeleistende nicht ihren Raspberry Pi booten oder sich selbigen erst kaufen müssen, daher wär ein Minimalbeispiel ne tolle Sache.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

@der_Angler: Im Gegensatz zum ersten Quelltext wendest Du im zweiten das polling auf das Server-Socket an und nicht auf das Socket über das die Kommunikation geht.

Und das pollen passiert auch nur in der `handle()`-Method und da die nur aufgerufen wird wenn jemand eine TCP-Verbindung aufbaut, werden auch nur dann die GPIOs abgefragt.
der_Angler
User
Beiträge: 25
Registriert: Montag 28. Januar 2013, 00:48

Hallo, und danke schon einmal für die Arbeit.
Durch die viele Arbeit im Job konnte ich mich jetzt erst wieder mit dem Thema beschäftigen.
Es wäre sicherlich hilfreich wenn du dein Skript soweit vereinfachen könntest, dass etwaige Hilfeleistende nicht ihren Raspberry Pi booten oder sich selbigen erst kaufen müssen, daher wär ein Minimalbeispiel ne tolle Sache.
Das würde ich sehr gerne tun, nur weiß ich ehrlich gesagt nicht wie?
Deswegen habe ich auch die beiden Scripts gepostet, aus welchen ich das Ganze zusammengemixt habe, in der Hoffnung dann würde man mich verstehen.
Grundsätzlich geht es darum das ich ein Programm aufsetzen muss, welches in Dauerschleife läuft (also am besten als server-script) und per EventHandling sowohl auf einem bestimmten Netzwerk-Port lauscht (und wenn was kommt entsprechend reagiert) als auch auf Hardware-Eingaben (das ist der Bereich den ich schlecht vereinfachen kann) wartet.
Im Augenblick macht es das leider nicht.
Im Gegensatz zum ersten Quelltext wendest Du im zweiten das polling auf das Server-Socket an und nicht auf das Socket über das die Kommunikation geht.
Irgendsowas habe ich mir schon gedacht, aber auf was müsste ich im 2. Quelltext mein Polling anwenden um das gleiche Ergebnis wie im 1. Quelltext zu haben?
Ich habe da soweit alles versucht, aber nichts hat funktioniert.
Und das pollen passiert auch nur in der `handle()`-Method und da die nur aufgerufen wird wenn jemand eine TCP-Verbindung aufbaut, werden auch nur dann die GPIOs abgefragt.
Leuchtet ein, aber wie kann ich die Abfrage der GPIO-Ports außerhalb handle-Methode programmieren, und zwar so das dies auch noch in einer Art Dauerschleife läuft?
Wenn ich den entsprechenden Code-Abschnitt

Code: Alles auswählen

                if fileno == light_bed_switch.fileno():
                    if light_bed.value:
                        light_bed.value = 0
                    else:
                        light_bed.value = 1
vor

Code: Alles auswählen

server.serve_forever()
setze, wird er dann nicht nur 1 x ausgeführt?

Ich glaube ich brauche nur noch 1-2 Gedankenanstöße, vielleicht nen bißchen Code und dann bekomme ich es hin und kann endlich über meinen Raspberry Pi schalten.

Aber bis hierher wie gesagt Danke!
BlackJack

@der_Angler: Es wäre eventuell einfacher das nicht alles über `epoll` zu lösen, sondern zum Beispiel die Netzwerkgeschichte in einen eigenen Thread auszulagern. Wobei man sich dann natürlich die ganzen Probleme von Nebenläufigkeit einhandelt.

Und man sollte das auch sauberer trennen und nicht einfach im `MyTCPHandler` auf modulglobale Variablen zugreifen.

Die Behandlung des TCP-Datenstroms ist auch alles andere als robust. Du musst damit rechnen das bei dem `recv()` sowohl weniger als auch mehr als eine Zeile mit Daten kommen kann. Man muss dort also so lange lesen bis man tatsächlich mindestens eine Zeile hat, und falls es mehr ist, muss man sich das für den nächsten Durchgang merken. Oder man lässt sich vom `socket`-Objekt ein Dateiobjekt geben. Dann kann man Zeilen ganz einfach mit `next()` abfragen und sicher sein, dass es wirklich immer eine komplette Zeile ist und das überzählige Daten gepuffert werden.

Statt der vielen ``if``/``elif``\s wäre ein datengetriebener Ansatz und eine konsistentere API für das Protokoll vielleicht besser. Man könnte den Pins Namen geben die keine Leerzeichen enthalten wie 'lampe_bett' und 'lampe_nachtlicht' und festlegen, dass am Anfang immer ein Kommando stehen muss. Also zum Beispiel 'switch' und 'get_status'. Dann könnte man im Handler ein Wörterbuch anlegen welches Namen auf Pin-Objekte abbildet und kann das Ganze dann einfacher und leichter erweiterbar formulieren.
der_Angler
User
Beiträge: 25
Registriert: Montag 28. Januar 2013, 00:48

Das hört sich alles sehr gut an, befürchte jedoch das es meine Fähigkeiten leider bei weitem übersteigt.
BlackJack

@der_Angler: Ich habe über das eigentliche Problem noch einmal nachgedacht: Der `SocketServer` wird nicht helfen weil der von Deinem `epoll` nichts weiss und wenn `serve_forever()` läuft, kannst Du nicht gleichzeitig pollen. Jedenfalls nicht ohne Threads. Du müsstest das Serversocket mit in Dein Polling aufnehmen. Also nicht erst mit `accept()` auf eine TCP-Verbindung von aussen warten und dann pollen, sondern auch auf dem Serversocket pollen und erst `accept()` ausführen wenn dort auch etwas anliegt. Dann hast Du drei Arten von Dateien auf denen gepollt wird und die jeweils unterschiedlich behandelt werden müssen: 1. Serversocket, 2. GPIO-Pin, 3. Clientsocket.

Was passieren muss:

Serversocket: `accept()` und das resultierende Clientsocket muss ins Polling aufgenommen werden.

GPIO-Pin: Was auch immer jetzt schon passiert.

Clientsocket: Bei eingehenden Daten, diese verarbeiten. Bei Fehlern oder Verbindungsende den Socket wieder aus dem Polling heraus nehmen.

Problematisch ist eventuell noch das Clientsocket, denn wie gesagt gibt `recv()` wenig Garantien über den Ausschnitt den ein Aufruf vom TCP-Datenstrom liefert. Man kann sich vom `socket` ein Python-Dateiobjekt geben lassen um das zu ändern, allerdings muss man dann aufpassen, dass man sich einen Vektor für DOS-Angriffe schaffen kann. Wenn die Gegenseite beispielsweise nur eine halbe Zeile sendet und dann mutwillig nichts mehr, hängt Dein Server an der Stelle beim Warten auf das Zeilenende.

Zentral bräuchte man dafür eine Klasse, die sich ums Polling und Dispatching kümmert, bei der man Dateiobjekte und Ereignisse und dazugehörige Funktionen registrieren kann, die aufgerufen werden wenn ein Ereignis auftritt.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

asyncore aus der Standardbibliothek nimmt einige Arbeit ab, was das handeln von Verbindungen betrifft.
Man muß natürlich darauf achten, kein blocking-recv zu verwenden, und nur dann Daten vom Clientsocket zu lesen, wenn auch welche anstehen.

Das ganze müßte ungefähr so aussehen (umgetestet):

Code: Alles auswählen


from quick2wire.gpio import GPIOPin, In, Out, Pin, exported
import asyncore
import socket

pin_bed = 0
pin_bed_switch = 4
pin_nightlight = 3

class Switch(object):
    def __init__(self, switch, light):
        self.switch = switch
        self.light = light
        asyncore.socket_map[switch.fileno()] = self
    
    def readable(self):
        return True

    def writable(self):
        return False

    def handle_read_event(self):
        self.light.value = 0 if self.light.value else 1

    def handle_write_event(self):
        print "shouldn't happen"
        
    def handle_expt_event(self):
        print "exception"
        
    def handle_error(self):
        print "something went wrong"


class Client(asyncore.dispatcher_with_send):
    def __init__(self, socket=None):
        asyncore.dispatcher_with_send.__init__(self, socket)
        self.data = ''

    def handle_read(self):
        receivedData = self.recv(8192)
        if not receivedData:
            self.close()
            return
        receivedData = self.data + receivedData
        while '\n' in receivedData:
            line, receivedData = receivedData.split('\n',1)
            self.handle_command(line)
        self.data = receivedData

    def handle_command(self, line):
        print line
        # TODO: switch on/off lights


class Server(asyncore.dispatcher):
    def __init__(self, listen_to):
        asyncore.dispatcher.__init__(self, map)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.bind(listen_to)
        self.listen(5)

    def handle_accept(self):
        newSocket, address = self.accept()
        print "Connected from", address
        Client(newSocket)

if __name__ == "__main__":
    with exported(GPIOPin(pin_bed, direction=Out)) as light_bed, \
        exported(GPIOPin(pin_bed_switch, Pin.In, Pin.Rising)) as light_bed_switch, \
        exported(GPIOPin(pin_nightlight, direction=Out)) as light_nightlight:

        light_bed.value = 0
        light_nightlight.value = 0

        server = Server(("",54321))
        siwtch = Switch(light_bed_switch, light_bed)
        asyncore.loop()
Grüße
Sirius
der_Angler
User
Beiträge: 25
Registriert: Montag 28. Januar 2013, 00:48

Wow :D

Vielen Dank, ich werde das nachher direkt einmal testen und dann berichten, aber wow, danke
der_Angler
User
Beiträge: 25
Registriert: Montag 28. Januar 2013, 00:48

So, habe das Ganze mal versucht, aber irgendwo scheint noch ein Fehler zu stecken

Code: Alles auswählen

pi@raspberrypi ~ $ python RaPiServer.py
Traceback (most recent call last):
  File "RaPiServer.py", line 75, in <module>
    server = Server(("",54321))
  File "RaPiServer.py", line 57, in __init__
    asyncore.dispatcher.__init__(self, map)
  File "/usr/lib/python2.7/asyncore.py", line 244, in __init__
    sock.setblocking(0)
AttributeError: 'builtin_function_or_method' object has no attribute 'setblocking'
pi@raspberrypi ~ $ python3 RaPiServer.py
  File "RaPiServer.py", line 25
    print "shouldn't happen"
                           ^
SyntaxError: invalid syntax
Mal schaun was Google sagt ...
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@der_Angler: es ist Python 2.x

Und in Zeile 57 hat sich noch alter Code von mir eingeschlichen. Die Zeile muß

Code: Alles auswählen

asyncore.dispatcher.__init__(self)
heißen. Wie schon geschrieben, keine Garantie dass es auf anhieb funktioniert,
es ist nur ein Test mit asyncore, den ich mal für mich geschrieben und auf Dein
Problem kurz umgeschrieben habe.
der_Angler
User
Beiträge: 25
Registriert: Montag 28. Januar 2013, 00:48

das ist mir leider klar das du es nicht testen kannst, um so mehr bin ich dir für deine Hilfe dankbar :)

Und ja, nachdem ich den Fehler korrigiert habe kam schonmal keine Python-Fehlermeldung mehr, jetzt haut er mir folgende Endloschleife raus:

Code: Alles auswählen

pi@raspberrypi ~ $ python RaPiServer.py
exception
exception
exception
exception
exception
exception
exception
exception
exception
exception
exception
exception
exception
exception
exception
exception
exception
exception
exception
exception
exception
exception
exception
exception
exception
exception
Aber schonmal einen Schritt weiter :)
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

scheint so, als ob der GIOpin ständig was zu melden hätte.
Erscheinen die Meldungen sehr schnell oder eher gemächlich?
Falls zweiteres, könnte man sie ja einfach ignorieren.
Möglich dass

Code: Alles auswählen

asyncore.loop(use_poll=True)
hilft, aber eher unwahrscheinlich.
Kommen wenigsten auch noch die anderen Meldungen (licht an/aus, connect) durch?
der_Angler
User
Beiträge: 25
Registriert: Montag 28. Januar 2013, 00:48

also die Fehlermeldung kommt wirklich rasend schnell durchgerasselt.
Andere Befehle kommen gar nicht an.

Ich habe dann mal den Part

Code: Alles auswählen

        server = Server(("",54321))
        # switch = Switch(light_bed_switch, light_bed)
        asyncore.loop()
auskommentiert, und der reine Netzwerkpart funktionert ziemlich perfekt :)

Sowie ich den switch aber reinnehme geht es nicht.
Wenn ich deine Zeile hier einfüge:

Code: Alles auswählen

    def __init__(self, switch, light):
        self.switch = switch
        self.light = light
        asyncore.loop(use_poll=True)
        asyncore.socket_map[switch.fileno()] = self
dann erscheint zwar keine Fehlermeldung mehr, auch der Netzwerkpart geht noch, aber er reagiert nicht auf Tastendrücke, sprich der switch scheint nicht zu funktionieren.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

nein, nein, nein, es macht keinen Sinn den loop im __init__ von Switch aufzurufen.
Ich meinte, die letzte Zeile um den Parameter use_poll=True zu erweitern.
der_Angler
User
Beiträge: 25
Registriert: Montag 28. Januar 2013, 00:48

mein Fehler :(

aber hey, jetzt gibt es Abwechslung

Code: Alles auswählen

something went wrong
exception
something went wrong
exception
something went wrong
exception
something went wrong
exception
something went wrong
exception
Die beiden Zeilen wechseln sich sehr schnell ab, reagieren tut es sonst nicht.

Ich habe aber das Gefühl das wir dicht dran sind, bzw. eher du :)

PS: Python Einsteiger Buch ist bereits bestellt.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

dann müssen wir wohl doch auf epoll umsteigen:

Code: Alles auswählen

from quick2wire.gpio import GPIOPin, In, Out, Pin, exported
import asyncore
import socket
import select

pin_bed = 0
pin_bed_switch = 4
pin_nightlight = 3

class Switch(object):
    def __init__(self, switch, light):
        self.switch = switch
        self.light = light
        asyncore.socket_map[switch.fileno()] = self
    
    def readable(self):
        return True

    def writable(self):
        return False

    def handle_read_event(self):
        self.light.value = 0 if self.light.value else 1

    def handle_write_event(self):
        print "shouldn't happen"
        
    def handle_expt_event(self):
        print "exception"
        
    def handle_error(self):
        print "something went wrong"


class Client(asyncore.dispatcher_with_send):
    def __init__(self, socket=None, pollster=None):
        asyncore.dispatcher_with_send.__init__(self, socket)
        self.data = ''
        if pollster:
            self.pollster = pollster
            pollster.register(self, select.EPOLLIN)

    def handle_close(self):
        if self.pollster:
            self.pollster.unregister(self)
        
    def handle_read(self):
        receivedData = self.recv(8192)
        if not receivedData:
            self.close()
            return
        receivedData = self.data + receivedData
        while '\n' in receivedData:
            line, receivedData = receivedData.split('\n',1)
            self.handle_command(line)
        self.data = receivedData

    def handle_command(self, line):
        print line
        # TODO: switch on/off lights


class Server(asyncore.dispatcher):
    def __init__(self, listen_to, pollster):
        asyncore.dispatcher.__init__(self)
        self.pollster = pollster
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.bind(listen_to)
        self.listen(5)

    def handle_accept(self):
        newSocket, address = self.accept()
        print "Connected from", address
        Client(newSocket,self.pollster)


def readwrite(obj, flags):
    try:
        if flags & select.EPOLLIN:
            obj.handle_read_event()
        if flags & select.EPOLLOUT:
            obj.handle_write_event()
        if flags & select.EPOLLPRI:
            obj.handle_expt_event()
        if flags & (select.EPOLLHUP | select.EPOLLERR | select.EPOLLNVAL):
            obj.handle_close()
    except socket.error, e:
        if e.args[0] not in asyncore._DISCONNECTED:
            obj.handle_error()
        else:
            obj.handle_close()
    except asyncore._reraised_exceptions:
        raise
    except:
        obj.handle_error()


if __name__ == "__main__":
    with exported(GPIOPin(pin_bed, direction=Out)) as light_bed, \
        exported(GPIOPin(pin_bed_switch, Pin.In, Pin.Rising)) as light_bed_switch, \
        exported(GPIOPin(pin_nightlight, direction=Out)) as light_nightlight:

        light_bed.value = 0
        light_nightlight.value = 0

        pollster = select.epoll()
        pollster.register(Server(("",54321),pollster), select.EPOLLIN)
        pollster.register(Switch(light_bed_switch, light_bed), select.EPOLLIN | select.EPOLLET)
        while True:
            evt = pollster.poll()
            for fd, flags in evt:
                readwrite(fd, flags)
der_Angler
User
Beiträge: 25
Registriert: Montag 28. Januar 2013, 00:48

Hmm, du machst das auch öfters? Ich meine in Python programmieren :)

Okay, bekomme folgendes Ergebnis

Code: Alles auswählen

pi@raspberrypi ~ $ python RaPiServer2.py
  File "RaPiServer2.py", line 100
    if flags & select.EPOLLIN:
                 ^
SyntaxError: invalid syntax
sind diese Zeilen so wirklich richtig?

Code: Alles auswählen

        if flags & select.EPOLLIN:
            obj.handle_read_event()
        if flags & select.EPOLLOUT:
            obj.handle_write_event()
        if flags & select.EPOLLPRI:
            obj.handle_expt_event()
        if flags & (select.EPOLLHUP | select.EPOLLERR | select.EPOLLNVAL):
BlackJack

@der_Angler: Da ist der Syntaxhighlighter in der Forensoftware schuld — der macht aus einem '&' ein '&'.
der_Angler
User
Beiträge: 25
Registriert: Montag 28. Januar 2013, 00:48

habs mir schon gedacht, aber man weiß ja nie ;)
Antworten