socketio.namespace

class socketio.namespace.BaseNamespace(environ, ns_name, request=None)[source]

The Namespace is the primary interface a developer will use to create a gevent-socketio-based application.

You should create your own subclass of this class, optionally using one of the socketio.mixins provided (or your own), and define methods such as:

1
2
3
4
5
6
def on_my_event(self, my_first_arg, my_second_arg):
    print "This is the my_first_arg object", my_first_arg
    print "This is the my_second_arg object", my_second_arg

def on_my_second_event(self, whatever):
    print "This holds the first arg that was passed", whatever

Handlers are automatically dispatched based on the name of the incoming event. For example, a ‘user message’ event will be handled by on_user_message(). To change this, override process_event().

We can also access the full packet directly by making an event handler that accepts a single argument named ‘packet’:

1
2
3
def on_third_event(self, packet):
    print "The full packet", packet
    print "See the BaseNamespace::call_method() method for details"

Namespace initialization

You can override this method:

BaseNamespace.initialize()[source]

This is called right after __init__, on the initial creation of a namespace so you may handle any setup job you need.

Namespaces are created only when some packets arrive that ask for the namespace. They are not created altogether when a new Socket connection is established, so you can have many many namespaces assigned (when calling socketio_manage()) without clogging the memory.

If you override this method, you probably want to initialize the variables you’re going to use in the events handled by this namespace, setup ACLs, etc..

This method is called on all base classes following the method resolution order <http://docs.python.org/library/stdtypes.html?highlight=mro#class.__mro__> so you don’t need to call super() to initialize the mixins or other derived classes.

Event flow

This is an attempt at catching the gotchas of the Socket.IO protocol, which, for historical reasons, sometimes have weird event flow.

The first function to fire is initialize(), which will be called only if there is an incoming packet for the Namespace. A successful javascript call to io.connect() is not sufficient for gevent-socketio to trigger the creation of a Namespace object. Some event has to flow from the client to the server. The connection will appear to have succeeded from the client’s side, but that is because gevent-socketio maintains the virtual socket up and running before it hits your application. This is why it is a good pratice to send a packet (often a login, or subscribe or connect JSON event, with io.emit() in the browser).

If you’re using the GLOBAL_NS, the recv_connect() will not fire on your namespace, because when the connection is opened, there is no such packet sent. The connect packet is only sent over (and explicitly sent) by the javascript client when it tries to communicate with some “non-global” namespaces. That is why it is recommended to always use namespaces, to avoid having a different behavior for your different namespaces. It also makes things explicit in your application, when you have something such as /chat, or /live_data. Before a certain version of Socket.IO, there was only a global namespace, and so this behavior was kept for backwards compatibility.

Then flows the normal events, back and forth as described elsewhere (elsewhere??).

Upon disconnection, here is what happens: [INSERT HERE the details flow of disconnection handling, events fired, physical closing of the connection and ways to terminate a socket, when is the Namespace killed, the state of the spawn’d processes for each Namespace and each virtsocket. This really needs to be done, and I’d appreciate having people investigate this thoroughly]

There you go :)

Namespace instance properties

BaseNamespace.session

The session is a simple dict that is created with each Socket instance, and is copied to each Namespace created under it. It is a general purpose store for any data you want to associated with an open Socket.

BaseNamespace.request

This is the request object (or really, any object) that you have passed as the request parameter to the socketio_manage() function.

BaseNamespace.ns_name

The name of the namespace, like /chat or the empty string, for the “global” namespace.

BaseNamespace.environ

The environ WSGI dictionary, as it was received upon reception of the first request that established the virtual Socket. This will never contain the subsequent environ for the next polling, so beware when using cookie-based sessions (like Beaker).

BaseNamespace.socket

A reference to the Socket instance this namespace is attached to.

Sending data

Functions to send data through the socket:

BaseNamespace.emit(event, *args, **kwargs)[source]

Use this to send a structured event, with a name and arguments, to the client.

By default, it uses this namespace’s endpoint. You can send messages on other endpoints with something like:

self.socket['/other_endpoint'].emit().

However, it is possible that the '/other_endpoint' was not initialized yet, and that would yield a KeyError.

The only supported kwargs is callback. All other parameters must be passed positionally.

Parameters:
  • event – The name of the event to trigger on the other end.
  • callback (callable) –

    Pass in the callback keyword argument to define a call-back that will be called when the client acks.

    This callback is slightly different from the one from send(), as this callback will receive parameters from the explicit call of the ack() function passed to the listener on the client side.

    The remote listener will need to explicitly ack (by calling its last argument, a function which is usually called ‘ack’) with some parameters indicating success or error. The ‘ack’ packet coming back here will then trigger the callback function with the returned values.

