Zugriff auf Modul welches bereits aktiv ist

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.
BlackJack

@harryberlin: Ein ganz wichtiger Rat wäre: Lass die Finger von `__del__()`! Es ist nicht garantiert wann und ob die Methode aufgerufen wird und ihr Vorhandensein kann sogar begünstigen das sie nie aufgerufen wird. Die Methode sollte man nur implementieren wenn man genau weiss was die Speicherverwaltung von Python garantiert und was *nicht*, und dann auch nur wenn man externe Ressourcen verwalten muss, von denen Python nichts weiss. Das ist hier nicht der Fall soweit ich das sehe.

Wenn man `Thread` in einer Klasse verwendet um in einer `start()`-Methode eine andere Methode in dieser Klasse asynchron zu starten, wäre es einfacher diese andere Methode `run()` zu nennen und von `Thread` zu erben.

Der Receiver kann so wie er da steht soweit ich das sehe nur von einer Gegenstelle einmal eine Verbindung annehmen und bearbeiten.

Ist das überhaupt das was Du tatsächlich laufen lässt? Denn in Zeile 60 sollte es einen `AttributeError` geben weil `CmdSender` gar kein `run`-Attribut kennt.
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

ok, del fliegt raus.

run oder start, da war ich tatsächlich hin und her gerissen :D
das mit der thread vererbung versteh ich grad nicht.

und ja du hast recht, das wäre dann als nächstes meine frage gewesen.
wenn die verbindung unterbrochen wird, müsste man es wieder starten.
gibt es bessere lösungen?

das mit zmq und argparse hab ich auch versucht, aber es passiert genauso nix.
empty Sig
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

ich glaub ich habs :)

zwar noch nicht als klasse, aber ich sehe etwas.
und das müsste dann auch ohne verbindungsunterbrechung laufen.

Code: Alles auswählen

import zmq
from threading import Thread

def server_run():
    # server
    context = zmq.Context()
    socket = context.socket(zmq.REP)
    socket.bind('tcp://127.0.0.1:5555')
    while True:
        msg = socket.recv()
        if msg == 'zeromq':
            socket.send('ah ha!')
        else:
            socket.send('...nah')

def client_send(msg):
    # client
    context = zmq.Context()
    socket = context.socket(zmq.REQ)
    socket.connect('tcp://127.0.0.1:5555')
    socket.send(msg)
    msg = socket.recv()
    print msg

Thread(target=server_run).start()
client_send('zeromq')
edit:
hab über die Thread-Vererbung noch mal drüber nachgedacht, meinst du das so?
jetzt mal schnell hier runter geschrieben.
Bin mit classen nicht so bewandert.

Code: Alles auswählen

class CmdReceiver(Thread):
    def __ini__(self):
        self.target = run
        self.run = True

    def start(self):
        self.start()

    def run(self):
        while self.run:
            pass

    def stop(self):
        self.run = False
empty Sig
BlackJack

@harryberlin: Mit von `Thread` erben war gemeint von `Thread` zu erben (ungetestet):

Code: Alles auswählen

from __future__ import absolute_import, division, print_function
from contextlib import closing
from threading import Thread
# ...


class CmdReceiver(Thread):

    def __init__(self):
        print('server init')
        Thread.__init__(self)
        self.address = ('localhost', 6000)
        self.stop_requested = False
        self.listener = None
 
    def run(self):
        print('server start')
        with closing(
            Listener(self.address, authkey='bmwibus')
        ) as self.listener:
            with closing(self.listener.accept()) as connection:
                print(
                    'server connection accepted from',
                    self.listener.last_accepted
                )
                while not self.stop_requested:
                    message = connection.recv()
                    # 
                    # Do something with `message`.
                    # 
                    print(message)
                    if message == 'close':
                        break
 
    def stop(self):
        print('server stop')
        self.stop_requested = True
        self.listener.close()
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

ich glaub ich bleib bei zmq, weil "multiprocessing.connection" vermutlich auch nur eine verbindung zulässt.
zmq müsste ja auch mehrere clients zulassen.

zur klasse:
Ok bei Thread.__init__(self) komm ich noch mit, dass er sich sozusagen einrichtet.
Aber legt der dann gleich los und weiß auch was auszuführen ist?
Ich sehe nirgends ein target oder start()
empty Sig
BlackJack

