ExamplesΒΆ

The following is a simple one page example of using WebSocketAwareResource and WebSocketView with [Traversal] to control persistent objects in the resource tree. These persistent objects communicate the control changes to connected clients via a [WebSockets] connection.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import eventlet
from eventlet import wsgi
from paste.httpserver import serve
from pyramid.config import Configurator
from pyramid.response import Response
from pyramid.view import view_config
from stargate import WebSocketAwareResource, WebSocketView, is_websocket
import simplejson as json

host = "127.0.0.1"
port = 9999

home_html = """\
<html>
    <head>
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
        <script>
            $(function() {
    ws = new WebSocket("ws://%(host)s:%(port)s/jobs/1/");
    ws.onmessage = function(msg) {
        $("body").append("<p>" + msg.data + "</p>");
    };
    STARTED = false;
    $("#start-stop").click(function(evt) {
        $.post("/jobs/1/", {state: STARTED ? "stop" : "start"}, function(result) {
            STARTED = !STARTED;
            $('#start-stop span').text(STARTED ? "on" : "off");

        });
    });
});
        </script>
    </head>
    <body>
        <h1>Hi</h1>
        <button id="start-stop">Job 1 <span>off</span></button>
    </body>
</html>
""" % dict(host=host, port=port)

class JobRoot(object):
    """A container for jobs

    Gets or creates Job objects for certain ids
    """

    def __init__(self):
        self._jobs = {}

    def __getitem__(self, item):
        try:
            return self._jobs[item]
        except KeyError:
            return self.create_job(item)

    def create_job(self, id):
        job = Job(id, self)
        self._jobs[id] = job
        return job


class Job(WebSocketAwareResource):
    """This is a permanent object.

    It's responsible for maintaning a list of connected clients (websockets)
    and updating them when its state changes
    """

    def __init__(self, id, parent):
        self.__name__ = id
        self.__parent__ = parent
        self.state = "OFF"

    def control(self, state):
        """This function updates the state

        Its called by the control function (in response to a post)
        It triggers the sending of self.state to all connected clients. If you
        connect multiple browsers (or tabs) they will all be updated
        """
        self.state = state
        self.send(state)

class JobView(WebSocketView):
    """The view connects pyramid with the resource

    In this simple example it simply adds the websocket to the Job's listeners.
    It then goes to sleep *blocking the thread it's in* this is where eventlet
    comes in. In real life you'd do things like listening for updates and
    handling messages coming in on the websocket in the while block.
    """

    def handler(self, websocket):
        job = self.request.context
        job.add_listener(websocket)
        while True:
            eventlet.sleep(60)

def control(job, request):
    """Post to this view to set the state

    this will trigger Job to report the state to connected clients
    """
    state = request.POST.get("state")
    if state:
        job.control(state)
    return dict(id=job.__name__, state=job.state)

class Root(dict):
    """The root of url traversal"""

    def __init__(self):
        super(Root, self).__init__(jobs=JobRoot())

def home(request):
    """Serves up home_html, setting up a simple js demo"""
    return Response(home_html)

root = Root()

def root_factory(request):
    return root

if __name__ == '__main__':
    config = Configurator(root_factory=root_factory)
    config.add_view(home, context=Root)
    config.add_view(JobView, context=Job, custom_predicates=[is_websocket])
    config.add_view(control, context=Job, renderer="json", xhr=True)
    app = config.make_wsgi_app()
    listener = eventlet.listen((host, port))
    wsgi.server(listener, app)
[Traversal]https://pylonsproject.org/projects/pyramid/dev/narr/traversal.html

Previous topic

What are WebSockets?

Next topic

Modules