<NME> namespace.py
<BEF> import gevent
import re
import logging
import inspect

log = logging.getLogger(__name__)


class BaseNamespace(object):

    _event_name_regex = re.compile(r'^[A-Za-z][A-Za-z0-9_ ]*$')
    """Used to match the event names, so they don't leak bizarre characters"""

    def __init__(self, environ, ns_name, request=None):
        self.environ = environ
        self.socket = environ['socketio']
    You should create your own subclass of this class, optionally using one
    of the :mod:`socketio.mixins` provided (or your own), and define methods
    such as:

    .. code-block:: python
      :linenos:

      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 :meth:`process_event`.
    
    We can also access the full packet directly by making an event handler
    that accepts a single argument named 'packet':

    .. code-block:: python
      :linenos:

      def on_third_event(self, packet):
          print "The full packet", packet
          print "See the BaseNamespace::call_method() method for details"
    """
    def __init__(self, environ, ns_name, request=None):
        self.environ = environ
        self.socket = environ['socketio']
        self.session = self.socket.session  # easily accessible session
        self.request = request
        self.ns_name = ns_name
        #: Store for ACL allowed methods.  Be careful as ``None`` means
        #: that all methods are allowed, while an empty list means every
        #: method is denied.  Value: list of strings or ``None``.  You
        #: can and should use the various ``acl`` methods to tweak this.
        self.allowed_methods = None
        self.jobs = []

        self.reset_acl()

        # Init the mixins if specified after.
        super(BaseNamespace, self).__init__()

    def is_method_allowed(self, method_name):
        """ACL system: this checks if you have access to that method_name,
        according to the set ACLs"""
        if self.allowed_methods is None:
            return True
        else:
            return method_name in self.allowed_methods

    def add_acl_method(self, method_name):
        """ACL system: make the method_name accessible to the current socket"""

        if isinstance(self.allowed_methods, set):
            self.allowed_methods.add(method_name)
        else:
            self.allowed_methods = set([method_name])

    def del_acl_method(self, method_name):
        """ACL system: ensure the user will not have access to that method."""
        if self.allowed_methods is None:
            raise ValueError(
                "Trying to delete an ACL method, but none were"
                + " defined yet! Or: No ACL restrictions yet, why would you"
                + " delete one?"
            )

        self.allowed_methods.remove(method_name)

    def lift_acl_restrictions(self):
        """ACL system: This removes restrictions on the Namespace's methods, so
        that all the ``on_*()`` and ``recv_*()`` can be accessed.
        """
        self.allowed_methods = None

    def get_initial_acl(self):
        """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:

        .. code-block:: python

           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 :meth:`process_packet` and/or
        :meth:`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.
        """
        return None
        """
        args = pkt['args']
        name = pkt['name']
        if not self._event_name_regex.match(name):
            print "Message ignored, the bastard", name
            return

    def process_packet(self, packet):
        """If you override this, NONE of the functions in this class
        will be called.  It is responsible for dispatching to
        :meth:`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_*()
        """
        packet_type = packet['type']

        if packet_type == 'event':
            return self.process_event(packet)
        elif packet_type == 'message':
            return self.call_method_with_acl('recv_message', packet,
                                             packet['data'])
        elif packet_type == 'json':
            return self.call_method_with_acl('recv_json', packet,
                                             packet['data'])
        elif packet_type == 'connect':
            self.socket.send_packet(packet)
            return self.call_method_with_acl('recv_connect', packet)
        elif packet_type == 'error':
            return self.call_method_with_acl('recv_error', packet)
        elif packet_type == 'ack':
            callback = self.socket._pop_ack_callback(packet['ackId'])
            if not callback:
                print("ERROR: No such callback for ackId %s" % packet['ackId'])
                return
            return callback(*(packet['args']))
        elif packet_type == 'disconnect':
            # Force a disconnect on the namespace.
            return self.call_method_with_acl('recv_disconnect', packet)
        else:
            print("Unprocessed packet", packet)
        # TODO: manage the other packet types: disconnect

    def process_event(self, packet):
        """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:

        .. code-block:: python

          def on_my_callback(self, packet):
              if 'ack' in packet:
                  self.emit('go_back', 'param1', id=packet['id'])
        """
        args = packet['args']
        name = packet['name']
        if not allowed_event_name_regex.match(name):
            self.error("unallowed_event_name",
                       "name must only contains alpha numerical characters")
            return

        method_name = 'on_' + name.replace(' ', '_')
        # This means the args, passed as a list, will be expanded to
        # the method arg and if you passed a dict, it will be a dict
        # as the first parameter.

        return self.call_method_with_acl(method_name, packet, *args)

    def call_method_with_acl(self, method_name, packet, *args):
        """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 :meth:`process_packet` or
        :meth:`process_event`, you should definitely want to use this
        instead of ``getattr(self, 'my_method')()``
        """
        if not self.is_method_allowed(method_name):
            self.error('method_access_denied',
                       'You do not have access to method "%s"' % method_name)
            return

        return self.call_method(method_name, packet, *args)

    def call_method(self, method_name, packet, *args):
        """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
          :meth:`process_event` documentation.

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

        """
        method = getattr(self, method_name, None)
        if method is None:
            self.error('no_such_method',
                       'The method "%s" was not found' % method_name)
            return

        specs = inspect.getargspec(method)
        func_args = specs.args
        if not len(func_args) or func_args[0] != 'self':
            self.error("invalid_method_args",
                "The server-side method is invalid, as it doesn't "
                "have 'self' as its first argument")
            return

        # Check if we need to decorate to handle exceptions
        if hasattr(self, 'exception_handler_decorator'):
            method = self.exception_handler_decorator(method)

        if len(func_args) == 2 and func_args[1] == 'packet':
            return method(packet)
        else:
            return method(*args)

    def initialize(self):
        """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
        :class:`~socketio.virtsocket.Socket` connection is established,
        so you can have many many namespaces assigned (when calling
        :func:`~socketio.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.

        """
        pass

    def recv_message(self, data):
        """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.
        """
        return data

    def recv_json(self, data):
        """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.
        """
        return data

    def recv_disconnect(self):
        """Override this function if you want to do something when you get a
        *force disconnect* packet.

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

    def recv_connect(self):
        """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 :meth:`get_initial_acl` return only
        ['recv_connect'])

        Also see the different :ref:`mixins <mixins_module>` (like
        `RoomsMixin`, `BroadcastMixin`).
        """
        pass

    def recv_error(self, packet):
        """Override this function to handle the errors we get from the client.

        :param packet: the full packet.
        """
        pass

    def error(self, error_name, error_message, msg_id=None, quiet=False):
        """Use this to use the configured ``error_handler`` yield an
        error message to your application.

        :param error_name: is a short string, to associate messages to recovery
                           methods
        :param error_message: is some human-readable text, describing the error
        :param msg_id: is used to associate with a request
        :param quiet: specific to error_handlers. The default doesn't send a
                      message to the user, but shows a debug message on the
                      developer console.
        """
        self.socket.error(error_name, error_message, endpoint=self.ns_name,
                          msg_id=msg_id, quiet=quiet)

    def send(self, message, json=False, callback=None):
        """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.

        :param callback: 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.
        :type callback: callable
        """
        pkt = dict(type="message", data=message, endpoint=self.ns_name)
        if json:
            pkt['type'] = "json"

        if callback:
            # By passing ack=True, we use the old behavior of being returned
            # an 'ack' packet, automatically triggered by the client-side
            # with no user-code being run.  The emit() version of the
            # callback is more useful I think :)  So migrate your code.
            pkt['ack'] = True
            pkt['id'] = msgid = self.socket._get_next_msgid()
            self.socket._save_ack_callback(msgid, callback)

        self.socket.send_packet(pkt)

    def emit(self, event, *args, **kwargs):
        """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.

        :param event: The name of the event to trigger on the other end.
        :param callback: 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.
        :type callback: callable
        """
        callback = kwargs.pop('callback', None)

        if kwargs:
            raise ValueError(
                "emit() only supports positional argument, to stay "
                "compatible with the Socket.IO protocol. You can "
                "however pass in a dictionary as the first argument")
        pkt = dict(type="event", name=event, args=args,
                   endpoint=self.ns_name)

        if callback:
            # By passing 'data', we indicate that we *want* an explicit ack
            # by the client code, not an automatic as with send().
            pkt['ack'] = 'data'
            pkt['id'] = msgid = self.socket._get_next_msgid()
            self.socket._save_ack_callback(msgid, callback)

        self.socket.send_packet(pkt)

    def spawn(self, fn, *args, **kwargs):
        """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.

        """
        # self.log.debug("Spawning sub-Namespace Greenlet: %s" % fn.__name__)
        if hasattr(self, 'exception_handler_decorator'):
            fn = self.exception_handler_decorator(fn)
        new = gevent.spawn(fn, *args, **kwargs)
        self.jobs.append(new)
        return new

    def disconnect(self, silent=False):
        """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.

        :param 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.
        """
        if not silent:
            packet = {"type": "disconnect",
                      "endpoint": self.ns_name}
            self.socket.send_packet(packet)
        # remove_namespace might throw GreenletExit so
        # kill_local_jobs must be in finally
        try:
            self.socket.remove_namespace(self.ns_name)
        finally:
            self.kill_local_jobs()

    def kill_local_jobs(self):
        """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.
        """
        gevent.killall(self.jobs)
        self.jobs = []

<MSG> moved regex out of class for memory efficiency

<DFF> @@ -5,12 +5,11 @@ import logging
 
 log = logging.getLogger(__name__)
 
+# Check the event name contains only alpha numerical characters
+allowed_event_name_regex = re.compile(r'^[A-Za-z][A-Za-z0-9_ ]*$')
 
 class BaseNamespace(object):
 
-    _event_name_regex = re.compile(r'^[A-Za-z][A-Za-z0-9_ ]*$')
-    """Used to match the event names, so they don't leak bizarre characters"""
-
     def __init__(self, environ, ns_name, request=None):
         self.environ = environ
         self.socket = environ['socketio']
@@ -128,7 +127,7 @@ class BaseNamespace(object):
         """
         args = pkt['args']
         name = pkt['name']
-        if not self._event_name_regex.match(name):
+        if not allowed_event_name_regex.match(name):
             print "Message ignored, the bastard", name
             return
 

{'commit': 'diff --git a/socketio/namespace.py b/socketio/namespace.py\nindex 1a3974f..bb451f9 100644\n--- a/socketio/namespace.py\n+++ b/socketio/namespace.py\n@@ -5,12 +5,11 @@ import logging\n \n log = logging.getLogger(__name__)\n \n+# Check the event name contains only alpha numerical characters\n+allowed_event_name_regex = re.compile(r\'^[A-Za-z][A-Za-z0-9_ ]*$\')\n \n class BaseNamespace(object):\n \n-    _event_name_regex = re.compile(r\'^[A-Za-z][A-Za-z0-9_ ]*$\')\n-    """Used to match the event names, so they don\'t leak bizarre characters"""\n-\n     def __init__(self, environ, ns_name, request=None):\n         self.environ = environ\n         self.socket = environ[\'socketio\']\n@@ -128,7 +127,7 @@ class BaseNamespace(object):\n         """\n         args = pkt[\'args\']\n         name = pkt[\'name\']\n-        if not self._event_name_regex.match(name):\n+        if not allowed_event_name_regex.match(name):\n             print "Message ignored, the bastard", name\n             return\n \n', 'commit_subject': 'moved regex out of class for memory efficiency', 'repo_name': 'abourget/gevent-socketio', 'committer': {'name': 'Sébastien Béal', 'email': '5616b7610becf8360f547e276e9e87eacd397c63@gmail.com', 'time_sec': '1331788713', 'tz_offset': '-420', 'date': {'seconds': '1331788713'}}, 'commit_tree': 'e8a24d98ec35283b9e60d9bc29243a18817a2937', 'commit_message': 'moved regex out of class for memory efficiency\n', 'language_name': 'Python', 'license': 'bsd-3-clause', 'hunks': '@@ -5,12 +5,11 @@ import logging\n \n log = logging.getLogger(__name__)\n \n+# Check the event name contains only alpha numerical characters\n+allowed_event_name_regex = re.compile(r\'^[A-Za-z][A-Za-z0-9_ ]*$\')\n \n class BaseNamespace(object):\n \n-    _event_name_regex = re.compile(r\'^[A-Za-z][A-Za-z0-9_ ]*$\')\n-    """Used to match the event names, so they don\'t leak bizarre characters"""\n-\n     def __init__(self, environ, ns_name, request=None):\n         self.environ = environ\n         self.socket = environ[\'socketio\']\n@@ -128,7 +127,7 @@ class BaseNamespace(object):\n         """\n         args = pkt[\'args\']\n         name = pkt[\'name\']\n-        if not self._event_name_regex.match(name):\n+        if not allowed_event_name_regex.match(name):\n             print "Message ignored, the bastard", name\n             return\n \n', 'addition_count': 3, 'deletion_count': 4, 'src_file': 'a/socketio/namespace.py', 'tgt_file': 'b/socketio/namespace.py', 'file_extension': '.py', 'before_file': 'import gevent\nimport re\nimport logging\nimport inspect\n\nlog = logging.getLogger(__name__)\n\n\nclass BaseNamespace(object):\n\n    _event_name_regex = re.compile(r\'^[A-Za-z][A-Za-z0-9_ ]*$\')\n    """Used to match the event names, so they don\'t leak bizarre characters"""\n\n    def __init__(self, environ, ns_name, request=None):\n        self.environ = environ\n        self.socket = environ[\'socketio\']\n    You should create your own subclass of this class, optionally using one\n    of the :mod:`socketio.mixins` provided (or your own), and define methods\n    such as:\n\n    .. code-block:: python\n      :linenos:\n\n      def on_my_event(self, my_first_arg, my_second_arg):\n          print "This is the my_first_arg object", my_first_arg\n          print "This is the my_second_arg object", my_second_arg\n\n      def on_my_second_event(self, whatever):\n          print "This holds the first arg that was passed", whatever\n\n    Handlers are automatically dispatched based on the name of the incoming\n    event. For example, a \'user message\' event will be handled by\n    ``on_user_message()``. To change this, override :meth:`process_event`.\n    \n    We can also access the full packet directly by making an event handler\n    that accepts a single argument named \'packet\':\n\n    .. code-block:: python\n      :linenos:\n\n      def on_third_event(self, packet):\n          print "The full packet", packet\n          print "See the BaseNamespace::call_method() method for details"\n    """\n    def __init__(self, environ, ns_name, request=None):\n        self.environ = environ\n        self.socket = environ[\'socketio\']\n        self.session = self.socket.session  # easily accessible session\n        self.request = request\n        self.ns_name = ns_name\n        #: Store for ACL allowed methods.  Be careful as ``None`` means\n        #: that all methods are allowed, while an empty list means every\n        #: method is denied.  Value: list of strings or ``None``.  You\n        #: can and should use the various ``acl`` methods to tweak this.\n        self.allowed_methods = None\n        self.jobs = []\n\n        self.reset_acl()\n\n        # Init the mixins if specified after.\n        super(BaseNamespace, self).__init__()\n\n    def is_method_allowed(self, method_name):\n        """ACL system: this checks if you have access to that method_name,\n        according to the set ACLs"""\n        if self.allowed_methods is None:\n            return True\n        else:\n            return method_name in self.allowed_methods\n\n    def add_acl_method(self, method_name):\n        """ACL system: make the method_name accessible to the current socket"""\n\n        if isinstance(self.allowed_methods, set):\n            self.allowed_methods.add(method_name)\n        else:\n            self.allowed_methods = set([method_name])\n\n    def del_acl_method(self, method_name):\n        """ACL system: ensure the user will not have access to that method."""\n        if self.allowed_methods is None:\n            raise ValueError(\n                "Trying to delete an ACL method, but none were"\n                + " defined yet! Or: No ACL restrictions yet, why would you"\n                + " delete one?"\n            )\n\n        self.allowed_methods.remove(method_name)\n\n    def lift_acl_restrictions(self):\n        """ACL system: This removes restrictions on the Namespace\'s methods, so\n        that all the ``on_*()`` and ``recv_*()`` can be accessed.\n        """\n        self.allowed_methods = None\n\n    def get_initial_acl(self):\n        """ACL system: If you define this function, you must return\n        all the \'event\' names that you want your User (the established\n        virtual Socket) to have access to.\n\n        If you do not define this function, the user will have free\n        access to all of the ``on_*()`` and ``recv_*()`` functions,\n        etc.. methods.\n\n        Return something like: ``set([\'recv_connect\', \'on_public_method\'])``\n\n        You can later modify this list dynamically (inside\n        ``on_connect()`` for example) using:\n\n        .. code-block:: python\n\n           self.add_acl_method(\'on_secure_method\')\n\n        ``self.request`` is available in here, if you\'re already ready to\n        do some auth. check.\n\n        The ACLs are checked by the :meth:`process_packet` and/or\n        :meth:`process_event` default implementations, before calling\n        the class\'s methods.\n\n        **Beware**, returning ``None`` leaves the namespace completely\n        accessible.\n\n        The methods that are open are stored in the ``allowed_methods``\n        attribute of the ``Namespace`` instance.\n        """\n        return None\n        """\n        args = pkt[\'args\']\n        name = pkt[\'name\']\n        if not self._event_name_regex.match(name):\n            print "Message ignored, the bastard", name\n            return\n\n    def process_packet(self, packet):\n        """If you override this, NONE of the functions in this class\n        will be called.  It is responsible for dispatching to\n        :meth:`process_event` (which in turn calls ``on_*()`` and\n        ``recv_*()`` methods).\n\n        If the packet arrived here, it is because it belongs to this endpoint.\n\n        For each packet arriving, the only possible path of execution, that is,\n        the only methods that *can* be called are the following:\n\n        * recv_connect()\n        * recv_message()\n        * recv_json()\n        * recv_error()\n        * recv_disconnect()\n        * on_*()\n        """\n        packet_type = packet[\'type\']\n\n        if packet_type == \'event\':\n            return self.process_event(packet)\n        elif packet_type == \'message\':\n            return self.call_method_with_acl(\'recv_message\', packet,\n                                             packet[\'data\'])\n        elif packet_type == \'json\':\n            return self.call_method_with_acl(\'recv_json\', packet,\n                                             packet[\'data\'])\n        elif packet_type == \'connect\':\n            self.socket.send_packet(packet)\n            return self.call_method_with_acl(\'recv_connect\', packet)\n        elif packet_type == \'error\':\n            return self.call_method_with_acl(\'recv_error\', packet)\n        elif packet_type == \'ack\':\n            callback = self.socket._pop_ack_callback(packet[\'ackId\'])\n            if not callback:\n                print("ERROR: No such callback for ackId %s" % packet[\'ackId\'])\n                return\n            return callback(*(packet[\'args\']))\n        elif packet_type == \'disconnect\':\n            # Force a disconnect on the namespace.\n            return self.call_method_with_acl(\'recv_disconnect\', packet)\n        else:\n            print("Unprocessed packet", packet)\n        # TODO: manage the other packet types: disconnect\n\n    def process_event(self, packet):\n        """This function dispatches ``event`` messages to the correct\n        functions. You should override this method only if you are not\n        satisfied with the automatic dispatching to\n        ``on_``-prefixed methods.  You could then implement your own dispatch.\n        See the source code for inspiration.\n\n        There are two ways to deal with callbacks from the client side\n        (meaning, the browser has a callback waiting for data that this\n        server will be sending back):\n\n        The first one is simply to return an object.  If the incoming\n        packet requested has an \'ack\' field set, meaning the browser is\n        waiting for callback data, it will automatically be packaged\n        and sent, associated with the \'ackId\' from the browser. The\n        return value must be a *sequence* of elements, that will be\n        mapped to the positional parameters of the callback function\n        on the browser side.\n\n        If you want to *know* that you\'re dealing with a packet\n        that requires a return value, you can do those things manually\n        by inspecting the ``ack`` and ``id`` keys from the ``packet``\n        object.  Your callback will behave specially if the name of\n        the argument to your method is ``packet``.  It will fill it\n        with the unprocessed ``packet`` object for your inspection,\n        like this:\n\n        .. code-block:: python\n\n          def on_my_callback(self, packet):\n              if \'ack\' in packet:\n                  self.emit(\'go_back\', \'param1\', id=packet[\'id\'])\n        """\n        args = packet[\'args\']\n        name = packet[\'name\']\n        if not allowed_event_name_regex.match(name):\n            self.error("unallowed_event_name",\n                       "name must only contains alpha numerical characters")\n            return\n\n        method_name = \'on_\' + name.replace(\' \', \'_\')\n        # This means the args, passed as a list, will be expanded to\n        # the method arg and if you passed a dict, it will be a dict\n        # as the first parameter.\n\n        return self.call_method_with_acl(method_name, packet, *args)\n\n    def call_method_with_acl(self, method_name, packet, *args):\n        """You should always use this function to call the methods,\n        as it checks if the user is allowed according to the ACLs.\n\n        If you override :meth:`process_packet` or\n        :meth:`process_event`, you should definitely want to use this\n        instead of ``getattr(self, \'my_method\')()``\n        """\n        if not self.is_method_allowed(method_name):\n            self.error(\'method_access_denied\',\n                       \'You do not have access to method "%s"\' % method_name)\n            return\n\n        return self.call_method(method_name, packet, *args)\n\n    def call_method(self, method_name, packet, *args):\n        """This function is used to implement the two behaviors on dispatched\n        ``on_*()`` and ``recv_*()`` method calls.\n\n        Those are the two behaviors:\n\n        * If there is only one parameter on the dispatched method and\n          it is named ``packet``, then pass in the packet dict as the\n          sole parameter.\n\n        * Otherwise, pass in the arguments as specified by the\n          different ``recv_*()`` methods args specs, or the\n          :meth:`process_event` documentation.\n\n        This method will also consider the\n        ``exception_handler_decorator``.  See Namespace documentation\n        for details and examples.\n\n        """\n        method = getattr(self, method_name, None)\n        if method is None:\n            self.error(\'no_such_method\',\n                       \'The method "%s" was not found\' % method_name)\n            return\n\n        specs = inspect.getargspec(method)\n        func_args = specs.args\n        if not len(func_args) or func_args[0] != \'self\':\n            self.error("invalid_method_args",\n                "The server-side method is invalid, as it doesn\'t "\n                "have \'self\' as its first argument")\n            return\n\n        # Check if we need to decorate to handle exceptions\n        if hasattr(self, \'exception_handler_decorator\'):\n            method = self.exception_handler_decorator(method)\n\n        if len(func_args) == 2 and func_args[1] == \'packet\':\n            return method(packet)\n        else:\n            return method(*args)\n\n    def initialize(self):\n        """This is called right after ``__init__``, on the initial\n        creation of a namespace so you may handle any setup job you\n        need.\n\n        Namespaces are created only when some packets arrive that ask\n        for the namespace.  They are not created altogether when a new\n        :class:`~socketio.virtsocket.Socket` connection is established,\n        so you can have many many namespaces assigned (when calling\n        :func:`~socketio.socketio_manage`) without clogging the\n        memory.\n\n        If you override this method, you probably want to initialize\n        the variables you\'re going to use in the events handled by this\n        namespace, setup ACLs, etc..\n\n        This method is called on all base classes following the _`method resolution order <http://docs.python.org/library/stdtypes.html?highlight=mro#class.__mro__>`\n        so you don\'t need to call super() to initialize the mixins or\n        other derived classes.\n\n        """\n        pass\n\n    def recv_message(self, data):\n        """This is more of a backwards compatibility hack. This will be\n        called for messages sent with the original send() call on the client\n        side. This is NOT the \'message\' event, which you will catch with\n        \'on_message()\'. The data arriving here is a simple string, with no\n        other info.\n\n        If you want to handle those messages, you should override this method.\n        """\n        return data\n\n    def recv_json(self, data):\n        """This is more of a backwards compatibility hack. This will be\n        called for JSON packets sent with the original json() call on the\n        JavaScript side. This is NOT the \'json\' event, which you will catch\n        with \'on_json()\'. The data arriving here is a python dict, with no\n        event name.\n\n        If you want to handle those messages, you should override this method.\n        """\n        return data\n\n    def recv_disconnect(self):\n        """Override this function if you want to do something when you get a\n        *force disconnect* packet.\n\n        By default, this function calls the :meth:`disconnect` clean-up\n        function.  You probably want to call it yourself also, and put\n        your clean-up routines in :meth:`disconnect` rather than here,\n        because that :meth:`disconnect` function gets called\n        automatically upon disconnection.  This function is a\n        pre-handle for when you get the `disconnect packet`.\n        """\n        self.disconnect(silent=True)\n\n    def recv_connect(self):\n        """Called the first time a client connection is open on a\n        Namespace. This *does not* fire on the global namespace.\n\n        This allows you to do boilerplate stuff for\n        the namespace like connecting to rooms, broadcasting events\n        to others, doing authorization work, and tweaking the ACLs to open\n        up the rest of the namespace (if it was closed at the\n        beginning by having :meth:`get_initial_acl` return only\n        [\'recv_connect\'])\n\n        Also see the different :ref:`mixins <mixins_module>` (like\n        `RoomsMixin`, `BroadcastMixin`).\n        """\n        pass\n\n    def recv_error(self, packet):\n        """Override this function to handle the errors we get from the client.\n\n        :param packet: the full packet.\n        """\n        pass\n\n    def error(self, error_name, error_message, msg_id=None, quiet=False):\n        """Use this to use the configured ``error_handler`` yield an\n        error message to your application.\n\n        :param error_name: is a short string, to associate messages to recovery\n                           methods\n        :param error_message: is some human-readable text, describing the error\n        :param msg_id: is used to associate with a request\n        :param quiet: specific to error_handlers. The default doesn\'t send a\n                      message to the user, but shows a debug message on the\n                      developer console.\n        """\n        self.socket.error(error_name, error_message, endpoint=self.ns_name,\n                          msg_id=msg_id, quiet=quiet)\n\n    def send(self, message, json=False, callback=None):\n        """Use send to send a simple string message.\n\n        If ``json`` is True, the message will be encoded as a JSON object\n        on the wire, and decoded on the other side.\n\n        This is mostly for backwards compatibility.  ``emit()`` is more fun.\n\n        :param callback: This is a callback function that will be\n                         called automatically by the client upon\n                         reception.  It does not verify that the\n                         listener over there was completed with\n                         success.  It just tells you that the browser\n                         got a hold of the packet.\n        :type callback: callable\n        """\n        pkt = dict(type="message", data=message, endpoint=self.ns_name)\n        if json:\n            pkt[\'type\'] = "json"\n\n        if callback:\n            # By passing ack=True, we use the old behavior of being returned\n            # an \'ack\' packet, automatically triggered by the client-side\n            # with no user-code being run.  The emit() version of the\n            # callback is more useful I think :)  So migrate your code.\n            pkt[\'ack\'] = True\n            pkt[\'id\'] = msgid = self.socket._get_next_msgid()\n            self.socket._save_ack_callback(msgid, callback)\n\n        self.socket.send_packet(pkt)\n\n    def emit(self, event, *args, **kwargs):\n        """Use this to send a structured event, with a name and arguments, to\n        the client.\n\n        By default, it uses this namespace\'s endpoint. You can send messages on\n        other endpoints with something like:\n\n            ``self.socket[\'/other_endpoint\'].emit()``.\n\n        However, it is possible that the ``\'/other_endpoint\'`` was not\n        initialized yet, and that would yield a ``KeyError``.\n\n        The only supported ``kwargs`` is ``callback``.  All other parameters\n        must be passed positionally.\n\n        :param event: The name of the event to trigger on the other end.\n        :param callback: Pass in the callback keyword argument to define a\n                         call-back that will be called when the client acks.\n\n                         This callback is slightly different from the one from\n                         ``send()``, as this callback will receive parameters\n                         from the explicit call of the ``ack()`` function\n                         passed to the listener on the client side.\n\n                         The remote listener will need to explicitly ack (by\n                         calling its last argument, a function which is\n                         usually called \'ack\') with some parameters indicating\n                         success or error.  The \'ack\' packet coming back here\n                         will then trigger the callback function with the\n                         returned values.\n        :type callback: callable\n        """\n        callback = kwargs.pop(\'callback\', None)\n\n        if kwargs:\n            raise ValueError(\n                "emit() only supports positional argument, to stay "\n                "compatible with the Socket.IO protocol. You can "\n                "however pass in a dictionary as the first argument")\n        pkt = dict(type="event", name=event, args=args,\n                   endpoint=self.ns_name)\n\n        if callback:\n            # By passing \'data\', we indicate that we *want* an explicit ack\n            # by the client code, not an automatic as with send().\n            pkt[\'ack\'] = \'data\'\n            pkt[\'id\'] = msgid = self.socket._get_next_msgid()\n            self.socket._save_ack_callback(msgid, callback)\n\n        self.socket.send_packet(pkt)\n\n    def spawn(self, fn, *args, **kwargs):\n        """Spawn a new process, attached to this Namespace.\n\n        It will be monitored by the "watcher" process in the Socket. If the\n        socket disconnects, all these greenlets are going to be killed, after\n        calling BaseNamespace.disconnect()\n\n        This method uses the ``exception_handler_decorator``.  See\n        Namespace documentation for more information.\n\n        """\n        # self.log.debug("Spawning sub-Namespace Greenlet: %s" % fn.__name__)\n        if hasattr(self, \'exception_handler_decorator\'):\n            fn = self.exception_handler_decorator(fn)\n        new = gevent.spawn(fn, *args, **kwargs)\n        self.jobs.append(new)\n        return new\n\n    def disconnect(self, silent=False):\n        """Send a \'disconnect\' packet, so that the user knows it has been\n        disconnected (booted actually).  This will trigger an onDisconnect()\n        call on the client side.\n\n        Over here, we will kill all ``spawn``ed processes and remove the\n        namespace from the Socket object.\n\n        :param silent: do not actually send the packet (if they asked for a\n                       disconnect for example), but just kill all jobs spawned\n                       by this Namespace, and remove it from the Socket.\n        """\n        if not silent:\n            packet = {"type": "disconnect",\n                      "endpoint": self.ns_name}\n            self.socket.send_packet(packet)\n        # remove_namespace might throw GreenletExit so\n        # kill_local_jobs must be in finally\n        try:\n            self.socket.remove_namespace(self.ns_name)\n        finally:\n            self.kill_local_jobs()\n\n    def kill_local_jobs(self):\n        """Kills all the jobs spawned with BaseNamespace.spawn() on a namespace\n        object.\n\n        This will be called automatically if the ``watcher`` process detects\n        that the Socket was closed.\n        """\n        gevent.killall(self.jobs)\n        self.jobs = []\n', 'message': 'moved regex out of class for memory efficiency\n', 'author_name': 'Sébastien Béal', 'lang': 'py'}