@harryberlin: Dann schau mal in die Dokumentation zu `Thread`, da findest Du die `start()`-Methode. Ich glaube Du solltest Dich noch mal mit Klassen und Vererbung auseinandersetzen. :-)
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

hab die docu hier gefunden:
http://pythoncentral.io/how-to-create-a ... in-python/

Ich weiß nun zwar was ich bei Thread machen muss, aber irgendwie fällt der groschen bei mir nicht, wenn es um klassen geht. :|
gibts irgendwo sowas wie "python klassen für dummies" auf deutsch?

edit:
hab ein ebook gefunden
empty Sig
BlackJack

@harryberlin: Die Dokumentation von `Thread` befindet sich in der Dokumentation von Python, da muss man nicht irgendwo im Internet nach suchen. Klassen sollten im Tutorial in der Python-Dokumentation auch vorkommen.
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

ich hab nun mal was zusammen gezaubert.
kann ich das mit dem start() beim init machen, oder rät man eher davon ab?
schon mal in vorbereitung mit rückgabewert.
was hälst sonst davon.

Code: Alles auswählen

from threading import Thread
import zmq

class tcp_server(Thread):
    def __init__(self):
        Thread.__init__(self)
        self.stopserver = False
        context = zmq.Context()
        self.socket = context.socket(zmq.REP)
        self.socket.bind('tcp://127.0.0.1:5555')
        self.start()

    def run(self):
        while not self.stopserver:
            message = self.socket.recv()
            if message == 'stop_server':
                self.stop()
            else:
                self.onMessage(message)

    def stop(self):
        self.stopserver = True
        self.socket.close()
        self.join()

    def onMessage(self, message):
        print 'SERVER: Handle Message'
        self.socket.send('ok')
        pass

class tcp_client:
    def __init__(self):
        context = zmq.Context()
        self.socket = context.socket(zmq.REQ)
        self.socket.connect('tcp://127.0.0.1:5555')

    def send(self, message):
        print message
        self.socket.send(message)
        message = self.socket.recv()
        print message
        return message


msg_server = tcp_server()
tcp_client().send('test')
empty Sig
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@harryberlin: wenn Du den Thread schon in __init__ startest, verbaust Du Dir die Flexibilität, den Thread anderweitig zu starten. Nicht zuletzt gibt es ja die extra Methode start. Starte den Thread also außerhalb von __init__ im Hauptprogramm. Den zmq-Server würde ich erst in run starten.
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

ok.
sollte ich zum schließen der sockets noch was spezielles beachten?
reicht close() oder auch noch unbind()?
oder ist unbind() sinnvoll, um einen evtl. blockierten port wieder frei zu kriegen?
empty Sig
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

ich hab jetzt mal bissl mit zmq herumgespielt.
aber habe ein problem, wenn man send oder recv ausführt, bleibt der client oder server im code stehen, wenn die andere seite nicht vorhanden ist bzw. reagiert.
entsprechend lässt sich auch der thread nicht beenden.
irgendwie blöd.
empty Sig
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

OK, jetzt hab ich grad gesehn, dass mein thema wohl besser unter netzwerkprogrammierung aufgehoben wäre.
deswegen gehts hier eher in eine alleinunterhaltung....

hab nun mal bissl herum experimentiert und folgendes geschrieben (ja auch c&p)
bin jetzt noch über diesen link hier gestolpert, ich glaub da kann einiges an code eingespart werden.:
viewtopic.php?f=3&t=36933
https://docs.python.org/2/library/socketserver.html

Code: Alles auswählen

from threading import Thread
import socket

import time

class tcp_server(Thread):
    def __init__(self):
        Thread.__init__(self)
        self.stopserver = False
        self.start()

    def run(self):
        # Create a TCP/IP socket
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        
        # Bind the socket to the port
        server_address = ('localhost', 10000)
        print 'starting up on %s port %s' % server_address
        self.sock.bind(server_address)
        
        # Listen for incoming connections
        self.sock.listen(1)
        self.sock.settimeout(0.1)
        while not self.stopserver:
            # Wait for a connection
            #print 'SERVER: waiting for a connection'
            try:
                self.connection, client_address = self.sock.accept()
                try:
                    print 'SERVER: connection from', client_address

                    # Receive the data in small chunks and retransmit it
                    data = self.connection.recv(50)
                    print 'SERVER: received "%s"' % data
                    if data:
                        self.onMessage(data)


                finally:
                    # Clean up the connection
                    self.connection.close()
            except:
                pass

    def stop(self):
        print 'SERVER: Close'
        self.stopserver = True
        self.connection.close()
        self.sock.close()
        self.join(0)

    def onMessage(self, message):
        print 'SERVER: Handle Message "%s"' % message
        self.connection.sendall('ok')
        pass


class tcp_client:
    def __init__(self):
        pass

    def send(self, message):
        # Create a TCP/IP socket
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        # Connect the socket to the port where the server is listening
        server_address = ('localhost', 10000)
        print 'CLIENT: Try to connect %s port %s' % server_address

        if sock.connect_ex(server_address) != 0:
            print 'CLIENT: Connection to server is not possible'
            return False

        try:
            # Send data
            print 'CLIENT: sending "%s"' % message
            sock.sendall(message)

            # Look for the response
            data = sock.recv(50)
            print 'CLIENT: response from server "%s"' % data

        except:
            print 'CLIENT: Error by sending'
            return False

        finally:
            print 'CLIENT: Close'
            sock.close()
            return True


tcp_srv = tcp_server()
tcp_client().send('This is the message.')

time.sleep(0.5)
tcp_srv.stop()
del tcp_srv
time.sleep(0.5)
tcp_client().send('test2')
print('end')
pass
empty Sig
BlackJack

@harryberlin: Auf Modulebene sollte kein Code stehen der nicht Konstanten, Funktionen, oder Klassen definiert. Das Hauptprogramm steht üblicherweise in einer `main()`-Funktion.

Die Namensgebung folgt nicht immer dem Style Guide for Python Code. Gerade bei Klassen sollte man nicht abweichen, damit der Leser sie als Klassen erkennen kann.

Das man `start()` nicht in der `__init__()` von `Thread`-Unterklassen aufrufen sollte hatten wir glaube ich schon etwas weiter oben.

Attribute sollten nicht ausserhalb der `__init__()` eingeführt werden. Ein Objekt sollte nach deren Abarbeitung in der Regel einem vollständigen und benutzbaren Zustand sein und ein Leser sollte nicht die gesamte Klasse lesen müssen um zu wissen welche Attribute die Objekte am Ende haben. Wenn man bei der Initialisierung noch keine entgültigen Werte zuweisen kann, dann kann man `None` an die Attribute binden.

Es gibt drei überflüssige ``pass``-Anweisungen und eine die zwar syntaktisch richtig ist, dort aber trotzdem nicht als Behandlung in einem ”nackten” ``except:``-Zweig stehen sollte. Dort ”behandelst” Du *jede* Ausnahme durch nichtstun, was sicher nicht bei *jeder* Ausnahme die richtige Behandlung ist. Eigentlich ja nur bei der die Du dort erwartest, nämlich die Zeitüberschreitung vom `accept()` auf dem Serversocket. Dann sollte man das aber auch so schreiben. Ausserdem sollte man den ``try``-Block auf das nötigste beschränken um nicht irgendwann Ausnahmen zu behandeln, die man eigentlich gar nicht erwartet hat weil sie von einer anderen Stelle des ``try``-Blocks oder einer dort aufgerufenen Operation stammen. Dabei hilft ein ``else``-Zweig beim ``try``/``except``-Konstrukt.

Der Kommentar ``# Receive the data in small chunks and retransmit it`` ist falsch weil der Code etwas anderes macht. Und der Code ist falsch weil der `recv()` irgendetwas zwischen einem und 50 Bytes aus dem Datenstrom liest, die nicht die gesamten Daten ausmachen müssen, die der Client gesendet hat. Die Namen `onMessage()` und `message` sind hier irreführend weil Du nirgends sicherstellst, das es sich um die oder eine komplette Nachricht handelt. Korrekter Socket-Code muss an der Stelle im Extremfall damit klar kommen, dass jeder `recv()`-Aufruf nur ein einziges Byte liefert, man die Methode also tatsächlich so oft aufrufen muss, wie Bytes in der Nachricht gesendet wurden. Dazu muss man irgendwie erkennen können wann eine Nachricht komplett ist. Zum Beispiel weil die Verbindung vom Sender (halb) geschlossen wird wenn er alles gesendet hat, oder weil alle Nachrichten eine feste Grösse haben, oder mit einer bestimmten Bytefolge abgeschlossen sind, die innerhalb der Nachricht nicht vorkommen kann, oder weil am Anfang die Grösse der Nachricht übermittelt wurde. Die Grösse dann auch wieder entweder durch eine bestimmte Bytefolge abgeschlossen, die nicht innerhalb der Grössenangabe vorkommen kann, oder in einer festen Byteanzahl.

