Skip to main content
Warning: You are using the test version of PyPI. This is a pre-production deployment of Warehouse. Changes made here affect the production instance of TestPyPI (
Help us improve Python packaging - Donate today!

Framework for developing WebSocket servers

Project Description

Snorky is a framework for building WebSocket servers based on common patterns.

Snorky runs on top of Tornado a fast, performant, asynchronous web server. Snorky is intended to run as a separated process, therefore being able to communicate with web applications written in any programming language or web framework.

You can use Snorky DataSync service to synchronize a server-side database with a web view. You only need to add hooks somewhere (e.g. in an ORM layer) so that Snorky is notified of them. Clients need a subscription token in order to get data from Snorky.

Snorky integrates in the server side with Django ORM and Django REST Framework in order to streamline this process, but you can use it with any server technology with a bit more coding. On the client side, Snorky provides a JavaScript library that handles connections and notifications. You can also connect it easily to client-side MVC-like frameworks like AngularJS in order to close the gap between server and client MVC.

You can also use the Snorky architecture of self-contained services with an RPC over JSON interface to add new functionality other than data entities synchronization: e.g. PubSub, person to person chat or cursor synchronization.


To run a Snorky server you only need a Python interpreter (both Python 2 and Python 3 are fine) and a few dependencies.

You can install Snorky from the Python package index:

pip install snorky

Simple PubSub server

Snorky groups functionality in services, which are classes intended to attend user events in different ways. The following code shows a Snorky server with an PubSub service.

import os
from tornado.ioloop import IOLoop
from tornado.web import Application
from snorky import ServiceRegistry

from snorky.request_handlers.websocket import SnorkyWebSocketHandler
from import PubSubService

if __name__ == "__main__":
    service_registry = ServiceRegistry()
    # Every service instance has a name, here: pubsub

    # Register HTTP endpoint: ws://localhost:8002/websocket
    application = Application([
        # Each endpoint connects clients with the services of a registry
        SnorkyWebSocketHandler.get_route(service_registry, "/websocket"),
    application.listen(8002, address="") # listen on all network interfaces

        print("Snorky running...")
    except KeyboardInterrupt:

This is a minimal application making use of this service:

<!DOCTYPE html>
<html lang="en">
  <title>Snorky is easy</title>
  <script src="lib/jquery.min.js"></script>
  <script src="lib/snorky.bundle.js"></script>
    <input type="text" id="message">
    <button type="submit">Send</button>
  <ul id="messages">
  <script src="pubsub.js"></script>
var snorky = new Snorky(WebSocket, "ws://localhost:8002/websocket", {
  "pubsub": Snorky.PubSub
var pubsub =;

pubsub.subscribe({channel: 'messages'})
.then(function() {
  // Confirmation received! (optional)

pubsub.messagePublished.add(function(messageObject) {
    $('<li/>', {
    text: messageObject.message

$('form').on('submit', function(event) {
  event.preventDefault(); // don't reload the page

    channel: 'messages',
    message: $('#message').val()

DataSync service with Django and Angular

The following code shows a Django model integrated with Snorky. The @subscribable decorator adds event handlers that send notifications to the Snorky server configured in Django’s file.

from django.db import models
from snorky.backend.django import subscribable

class Task(models.Model):
    title = models.CharField(max_length=100)
    completed = models.BooleanField(default=False)

    def jsonify(self):
        # This is the model representation sent to Snorky
        # In this case it is generated by Django REST Framework,
        # but it could a simple `return json.dumps(...)`.
        from .serializers import TaskSerializer
        return TaskSerializer(self).data

The following code shows the Snorky server. It contains two registries, a frontend one (public), which is exposed to the end users and a backend one (private) who is exposed only to the server applications, protected by a password.

# Dealers (model classes and filters)                                         #

class AllTodos(BroadcastDealer):
    name = "AllTasks"
    model = "Task"

# Server startup                                                              #
if __name__ == "__main__":
    # Create two services
    datasync = DataSyncService("datasync", [AllTodos])
    datasync_backend = DataSyncBackend("datasync_backend", datasync)


    # Register the frontend and backend services in different handlers
    frontend = ServiceRegistry([datasync])
    backend = ServiceRegistry([datasync_backend])

    # Create a WebSocket frontend
    app_frontend = Application([
        SnorkyWebSocketHandler.get_route(frontend, "/ws"),

    # Create a backend, set a secret key, port and address
    app_backend = Application([
        ("/backend", BackendHTTPHandler, {
            "service_registry": backend,
            "api_key": "swordfish"

    # Start processing
    except KeyboardInterrupt:

Dealers, like AllTodos are classes that track client subscriptions to certain kinds of models. There are several kinds of dealers. Broadcast dealers notify of all changes to all subscribers, but there are other dealers that allow to specify arbitrary filtering.

Data change notifications are sent from Django ORM to the DataSyncBackend service in the backend registry, accessible through port 5002. Clients connect to receive notifications to the DataSyncService from the frontend registry, accessible through port 5001.

This is the API views file, built with Django REST Framework. It supports GET, POST, PUT and DELETE.

from . import models
from rest_framework import viewsets
import snorky.backend.django.rest_framework as snorky

class TaskViewSet(snorky.ListSubscribeModelMixin,
    model = models.Task
    dealer = "AllTasks"

Using ListSubscribeModelMixin, the view will accept an optional HTTP header, X-Snorky: Subscribe allowing the client to request a subscription token that can be exchanged for real time notifications over WebSocket.

Finally, the following code shows how data can be fetched in AngularJS, in this case querying the REST API with Restangular:

var snorky = new Snorky(WebSocket, "ws://localhost:5001/ws", {
  "datasync": Snorky.DataSync
var deltaProcessor = new Snorky.DataSync.CollectionDeltaProcessor(); = function(delta) {
  // Called each time a data change notification (delta) is received.
  // CollectionDeltaProcessor is a class that applies these deltas
  // in a collection (usually an array).

  // Here we could also inspect the delta element and show alerts to the
  // user or play a sound when data changes.

var tasks = Restangular.all("tasks").getListAndSubscription()
.then(function(response) {
  var taskArray =;

  // A collection wraps an array over an interface which is understood
  // by deltaProcessor.
  // e.g. when an insertion delta is received, deltaProcessor will push
  // an element in the collection.
  // It also allows us to specify a transformation
  // function.
  var taskCollection = new Snorky.DataSync.ArrayCollection(taskArray, {
    transformItem: function(item) {
      // Allows us to define how a data element received from a delta as
      // simple JSON will be translated to an element of this array.

      // This is useful if we use fat elements (e.g. each element has a
      // .delete() method).
      return Restangular.restangularizeElement(
        null, item, "tasks", true,, null

  // Tell the collection delta processor: updates of elements of class Task
  // should be applied to taskCollection.
  deltaProcessor.collections["Task"] = taskCollection;

  // Send our new subscription token to Snorky, so that we can receive
  // notifications for changes in tasks.{
    token: response.subscriptionToken

  // Return the array, which will be automatically updated thanks to
  // Snorky deltaProcessor.
  return taskArray;

.getListAndSubscription() is an extension method that adds the X-Snorky: Subscribe header to the request and puts the content of the X-Subscription-Token response header in response.subscriptionToken. Changes to taskArray will be automatically detected by AngularJS and will trigger the template code to update the view.

The following code shows how this array of tasks could be used in an AngularJS template:

<ul id="todo-list">
  <li ng-repeat="todo in todos track by $index">
    <div class="view">
      <input class="toggle" type="checkbox"
       ng-model-options="{ getterSetter: true }">

      <label ng-dblclick="editTodo(todo)">{{todo.title}}</label>

      <button class="destroy" ng-click="removeTodo(todo)"></button>

The full demo code is available in snorky/demos/snorky_todo, based on TodoMVC.

Other protocols

Although Snorky was built upon WebSocket, there is nothing in it preventing you to use other protocols. Indeed, Snorky comes with a SockJS so that you can use it with jurassic browsers (IE6+) with no WebSocket support, should you ever need that.


Snorky is licensed under the terms of Mozilla Public License 2.0.

This means you can use the software in both free and proprietary works of any other license without restrictions.

In case you modify the library code and make it available to others, those modifications are covered by the license too, which implies you must make source code available for the modified library files. This does not forbid you from developing extensions with other licenses though, as long as they don’t modify Snorky source code or maintain the MPL license for these parts.

Release History

Release History

This version
History Node


History Node


Download Files

Download Files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

File Name & Checksum SHA256 Checksum Help Version File Type Upload Date
snorky-0.1.0a2.tar.gz (43.7 kB) Copy SHA256 Checksum SHA256 Source Apr 12, 2015

Supported By

WebFaction WebFaction Technical Writing Elastic Elastic Search Pingdom Pingdom Monitoring Dyn Dyn DNS Sentry Sentry Error Logging CloudAMQP CloudAMQP RabbitMQ Heroku Heroku PaaS Kabu Creative Kabu Creative UX & Design Fastly Fastly CDN DigiCert DigiCert EV Certificate Rackspace Rackspace Cloud Servers DreamHost DreamHost Log Hosting