Using websocket client as a class in python
I'm trying to access some data using websockets, but I can't seem to get around the examples provided in the websockets documentation.
I have this code ( https://pypi.org/project/websocket_client/ ) and I want to convert it to a class.
import websocket
import thread
import time
def on_message(ws, message):
print message
def on_error(ws, error):
print error
def on_close(ws):
print "### closed ###"
def on_open(ws):
def run(*args):
for i in range(3):
time.sleep(1)
ws.send("Hello %d" % i)
time.sleep(1)
ws.close()
print "thread terminating..."
thread.start_new_thread(run, ())
if __name__ == "__main__":
websocket.enableTrace(True)
ws = websocket.WebSocketApp("ws://echo.websocket.org/",
on_message = on_message,
on_error = on_error,
on_close = on_close)
ws.on_open = on_open
ws.run_forever()
The idea is to have all this websocket functionality in a class so that I can just create an object of that class.
I tried to start doing this, but I can't even get past this:
class MySocket(object):
def __init__(self):
websocket.enableTrace(True)
self.ws = websocket.WebSocketApp("ws://echo.websocket.org:12300/foo",
on_message = on_message,
on_error = on_error,
on_close = on_close)
def on_message(ws, message):
print message
def on_error(ws, error):
print error
def on_close(ws):
print "### closed ###"
def on_open(ws):
ws.send("Hello %d" % i)
The error starts right off on_message
saying that this is an "unresolved link".
source to share
Call batch inside anonymous lambda
function for correct call with correct self
:
class Client:
def __init__(self, db, symbols):
self.ws = websocket.WebSocketApp("wss://the.server.com/api",
on_message = lambda ws,msg: self.on_message(ws, msg),
on_error = lambda ws,msg: self.on_error(ws, msg),
on_close = lambda ws: self.on_close(ws),
on_open = lambda ws: self.on_open(ws))
def on_message(self, ws, message):
msg = json.loads(message)
print(msg)
...
source to share
WebSocketApp
need callable objects for their callbacks (both the ones you pass in the constructor, for example on_message
, and the one you set after the fact on_open
).
Regular functions are callable objects, so your non-OO version works fine because you are passing simple functions.
Bound methods are also callable objects. But your OO version doesn't pass associated methods. A bound method, as the name suggests, is bound to an object. You do this using notation obj.method
. In your case, this is self.on_message
:
self.ws = websocket.WebSocketApp("ws://echo.websocket.org/",
on_message = self.on_message,
on_error = self.on_error,
on_close = self.on_close)
self.ws.on_open = self.on_open
However, you have another problem. While this will make your mistake go away, it will not make your code actually work. A normal method should take self
as its first argument:
def on_message(self, ws, message):
print message
It's also worth noting that you are not actually using this class for anything. If you never get anything from self
, the class just acts like a namespace. Not that it's always a bad thing, but it's usually a sign that you need to at least think through your design. Is there any state you need to maintain? If not, why do you need a class?
You can reread the section on Classes to understand about methods, self
etc.
source to share
You need to add "self" to your class methods:
class MySocket(object):
def __init__(self):
websocket.enableTrace(True)
self.ws = websocket.WebSocketApp("ws://echo.websocket.org:12300/foo",
on_message = self.on_message,
on_error = self.on_error,
on_close = self.on_close)
def on_message(self, ws, message):
print message
def on_error(self, ws, error):
print error
def on_close(self, ws):
print "### closed ###"
def on_open(self, ws):
ws.send("Hello %d" % i)
source to share
"I" makes these methods as class methods. Got this one working as the signature of the on_error / message / close method, will be satisfied if called itself, as will be the case for the class itself.
class MySocket(object):
def __init__(self,x):
websocket.enableTrace(True)
## Only Keep the object Initialisation here
self.x=x
self.ws=None
# call This method from a Object and it will create and run the websocket
def ws_comm(self):
self.ws = websocket.WebSocketApp(self.WS_URL,on_message =
self.on_message,on_error =self.on_error,on_close = self.on_close)
self.ws.on_open = self.on_open
self.ws.run_forever()
def on_error(self,ws, error):
print "onError", error
def on_close(self,ws):
print "onClosed"
#Send some message on open
def on_open(self,ws):
self.ws.send(json.dumps(register_msg))
def on_message(self,ws, msg):
self.ws.send(json.dumps(msg))
user1=Userapp('x')
user1.ws_comm()
source to share
I would like to try:
class FooClient(object):
def __init__(self):
def on_message(ws, message):
print message
# use 'self' variable to access other resource
# handle message from websocket server like this
self.handler.handle(message)
def on_error(ws, error):
print error
def on_close(ws):
print "### closed ###"
def on_open(ws):
ws.send("Hello %d" % i)
# assign to 'self.handler'
self.handler = FooHandler()
# maybe there are another module should be initiated
# ...
websocket.enableTrace(True)
self.ws = websocket.WebSocketApp("ws://echo.websocket.org:12300/foo",
on_message = on_message,
on_error = on_error,
on_close = on_close)
def run_forever(self):
self.ws.run_forever()
def close(self):
"""clean other resources"""
pass
Using an inner function in a method __init__(self)
could avoid the problem that the method's number arguments on_message(self, ws, message)
do not match the number it WebSocketApp
provides its argument on_message
(a class method has one more argument self
).
I have handler
above for handling a message, a method close(self)
to clean up some resources, if I have one, run_forever(self)
to start a websocket.
source to share
It works:
class MySocket(object):
def __init__(self):
websocket.enableTrace(True)
self.ws = websocket.WebSocketApp("ws://echo.websocket.org:12300/foo",
on_message = self.on_message,
on_error = self.on_error,
on_close = self.on_close)
@staticmethod
def on_message(ws, message):
print message
@staticmethod
def on_error(ws, error):
print error
@staticmethod
def on_close(ws):
print "### closed ###"
@staticmethod
def on_open(ws):
ws.send("Hello %d" % i)
But you don't have access to yourself
source to share
import websocket
try:
import thread
except ImportError:
import _thread as thread
import time
class OnyxGenericClient:
"""
Onyx Client Interface
"""
def __init__(self, ):
websocket.enableTrace(True)
ws = websocket.WebSocketApp("ws://localhost:3000/",
on_message=self.on_message,
on_error=self.on_error,
on_close=self.on_close)
self.ws = ws
self.ws.on_open = self.on_open
self.ws.run_forever()
# def initiate(self):
def on_message(self, message):
print(message)
return message
def on_error(self, error):
return error
def on_close(self):
print("### closed ###")
def run(self, *args):
global driver
driver = True
while driver:
try:
time.sleep(1)
print("Say something nice")
p = input()
self.ws.send(p)
except KeyboardInterrupt:
driver = False
time.sleep(1)
self.ws.close()
print("thread terminating...")
def on_open(self):
thread.start_new_thread(self.run, ())
if __name__ == "__main__":
websocket.enableTrace(True)
onyx_client = OnyxGenericClient()
I wonder why the parameter is still being exposed ws
?
Read the error log.
File "venv / lib / python3.7 / site-packages / websocket / _app.py" line 343 in _callback callback (* args)
def _callback(self, callback, *args):
if callback:
try:
if inspect.ismethod(callback):
callback(*args)
else:
callback(self, *args)
except Exception as e:
_logging.error("error from callback {}: {}".format(callback, e))
if _logging.isEnabledForDebug():
_, _, tb = sys.exc_info()
traceback.print_tb(tb)
Looking at our callbacks, on_open(self, ws)
When the block is executed, try
it checks if our callback is a method or a function. if it's a method, it will execute callback(*args)
already our own from our CustomClient
already passed as an argument to (* args). Keep in mind, it already has its own self
c def _callback(self, callback, *args)
. Hence, every callback that is an instance of yours CustomClient
should not have a ws argument.
source to share