`tcp_client` ist keine Klasse. Die `__init__()` ist leer (``pass``), kann also weg, womit nur noch eine einzige ”Methode” übrigbleibt, welche das Objekt auf dem sie definiert ist, nicht verwendet, und die jedes mal direkt nach dem erstellen eines Exemplars aufgerufen wird und das Exemplar wird nach dem Aufruf auch gleich wieder verworfen. Damit ist das alles nur eine sehr umständliche Art eine Funktion zu schreiben und aufzurufen.

Die Fehlerbehandlung in der Funktion ist schlecht. Du vermeidest Ausnahmen oder wandelst sie in einen `bool`-Rückgabewert um. Und dieser Rückgabewert wird nirgends geprüft, also genau das weshalb man Ausnahmen erfunden hat: Damit man keine speziellen Rückgabewerte prüfen muss und *vergessen* kann das zu tun.

``del`` auf einen Namen anzuwenden macht hier nicht viel Sinn. Das löscht den *Namen*, *nicht* das Objekt.

Ich komme dann ungefähr auf das hier:

Code: Alles auswählen

import socket
import time
from contextlib import closing
from threading import Thread

SERVER_ADDRESS = ('localhost', 10001)


class TCPServer(Thread):

    def __init__(self):
        Thread.__init__(self)
        self.stop_requested = False
        self.server_socket = None
        self.connection = None

    def run(self):
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        print 'SERVER: starting up on %s port %s' % SERVER_ADDRESS
        self.server_socket.bind(SERVER_ADDRESS)
       
        self.server_socket.listen(1)
        self.server_socket.settimeout(0.1)
        while not self.stop_requested:
            try:
                self.connection, client_address = self.server_socket.accept()
            except socket.timeout:
                pass  # Intentionally ignored.
            else:
                print 'SERVER: connection from', client_address
                with closing(self.connection):
                    data = self.connection.makefile().read()
                    print 'SERVER: received %r' % data
                    if data:
                        self.on_message(data)

    def stop(self):
        print 'SERVER: Close'
        self.stop_requested = True
        self.server_socket.close()
        self.connection.close()
        self.join()

    def on_message(self, message):
        print 'SERVER: Handle Message %r' % message
        self.connection.sendall('ok')


def send(message):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print 'CLIENT: Try to connect %s port %s' % SERVER_ADDRESS
    sock.connect(SERVER_ADDRESS)
    with closing(sock):
        print 'CLIENT: sending %r' % message
        sock.sendall(message)
        sock.shutdown(socket.SHUT_WR)
        data = sock.makefile().read()
        print 'CLIENT: response from server %r' % data
        print 'CLIENT: Close'


def main():
    tcp_server = TCPServer()
    tcp_server.daemon = True
    tcp_server.start()
    time.sleep(0.5)  # Wait for server to start.
    send('This is the message.')
    time.sleep(0.5)
    tcp_server.stop()
    time.sleep(0.5)
    send('test2')
    print 'end'


if __name__ == '__main__':
    main()
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

uff, ich werte es mal wieder als klatsche. :(
hast du für diese analysen auswertungen ein tool?
darf ich fragen, was du beruflich machst?
empty Sig
BlackJack

@harryberlin: „Klatsche“ klingt ein bisschen hart. Sieh's als konstruktive Kritik. :-)

Es gibt mehrere Werkzeuge, die Python-Code statisch analysieren, oder es zumindest versuchen. Das klappt für so einfache Sachen wie die Namensschreibweisen oder überflüssige ``pass``-Anweisungen sehr gut (wobei man das natürlich auch ohne ein Werkzeug leicht sehen kann wenn man den Quelltext durchliest), und für Sachen die mit Attributen und Vererbung zu tun haben, so gut wie es eben bei einer dynamisch typisierten Programmiersprache geht. Die verbreitetsten Werkzeuge dürften `pep8`, `pyflakes`, und `pylint` sein. In vernünftigen™ Editoren und IDEs kann man so etwas auch integrieren, so dass man auf Knopfdruck oder automatisch beim Speichern einen Analyselauf anstossen kann, und das Ergebnis irgendwie sinnvoll präsentiert wird.

Semantische Sachen wie `start()` in der `Thread.__init__()`, die falsche Verwendung von Sockets, oder irreführende Namen weil deren Bedeutung nicht zu dem passt wofür sie stehen, kann der Rechner nicht automatisch finden. Wenn er so etwas könnte, währen wir wahrscheinlich nicht weit von Rechnern entfernt die sich selber programmieren. Da lauert dann der Terminator um die Ecke. :-D

Beruflich programmiere ich viel. Welch eine Überraschung. :-)
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

zukünftig wird aus dem script zwei. das war der hintergrund, zwischen modulen zu arbeiten.
deswegen adresse und port direkt in der funktion.
woran kann man variablen und konstanten erkennen?

welchen zweck hat jetzt das closing?

die client klasse habe ich wie gesagt, weil mir nahe gelegt wurde ich soll es so machen. lässt sich später auch mal einfacher importieren.

noch ne Verständnisfrage, weil ich ständig module, functionen und methoden lese:
wie definiert ihr das?

ebenso bei argumenten und eigenschaften.
empty Sig
BlackJack

@harryberlin: Variablen sind veränderbar, also variabel, und Konstanten bleiben immer gleich, also konstant. Variablen haben auf Modulebene nichts zu suchen und Konstanten werden per Konvention komplett in Grossbuchstaben geschrieben, damit man beim Lesen weiss das sich der Wert nicht ändert.

Was ist an der Dokumentation zu `contextlib.closing()` denn konkret unklar?

Bei der Client-Klasse würde ich eher nahelegen es *nicht* so zu machen, eben weil es semantisch keine Klasse ist.

Module, Funktionen, Methoden, und Argumente definieren wir so wie Python das tut. Wobei Funktionen, Methoden, und Argumente auch generell Begriffe aus der Programmierung sind und von sehr vielen Programmiersprachen gleich oder sehr ähnlich definiert werden. Das sollte eigentlich alles in einem Python-Tutorial oder -Buch stehen. Allgemein hilft Wikipedia auch oft was Programmierbegriffe angeht. Kurze Definitionen zu den Begriffen aus Python-Sicht finden sich in der Python-Dokumentation im Glossar.

Eigenschaften ist ein Begriff der ein bisschen mehrdeutig ist. Zum einen gibt es die ganz normale Bedeutung des Wortes im Deutschen. Und dann wird der Begriff auch für das gebraucht was man in Python mit `property()` erstellt.
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

komisch, irgendwie reden wir immer aneinander vorbei.

1. d.h. sobald ein name in großbuchenstaben definiert ist, dann gilt es für python als konstante deklariert?

2. irgendwas muss dich doch dazu bewegt haben, "closing" rein zu nehmen. dass ich mir die manual dazu durchlese, kann ich mir nicht vorstellen.
geht damit etwas besser als zuvor?

3. Das Nutzen von Klassen hat mir BJ1 aus'm Kodinerds Forum ans Herz gelegt. Kennste bestimmt ;)

4. Zu den Definitionen aus meiner Sicht:
Modul: das Python File, was Klassen, Funktionen, Konstanten, Variablen enthalten kann. Sprich auch importiert werden kann.
Funktionen: Methode, Function, wobei ich auch schon gelesen hab, dass functionen als Objekt betitelt wurden in Bezug auf OOP.
Eigenschaften: Variablen oder Konstanten innerhalb Klassen.
Argumente: werden für Funktionen übergeben.
Property: Ok wieder was neues, muss ich mich mal belesen.
Widersprüchliches oder Richtigstellung bitte dazu schreiben, sollte ich falsches Verständnis haben.
empty Sig
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

harryberlin hat geschrieben:1. d.h. sobald ein name in großbuchenstaben definiert ist, dann gilt es für python als konstante deklariert?
Nein, er gilt für den Programmierer als Konstante. Dem Python ist das wurst, aber wenn ich als Programmierer einen solchen Namen im Pythoncode sehe, erwarte ich, dass er als Konstante verwendet wird, qua Konvention. In Python ist sehr vieles reine Konvention. Mehr dazu in PEP8.
In specifications, Murphy's Law supersedes Ohm's.
Antworten