BaseNamespace.send(message, json=False, callback=None)[source]

Use send to send a simple string message.

If json is True, the message will be encoded as a JSON object on the wire, and decoded on the other side.

This is mostly for backwards compatibility. emit() is more fun.

Parameters:callback (callable) – This is a callback function that will be called automatically by the client upon reception. It does not verify that the listener over there was completed with success. It just tells you that the browser got a hold of the packet.
BaseNamespace.error(error_name, error_message, msg_id=None, quiet=False)[source]

Use this to use the configured error_handler yield an error message to your application.

Parameters:
  • error_name – is a short string, to associate messages to recovery methods
  • error_message – is some human-readable text, describing the error
  • msg_id – is used to associate with a request
  • quiet – specific to error_handlers. The default doesn’t send a message to the user, but shows a debug message on the developer console.
BaseNamespace.disconnect(silent=False)[source]

Send a ‘disconnect’ packet, so that the user knows it has been disconnected (booted actually). This will trigger an onDisconnect() call on the client side.

Over here, we will kill all ``spawn``ed processes and remove the namespace from the Socket object.

Parameters:silent – do not actually send the packet (if they asked for a disconnect for example), but just kill all jobs spawned by this Namespace, and remove it from the Socket.

Dealing with incoming data

BaseNamespace.recv_connect()[source]

Called the first time a client connection is open on a Namespace. This does not fire on the global namespace.

This allows you to do boilerplate stuff for the namespace like connecting to rooms, broadcasting events to others, doing authorization work, and tweaking the ACLs to open up the rest of the namespace (if it was closed at the beginning by having get_initial_acl() return only [‘recv_connect’])

Also see the different mixins (like RoomsMixin, BroadcastMixin).

BaseNamespace.recv_message(data)[source]

This is more of a backwards compatibility hack. This will be called for messages sent with the original send() call on the client side. This is NOT the ‘message’ event, which you will catch with ‘on_message()’. The data arriving here is a simple string, with no other info.

If you want to handle those messages, you should override this method.

BaseNamespace.recv_json(data)[source]

This is more of a backwards compatibility hack. This will be called for JSON packets sent with the original json() call on the JavaScript side. This is NOT the ‘json’ event, which you will catch with ‘on_json()’. The data arriving here is a python dict, with no event name.

If you want to handle those messages, you should override this method.

BaseNamespace.recv_error(packet)[source]

Override this function to handle the errors we get from the client.

Parameters:packet – the full packet.
BaseNamespace.recv_disconnect()[source]

Override this function if you want to do something when you get a force disconnect packet.

By default, this function calls the disconnect() clean-up function. You probably want to call it yourself also, and put your clean-up routines in disconnect() rather than here, because that disconnect() function gets called automatically upon disconnection. This function is a pre-handle for when you get the disconnect packet.

BaseNamespace.exception_handler_decorator(fn)

This method can be a static, class or bound method (that is, with @staticmethod, @classmethod or without). It receives one single parameter, and that parameter will be the function the framework is trying to call because some information arrived from the remote client, for instance: on_* and recv_* functions that you declared on your namespace.

The decorator is also used to wrap called to self.spawn(self.job_something), so that if anything happens after you’ve spawn’d a greenlet, it will still catch it and handle it.

It should return a decorator with exception handling properly dealt with. For example:

import traceback, sys
import logging
def exception_handler_decorator(self, fn):
    def wrap(*args, **kwargs):
        try:
            return fn(*args, **kwargs)
        except Exception, e:
            stack = traceback.format_exception(*sys.exc_info())
            db.Evtrack.write("socketio_exception",
                             {"error": str(e),
                              "trace": stack},
                             self.request.email)
            logging.getLogger('exc_logger').exception(e)
    return wrap
BaseNamespace.process_event(packet)[source]

This function dispatches event messages to the correct functions. You should override this method only if you are not satisfied with the automatic dispatching to on_-prefixed methods. You could then implement your own dispatch. See the source code for inspiration.

There are two ways to deal with callbacks from the client side (meaning, the browser has a callback waiting for data that this server will be sending back):

The first one is simply to return an object. If the incoming packet requested has an ‘ack’ field set, meaning the browser is waiting for callback data, it will automatically be packaged and sent, associated with the ‘ackId’ from the browser. The return value must be a sequence of elements, that will be mapped to the positional parameters of the callback function on the browser side.

If you want to know that you’re dealing with a packet that requires a return value, you can do those things manually by inspecting the ack and id keys from the packet object. Your callback will behave specially if the name of the argument to your method is packet. It will fill it with the unprocessed packet object for your inspection, like this:

def on_my_callback(self, packet):
    if 'ack' in packet:
        self.emit('go_back', 'param1', id=packet['id'])

You would override this method only if you are not completely satisfied with the automatic dispatching to on_-prefixed methods. You could then implement your own dispatch. See the source code for inspiration.

Process management

Managing the different callbacks, greenlets and tasks you spawn from this namespace:

BaseNamespace.spawn(fn, *args, **kwargs)[source]

Spawn a new process, attached to this Namespace.

It will be monitored by the “watcher” process in the Socket. If the socket disconnects, all these greenlets are going to be killed, after calling BaseNamespace.disconnect()

This method uses the exception_handler_decorator. See Namespace documentation for more information.

BaseNamespace.kill_local_jobs()[source]

Kills all the jobs spawned with BaseNamespace.spawn() on a namespace object.

This will be called automatically if the watcher process detects that the Socket was closed.

ACL system

The ACL system grants access to the different on_*() and recv_*() methods of your subclass.

Developers will normally override get_initial_acl() to return a list of the functions they want to initially open. Usually, it will be an on_connect event handler, that will perform authentication and/or authorization, set some variables on the Namespace, and then open up the rest of the Namespace using lift_acl_restrictions() or more granularly with add_acl_method() and del_acl_method(). It is also possible to check these things inside initialize() when, for example, you have authenticated a Global Namespace object, and you want to re-use those credentials or authentication infos in a new Namespace:

# GLOBAL_NS = ''

class MyNamespace(BaseNamespace):
    ...
    def initialize(self):
        self.my_auth = MyAuthObjet()
        if self.socket[GLOBAL_NS].my_auth.logged_in == True:
            self.my_auth.logged_in = True

The content of the ACL is a list of strings corresponding to the full name of the methods defined on your subclass, like: "on_my_event" or "recv_json".

BaseNamespace.get_initial_acl()[source]

ACL system: If you define this function, you must return all the ‘event’ names that you want your User (the established virtual Socket) to have access to.

If you do not define this function, the user will have free access to all of the on_*() and recv_*() functions, etc.. methods.

Return something like: set(['recv_connect', 'on_public_method'])

You can later modify this list dynamically (inside on_connect() for example) using:

self.add_acl_method('on_secure_method')

self.request is available in here, if you’re already ready to do some auth. check.

The ACLs are checked by the process_packet() and/or process_event() default implementations, before calling the class’s methods.

Beware, returning None leaves the namespace completely accessible.

The methods that are open are stored in the allowed_methods attribute of the Namespace instance.

BaseNamespace.add_acl_method(method_name)[source]

ACL system: make the method_name accessible to the current socket

BaseNamespace.del_acl_method(method_name)[source]

ACL system: ensure the user will not have access to that method.

BaseNamespace.lift_acl_restrictions()[source]

ACL system: This removes restrictions on the Namespace’s methods, so that all the on_*() and recv_*() can be accessed.

BaseNamespace.reset_acl()[source]

Resets ACL to its initial value (calling get_initial_acl`() and applying that again).

This function is used internally, but can be useful to the developer:

This is the attribute where the allowed methods are stored, as a list of strings, or a single None:

.. autoattribute:: allowed_methods

Low-level methods

Packet dispatching methods. These functions are normally not overriden if you are satisfied with the normal dispatch behavior:

BaseNamespace.process_packet(packet)[source]

If you override this, NONE of the functions in this class will be called. It is responsible for dispatching to process_event() (which in turn calls on_*() and recv_*() methods).

If the packet arrived here, it is because it belongs to this endpoint.

For each packet arriving, the only possible path of execution, that is, the only methods that can be called are the following:

  • recv_connect()
  • recv_message()
  • recv_json()
  • recv_error()
  • recv_disconnect()
  • on_*()
BaseNamespace.call_method_with_acl(method_name, packet, *args)[source]

You should always use this function to call the methods, as it checks if the user is allowed according to the ACLs.

If you override process_packet() or process_event(), you should definitely want to use this instead of getattr(self, 'my_method')()

BaseNamespace.call_method(method_name, packet, *args)[source]

This function is used to implement the two behaviors on dispatched on_*() and recv_*() method calls.

Those are the two behaviors:

  • If there is only one parameter on the dispatched method and it is named packet, then pass in the packet dict as the sole parameter.
  • Otherwise, pass in the arguments as specified by the different recv_*() methods args specs, or the process_event() documentation.

This method will also consider the exception_handler_decorator. See Namespace documentation for details and examples.