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:1def on_my_event(self, my_first_arg, my_second_arg): 2 print "This is the my_first_arg object", my_first_arg 3 print "This is the my_second_arg object", my_second_arg 4 5def on_my_second_event(self, whatever): 6 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, overrideprocess_event()
.We can also access the full packet directly by making an event handler that accepts a single argument named ‘packet’:
1def on_third_event(self, packet): 2 print "The full packet", packet 3 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 callingsocketio_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 eachSocket
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 openSocket
.
- BaseNamespace.request¶
This is the
request
object (or really, any object) that you have passed as therequest
parameter to thesocketio_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 subsequentenviron
for the next polling, so beware when using cookie-based sessions (like Beaker).
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 aKeyError
.The only supported
kwargs
iscallback
. 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 theack()
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 indisconnect()
rather than here, because thatdisconnect()
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_*
andrecv_*
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 toon_
-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
andid
keys from thepacket
object. Your callback will behave specially if the name of the argument to your method ispacket
. It will fill it with the unprocessedpacket
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.
ACL system¶
The ACL system grants access to the different
on_*()
andrecv_*()
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 anon_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 usinglift_acl_restrictions()
or more granularly withadd_acl_method()
anddel_acl_method()
. It is also possible to check these things insideinitialize()
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 = TrueThe 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_*()
andrecv_*()
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/orprocess_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 theNamespace
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_*()
andrecv_*()
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 callson_*()
andrecv_*()
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()
orprocess_event()
, you should definitely want to use this instead ofgetattr(self, 'my_method')()
- BaseNamespace.call_method(method_name, packet, *args)[source]¶
This function is used to implement the two behaviors on dispatched
on_*()
andrecv_*()
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 theprocess_event()
documentation.This method will also consider the
exception_handler_decorator
. See Namespace documentation for details and examples.