Skip to content

Instantly share code, notes, and snippets.

@rigelk
Last active May 8, 2017 17:27
Show Gist options
  • Save rigelk/c194668d88fb0838544a581f21af251b to your computer and use it in GitHub Desktop.
Save rigelk/c194668d88fb0838544a581f21af251b to your computer and use it in GitHub Desktop.
websocket client with events for an 2.7 only python experiment
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