Last active
May 8, 2017 17:27
-
-
Save rigelk/c194668d88fb0838544a581f21af251b to your computer and use it in GitHub Desktop.
websocket client with events for an 2.7 only python experiment
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from ws4py.client.geventclient import WebSocketClient | |
from event_dispatching import EventEmitter | |
from app.setup_event_loop import start_event_loop | |
from app.setup_app_logging import setup_logging | |
from gevent.event import Event | |
import gevent | |
import signal | |
import copy | |
import sys | |
import json | |
import logging | |
def react_to_incoming_messages(server): | |
""" | |
Greenlet waiting for incoming messages | |
until ``None`` is received, indicating we can | |
leave the loop. | |
""" | |
logging.info("[__GREENLET__]: started incoming loop") | |
while True: | |
m = ws.receive() | |
if m is not None: | |
print str(m) | |
#server.receive(m) | |
else: | |
break | |
def declare_ready_to(server): | |
""" | |
Greenlet to notify the Sampling Server that we | |
are ready. | |
""" | |
logging.info("[__GREENLET__]: notifying the remote sampling server") | |
server.notify_ready_to_start_experiment() | |
class Client(WebSocketClient): | |
def received_message(self, m): | |
logging.info("message received") | |
#print "=> %s" % str(m) | |
class Server(object): | |
""" | |
Models the Sampling Server and the actions that can be performed | |
It just implements 3 functions corresponding to the three messages the Sampling Server expects to be implemented. | |
:param ee: EventEmitter | |
:param server_connection: WebSocket (probably a WebSocketClient) that is already connected to the client | |
:param MESSAGES: { Experiment__ReadyToStart, Experiment__Error, Experiment__Completed } | |
""" | |
def __init__(self, ee, server_connection, MESSAGES): | |
self.ee = ee | |
self.MESSAGES = MESSAGES | |
self.server_connection = server_connection | |
def send(f): # decorator to send messages | |
def _send(self, *args, **kwargs): # decorator wrapper to access self | |
self.server_connection.send(json.dumps(f(self, *args, **kwargs))) # it sends the return value of the decorated function | |
return _send | |
@send | |
def notify_ready_to_start_experiment(self): | |
return copy.copy(self.MESSAGES['Experiment__ReadyToStart']) | |
@send | |
def send_error(self, result, error): | |
msg = copy.copy(self.MESSAGES['Experiment__Error']) | |
msg['result'] = result | |
msg['error'] = error | |
return msg | |
@send | |
def send_result(self, result): | |
msg = copy.copy(self.MESSAGES['Experiment__Completed']) | |
msg['result'] = result | |
msg['error'] = False | |
return msg | |
if __name__ == '__main__': | |
try: | |
ws = None | |
ws_server = None # the Sampling Server interface model over ws | |
event_loop = None | |
event_g = Event() # gevent event | |
# setting up the logging (otherwise it won't show) | |
logging.getLogger().setLevel(logging.INFO) | |
# termination routine to close the program (stop all active entities) | |
def terminate(signum = None, frame = None): | |
try: | |
ws.close() | |
except: | |
import traceback | |
traceback.print_exc() | |
# setup ws client | |
logging.info("[__MAIN__]: ws client connected") | |
ws = Client('ws://localhost:8000', protocols=['http-only', 'chat']) | |
ws.connect() | |
# setup event based framework | |
ee = EventEmitter() | |
event_loop = start_event_loop(ee) | |
logging.info("[__MAIN__]: ws_server setup") | |
ws_server = Server( | |
event_loop, | |
ws, | |
{ | |
"Experiment__ReadyToStart": { "code": 1, "type": "experiment" , "text": "experiment ready to start" }, | |
"Experiment__Completed": { "code": 2, "type": "experiment" , "text": "experiment completed" }, | |
"Experiment__Error": { "code": 3, "type": "experiment" , "text": "experiment failed" } | |
} | |
) | |
# setup greenlets (remember ws is a greenlet too!) | |
# | |
# Greenlets provide concurrency but not parallelism. Greenlets really shine | |
# in network programming where interactions with one socket can occur | |
# independently of interactions with other sockets. | |
# | |
# This is good because threads are very expensive in terms of virtual memory | |
# and kernel overhead, so the concurrency you can achieve with threads is | |
# significantly less. Additionally, threading in Python is more expensive | |
# and more limited than usual due to the GIL. Alternatives to concurrency | |
# are usually projects like Twisted, libevent, libuv, node.js etc, where | |
# all your code shares the same execution context, and register event handlers. | |
# | |
# It's an excellent idea to use greenlets (with appropriate networking | |
# support such as through gevent) for writing a proxy, as your handling | |
# of requests are able to execute independently and should be written as such. | |
greenlets = [ | |
gevent.spawn(declare_ready_to(ws_server)), # notify that we are ready | |
#gevent.spawn(react_to_incoming_messages(ws_server)) # wait for messages | |
] | |
gevent.joinall(greenlets) # actually launch them | |
except Exception: | |
logging.exception("[__MAIN__]: exiting due to exception") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment