Browse Source

Internal code restructure (no functional changes)

pull/1255/head
Miguel Grinberg 2 years ago
parent
commit
ef0f88f6cf
Failed to extract signature
  1. 79
      docs/api.rst
  2. 4
      docs/conf.py
  3. 34
      src/socketio/__init__.py
  4. 2
      src/socketio/async_aiopika_manager.py
  5. 8
      src/socketio/async_client.py
  6. 8
      src/socketio/async_manager.py
  7. 6
      src/socketio/async_namespace.py
  8. 2
      src/socketio/async_pubsub_manager.py
  9. 2
      src/socketio/async_redis_manager.py
  10. 8
      src/socketio/async_server.py
  11. 2
      src/socketio/async_simple_client.py
  12. 216
      src/socketio/base_client.py
  13. 87
      src/socketio/base_manager.py
  14. 33
      src/socketio/base_namespace.py
  15. 209
      src/socketio/base_server.py
  16. 209
      src/socketio/client.py
  17. 92
      src/socketio/manager.py
  18. 62
      src/socketio/namespace.py
  19. 4
      src/socketio/pubsub_manager.py
  20. 192
      src/socketio/server.py
  21. 0
      tests/async/__init__.py
  22. 0
      tests/async/helpers.py
  23. 150
      tests/async/test_client.py
  24. 4
      tests/async/test_manager.py
  25. 44
      tests/async/test_namespace.py
  26. 32
      tests/async/test_pubsub_manager.py
  27. 158
      tests/async/test_server.py
  28. 5
      tests/async/test_simple_client.py
  29. 4
      tests/common/test_client.py
  30. 4
      tests/common/test_manager.py
  31. 12
      tests/common/test_namespace.py
  32. 22
      tests/common/test_pubsub_manager.py

79
docs/api.rst

@ -6,141 +6,86 @@ API Reference
.. module:: socketio
``SimpleClient`` class
----------------------
.. autoclass:: SimpleClient
:members:
``AsyncSimpleClient`` class
---------------------------
:inherited-members:
.. autoclass:: AsyncSimpleClient
:members:
``Client`` class
----------------
:inherited-members:
.. autoclass:: Client
:members:
``AsyncClient`` class
---------------------
:inherited-members:
.. autoclass:: AsyncClient
:members:
:inherited-members:
``Server`` class
----------------
.. autoclass:: Server
:members:
``AsyncServer`` class
---------------------
:inherited-members:
.. autoclass:: AsyncServer
:members:
:inherited-members:
``ConnectionRefusedError`` class
--------------------------------
.. autoclass:: socketio.exceptions.ConnectionRefusedError
:members:
``WSGIApp`` class
-----------------
.. autoclass:: WSGIApp
:members:
``ASGIApp`` class
-----------------
.. autoclass:: ASGIApp
:members:
``Middleware`` class (deprecated)
---------------------------------
.. autoclass:: Middleware
:members:
``ClientNamespace`` class
-------------------------
.. autoclass:: ClientNamespace
:members:
:inherited-members:
``Namespace`` class
-------------------
.. autoclass:: Namespace
:members:
:inherited-members:
``AsyncClientNamespace`` class
------------------------------
.. autoclass:: AsyncClientNamespace
:members:
:inherited-members:
``AsyncNamespace`` class
------------------------
.. autoclass:: AsyncNamespace
:members:
:inherited-members:
``BaseManager`` class
---------------------
.. autoclass:: BaseManager
.. autoclass:: Manager
:members:
``PubSubManager`` class
-----------------------
:inherited-members:
.. autoclass:: PubSubManager
:members:
``KombuManager`` class
----------------------
:inherited-members:
.. autoclass:: KombuManager
:members:
``RedisManager`` class
----------------------
:inherited-members:
.. autoclass:: RedisManager
:members:
``KafkaManager`` class
----------------------
:inherited-members:
.. autoclass:: KafkaManager
:members:
``AsyncManager`` class
----------------------
:inherited-members:
.. autoclass:: AsyncManager
:members:
:inherited-members:
``AsyncRedisManager`` class
---------------------------
.. autoclass:: AsyncRedisManager
:members:
``AsyncAioPikaManager`` class
-----------------------------
:inherited-members:
.. autoclass:: AsyncAioPikaManager
:members:
:inherited-members:

4
docs/conf.py

@ -42,7 +42,7 @@ extensions = [
'sphinx.ext.autodoc',
]
autodoc_member_order = 'bysource'
autodoc_member_order = 'alphabetical'
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@ -61,7 +61,7 @@ master_doc = 'index'
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
language = 'en'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.

34
src/socketio/__init__.py

@ -1,8 +1,6 @@
import sys
from .client import Client
from .simple_client import SimpleClient
from .base_manager import BaseManager
from .manager import Manager
from .pubsub_manager import PubSubManager
from .kombu_manager import KombuManager
from .redis_manager import RedisManager
@ -12,29 +10,19 @@ from .server import Server
from .namespace import Namespace, ClientNamespace
from .middleware import WSGIApp, Middleware
from .tornado import get_tornado_handler
if sys.version_info >= (3, 5): # pragma: no cover
from .asyncio_client import AsyncClient
from .asyncio_simple_client import AsyncSimpleClient
from .asyncio_server import AsyncServer
from .asyncio_manager import AsyncManager
from .asyncio_namespace import AsyncNamespace, AsyncClientNamespace
from .asyncio_redis_manager import AsyncRedisManager
from .asyncio_aiopika_manager import AsyncAioPikaManager
from .async_client import AsyncClient
from .async_simple_client import AsyncSimpleClient
from .async_server import AsyncServer
from .async_manager import AsyncManager
from .async_namespace import AsyncNamespace, AsyncClientNamespace
from .async_redis_manager import AsyncRedisManager
from .async_aiopika_manager import AsyncAioPikaManager
from .asgi import ASGIApp
else: # pragma: no cover
AsyncSimpleClient = None
AsyncClient = None
AsyncServer = None
AsyncManager = None
AsyncNamespace = None
AsyncRedisManager = None
AsyncAioPikaManager = None
__all__ = ['SimpleClient', 'Client', 'Server', 'BaseManager', 'PubSubManager',
__all__ = ['SimpleClient', 'Client', 'Server', 'Manager', 'PubSubManager',
'KombuManager', 'RedisManager', 'ZmqManager', 'KafkaManager',
'Namespace', 'ClientNamespace', 'WSGIApp', 'Middleware']
if AsyncServer is not None: # pragma: no cover
__all__ += ['AsyncSimpleClient', 'AsyncClient', 'AsyncServer',
'Namespace', 'ClientNamespace', 'WSGIApp', 'Middleware',
'AsyncSimpleClient', 'AsyncClient', 'AsyncServer',
'AsyncNamespace', 'AsyncClientNamespace', 'AsyncManager',
'AsyncRedisManager', 'ASGIApp', 'get_tornado_handler',
'AsyncAioPikaManager']

2
src/socketio/asyncio_aiopika_manager.py → src/socketio/async_aiopika_manager.py

@ -1,7 +1,7 @@
import asyncio
import pickle
from socketio.asyncio_pubsub_manager import AsyncPubSubManager
from .async_pubsub_manager import AsyncPubSubManager
try:
import aio_pika

8
src/socketio/asyncio_client.py → src/socketio/async_client.py

@ -4,14 +4,14 @@ import random
import engineio
from . import client
from . import base_client
from . import exceptions
from . import packet
default_logger = logging.getLogger('socketio.client')
class AsyncClient(client.Client):
class AsyncClient(base_client.BaseClient):
"""A Socket.IO client for asyncio.
This class implements a fully compliant Socket.IO web client with support
@ -456,7 +456,7 @@ class AsyncClient(client.Client):
if self._reconnect_abort is None: # pragma: no cover
self._reconnect_abort = self.eio.create_event()
self._reconnect_abort.clear()
client.reconnecting_clients.append(self)
base_client.reconnecting_clients.append(self)
attempt_count = 0
current_delay = self.reconnection_delay
while True:
@ -499,7 +499,7 @@ class AsyncClient(client.Client):
await self._trigger_event('__disconnect_final',
namespace=n)
break
client.reconnecting_clients.remove(self)
base_client.reconnecting_clients.remove(self)
async def _handle_eio_connect(self):
"""Handle the Engine.IO connection event."""

8
src/socketio/asyncio_manager.py → src/socketio/async_manager.py

@ -67,28 +67,28 @@ class AsyncManager(BaseManager):
Note: this method is a coroutine.
"""
return super().disconnect(sid, namespace, **kwargs)
return self.basic_disconnect(sid, namespace, **kwargs)
async def enter_room(self, sid, namespace, room, eio_sid=None):
"""Add a client to a room.
Note: this method is a coroutine.
"""
return super().enter_room(sid, namespace, room, eio_sid=eio_sid)
return self.basic_enter_room(sid, namespace, room, eio_sid=eio_sid)
async def leave_room(self, sid, namespace, room):
"""Remove a client from a room.
Note: this method is a coroutine.
"""
return super().leave_room(sid, namespace, room)
return self.basic_leave_room(sid, namespace, room)
async def close_room(self, room, namespace):
"""Remove all participants from a room.
Note: this method is a coroutine.
"""
return super().close_room(room, namespace)
return self.basic_close_room(room, namespace)
async def trigger_callback(self, sid, id, data):
"""Invoke an application callback.

6
src/socketio/asyncio_namespace.py → src/socketio/async_namespace.py

@ -1,9 +1,9 @@
import asyncio
from socketio import namespace
from socketio import base_namespace
class AsyncNamespace(namespace.Namespace):
class AsyncNamespace(base_namespace.BaseServerNamespace):
"""Base class for asyncio server-side class-based namespaces.
A class-based namespace is a class that contains all the event handlers
@ -168,7 +168,7 @@ class AsyncNamespace(namespace.Namespace):
sid, namespace=namespace or self.namespace)
class AsyncClientNamespace(namespace.ClientNamespace):
class AsyncClientNamespace(base_namespace.BaseClientNamespace):
"""Base class for asyncio client-side class-based namespaces.
A class-based namespace is a class that contains all the event handlers

2
src/socketio/asyncio_pubsub_manager.py → src/socketio/async_pubsub_manager.py

@ -5,7 +5,7 @@ import uuid
from engineio import json
import pickle
from .asyncio_manager import AsyncManager
from .async_manager import AsyncManager
class AsyncPubSubManager(AsyncManager):

2
src/socketio/asyncio_redis_manager.py → src/socketio/async_redis_manager.py

@ -12,7 +12,7 @@ except ImportError: # pragma: no cover
aioredis = None
RedisError = None
from .asyncio_pubsub_manager import AsyncPubSubManager
from .async_pubsub_manager import AsyncPubSubManager
class AsyncRedisManager(AsyncPubSubManager): # pragma: no cover

8
src/socketio/asyncio_server.py → src/socketio/async_server.py

@ -2,13 +2,13 @@ import asyncio
import engineio
from . import asyncio_manager
from . import async_manager
from . import base_server
from . import exceptions
from . import packet
from . import server
class AsyncServer(server.Server):
class AsyncServer(base_server.BaseServer):
"""A Socket.IO server for asyncio.
This class implements a fully compliant Socket.IO web server with support
@ -104,7 +104,7 @@ class AsyncServer(server.Server):
def __init__(self, client_manager=None, logger=False, json=None,
async_handlers=True, namespaces=None, **kwargs):
if client_manager is None:
client_manager = asyncio_manager.AsyncManager()
client_manager = async_manager.AsyncManager()
super().__init__(client_manager=client_manager, logger=logger,
json=json, async_handlers=async_handlers,
namespaces=namespaces, **kwargs)

2
src/socketio/asyncio_simple_client.py → src/socketio/async_simple_client.py

@ -192,7 +192,7 @@ class AsyncSimpleClient:
"""Disconnect from the server.
Note: this method is a coroutine.
i """
"""
if self.connected:
await self.client.disconnect()
self.client = None

216
src/socketio/base_client.py

@ -0,0 +1,216 @@
import itertools
import logging
from . import base_namespace
from . import packet
default_logger = logging.getLogger('socketio.client')
reconnecting_clients = []
class BaseClient:
reserved_events = ['connect', 'connect_error', 'disconnect',
'__disconnect_final']
def __init__(self, reconnection=True, reconnection_attempts=0,
reconnection_delay=1, reconnection_delay_max=5,
randomization_factor=0.5, logger=False, serializer='default',
json=None, handle_sigint=True, **kwargs):
self.reconnection = reconnection
self.reconnection_attempts = reconnection_attempts
self.reconnection_delay = reconnection_delay
self.reconnection_delay_max = reconnection_delay_max
self.randomization_factor = randomization_factor
self.handle_sigint = handle_sigint
engineio_options = kwargs
engineio_options['handle_sigint'] = handle_sigint
engineio_logger = engineio_options.pop('engineio_logger', None)
if engineio_logger is not None:
engineio_options['logger'] = engineio_logger
if serializer == 'default':
self.packet_class = packet.Packet
elif serializer == 'msgpack':
from . import msgpack_packet
self.packet_class = msgpack_packet.MsgPackPacket
else:
self.packet_class = serializer
if json is not None:
self.packet_class.json = json
engineio_options['json'] = json
self.eio = self._engineio_client_class()(**engineio_options)
self.eio.on('connect', self._handle_eio_connect)
self.eio.on('message', self._handle_eio_message)
self.eio.on('disconnect', self._handle_eio_disconnect)
if not isinstance(logger, bool):
self.logger = logger
else:
self.logger = default_logger
if self.logger.level == logging.NOTSET:
if logger:
self.logger.setLevel(logging.INFO)
else:
self.logger.setLevel(logging.ERROR)
self.logger.addHandler(logging.StreamHandler())
self.connection_url = None
self.connection_headers = None
self.connection_auth = None
self.connection_transports = None
self.connection_namespaces = []
self.socketio_path = None
self.sid = None
self.connected = False #: Indicates if the client is connected or not.
self.namespaces = {} #: set of connected namespaces.
self.handlers = {}
self.namespace_handlers = {}
self.callbacks = {}
self._binary_packet = None
self._connect_event = None
self._reconnect_task = None
self._reconnect_abort = None
def is_asyncio_based(self):
return False
def on(self, event, handler=None, namespace=None):
"""Register an event handler.
:param event: The event name. It can be any string. The event names
``'connect'``, ``'message'`` and ``'disconnect'`` are
reserved and should not be used.
:param handler: The function that should be invoked to handle the
event. When this parameter is not given, the method
acts as a decorator for the handler function.
:param namespace: The Socket.IO namespace for the event. If this
argument is omitted the handler is associated with
the default namespace.
Example usage::
# as a decorator:
@sio.on('connect')
def connect_handler():
print('Connected!')
# as a method:
def message_handler(msg):
print('Received message: ', msg)
sio.send( 'response')
sio.on('message', message_handler)
The ``'connect'`` event handler receives no arguments. The
``'message'`` handler and handlers for custom event names receive the
message payload as only argument. Any values returned from a message
handler will be passed to the client's acknowledgement callback
function if it exists. The ``'disconnect'`` handler does not take
arguments.
"""
namespace = namespace or '/'
def set_handler(handler):
if namespace not in self.handlers:
self.handlers[namespace] = {}
self.handlers[namespace][event] = handler
return handler
if handler is None:
return set_handler
set_handler(handler)
def event(self, *args, **kwargs):
"""Decorator to register an event handler.
This is a simplified version of the ``on()`` method that takes the
event name from the decorated function.
Example usage::
@sio.event
def my_event(data):
print('Received data: ', data)
The above example is equivalent to::
@sio.on('my_event')
def my_event(data):
print('Received data: ', data)
A custom namespace can be given as an argument to the decorator::
@sio.event(namespace='/test')
def my_event(data):
print('Received data: ', data)
"""
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
# the decorator was invoked without arguments
# args[0] is the decorated function
return self.on(args[0].__name__)(args[0])
else:
# the decorator was invoked with arguments
def set_handler(handler):
return self.on(handler.__name__, *args, **kwargs)(handler)
return set_handler
def register_namespace(self, namespace_handler):
"""Register a namespace handler object.
:param namespace_handler: An instance of a :class:`Namespace`
subclass that handles all the event traffic
for a namespace.
"""
if not isinstance(namespace_handler,
base_namespace.BaseClientNamespace):
raise ValueError('Not a namespace instance')
if self.is_asyncio_based() != namespace_handler.is_asyncio_based():
raise ValueError('Not a valid namespace class for this client')
namespace_handler._set_client(self)
self.namespace_handlers[namespace_handler.namespace] = \
namespace_handler
def get_sid(self, namespace=None):
"""Return the ``sid`` associated with a connection.
:param namespace: The Socket.IO namespace. If this argument is omitted
the handler is associated with the default
namespace. Note that unlike previous versions, the
current version of the Socket.IO protocol uses
different ``sid`` values per namespace.
This method returns the ``sid`` for the requested namespace as a
string.
"""
return self.namespaces.get(namespace or '/')
def transport(self):
"""Return the name of the transport used by the client.
The two possible values returned by this function are ``'polling'``
and ``'websocket'``.
"""
return self.eio.transport()
def _generate_ack_id(self, namespace, callback):
"""Generate a unique identifier for an ACK packet."""
namespace = namespace or '/'
if namespace not in self.callbacks:
self.callbacks[namespace] = {0: itertools.count(1)}
id = next(self.callbacks[namespace][0])
self.callbacks[namespace][id] = callback
return id
def _handle_eio_connect(self): # pragma: no cover
raise NotImplementedError()
def _handle_eio_message(self, data): # pragma: no cover
raise NotImplementedError()
def _handle_eio_disconnect(self): # pragma: no cover
raise NotImplementedError()
def _engineio_client_class(self): # pragma: no cover
raise NotImplementedError()

87
src/socketio/base_manager.py

@ -2,21 +2,11 @@ import itertools
import logging
from bidict import bidict, ValueDuplicationError
from engineio import packet as eio_packet
from socketio import packet
default_logger = logging.getLogger('socketio')
class BaseManager(object):
"""Manage client connections.
This class keeps track of all the clients and the rooms they are in, to
support the broadcasting of messages. The data used by this class is
stored in a memory structure, making it appropriate only for single process
services. More sophisticated storage backends can be implemented by
subclasses.
"""
class BaseManager:
def __init__(self):
self.logger = None
self.server = None
@ -82,9 +72,6 @@ class BaseManager(object):
if namespace in self.rooms:
return self.rooms[namespace][None].get(sid)
def can_disconnect(self, sid, namespace):
return self.is_connected(sid, namespace)
def pre_disconnect(self, sid, namespace):
"""Put the client in the to-be-disconnected list.
@ -97,8 +84,7 @@ class BaseManager(object):
self.pending_disconnect[namespace].append(sid)
return self.rooms[namespace][None].get(sid)
def disconnect(self, sid, namespace, **kwargs):
"""Register a client disconnect from a namespace."""
def basic_disconnect(self, sid, namespace, **kwargs):
if namespace not in self.rooms:
return
rooms = []
@ -116,7 +102,6 @@ class BaseManager(object):
del self.pending_disconnect[namespace]
def basic_enter_room(self, sid, namespace, room, eio_sid=None):
"""Add a client to a room."""
if eio_sid is None and namespace not in self.rooms:
raise ValueError('sid is not connected to requested namespace')
if namespace not in self.rooms:
@ -128,7 +113,6 @@ class BaseManager(object):
self.rooms[namespace][room][sid] = eio_sid
def basic_leave_room(self, sid, namespace, room):
"""Remove a client from a room."""
try:
del self.rooms[namespace][room][sid]
if len(self.rooms[namespace][room]) == 0:
@ -138,16 +122,7 @@ class BaseManager(object):
except KeyError:
pass
def enter_room(self, sid, namespace, room, eio_sid=None):
"""Add a client to a room."""
self.basic_enter_room(sid, namespace, room, eio_sid=eio_sid)
def leave_room(self, sid, namespace, room):
"""Remove a client from a room."""
self.basic_leave_room(sid, namespace, room)
def close_room(self, room, namespace):
"""Remove all participants from a room."""
def basic_close_room(self, room, namespace):
try:
for sid, _ in self.get_participants(namespace, room):
self.basic_leave_room(sid, namespace, room)
@ -165,62 +140,6 @@ class BaseManager(object):
pass
return r
def emit(self, event, data, namespace, room=None, skip_sid=None,
callback=None, **kwargs):
"""Emit a message to a single client, a room, or all the clients
connected to the namespace."""
if namespace not in self.rooms:
return
if isinstance(data, tuple):
# tuples are expanded to multiple arguments, everything else is
# sent as a single argument
data = list(data)
elif data is not None:
data = [data]
else:
data = []
if not isinstance(skip_sid, list):
skip_sid = [skip_sid]
if not callback:
# when callbacks aren't used the packets sent to each recipient are
# identical, so they can be generated once and reused
pkt = self.server.packet_class(
packet.EVENT, namespace=namespace, data=[event] + data)
encoded_packet = pkt.encode()
if not isinstance(encoded_packet, list):
encoded_packet = [encoded_packet]
eio_pkt = [eio_packet.Packet(eio_packet.MESSAGE, p)
for p in encoded_packet]
for sid, eio_sid in self.get_participants(namespace, room):
if sid not in skip_sid:
for p in eio_pkt:
self.server._send_eio_packet(eio_sid, p)
else:
# callbacks are used, so each recipient must be sent a packet that
# contains a unique callback id
# note that callbacks when addressing a group of people are
# implemented but not tested or supported
for sid, eio_sid in self.get_participants(namespace, room):
if sid not in skip_sid: # pragma: no branch
id = self._generate_ack_id(sid, callback)
pkt = self.server.packet_class(
packet.EVENT, namespace=namespace, data=[event] + data,
id=id)
self.server._send_packet(eio_sid, pkt)
def trigger_callback(self, sid, id, data):
"""Invoke an application callback."""
callback = None
try:
callback = self.callbacks[sid][id]
except KeyError:
# if we get an unknown callback we just ignore it
self._get_logger().warning('Unknown callback received, ignoring.')
else:
del self.callbacks[sid][id]
if callback is not None:
callback(*data)
def _generate_ack_id(self, sid, callback):
"""Generate a unique identifier for an ACK packet."""
if sid not in self.callbacks:

33
src/socketio/base_namespace.py

@ -0,0 +1,33 @@
class BaseNamespace(object):
def __init__(self, namespace=None):
self.namespace = namespace or '/'
def is_asyncio_based(self):
return False
class BaseServerNamespace(BaseNamespace):
def __init__(self, namespace=None):
super().__init__(namespace=namespace)
self.server = None
def _set_server(self, server):
self.server = server
def rooms(self, sid, namespace=None):
"""Return the rooms a client is in.
The only difference with the :func:`socketio.Server.rooms` method is
that when the ``namespace`` argument is not given the namespace
associated with the class is used.
"""
return self.server.rooms(sid, namespace=namespace or self.namespace)
class BaseClientNamespace(BaseNamespace):
def __init__(self, namespace=None):
super().__init__(namespace=namespace)
self.client = None
def _set_client(self, client):
self.client = client

209
src/socketio/base_server.py

@ -0,0 +1,209 @@
import logging
from . import manager
from . import base_namespace
from . import packet
default_logger = logging.getLogger('socketio.server')
class BaseServer:
reserved_events = ['connect', 'disconnect']
def __init__(self, client_manager=None, logger=False, serializer='default',
json=None, async_handlers=True, always_connect=False,
namespaces=None, **kwargs):
engineio_options = kwargs
engineio_logger = engineio_options.pop('engineio_logger', None)
if engineio_logger is not None:
engineio_options['logger'] = engineio_logger
if serializer == 'default':
self.packet_class = packet.Packet
elif serializer == 'msgpack':
from . import msgpack_packet
self.packet_class = msgpack_packet.MsgPackPacket
else:
self.packet_class = serializer
if json is not None:
self.packet_class.json = json
engineio_options['json'] = json
engineio_options['async_handlers'] = False
self.eio = self._engineio_server_class()(**engineio_options)
self.eio.on('connect', self._handle_eio_connect)
self.eio.on('message', self._handle_eio_message)
self.eio.on('disconnect', self._handle_eio_disconnect)
self.environ = {}
self.handlers = {}
self.namespace_handlers = {}
self.not_handled = object()
self._binary_packet = {}
if not isinstance(logger, bool):
self.logger = logger
else:
self.logger = default_logger
if self.logger.level == logging.NOTSET:
if logger:
self.logger.setLevel(logging.INFO)
else:
self.logger.setLevel(logging.ERROR)
self.logger.addHandler(logging.StreamHandler())
if client_manager is None:
client_manager = manager.Manager()
self.manager = client_manager
self.manager.set_server(self)
self.manager_initialized = False
self.async_handlers = async_handlers
self.always_connect = always_connect
self.namespaces = namespaces or ['/']
self.async_mode = self.eio.async_mode
def is_asyncio_based(self):
return False
def on(self, event, handler=None, namespace=None):
"""Register an event handler.
:param event: The event name. It can be any string. The event names
``'connect'``, ``'message'`` and ``'disconnect'`` are
reserved and should not be used.
:param handler: The function that should be invoked to handle the
event. When this parameter is not given, the method
acts as a decorator for the handler function.
:param namespace: The Socket.IO namespace for the event. If this
argument is omitted the handler is associated with
the default namespace.
Example usage::
# as a decorator:
@sio.on('connect', namespace='/chat')
def connect_handler(sid, environ):
print('Connection request')
if environ['REMOTE_ADDR'] in blacklisted:
return False # reject
# as a method:
def message_handler(sid, msg):
print('Received message: ', msg)
sio.send(sid, 'response')
socket_io.on('message', namespace='/chat', handler=message_handler)
The handler function receives the ``sid`` (session ID) for the
client as first argument. The ``'connect'`` event handler receives the
WSGI environment as a second argument, and can return ``False`` to
reject the connection. The ``'message'`` handler and handlers for
custom event names receive the message payload as a second argument.
Any values returned from a message handler will be passed to the
client's acknowledgement callback function if it exists. The
``'disconnect'`` handler does not take a second argument.
"""
namespace = namespace or '/'
def set_handler(handler):
if namespace not in self.handlers:
self.handlers[namespace] = {}
self.handlers[namespace][event] = handler
return handler
if handler is None:
return set_handler
set_handler(handler)
def event(self, *args, **kwargs):
"""Decorator to register an event handler.
This is a simplified version of the ``on()`` method that takes the
event name from the decorated function.
Example usage::
@sio.event
def my_event(data):
print('Received data: ', data)
The above example is equivalent to::
@sio.on('my_event')
def my_event(data):
print('Received data: ', data)
A custom namespace can be given as an argument to the decorator::
@sio.event(namespace='/test')
def my_event(data):
print('Received data: ', data)
"""
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
# the decorator was invoked without arguments
# args[0] is the decorated function
return self.on(args[0].__name__)(args[0])
else:
# the decorator was invoked with arguments
def set_handler(handler):
return self.on(handler.__name__, *args, **kwargs)(handler)
return set_handler
def register_namespace(self, namespace_handler):
"""Register a namespace handler object.
:param namespace_handler: An instance of a :class:`Namespace`
subclass that handles all the event traffic
for a namespace.
"""
if not isinstance(namespace_handler,
base_namespace.BaseServerNamespace):
raise ValueError('Not a namespace instance')
if self.is_asyncio_based() != namespace_handler.is_asyncio_based():
raise ValueError('Not a valid namespace class for this server')
namespace_handler._set_server(self)
self.namespace_handlers[namespace_handler.namespace] = \
namespace_handler
def rooms(self, sid, namespace=None):
"""Return the rooms a client is in.
:param sid: Session ID of the client.
:param namespace: The Socket.IO namespace for the event. If this
argument is omitted the default namespace is used.
"""
namespace = namespace or '/'
return self.manager.get_rooms(sid, namespace)
def transport(self, sid):
"""Return the name of the transport used by the client.
The two possible values returned by this function are ``'polling'``
and ``'websocket'``.
:param sid: The session of the client.
"""
return self.eio.transport(sid)
def get_environ(self, sid, namespace=None):
"""Return the WSGI environ dictionary for a client.
:param sid: The session of the client.
:param namespace: The Socket.IO namespace. If this argument is omitted
the default namespace is used.
"""
eio_sid = self.manager.eio_sid_from_sid(sid, namespace or '/')
return self.environ.get(eio_sid)
def _handle_eio_connect(self): # pragma: no cover
raise NotImplementedError()
def _handle_eio_message(self, data): # pragma: no cover
raise NotImplementedError()
def _handle_eio_disconnect(self): # pragma: no cover
raise NotImplementedError()
def _engineio_server_class(self): # pragma: no cover
raise NotImplementedError('Must be implemented in subclasses')

209
src/socketio/client.py

@ -1,18 +1,13 @@
import itertools
import logging
import random
import signal
import threading
import engineio
from . import base_client
from . import exceptions
from . import namespace
from . import packet
default_logger = logging.getLogger('socketio.client')
reconnecting_clients = []
def signal_handler(sig, frame): # pragma: no cover
"""SIGINT handler.
@ -20,7 +15,7 @@ def signal_handler(sig, frame): # pragma: no cover
Notify any clients that are in a reconnect loop to abort. Other
disconnection tasks are handled at the engine.io level.
"""
for client in reconnecting_clients[:]:
for client in base_client.reconnecting_clients[:]:
client._reconnect_abort.set()
if callable(original_signal_handler):
return original_signal_handler(sig, frame)
@ -32,7 +27,7 @@ def signal_handler(sig, frame): # pragma: no cover
original_signal_handler = None
class Client(object):
class Client(base_client.BaseClient):
"""A Socket.IO client.
This class implements a fully compliant Socket.IO web client with support
@ -92,9 +87,6 @@ class Client(object):
fatal errors are logged even when
``engineio_logger`` is ``False``.
"""
reserved_events = ['connect', 'connect_error', 'disconnect',
'__disconnect_final']
def __init__(self, reconnection=True, reconnection_attempts=0,
reconnection_delay=1, reconnection_delay_max=5,
randomization_factor=0.5, logger=False, serializer='default',
@ -104,160 +96,14 @@ class Client(object):
threading.current_thread() == threading.main_thread():
original_signal_handler = signal.signal(signal.SIGINT,
signal_handler)
self.reconnection = reconnection
self.reconnection_attempts = reconnection_attempts
self.reconnection_delay = reconnection_delay
self.reconnection_delay_max = reconnection_delay_max
self.randomization_factor = randomization_factor
self.handle_sigint = handle_sigint
engineio_options = kwargs
engineio_options['handle_sigint'] = handle_sigint
engineio_logger = engineio_options.pop('engineio_logger', None)
if engineio_logger is not None:
engineio_options['logger'] = engineio_logger
if serializer == 'default':
self.packet_class = packet.Packet
elif serializer == 'msgpack':
from . import msgpack_packet
self.packet_class = msgpack_packet.MsgPackPacket
else:
self.packet_class = serializer
if json is not None:
self.packet_class.json = json
engineio_options['json'] = json
self.eio = self._engineio_client_class()(**engineio_options)
self.eio.on('connect', self._handle_eio_connect)
self.eio.on('message', self._handle_eio_message)
self.eio.on('disconnect', self._handle_eio_disconnect)
if not isinstance(logger, bool):
self.logger = logger
else:
self.logger = default_logger
if self.logger.level == logging.NOTSET:
if logger:
self.logger.setLevel(logging.INFO)
else:
self.logger.setLevel(logging.ERROR)
self.logger.addHandler(logging.StreamHandler())
self.connection_url = None
self.connection_headers = None
self.connection_auth = None
self.connection_transports = None
self.connection_namespaces = []
self.socketio_path = None
self.sid = None
self.connected = False #: Indicates if the client is connected or not.
self.namespaces = {} #: set of connected namespaces.
self.handlers = {}
self.namespace_handlers = {}
self.callbacks = {}
self._binary_packet = None
self._connect_event = None
self._reconnect_task = None
self._reconnect_abort = None
def is_asyncio_based(self):
return False
def on(self, event, handler=None, namespace=None):
"""Register an event handler.
:param event: The event name. It can be any string. The event names
``'connect'``, ``'message'`` and ``'disconnect'`` are
reserved and should not be used.
:param handler: The function that should be invoked to handle the
event. When this parameter is not given, the method
acts as a decorator for the handler function.
:param namespace: The Socket.IO namespace for the event. If this
argument is omitted the handler is associated with
the default namespace.
Example usage::
# as a decorator:
@sio.on('connect')
def connect_handler():
print('Connected!')
# as a method:
def message_handler(msg):
print('Received message: ', msg)
sio.send( 'response')
sio.on('message', message_handler)
The ``'connect'`` event handler receives no arguments. The
``'message'`` handler and handlers for custom event names receive the
message payload as only argument. Any values returned from a message
handler will be passed to the client's acknowledgement callback
function if it exists. The ``'disconnect'`` handler does not take
arguments.
"""
namespace = namespace or '/'
def set_handler(handler):
if namespace not in self.handlers:
self.handlers[namespace] = {}
self.handlers[namespace][event] = handler
return handler
if handler is None:
return set_handler
set_handler(handler)
def event(self, *args, **kwargs):
"""Decorator to register an event handler.
This is a simplified version of the ``on()`` method that takes the
event name from the decorated function.
Example usage::
@sio.event
def my_event(data):
print('Received data: ', data)
The above example is equivalent to::
@sio.on('my_event')
def my_event(data):
print('Received data: ', data)
A custom namespace can be given as an argument to the decorator::
@sio.event(namespace='/test')
def my_event(data):
print('Received data: ', data)
"""
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
# the decorator was invoked without arguments
# args[0] is the decorated function
return self.on(args[0].__name__)(args[0])
else:
# the decorator was invoked with arguments
def set_handler(handler):
return self.on(handler.__name__, *args, **kwargs)(handler)
return set_handler
def register_namespace(self, namespace_handler):
"""Register a namespace handler object.
:param namespace_handler: An instance of a :class:`Namespace`
subclass that handles all the event traffic
for a namespace.
"""
if not isinstance(namespace_handler, namespace.ClientNamespace):
raise ValueError('Not a namespace instance')
if self.is_asyncio_based() != namespace_handler.is_asyncio_based():
raise ValueError('Not a valid namespace class for this client')
namespace_handler._set_client(self)
self.namespace_handlers[namespace_handler.namespace] = \
namespace_handler
super().__init__(reconnection=reconnection,
reconnection_attempts=reconnection_attempts,
reconnection_delay=reconnection_delay,
reconnection_delay_max=reconnection_delay_max,
randomization_factor=randomization_factor,
logger=logger, serializer=serializer, json=json,
handle_sigint=handle_sigint, **kwargs)
def connect(self, url, headers={}, auth=None, transports=None,
namespaces=None, socketio_path='socket.io', wait=True,
@ -484,28 +330,6 @@ class Client(object):
packet.DISCONNECT, namespace=n))
self.eio.disconnect(abort=True)
def get_sid(self, namespace=None):
"""Return the ``sid`` associated with a connection.
:param namespace: The Socket.IO namespace. If this argument is omitted
the handler is associated with the default
namespace. Note that unlike previous versions, the
current version of the Socket.IO protocol uses
different ``sid`` values per namespace.
This method returns the ``sid`` for the requested namespace as a
string.
"""
return self.namespaces.get(namespace or '/')
def transport(self):
"""Return the name of the transport used by the client.
The two possible values returned by this function are ``'polling'``
and ``'websocket'``.
"""
return self.eio.transport()
def start_background_task(self, target, *args, **kwargs):
"""Start a background task using the appropriate async model.
@ -549,15 +373,6 @@ class Client(object):
else:
self.eio.send(encoded_packet)
def _generate_ack_id(self, namespace, callback):
"""Generate a unique identifier for an ACK packet."""
namespace = namespace or '/'
if namespace not in self.callbacks:
self.callbacks[namespace] = {0: itertools.count(1)}
id = next(self.callbacks[namespace][0])
self.callbacks[namespace][id] = callback
return id
def _handle_connect(self, namespace, data):
namespace = namespace or '/'
if namespace not in self.namespaces:
@ -643,7 +458,7 @@ class Client(object):
if self._reconnect_abort is None: # pragma: no cover
self._reconnect_abort = self.eio.create_event()
self._reconnect_abort.clear()
reconnecting_clients.append(self)
base_client.reconnecting_clients.append(self)
attempt_count = 0
current_delay = self.reconnection_delay
while True:
@ -681,7 +496,7 @@ class Client(object):
for n in self.connection_namespaces:
self._trigger_event('__disconnect_final', namespace=n)
break
reconnecting_clients.remove(self)
base_client.reconnecting_clients.remove(self)
def _handle_eio_connect(self):
"""Handle the Engine.IO connection event."""

92
src/socketio/manager.py

@ -0,0 +1,92 @@
import logging
from engineio import packet as eio_packet
from . import base_manager
from . import packet
default_logger = logging.getLogger('socketio')
class Manager(base_manager.BaseManager):
"""Manage client connections.
This class keeps track of all the clients and the rooms they are in, to
support the broadcasting of messages. The data used by this class is
stored in a memory structure, making it appropriate only for single process
services. More sophisticated storage backends can be implemented by
subclasses.
"""
def can_disconnect(self, sid, namespace):
return self.is_connected(sid, namespace)
def emit(self, event, data, namespace, room=None, skip_sid=None,
callback=None, **kwargs):
"""Emit a message to a single client, a room, or all the clients
connected to the namespace."""
if namespace not in self.rooms:
return
if isinstance(data, tuple):
# tuples are expanded to multiple arguments, everything else is
# sent as a single argument
data = list(data)
elif data is not None:
data = [data]
else:
data = []
if not isinstance(skip_sid, list):
skip_sid = [skip_sid]
if not callback:
# when callbacks aren't used the packets sent to each recipient are
# identical, so they can be generated once and reused
pkt = self.server.packet_class(
packet.EVENT, namespace=namespace, data=[event] + data)
encoded_packet = pkt.encode()
if not isinstance(encoded_packet, list):
encoded_packet = [encoded_packet]
eio_pkt = [eio_packet.Packet(eio_packet.MESSAGE, p)
for p in encoded_packet]
for sid, eio_sid in self.get_participants(namespace, room):
if sid not in skip_sid:
for p in eio_pkt:
self.server._send_eio_packet(eio_sid, p)
else:
# callbacks are used, so each recipient must be sent a packet that
# contains a unique callback id
# note that callbacks when addressing a group of people are
# implemented but not tested or supported
for sid, eio_sid in self.get_participants(namespace, room):
if sid not in skip_sid: # pragma: no branch
id = self._generate_ack_id(sid, callback)
pkt = self.server.packet_class(
packet.EVENT, namespace=namespace, data=[event] + data,
id=id)
self.server._send_packet(eio_sid, pkt)
def disconnect(self, sid, namespace, **kwargs):
"""Register a client disconnect from a namespace."""
return self.basic_disconnect(sid, namespace)
def enter_room(self, sid, namespace, room, eio_sid=None):
"""Add a client to a room."""
return self.basic_enter_room(sid, namespace, room, eio_sid=eio_sid)
def leave_room(self, sid, namespace, room):
"""Remove a client from a room."""
return self.basic_leave_room(sid, namespace, room)
def close_room(self, room, namespace):
"""Remove all participants from a room."""
return self.basic_close_room(room, namespace)
def trigger_callback(self, sid, id, data):
"""Invoke an application callback."""
callback = None
try:
callback = self.callbacks[sid][id]
except KeyError:
# if we get an unknown callback we just ignore it
self._get_logger().warning('Unknown callback received, ignoring.')
else:
del self.callbacks[sid][id]
if callback is not None:
callback(*data)

62
src/socketio/namespace.py

@ -1,24 +1,7 @@
class BaseNamespace(object):
def __init__(self, namespace=None):
self.namespace = namespace or '/'
from . import base_namespace
def is_asyncio_based(self):
return False
def trigger_event(self, event, *args):
"""Dispatch an event to the proper handler method.
In the most common usage, this method is not overloaded by subclasses,
as it performs the routing of events to methods. However, this
method can be overridden if special dispatching rules are needed, or if
having a single method that catches all events is desired.
"""
handler_name = 'on_' + event
if hasattr(self, handler_name):
return getattr(self, handler_name)(*args)
class Namespace(BaseNamespace):
class Namespace(base_namespace.BaseServerNamespace):
"""Base class for server-side class-based namespaces.
A class-based namespace is a class that contains all the event handlers
@ -30,12 +13,17 @@ class Namespace(BaseNamespace):
handlers defined in this class. If this argument is
omitted, the default namespace is used.
"""
def __init__(self, namespace=None):
super().__init__(namespace=namespace)
self.server = None
def trigger_event(self, event, *args):
"""Dispatch an event to the proper handler method.
def _set_server(self, server):
self.server = server
In the most common usage, this method is not overloaded by subclasses,
as it performs the routing of events to methods. However, this
method can be overridden if special dispatching rules are needed, or if
having a single method that catches all events is desired.
"""
handler_name = 'on_' + event
if hasattr(self, handler_name):
return getattr(self, handler_name)(*args)
def emit(self, event, data=None, to=None, room=None, skip_sid=None,
namespace=None, callback=None, ignore_queue=False):
@ -104,15 +92,6 @@ class Namespace(BaseNamespace):
return self.server.close_room(room,
namespace=namespace or self.namespace)
def rooms(self, sid, namespace=None):
"""Return the rooms a client is in.
The only difference with the :func:`socketio.Server.rooms` method is
that when the ``namespace`` argument is not given the namespace
associated with the class is used.
"""
return self.server.rooms(sid, namespace=namespace or self.namespace)
def get_session(self, sid, namespace=None):
"""Return the user session for a client.
@ -153,7 +132,7 @@ class Namespace(BaseNamespace):
namespace=namespace or self.namespace)
class ClientNamespace(BaseNamespace):
class ClientNamespace(base_namespace.BaseClientNamespace):
"""Base class for client-side class-based namespaces.
A class-based namespace is a class that contains all the event handlers
@ -165,12 +144,17 @@ class ClientNamespace(BaseNamespace):
handlers defined in this class. If this argument is
omitted, the default namespace is used.
"""
def __init__(self, namespace=None):
super().__init__(namespace=namespace)
self.client = None
def trigger_event(self, event, *args):
"""Dispatch an event to the proper handler method.
def _set_client(self, client):
self.client = client
In the most common usage, this method is not overloaded by subclasses,
as it performs the routing of events to methods. However, this
method can be overridden if special dispatching rules are needed, or if
having a single method that catches all events is desired.
"""
handler_name = 'on_' + event
if hasattr(self, handler_name):
return getattr(self, handler_name)(*args)
def emit(self, event, data=None, namespace=None, callback=None):
"""Emit a custom event to the server.

4
src/socketio/pubsub_manager.py

@ -4,10 +4,10 @@ import uuid
from engineio import json
import pickle
from .base_manager import BaseManager
from .manager import Manager
class PubSubManager(BaseManager):
class PubSubManager(Manager):
"""Manage a client list attached to a pub/sub backend.
This is a base class that enables multiple servers to share the list of

192
src/socketio/server.py

@ -2,15 +2,14 @@ import logging
import engineio
from . import base_manager
from . import base_server
from . import exceptions
from . import namespace
from . import packet
default_logger = logging.getLogger('socketio.server')
class Server(object):
class Server(base_server.BaseServer):
"""A Socket.IO server.
This class implements a fully compliant Socket.IO web server with support
@ -111,163 +110,6 @@ class Server(object):
fatal errors are logged even when
``engineio_logger`` is ``False``.
"""
reserved_events = ['connect', 'disconnect']
def __init__(self, client_manager=None, logger=False, serializer='default',
json=None, async_handlers=True, always_connect=False,
namespaces=None, **kwargs):
engineio_options = kwargs
engineio_logger = engineio_options.pop('engineio_logger', None)
if engineio_logger is not None:
engineio_options['logger'] = engineio_logger
if serializer == 'default':
self.packet_class = packet.Packet
elif serializer == 'msgpack':
from . import msgpack_packet
self.packet_class = msgpack_packet.MsgPackPacket
else:
self.packet_class = serializer
if json is not None:
self.packet_class.json = json
engineio_options['json'] = json
engineio_options['async_handlers'] = False
self.eio = self._engineio_server_class()(**engineio_options)
self.eio.on('connect', self._handle_eio_connect)
self.eio.on('message', self._handle_eio_message)
self.eio.on('disconnect', self._handle_eio_disconnect)
self.environ = {}
self.handlers = {}
self.namespace_handlers = {}
self.not_handled = object()
self._binary_packet = {}
if not isinstance(logger, bool):
self.logger = logger
else:
self.logger = default_logger
if self.logger.level == logging.NOTSET:
if logger:
self.logger.setLevel(logging.INFO)
else:
self.logger.setLevel(logging.ERROR)
self.logger.addHandler(logging.StreamHandler())
if client_manager is None:
client_manager = base_manager.BaseManager()
self.manager = client_manager
self.manager.set_server(self)
self.manager_initialized = False
self.async_handlers = async_handlers
self.always_connect = always_connect
self.namespaces = namespaces or ['/']
self.async_mode = self.eio.async_mode
def is_asyncio_based(self):
return False
def on(self, event, handler=None, namespace=None):
"""Register an event handler.
:param event: The event name. It can be any string. The event names
``'connect'``, ``'message'`` and ``'disconnect'`` are
reserved and should not be used.
:param handler: The function that should be invoked to handle the
event. When this parameter is not given, the method
acts as a decorator for the handler function.
:param namespace: The Socket.IO namespace for the event. If this
argument is omitted the handler is associated with
the default namespace.
Example usage::
# as a decorator:
@sio.on('connect', namespace='/chat')
def connect_handler(sid, environ):
print('Connection request')
if environ['REMOTE_ADDR'] in blacklisted:
return False # reject
# as a method:
def message_handler(sid, msg):
print('Received message: ', msg)
sio.send(sid, 'response')
socket_io.on('message', namespace='/chat', handler=message_handler)
The handler function receives the ``sid`` (session ID) for the
client as first argument. The ``'connect'`` event handler receives the
WSGI environment as a second argument, and can return ``False`` to
reject the connection. The ``'message'`` handler and handlers for
custom event names receive the message payload as a second argument.
Any values returned from a message handler will be passed to the
client's acknowledgement callback function if it exists. The
``'disconnect'`` handler does not take a second argument.
"""
namespace = namespace or '/'
def set_handler(handler):
if namespace not in self.handlers:
self.handlers[namespace] = {}
self.handlers[namespace][event] = handler
return handler
if handler is None:
return set_handler
set_handler(handler)
def event(self, *args, **kwargs):
"""Decorator to register an event handler.
This is a simplified version of the ``on()`` method that takes the
event name from the decorated function.
Example usage::
@sio.event
def my_event(data):
print('Received data: ', data)
The above example is equivalent to::
@sio.on('my_event')
def my_event(data):
print('Received data: ', data)
A custom namespace can be given as an argument to the decorator::
@sio.event(namespace='/test')
def my_event(data):
print('Received data: ', data)
"""
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
# the decorator was invoked without arguments
# args[0] is the decorated function
return self.on(args[0].__name__)(args[0])
else:
# the decorator was invoked with arguments
def set_handler(handler):
return self.on(handler.__name__, *args, **kwargs)(handler)
return set_handler
def register_namespace(self, namespace_handler):
"""Register a namespace handler object.
:param namespace_handler: An instance of a :class:`Namespace`
subclass that handles all the event traffic
for a namespace.
"""
if not isinstance(namespace_handler, namespace.Namespace):
raise ValueError('Not a namespace instance')
if self.is_asyncio_based() != namespace_handler.is_asyncio_based():
raise ValueError('Not a valid namespace class for this server')
namespace_handler._set_server(self)
self.namespace_handlers[namespace_handler.namespace] = \
namespace_handler
def emit(self, event, data=None, to=None, room=None, skip_sid=None,
namespace=None, callback=None, ignore_queue=False):
"""Emit a custom event to one or more connected clients.
@ -464,16 +306,6 @@ class Server(object):
self.logger.info('room %s is closing [%s]', room, namespace)
self.manager.close_room(room, namespace)
def rooms(self, sid, namespace=None):
"""Return the rooms a client is in.
:param sid: Session ID of the client.
:param namespace: The Socket.IO namespace for the event. If this
argument is omitted the default namespace is used.
"""
namespace = namespace or '/'
return self.manager.get_rooms(sid, namespace)
def get_session(self, sid, namespace=None):
"""Return the user session for a client.
@ -579,26 +411,6 @@ class Server(object):
self.logger.info('Socket.IO is shutting down')
self.eio.shutdown()
def transport(self, sid):
"""Return the name of the transport used by the client.
The two possible values returned by this function are ``'polling'``
and ``'websocket'``.
:param sid: The session of the client.
"""
return self.eio.transport(sid)
def get_environ(self, sid, namespace=None):
"""Return the WSGI environ dictionary for a client.
:param sid: The session of the client.
:param namespace: The Socket.IO namespace. If this argument is omitted
the default namespace is used.
"""
eio_sid = self.manager.eio_sid_from_sid(sid, namespace or '/')
return self.environ.get(eio_sid)
def handle_request(self, environ, start_response):
"""Handle an HTTP request from the client.

0
tests/asyncio/__init__.py → tests/async/__init__.py

0
tests/asyncio/helpers.py → tests/async/helpers.py

150
tests/asyncio/test_asyncio_client.py → tests/async/test_client.py

@ -5,8 +5,8 @@ from unittest import mock
import pytest
from socketio import asyncio_client
from socketio import asyncio_namespace
from socketio import async_client
from socketio import async_namespace
from engineio import exceptions as engineio_exceptions
from socketio import exceptions
from socketio import packet
@ -16,11 +16,11 @@ from .helpers import AsyncMock, _run
@unittest.skipIf(sys.version_info < (3, 5), 'only for Python 3.5+')
class TestAsyncClient(unittest.TestCase):
def test_is_asyncio_based(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
assert c.is_asyncio_based()
def test_connect(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.eio.connect = AsyncMock()
_run(
c.connect(
@ -50,7 +50,7 @@ class TestAsyncClient(unittest.TestCase):
async def headers():
return 'headers'
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.eio.connect = AsyncMock()
_run(
c.connect(
@ -71,7 +71,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_connect_one_namespace(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.eio.connect = AsyncMock()
_run(
c.connect(
@ -96,7 +96,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_connect_default_namespaces(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.eio.connect = AsyncMock()
c.on('foo', mock.MagicMock(), namespace='/foo')
c.on('bar', mock.MagicMock(), namespace='/')
@ -123,7 +123,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_connect_no_namespaces(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.eio.connect = AsyncMock()
_run(
c.connect(
@ -147,7 +147,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_connect_error(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.eio.connect = AsyncMock(
side_effect=engineio_exceptions.ConnectionError('foo')
)
@ -165,7 +165,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_connect_twice(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.eio.connect = AsyncMock()
_run(
c.connect(
@ -182,7 +182,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_connect_wait_single_namespace(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.eio.connect = AsyncMock()
c._connect_event = mock.MagicMock()
@ -201,7 +201,7 @@ class TestAsyncClient(unittest.TestCase):
assert c.connected is True
def test_connect_wait_two_namespaces(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.eio.connect = AsyncMock()
c._connect_event = mock.MagicMock()
@ -227,7 +227,7 @@ class TestAsyncClient(unittest.TestCase):
assert c.namespaces == {'/bar': '123', '/foo': '456'}
def test_connect_timeout(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.eio.connect = AsyncMock()
c.disconnect = AsyncMock()
with pytest.raises(exceptions.ConnectionError):
@ -241,7 +241,7 @@ class TestAsyncClient(unittest.TestCase):
c.disconnect.mock.assert_called_once_with()
def test_wait_no_reconnect(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.eio.wait = AsyncMock()
c.sleep = AsyncMock()
c._reconnect_task = None
@ -250,7 +250,7 @@ class TestAsyncClient(unittest.TestCase):
c.sleep.mock.assert_called_once_with(1)
def test_wait_reconnect_failed(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.eio.wait = AsyncMock()
c.sleep = AsyncMock()
states = ['disconnected']
@ -264,7 +264,7 @@ class TestAsyncClient(unittest.TestCase):
c.sleep.mock.assert_called_once_with(1)
def test_wait_reconnect_successful(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.eio.wait = AsyncMock()
c.sleep = AsyncMock()
states = ['connected', 'disconnected']
@ -279,7 +279,7 @@ class TestAsyncClient(unittest.TestCase):
assert c.sleep.mock.call_count == 2
def test_emit_no_arguments(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.namespaces = {'/': '1'}
c._send_packet = AsyncMock()
_run(c.emit('foo'))
@ -292,7 +292,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_emit_one_argument(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.namespaces = {'/': '1'}
c._send_packet = AsyncMock()
_run(c.emit('foo', 'bar'))
@ -309,7 +309,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_emit_one_argument_list(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.namespaces = {'/': '1'}
c._send_packet = AsyncMock()
_run(c.emit('foo', ['bar', 'baz']))
@ -326,7 +326,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_emit_two_arguments(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.namespaces = {'/': '1'}
c._send_packet = AsyncMock()
_run(c.emit('foo', ('bar', 'baz')))
@ -343,7 +343,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_emit_namespace(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.namespaces = {'/foo': '1'}
c._send_packet = AsyncMock()
_run(c.emit('foo', namespace='/foo'))
@ -356,13 +356,13 @@ class TestAsyncClient(unittest.TestCase):
)
def test_emit_unknown_namespace(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.namespaces = {'/foo': '1'}
with pytest.raises(exceptions.BadNamespaceError):
_run(c.emit('foo', namespace='/bar'))
def test_emit_with_callback(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c._send_packet = AsyncMock()
c._generate_ack_id = mock.MagicMock(return_value=123)
c.namespaces = {'/': '1'}
@ -377,7 +377,7 @@ class TestAsyncClient(unittest.TestCase):
c._generate_ack_id.assert_called_once_with('/', 'cb')
def test_emit_namespace_with_callback(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.namespaces = {'/foo': '1'}
c._send_packet = AsyncMock()
c._generate_ack_id = mock.MagicMock(return_value=123)
@ -392,7 +392,7 @@ class TestAsyncClient(unittest.TestCase):
c._generate_ack_id.assert_called_once_with('/foo', 'cb')
def test_emit_binary(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.namespaces = {'/': '1'}
c._send_packet = AsyncMock()
_run(c.emit('foo', b'bar'))
@ -409,7 +409,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_emit_not_binary(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.namespaces = {'/': '1'}
c._send_packet = AsyncMock()
_run(c.emit('foo', 'bar'))
@ -426,7 +426,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_send(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.emit = AsyncMock()
_run(c.send('data', 'namespace', 'callback'))
c.emit.mock.assert_called_once_with(
@ -434,7 +434,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_send_with_defaults(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.emit = AsyncMock()
_run(c.send('data'))
c.emit.mock.assert_called_once_with(
@ -442,7 +442,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_call(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.namespaces = {'/': '1'}
async def fake_event_wait():
@ -462,7 +462,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_call_with_timeout(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.namespaces = {'/': '1'}
async def fake_event_wait():
@ -483,7 +483,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_disconnect(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.connected = True
c.namespaces = {'/': '1'}
c._trigger_event = AsyncMock()
@ -503,7 +503,7 @@ class TestAsyncClient(unittest.TestCase):
c.eio.disconnect.mock.assert_called_once_with(abort=True)
def test_disconnect_namespaces(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.namespaces = {'/foo': '1', '/bar': '2'}
c._trigger_event = AsyncMock()
c._send_packet = AsyncMock()
@ -525,7 +525,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_start_background_task(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.eio.start_background_task = mock.MagicMock(return_value='foo')
assert c.start_background_task('foo', 'bar', baz='baz') == 'foo'
c.eio.start_background_task.assert_called_once_with(
@ -533,19 +533,19 @@ class TestAsyncClient(unittest.TestCase):
)
def test_sleep(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.eio.sleep = AsyncMock()
_run(c.sleep(1.23))
c.eio.sleep.mock.assert_called_once_with(1.23)
def test_send_packet(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.eio.send = AsyncMock()
_run(c._send_packet(packet.Packet(packet.EVENT, 'foo')))
c.eio.send.mock.assert_called_once_with('2"foo"')
def test_send_packet_binary(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.eio.send = AsyncMock()
_run(c._send_packet(packet.Packet(packet.EVENT, b'foo')))
assert c.eio.send.mock.call_args_list == [
@ -557,13 +557,13 @@ class TestAsyncClient(unittest.TestCase):
]
def test_send_packet_default_binary(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.eio.send = AsyncMock()
_run(c._send_packet(packet.Packet(packet.EVENT, 'foo')))
c.eio.send.mock.assert_called_once_with('2"foo"')
def test_handle_connect(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c._connect_event = mock.MagicMock()
c._trigger_event = AsyncMock()
c._send_packet = AsyncMock()
@ -573,7 +573,7 @@ class TestAsyncClient(unittest.TestCase):
c._send_packet.mock.assert_not_called()
def test_handle_connect_with_namespaces(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.namespaces = {'/foo': '1', '/bar': '2'}
c._connect_event = mock.MagicMock()
c._trigger_event = AsyncMock()
@ -584,7 +584,7 @@ class TestAsyncClient(unittest.TestCase):
assert c.namespaces == {'/': '3', '/foo': '1', '/bar': '2'}
def test_handle_connect_namespace(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.namespaces = {'/foo': '1'}
c._connect_event = mock.MagicMock()
c._trigger_event = AsyncMock()
@ -598,7 +598,7 @@ class TestAsyncClient(unittest.TestCase):
assert c.namespaces == {'/foo': '1', '/bar': '2'}
def test_handle_disconnect(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.connected = True
c._trigger_event = AsyncMock()
_run(c._handle_disconnect('/'))
@ -613,7 +613,7 @@ class TestAsyncClient(unittest.TestCase):
assert c._trigger_event.mock.call_count == 2
def test_handle_disconnect_namespace(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.connected = True
c.namespaces = {'/foo': '1', '/bar': '2'}
c._trigger_event = AsyncMock()
@ -637,7 +637,7 @@ class TestAsyncClient(unittest.TestCase):
assert not c.connected
def test_handle_disconnect_unknown_namespace(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.connected = True
c.namespaces = {'/foo': '1', '/bar': '2'}
c._trigger_event = AsyncMock()
@ -652,7 +652,7 @@ class TestAsyncClient(unittest.TestCase):
assert c.connected
def test_handle_disconnect_default_namespaces(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.connected = True
c.namespaces = {'/foo': '1', '/bar': '2'}
c._trigger_event = AsyncMock()
@ -664,7 +664,7 @@ class TestAsyncClient(unittest.TestCase):
assert c.connected
def test_handle_event(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c._trigger_event = AsyncMock()
_run(c._handle_event('/', None, ['foo', ('bar', 'baz')]))
c._trigger_event.mock.assert_called_once_with(
@ -672,7 +672,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_handle_event_with_id_no_arguments(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c._trigger_event = AsyncMock(return_value=None)
c._send_packet = AsyncMock()
_run(c._handle_event('/', 123, ['foo', ('bar', 'baz')]))
@ -688,7 +688,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_handle_event_with_id_one_argument(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c._trigger_event = AsyncMock(return_value='ret')
c._send_packet = AsyncMock()
_run(c._handle_event('/', 123, ['foo', ('bar', 'baz')]))
@ -704,7 +704,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_handle_event_with_id_one_list_argument(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c._trigger_event = AsyncMock(return_value=['a', 'b'])
c._send_packet = AsyncMock()
_run(c._handle_event('/', 123, ['foo', ('bar', 'baz')]))
@ -720,7 +720,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_handle_event_with_id_two_arguments(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c._trigger_event = AsyncMock(return_value=('a', 'b'))
c._send_packet = AsyncMock()
_run(c._handle_event('/', 123, ['foo', ('bar', 'baz')]))
@ -736,7 +736,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_handle_ack(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
mock_cb = mock.MagicMock()
c.callbacks['/foo'] = {123: mock_cb}
_run(c._handle_ack('/foo', 123, ['bar', 'baz']))
@ -744,7 +744,7 @@ class TestAsyncClient(unittest.TestCase):
assert 123 not in c.callbacks['/foo']
def test_handle_ack_async(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
mock_cb = AsyncMock()
c.callbacks['/foo'] = {123: mock_cb}
_run(c._handle_ack('/foo', 123, ['bar', 'baz']))
@ -752,7 +752,7 @@ class TestAsyncClient(unittest.TestCase):
assert 123 not in c.callbacks['/foo']
def test_handle_ack_not_found(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
mock_cb = mock.MagicMock()
c.callbacks['/foo'] = {123: mock_cb}
_run(c._handle_ack('/foo', 124, ['bar', 'baz']))
@ -760,7 +760,7 @@ class TestAsyncClient(unittest.TestCase):
assert 123 in c.callbacks['/foo']
def test_handle_error(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.connected = True
c._connect_event = mock.MagicMock()
c._trigger_event = AsyncMock()
@ -774,7 +774,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_handle_error_with_no_arguments(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.connected = True
c._connect_event = mock.MagicMock()
c._trigger_event = AsyncMock()
@ -786,7 +786,7 @@ class TestAsyncClient(unittest.TestCase):
c._trigger_event.mock.assert_called_once_with('connect_error', '/')
def test_handle_error_namespace(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.connected = True
c.namespaces = {'/foo': '1', '/bar': '2'}
c._connect_event = mock.MagicMock()
@ -800,7 +800,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_handle_error_namespace_with_no_arguments(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.connected = True
c.namespaces = {'/foo': '1', '/bar': '2'}
c._connect_event = mock.MagicMock()
@ -812,7 +812,7 @@ class TestAsyncClient(unittest.TestCase):
c._trigger_event.mock.assert_called_once_with('connect_error', '/bar')
def test_handle_error_unknown_namespace(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.connected = True
c.namespaces = {'/foo': '1', '/bar': '2'}
c._connect_event = mock.MagicMock()
@ -822,7 +822,7 @@ class TestAsyncClient(unittest.TestCase):
c._connect_event.set.assert_called_once_with()
def test_trigger_event(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
handler = mock.MagicMock()
catchall_handler = mock.MagicMock()
c.on('foo', handler)
@ -834,7 +834,7 @@ class TestAsyncClient(unittest.TestCase):
catchall_handler.assert_called_once_with('bar', 1, '2', 3)
def test_trigger_event_namespace(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
handler = AsyncMock()
catchall_handler = AsyncMock()
c.on('foo', handler, namespace='/bar')
@ -845,10 +845,10 @@ class TestAsyncClient(unittest.TestCase):
catchall_handler.mock.assert_called_once_with('bar', 1, '2', 3)
def test_trigger_event_class_namespace(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
result = []
class MyNamespace(asyncio_namespace.AsyncClientNamespace):
class MyNamespace(async_namespace.AsyncClientNamespace):
def on_foo(self, a, b):
result.append(a)
result.append(b)
@ -858,10 +858,10 @@ class TestAsyncClient(unittest.TestCase):
assert result == [1, '2']
def test_trigger_event_unknown_namespace(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
result = []
class MyNamespace(asyncio_namespace.AsyncClientNamespace):
class MyNamespace(async_namespace.AsyncClientNamespace):
def on_foo(self, a, b):
result.append(a)
result.append(b)
@ -877,7 +877,7 @@ class TestAsyncClient(unittest.TestCase):
)
@mock.patch('socketio.client.random.random', side_effect=[1, 0, 0.5])
def test_handle_reconnect(self, random, wait_for):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c._reconnect_task = 'foo'
c.connect = AsyncMock(
side_effect=[ValueError, exceptions.ConnectionError, None]
@ -898,7 +898,7 @@ class TestAsyncClient(unittest.TestCase):
)
@mock.patch('socketio.client.random.random', side_effect=[1, 0, 0.5])
def test_handle_reconnect_max_delay(self, random, wait_for):
c = asyncio_client.AsyncClient(reconnection_delay_max=3)
c = async_client.AsyncClient(reconnection_delay_max=3)
c._reconnect_task = 'foo'
c.connect = AsyncMock(
side_effect=[ValueError, exceptions.ConnectionError, None]
@ -919,7 +919,7 @@ class TestAsyncClient(unittest.TestCase):
)
@mock.patch('socketio.client.random.random', side_effect=[1, 0, 0.5])
def test_handle_reconnect_max_attempts(self, random, wait_for):
c = asyncio_client.AsyncClient(reconnection_attempts=2, logger=True)
c = async_client.AsyncClient(reconnection_attempts=2, logger=True)
c.connection_namespaces = ['/']
c._reconnect_task = 'foo'
c._trigger_event = AsyncMock()
@ -943,7 +943,7 @@ class TestAsyncClient(unittest.TestCase):
)
@mock.patch('socketio.client.random.random', side_effect=[1, 0, 0.5])
def test_handle_reconnect_aborted(self, random, wait_for):
c = asyncio_client.AsyncClient(logger=True)
c = async_client.AsyncClient(logger=True)
c.connection_namespaces = ['/']
c._reconnect_task = 'foo'
c._trigger_event = AsyncMock()
@ -961,7 +961,7 @@ class TestAsyncClient(unittest.TestCase):
namespace='/')
def test_handle_eio_connect(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.connection_namespaces = ['/', '/foo']
c.connection_auth = 'auth'
c._send_packet = AsyncMock()
@ -984,7 +984,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_handle_eio_connect_function(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.connection_namespaces = ['/', '/foo']
c.connection_auth = lambda: 'auth'
c._send_packet = AsyncMock()
@ -1007,7 +1007,7 @@ class TestAsyncClient(unittest.TestCase):
)
def test_handle_eio_message(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c._handle_connect = AsyncMock()
c._handle_disconnect = AsyncMock()
c._handle_event = AsyncMock()
@ -1056,7 +1056,7 @@ class TestAsyncClient(unittest.TestCase):
_run(c._handle_eio_message('9'))
def test_eio_disconnect(self):
c = asyncio_client.AsyncClient()
c = async_client.AsyncClient()
c.namespaces = {'/': '1'}
c.connected = True
c._trigger_event = AsyncMock()
@ -1071,7 +1071,7 @@ class TestAsyncClient(unittest.TestCase):
assert not c.connected
def test_eio_disconnect_namespaces(self):
c = asyncio_client.AsyncClient(reconnection=False)
c = async_client.AsyncClient(reconnection=False)
c.namespaces = {'/foo': '1', '/bar': '2'}
c.connected = True
c._trigger_event = AsyncMock()
@ -1084,21 +1084,21 @@ class TestAsyncClient(unittest.TestCase):
assert not c.connected
def test_eio_disconnect_reconnect(self):
c = asyncio_client.AsyncClient(reconnection=True)
c = async_client.AsyncClient(reconnection=True)
c.start_background_task = mock.MagicMock()
c.eio.state = 'connected'
_run(c._handle_eio_disconnect())
c.start_background_task.assert_called_once_with(c._handle_reconnect)
def test_eio_disconnect_self_disconnect(self):
c = asyncio_client.AsyncClient(reconnection=True)
c = async_client.AsyncClient(reconnection=True)
c.start_background_task = mock.MagicMock()
c.eio.state = 'disconnected'
_run(c._handle_eio_disconnect())
c.start_background_task.assert_not_called()
def test_eio_disconnect_no_reconnect(self):
c = asyncio_client.AsyncClient(reconnection=False)
c = async_client.AsyncClient(reconnection=False)
c.namespaces = {'/': '1'}
c.connected = True
c._trigger_event = AsyncMock()

4
tests/asyncio/test_asyncio_manager.py → tests/async/test_manager.py

@ -2,7 +2,7 @@ import sys
import unittest
from unittest import mock
from socketio import asyncio_manager
from socketio import async_manager
from socketio import packet
from .helpers import AsyncMock, _run
@ -22,7 +22,7 @@ class TestAsyncManager(unittest.TestCase):
mock_server._send_eio_packet = AsyncMock()
mock_server.eio.generate_id = generate_id
mock_server.packet_class = packet.Packet
self.bm = asyncio_manager.AsyncManager()
self.bm = async_manager.AsyncManager()
self.bm.set_server(mock_server)
self.bm.initialize()

44
tests/asyncio/test_asyncio_namespace.py → tests/async/test_namespace.py

@ -2,7 +2,7 @@ import sys
import unittest
from unittest import mock
from socketio import asyncio_namespace
from socketio import async_namespace
from .helpers import AsyncMock, _run
@ -11,7 +11,7 @@ class TestAsyncNamespace(unittest.TestCase):
def test_connect_event(self):
result = {}
class MyNamespace(asyncio_namespace.AsyncNamespace):
class MyNamespace(async_namespace.AsyncNamespace):
async def on_connect(self, sid, environ):
result['result'] = (sid, environ)
@ -23,7 +23,7 @@ class TestAsyncNamespace(unittest.TestCase):
def test_disconnect_event(self):
result = {}
class MyNamespace(asyncio_namespace.AsyncNamespace):
class MyNamespace(async_namespace.AsyncNamespace):
async def on_disconnect(self, sid):
result['result'] = sid
@ -35,7 +35,7 @@ class TestAsyncNamespace(unittest.TestCase):
def test_sync_event(self):
result = {}
class MyNamespace(asyncio_namespace.AsyncNamespace):
class MyNamespace(async_namespace.AsyncNamespace):
def on_custom_message(self, sid, data):
result['result'] = (sid, data)
@ -47,7 +47,7 @@ class TestAsyncNamespace(unittest.TestCase):
def test_async_event(self):
result = {}
class MyNamespace(asyncio_namespace.AsyncNamespace):
class MyNamespace(async_namespace.AsyncNamespace):
async def on_custom_message(self, sid, data):
result['result'] = (sid, data)
@ -59,7 +59,7 @@ class TestAsyncNamespace(unittest.TestCase):
def test_event_not_found(self):
result = {}
class MyNamespace(asyncio_namespace.AsyncNamespace):
class MyNamespace(async_namespace.AsyncNamespace):
async def on_custom_message(self, sid, data):
result['result'] = (sid, data)
@ -71,7 +71,7 @@ class TestAsyncNamespace(unittest.TestCase):
assert result == {}
def test_emit(self):
ns = asyncio_namespace.AsyncNamespace('/foo')
ns = async_namespace.AsyncNamespace('/foo')
mock_server = mock.MagicMock()
mock_server.emit = AsyncMock()
ns._set_server(mock_server)
@ -113,7 +113,7 @@ class TestAsyncNamespace(unittest.TestCase):
)
def test_send(self):
ns = asyncio_namespace.AsyncNamespace('/foo')
ns = async_namespace.AsyncNamespace('/foo')
mock_server = mock.MagicMock()
mock_server.send = AsyncMock()
ns._set_server(mock_server)
@ -148,7 +148,7 @@ class TestAsyncNamespace(unittest.TestCase):
)
def test_call(self):
ns = asyncio_namespace.AsyncNamespace('/foo')
ns = async_namespace.AsyncNamespace('/foo')
mock_server = mock.MagicMock()
mock_server.call = AsyncMock()
ns._set_server(mock_server)
@ -175,7 +175,7 @@ class TestAsyncNamespace(unittest.TestCase):
)
def test_enter_room(self):
ns = asyncio_namespace.AsyncNamespace('/foo')
ns = async_namespace.AsyncNamespace('/foo')
mock_server = mock.MagicMock()
mock_server.enter_room = AsyncMock()
ns._set_server(mock_server)
@ -189,7 +189,7 @@ class TestAsyncNamespace(unittest.TestCase):
)
def test_leave_room(self):
ns = asyncio_namespace.AsyncNamespace('/foo')
ns = async_namespace.AsyncNamespace('/foo')
mock_server = mock.MagicMock()
mock_server.leave_room = AsyncMock()
ns._set_server(mock_server)
@ -203,7 +203,7 @@ class TestAsyncNamespace(unittest.TestCase):
)
def test_close_room(self):
ns = asyncio_namespace.AsyncNamespace('/foo')
ns = async_namespace.AsyncNamespace('/foo')
mock_server = mock.MagicMock()
mock_server.close_room = AsyncMock()
ns._set_server(mock_server)
@ -213,7 +213,7 @@ class TestAsyncNamespace(unittest.TestCase):
ns.server.close_room.mock.assert_called_with('room', namespace='/bar')
def test_rooms(self):
ns = asyncio_namespace.AsyncNamespace('/foo')
ns = async_namespace.AsyncNamespace('/foo')
ns._set_server(mock.MagicMock())
ns.rooms('sid')
ns.server.rooms.assert_called_with('sid', namespace='/foo')
@ -221,7 +221,7 @@ class TestAsyncNamespace(unittest.TestCase):
ns.server.rooms.assert_called_with('sid', namespace='/bar')
def test_session(self):
ns = asyncio_namespace.AsyncNamespace('/foo')
ns = async_namespace.AsyncNamespace('/foo')
mock_server = mock.MagicMock()
mock_server.get_session = AsyncMock()
mock_server.save_session = AsyncMock()
@ -244,7 +244,7 @@ class TestAsyncNamespace(unittest.TestCase):
ns.server.session.assert_called_with('sid', namespace='/bar')
def test_disconnect(self):
ns = asyncio_namespace.AsyncNamespace('/foo')
ns = async_namespace.AsyncNamespace('/foo')
mock_server = mock.MagicMock()
mock_server.disconnect = AsyncMock()
ns._set_server(mock_server)
@ -256,7 +256,7 @@ class TestAsyncNamespace(unittest.TestCase):
def test_sync_event_client(self):
result = {}
class MyNamespace(asyncio_namespace.AsyncClientNamespace):
class MyNamespace(async_namespace.AsyncClientNamespace):
def on_custom_message(self, sid, data):
result['result'] = (sid, data)
@ -268,7 +268,7 @@ class TestAsyncNamespace(unittest.TestCase):
def test_async_event_client(self):
result = {}
class MyNamespace(asyncio_namespace.AsyncClientNamespace):
class MyNamespace(async_namespace.AsyncClientNamespace):
async def on_custom_message(self, sid, data):
result['result'] = (sid, data)
@ -280,7 +280,7 @@ class TestAsyncNamespace(unittest.TestCase):
def test_event_not_found_client(self):
result = {}
class MyNamespace(asyncio_namespace.AsyncClientNamespace):
class MyNamespace(async_namespace.AsyncClientNamespace):
async def on_custom_message(self, sid, data):
result['result'] = (sid, data)
@ -292,7 +292,7 @@ class TestAsyncNamespace(unittest.TestCase):
assert result == {}
def test_emit_client(self):
ns = asyncio_namespace.AsyncClientNamespace('/foo')
ns = async_namespace.AsyncClientNamespace('/foo')
mock_client = mock.MagicMock()
mock_client.emit = AsyncMock()
ns._set_client(mock_client)
@ -306,7 +306,7 @@ class TestAsyncNamespace(unittest.TestCase):
)
def test_send_client(self):
ns = asyncio_namespace.AsyncClientNamespace('/foo')
ns = async_namespace.AsyncClientNamespace('/foo')
mock_client = mock.MagicMock()
mock_client.send = AsyncMock()
ns._set_client(mock_client)
@ -320,7 +320,7 @@ class TestAsyncNamespace(unittest.TestCase):
)
def test_call_client(self):
ns = asyncio_namespace.AsyncClientNamespace('/foo')
ns = async_namespace.AsyncClientNamespace('/foo')
mock_client = mock.MagicMock()
mock_client.call = AsyncMock()
ns._set_client(mock_client)
@ -334,7 +334,7 @@ class TestAsyncNamespace(unittest.TestCase):
)
def test_disconnect_client(self):
ns = asyncio_namespace.AsyncClientNamespace('/foo')
ns = async_namespace.AsyncClientNamespace('/foo')
mock_client = mock.MagicMock()
mock_client.disconnect = AsyncMock()
ns._set_client(mock_client)

32
tests/asyncio/test_asyncio_pubsub_manager.py → tests/async/test_pubsub_manager.py

@ -6,8 +6,8 @@ from unittest import mock
import pytest
from socketio import asyncio_manager
from socketio import asyncio_pubsub_manager
from socketio import async_manager
from socketio import async_pubsub_manager
from socketio import packet
from .helpers import AsyncMock, _run
@ -28,7 +28,7 @@ class TestAsyncPubSubManager(unittest.TestCase):
mock_server._send_packet = AsyncMock()
mock_server._send_eio_packet = AsyncMock()
mock_server.disconnect = AsyncMock()
self.pm = asyncio_pubsub_manager.AsyncPubSubManager()
self.pm = async_pubsub_manager.AsyncPubSubManager()
self.pm._publish = AsyncMock()
self.pm.set_server(mock_server)
self.pm.host_id = '123456'
@ -41,13 +41,13 @@ class TestAsyncPubSubManager(unittest.TestCase):
)
def test_custom_init(self):
pubsub = asyncio_pubsub_manager.AsyncPubSubManager(channel='foo')
pubsub = async_pubsub_manager.AsyncPubSubManager(channel='foo')
assert pubsub.channel == 'foo'
assert len(pubsub.host_id) == 32
def test_write_only_init(self):
mock_server = mock.MagicMock()
pm = asyncio_pubsub_manager.AsyncPubSubManager(write_only=True)
pm = async_pubsub_manager.AsyncPubSubManager(write_only=True)
pm.set_server(mock_server)
pm.initialize()
assert pm.channel == 'socketio'
@ -133,7 +133,7 @@ class TestAsyncPubSubManager(unittest.TestCase):
)
def test_emit_with_callback_without_server(self):
standalone_pm = asyncio_pubsub_manager.AsyncPubSubManager()
standalone_pm = async_pubsub_manager.AsyncPubSubManager()
with pytest.raises(RuntimeError):
_run(standalone_pm.emit('foo', 'bar', callback='cb'))
@ -218,7 +218,7 @@ class TestAsyncPubSubManager(unittest.TestCase):
def test_handle_emit(self):
with mock.patch.object(
asyncio_manager.AsyncManager, 'emit', new=AsyncMock()
async_manager.AsyncManager, 'emit', new=AsyncMock()
) as super_emit:
_run(self.pm._handle_emit({'event': 'foo', 'data': 'bar'}))
super_emit.mock.assert_called_once_with(
@ -233,7 +233,7 @@ class TestAsyncPubSubManager(unittest.TestCase):
def test_handle_emit_with_namespace(self):
with mock.patch.object(
asyncio_manager.AsyncManager, 'emit', new=AsyncMock()
async_manager.AsyncManager, 'emit', new=AsyncMock()
) as super_emit:
_run(
self.pm._handle_emit(
@ -252,7 +252,7 @@ class TestAsyncPubSubManager(unittest.TestCase):
def test_handle_emit_with_room(self):
with mock.patch.object(
asyncio_manager.AsyncManager, 'emit', new=AsyncMock()
async_manager.AsyncManager, 'emit', new=AsyncMock()
) as super_emit:
_run(
self.pm._handle_emit(
@ -271,7 +271,7 @@ class TestAsyncPubSubManager(unittest.TestCase):
def test_handle_emit_with_skip_sid(self):
with mock.patch.object(
asyncio_manager.AsyncManager, 'emit', new=AsyncMock()
async_manager.AsyncManager, 'emit', new=AsyncMock()
) as super_emit:
_run(
self.pm._handle_emit(
@ -290,7 +290,7 @@ class TestAsyncPubSubManager(unittest.TestCase):
def test_handle_emit_with_remote_callback(self):
with mock.patch.object(
asyncio_manager.AsyncManager, 'emit', new=AsyncMock()
async_manager.AsyncManager, 'emit', new=AsyncMock()
) as super_emit:
_run(
self.pm._handle_emit(
@ -325,7 +325,7 @@ class TestAsyncPubSubManager(unittest.TestCase):
def test_handle_emit_with_local_callback(self):
with mock.patch.object(
asyncio_manager.AsyncManager, 'emit', new=AsyncMock()
async_manager.AsyncManager, 'emit', new=AsyncMock()
) as super_emit:
_run(
self.pm._handle_emit(
@ -437,7 +437,7 @@ class TestAsyncPubSubManager(unittest.TestCase):
def test_handle_enter_room(self):
sid = self.pm.connect('123', '/')
with mock.patch.object(
asyncio_manager.AsyncManager, 'enter_room', new=AsyncMock()
async_manager.AsyncManager, 'enter_room', new=AsyncMock()
) as super_enter_room:
_run(
self.pm._handle_enter_room(
@ -458,7 +458,7 @@ class TestAsyncPubSubManager(unittest.TestCase):
def test_handle_leave_room(self):
sid = self.pm.connect('123', '/')
with mock.patch.object(
asyncio_manager.AsyncManager, 'leave_room', new=AsyncMock()
async_manager.AsyncManager, 'leave_room', new=AsyncMock()
) as super_leave_room:
_run(
self.pm._handle_leave_room(
@ -478,7 +478,7 @@ class TestAsyncPubSubManager(unittest.TestCase):
def test_handle_close_room(self):
with mock.patch.object(
asyncio_manager.AsyncManager, 'close_room', new=AsyncMock()
async_manager.AsyncManager, 'close_room', new=AsyncMock()
) as super_close_room:
_run(
self.pm._handle_close_room(
@ -491,7 +491,7 @@ class TestAsyncPubSubManager(unittest.TestCase):
def test_handle_close_room_with_namespace(self):
with mock.patch.object(
asyncio_manager.AsyncManager, 'close_room', new=AsyncMock()
async_manager.AsyncManager, 'close_room', new=AsyncMock()
) as super_close_room:
_run(
self.pm._handle_close_room(

158
tests/asyncio/test_asyncio_server.py → tests/async/test_server.py

@ -8,8 +8,8 @@ from engineio import json
from engineio import packet as eio_packet
import pytest
from socketio import asyncio_server
from socketio import asyncio_namespace
from socketio import async_server
from socketio import async_namespace
from socketio import exceptions
from socketio import namespace
from socketio import packet
@ -38,7 +38,7 @@ class TestAsyncServer(unittest.TestCase):
def test_create(self, eio):
eio.return_value.handle_request = AsyncMock()
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(
s = async_server.AsyncServer(
client_manager=mgr, async_handlers=True, foo='bar'
)
_run(s.handle_request({}))
@ -49,12 +49,12 @@ class TestAsyncServer(unittest.TestCase):
assert s.async_handlers
def test_attach(self, eio):
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
s.attach('app', 'path')
eio.return_value.attach.assert_called_once_with('app', 'path')
def test_on_event(self, eio):
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
@s.on('connect')
def foo():
@ -72,7 +72,7 @@ class TestAsyncServer(unittest.TestCase):
def test_emit(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s = async_server.AsyncServer(client_manager=mgr)
_run(
s.emit(
'my event',
@ -115,7 +115,7 @@ class TestAsyncServer(unittest.TestCase):
def test_emit_default_namespace(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s = async_server.AsyncServer(client_manager=mgr)
_run(
s.emit(
'my event',
@ -156,7 +156,7 @@ class TestAsyncServer(unittest.TestCase):
def test_send(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s = async_server.AsyncServer(client_manager=mgr)
_run(
s.send(
'foo',
@ -197,7 +197,7 @@ class TestAsyncServer(unittest.TestCase):
def test_call(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s = async_server.AsyncServer(client_manager=mgr)
async def fake_event_wait():
s.manager.emit.mock.call_args_list[0][1]['callback']('foo', 321)
@ -208,7 +208,7 @@ class TestAsyncServer(unittest.TestCase):
def test_call_with_timeout(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s = async_server.AsyncServer(client_manager=mgr)
async def fake_event_wait():
await asyncio.sleep(1)
@ -218,13 +218,13 @@ class TestAsyncServer(unittest.TestCase):
_run(s.call('foo', sid='123', timeout=0.01))
def test_call_with_broadcast(self, eio):
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
with pytest.raises(ValueError):
_run(s.call('foo'))
def test_call_without_async_handlers(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(
s = async_server.AsyncServer(
client_manager=mgr, async_handlers=False
)
with pytest.raises(RuntimeError):
@ -232,63 +232,63 @@ class TestAsyncServer(unittest.TestCase):
def test_enter_room(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s = async_server.AsyncServer(client_manager=mgr)
_run(s.enter_room('123', 'room', namespace='/foo'))
s.manager.enter_room.mock.assert_called_once_with('123', '/foo',
'room')
def test_enter_room_default_namespace(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s = async_server.AsyncServer(client_manager=mgr)
_run(s.enter_room('123', 'room'))
s.manager.enter_room.mock.assert_called_once_with('123', '/', 'room')
def test_leave_room(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s = async_server.AsyncServer(client_manager=mgr)
_run(s.leave_room('123', 'room', namespace='/foo'))
s.manager.leave_room.mock.assert_called_once_with('123', '/foo',
'room')
def test_leave_room_default_namespace(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s = async_server.AsyncServer(client_manager=mgr)
_run(s.leave_room('123', 'room'))
s.manager.leave_room.mock.assert_called_once_with('123', '/', 'room')
def test_close_room(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s = async_server.AsyncServer(client_manager=mgr)
_run(s.close_room('room', namespace='/foo'))
s.manager.close_room.mock.assert_called_once_with('room', '/foo')
def test_close_room_default_namespace(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s = async_server.AsyncServer(client_manager=mgr)
_run(s.close_room('room'))
s.manager.close_room.mock.assert_called_once_with('room', '/')
def test_rooms(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s = async_server.AsyncServer(client_manager=mgr)
s.rooms('123', namespace='/foo')
s.manager.get_rooms.assert_called_once_with('123', '/foo')
def test_rooms_default_namespace(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s = async_server.AsyncServer(client_manager=mgr)
s.rooms('123')
s.manager.get_rooms.assert_called_once_with('123', '/')
def test_handle_request(self, eio):
eio.return_value.handle_request = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
_run(s.handle_request('environ'))
s.eio.handle_request.mock.assert_called_once_with('environ')
def test_send_packet(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
_run(s._send_packet('123', packet.Packet(
packet.EVENT, ['my event', 'my data'], namespace='/foo')))
s.eio.send.mock.assert_called_once_with(
@ -297,7 +297,7 @@ class TestAsyncServer(unittest.TestCase):
def test_send_eio_packet(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
_run(s._send_eio_packet('123', eio_packet.Packet(
eio_packet.MESSAGE, 'hello')))
assert s.eio.send_packet.mock.call_count == 1
@ -307,7 +307,7 @@ class TestAsyncServer(unittest.TestCase):
def test_transport(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
s.eio.transport = mock.MagicMock(return_value='polling')
_run(s._handle_eio_connect('foo', 'environ'))
assert s.transport('foo') == 'polling'
@ -315,7 +315,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_connect(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
s.manager.initialize = mock.MagicMock()
handler = mock.MagicMock()
s.on('connect', handler)
@ -331,7 +331,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_connect_with_auth(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
s.manager.initialize = mock.MagicMock()
handler = mock.MagicMock()
s.on('connect', handler)
@ -347,7 +347,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_connect_with_auth_none(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
s.manager.initialize = mock.MagicMock()
handler = mock.MagicMock(side_effect=[TypeError, None, None])
s.on('connect', handler)
@ -363,7 +363,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_connect_async(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
s.manager.initialize = mock.MagicMock()
handler = AsyncMock()
s.on('connect', handler)
@ -379,7 +379,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_connect_with_default_implied_namespaces(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0'))
_run(s._handle_eio_message('123', '0/foo,'))
@ -388,7 +388,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_connect_with_implied_namespaces(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer(namespaces=['/foo'])
s = async_server.AsyncServer(namespaces=['/foo'])
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0'))
_run(s._handle_eio_message('123', '0/foo,'))
@ -397,7 +397,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_connect_with_all_implied_namespaces(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer(namespaces='*')
s = async_server.AsyncServer(namespaces='*')
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0'))
_run(s._handle_eio_message('123', '0/foo,'))
@ -406,7 +406,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_connect_namespace(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
handler = mock.MagicMock()
s.on('connect', handler, namespace='/foo')
_run(s._handle_eio_connect('123', 'environ'))
@ -417,7 +417,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_connect_always_connect(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer(always_connect=True)
s = async_server.AsyncServer(always_connect=True)
s.manager.initialize = mock.MagicMock()
handler = mock.MagicMock()
s.on('connect', handler)
@ -433,7 +433,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_connect_rejected(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
handler = mock.MagicMock(return_value=False)
s.on('connect', handler)
_run(s._handle_eio_connect('123', 'environ'))
@ -446,7 +446,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_connect_namespace_rejected(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
handler = mock.MagicMock(return_value=False)
s.on('connect', handler, namespace='/foo')
_run(s._handle_eio_connect('123', 'environ'))
@ -459,7 +459,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_connect_rejected_always_connect(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer(always_connect=True)
s = async_server.AsyncServer(always_connect=True)
handler = mock.MagicMock(return_value=False)
s.on('connect', handler)
_run(s._handle_eio_connect('123', 'environ'))
@ -473,7 +473,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_connect_namespace_rejected_always_connect(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer(always_connect=True)
s = async_server.AsyncServer(always_connect=True)
handler = mock.MagicMock(return_value=False)
s.on('connect', handler, namespace='/foo')
_run(s._handle_eio_connect('123', 'environ'))
@ -487,7 +487,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_connect_rejected_with_exception(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
handler = mock.MagicMock(
side_effect=exceptions.ConnectionRefusedError('fail_reason')
)
@ -502,7 +502,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_connect_rejected_with_empty_exception(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
handler = mock.MagicMock(
side_effect=exceptions.ConnectionRefusedError()
)
@ -517,7 +517,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_connect_namespace_rejected_with_exception(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
handler = mock.MagicMock(
side_effect=exceptions.ConnectionRefusedError(
'fail_reason', 1, '2')
@ -533,7 +533,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_connect_namespace_rejected_with_empty_exception(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
handler = mock.MagicMock(
side_effect=exceptions.ConnectionRefusedError()
)
@ -548,7 +548,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_disconnect(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
s.manager.disconnect = AsyncMock()
handler = mock.MagicMock()
s.on('disconnect', handler)
@ -562,7 +562,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_disconnect_namespace(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
handler = mock.MagicMock()
s.on('disconnect', handler)
handler_namespace = mock.MagicMock()
@ -576,7 +576,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_disconnect_only_namespace(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
handler = mock.MagicMock()
s.on('disconnect', handler)
handler_namespace = mock.MagicMock()
@ -590,12 +590,12 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_disconnect_unknown_client(self, eio):
mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr)
s = async_server.AsyncServer(client_manager=mgr)
_run(s._handle_eio_disconnect('123'))
def test_handle_event(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer(async_handlers=False)
s = async_server.AsyncServer(async_handlers=False)
sid = s.manager.connect('123', '/')
handler = AsyncMock()
catchall_handler = AsyncMock()
@ -609,7 +609,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_event_with_namespace(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer(async_handlers=False)
s = async_server.AsyncServer(async_handlers=False)
sid = s.manager.connect('123', '/foo')
handler = mock.MagicMock()
catchall_handler = mock.MagicMock()
@ -623,7 +623,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_event_with_disconnected_namespace(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer(async_handlers=False)
s = async_server.AsyncServer(async_handlers=False)
s.manager.connect('123', '/foo')
handler = mock.MagicMock()
s.on('my message', handler, namespace='/bar')
@ -632,7 +632,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_event_binary(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer(async_handlers=False)
s = async_server.AsyncServer(async_handlers=False)
sid = s.manager.connect('123', '/')
handler = mock.MagicMock()
s.on('my message', handler)
@ -650,7 +650,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_event_binary_ack(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer(async_handlers=False)
s = async_server.AsyncServer(async_handlers=False)
s.manager.trigger_callback = AsyncMock()
sid = s.manager.connect('123', '/')
_run(
@ -666,7 +666,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_event_with_ack(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer(async_handlers=False)
s = async_server.AsyncServer(async_handlers=False)
sid = s.manager.connect('123', '/')
handler = mock.MagicMock(return_value='foo')
s.on('my message', handler)
@ -678,7 +678,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_unknown_event_with_ack(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer(async_handlers=False)
s = async_server.AsyncServer(async_handlers=False)
s.manager.connect('123', '/')
handler = mock.MagicMock(return_value='foo')
s.on('my message', handler)
@ -687,7 +687,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_event_with_ack_none(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer(async_handlers=False)
s = async_server.AsyncServer(async_handlers=False)
sid = s.manager.connect('123', '/')
handler = mock.MagicMock(return_value=None)
s.on('my message', handler)
@ -697,7 +697,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_event_with_ack_tuple(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer(async_handlers=False)
s = async_server.AsyncServer(async_handlers=False)
sid = s.manager.connect('123', '/')
handler = mock.MagicMock(return_value=(1, '2', True))
s.on('my message', handler)
@ -709,7 +709,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_event_with_ack_list(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer(async_handlers=False)
s = async_server.AsyncServer(async_handlers=False)
sid = s.manager.connect('123', '/')
handler = mock.MagicMock(return_value=[1, '2', True])
s.on('my message', handler)
@ -721,7 +721,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_event_with_ack_binary(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer(async_handlers=False)
s = async_server.AsyncServer(async_handlers=False)
sid = s.manager.connect('123', '/')
handler = mock.MagicMock(return_value=b'foo')
s.on('my message', handler)
@ -729,18 +729,18 @@ class TestAsyncServer(unittest.TestCase):
handler.assert_any_call(sid, 'foo')
def test_handle_error_packet(self, eio):
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
with pytest.raises(ValueError):
_run(s._handle_eio_message('123', '4'))
def test_handle_invalid_packet(self, eio):
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
with pytest.raises(ValueError):
_run(s._handle_eio_message('123', '9'))
def test_send_with_ack(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
s.handlers['/'] = {}
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0'))
@ -756,7 +756,7 @@ class TestAsyncServer(unittest.TestCase):
def test_send_with_ack_namespace(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
s.handlers['/foo'] = {}
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0/foo,'))
@ -784,7 +784,7 @@ class TestAsyncServer(unittest.TestCase):
fake_session = session
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
s.handlers['/'] = {}
s.handlers['/ns'] = {}
s.eio.get_session = fake_get_session
@ -817,7 +817,7 @@ class TestAsyncServer(unittest.TestCase):
def test_disconnect(self, eio):
eio.return_value.send = AsyncMock()
eio.return_value.disconnect = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
s.handlers['/'] = {}
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0'))
@ -828,7 +828,7 @@ class TestAsyncServer(unittest.TestCase):
def test_disconnect_ignore_queue(self, eio):
eio.return_value.send = AsyncMock()
eio.return_value.disconnect = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
s.handlers['/'] = {}
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0'))
@ -839,7 +839,7 @@ class TestAsyncServer(unittest.TestCase):
def test_disconnect_namespace(self, eio):
eio.return_value.send = AsyncMock()
eio.return_value.disconnect = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
s.handlers['/foo'] = {}
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0/foo,'))
@ -850,7 +850,7 @@ class TestAsyncServer(unittest.TestCase):
def test_disconnect_twice(self, eio):
eio.return_value.send = AsyncMock()
eio.return_value.disconnect = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0'))
_run(s.disconnect('1'))
@ -861,7 +861,7 @@ class TestAsyncServer(unittest.TestCase):
def test_disconnect_twice_namespace(self, eio):
eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0/foo,'))
_run(s.disconnect('1', namespace='/foo'))
@ -874,7 +874,7 @@ class TestAsyncServer(unittest.TestCase):
eio.return_value.send = AsyncMock()
result = {}
class MyNamespace(asyncio_namespace.AsyncNamespace):
class MyNamespace(async_namespace.AsyncNamespace):
def on_connect(self, sid, environ):
result['result'] = (sid, environ)
@ -890,7 +890,7 @@ class TestAsyncServer(unittest.TestCase):
async def on_baz(self, sid, data1, data2):
result['result'] = (data1, data2)
s = asyncio_server.AsyncServer(async_handlers=False)
s = async_server.AsyncServer(async_handlers=False)
s.register_namespace(MyNamespace('/foo'))
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0/foo,'))
@ -911,7 +911,7 @@ class TestAsyncServer(unittest.TestCase):
class SyncNS(namespace.Namespace):
pass
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
with pytest.raises(ValueError):
s.register_namespace(123)
with pytest.raises(ValueError):
@ -924,20 +924,20 @@ class TestAsyncServer(unittest.TestCase):
s.register_namespace(SyncNS())
def test_logger(self, eio):
s = asyncio_server.AsyncServer(logger=False)
s = async_server.AsyncServer(logger=False)
assert s.logger.getEffectiveLevel() == logging.ERROR
s.logger.setLevel(logging.NOTSET)
s = asyncio_server.AsyncServer(logger=True)
s = async_server.AsyncServer(logger=True)
assert s.logger.getEffectiveLevel() == logging.INFO
s.logger.setLevel(logging.WARNING)
s = asyncio_server.AsyncServer(logger=True)
s = async_server.AsyncServer(logger=True)
assert s.logger.getEffectiveLevel() == logging.WARNING
s.logger.setLevel(logging.NOTSET)
s = asyncio_server.AsyncServer(logger='foo')
s = async_server.AsyncServer(logger='foo')
assert s.logger == 'foo'
def test_engineio_logger(self, eio):
asyncio_server.AsyncServer(engineio_logger='foo')
async_server.AsyncServer(engineio_logger='foo')
eio.assert_called_once_with(
**{'logger': 'foo', 'async_handlers': False}
)
@ -955,7 +955,7 @@ class TestAsyncServer(unittest.TestCase):
def loads(*args, **kwargs):
return '+++ decoded +++'
asyncio_server.AsyncServer(json=CustomJSON)
async_server.AsyncServer(json=CustomJSON)
eio.assert_called_once_with(
**{'json': CustomJSON, 'async_handlers': False}
)
@ -972,7 +972,7 @@ class TestAsyncServer(unittest.TestCase):
packet.Packet.json = json
def test_async_handlers(self, eio):
s = asyncio_server.AsyncServer(async_handlers=True)
s = async_server.AsyncServer(async_handlers=True)
s.manager.connect('123', '/')
_run(s._handle_eio_message('123', '2["my message","a","b","c"]'))
s.eio.start_background_task.assert_called_once_with(
@ -986,13 +986,13 @@ class TestAsyncServer(unittest.TestCase):
)
def test_shutdown(self, eio):
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
s.eio.shutdown = AsyncMock()
_run(s.shutdown())
s.eio.shutdown.mock.assert_called_once_with()
def test_start_background_task(self, eio):
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
s.start_background_task('foo', 'bar', baz='baz')
s.eio.start_background_task.assert_called_once_with(
'foo', 'bar', baz='baz'
@ -1000,6 +1000,6 @@ class TestAsyncServer(unittest.TestCase):
def test_sleep(self, eio):
eio.return_value.sleep = AsyncMock()
s = asyncio_server.AsyncServer()
s = async_server.AsyncServer()
_run(s.sleep(1.23))
s.eio.sleep.mock.assert_called_once_with(1.23)

5
tests/asyncio/test_asyncio_simple_client.py → tests/async/test_simple_client.py

@ -2,6 +2,7 @@ import asyncio
import unittest
from unittest import mock
import pytest
from socketio import AsyncSimpleClient
from socketio.exceptions import SocketIOError, TimeoutError, DisconnectedError
from .helpers import AsyncMock, _run
@ -18,7 +19,7 @@ class TestAsyncAsyncSimpleClient(unittest.TestCase):
def test_connect(self):
client = AsyncSimpleClient(123, a='b')
with mock.patch('socketio.asyncio_simple_client.AsyncClient') \
with mock.patch('socketio.async_simple_client.AsyncClient') \
as mock_client:
mock_client.return_value.connect = AsyncMock()
@ -37,7 +38,7 @@ class TestAsyncAsyncSimpleClient(unittest.TestCase):
def test_connect_context_manager(self):
async def _t():
async with AsyncSimpleClient(123, a='b') as client:
with mock.patch('socketio.asyncio_simple_client.AsyncClient') \
with mock.patch('socketio.async_simple_client.AsyncClient') \
as mock_client:
mock_client.return_value.connect = AsyncMock()

4
tests/common/test_client.py

@ -8,7 +8,7 @@ from engineio import json
from engineio import packet as engineio_packet
import pytest
from socketio import asyncio_namespace
from socketio import async_namespace
from socketio import client
from socketio import exceptions
from socketio import msgpack_packet
@ -157,7 +157,7 @@ class TestClient(unittest.TestCase):
@unittest.skipIf(sys.version_info < (3, 0), 'only for Python 3')
def test_namespace_handler_wrong_async(self):
class MyNamespace(asyncio_namespace.AsyncClientNamespace):
class MyNamespace(async_namespace.AsyncClientNamespace):
pass
c = client.Client()

4
tests/common/test_base_manager.py → tests/common/test_manager.py

@ -3,7 +3,7 @@ from unittest import mock
import pytest
from socketio import base_manager
from socketio import manager
from socketio import packet
@ -19,7 +19,7 @@ class TestBaseManager(unittest.TestCase):
mock_server = mock.MagicMock()
mock_server.eio.generate_id = generate_id
mock_server.packet_class = packet.Packet
self.bm = base_manager.BaseManager()
self.bm = manager.Manager()
self.bm.set_server(mock_server)
self.bm.initialize()

12
tests/common/test_namespace.py

@ -217,6 +217,18 @@ class TestNamespace(unittest.TestCase):
ns.disconnect('sid', namespace='/bar')
ns.server.disconnect.assert_called_with('sid', namespace='/bar')
def test_event_not_found_client(self):
result = {}
class MyNamespace(namespace.ClientNamespace):
def on_custom_message(self, sid, data):
result['result'] = (sid, data)
ns = MyNamespace('/foo')
ns._set_client(mock.MagicMock())
ns.trigger_event('another_custom_message', 'sid', {'data': 'data'})
assert result == {}
def test_emit_client(self):
ns = namespace.ClientNamespace('/foo')
ns._set_client(mock.MagicMock())

22
tests/common/test_pubsub_manager.py

@ -5,7 +5,7 @@ from unittest import mock
import pytest
from socketio import base_manager
from socketio import manager
from socketio import pubsub_manager
from socketio import packet
@ -223,7 +223,7 @@ class TestPubSubManager(unittest.TestCase):
)
def test_handle_emit(self):
with mock.patch.object(base_manager.BaseManager, 'emit') as super_emit:
with mock.patch.object(manager.Manager, 'emit') as super_emit:
self.pm._handle_emit({'event': 'foo', 'data': 'bar'})
super_emit.assert_called_once_with(
'foo',
@ -235,7 +235,7 @@ class TestPubSubManager(unittest.TestCase):
)
def test_handle_emit_with_namespace(self):
with mock.patch.object(base_manager.BaseManager, 'emit') as super_emit:
with mock.patch.object(manager.Manager, 'emit') as super_emit:
self.pm._handle_emit(
{'event': 'foo', 'data': 'bar', 'namespace': '/baz'}
)
@ -249,7 +249,7 @@ class TestPubSubManager(unittest.TestCase):
)
def test_handle_emit_with_room(self):
with mock.patch.object(base_manager.BaseManager, 'emit') as super_emit:
with mock.patch.object(manager.Manager, 'emit') as super_emit:
self.pm._handle_emit(
{'event': 'foo', 'data': 'bar', 'room': 'baz'}
)
@ -263,7 +263,7 @@ class TestPubSubManager(unittest.TestCase):
)
def test_handle_emit_with_skip_sid(self):
with mock.patch.object(base_manager.BaseManager, 'emit') as super_emit:
with mock.patch.object(manager.Manager, 'emit') as super_emit:
self.pm._handle_emit(
{'event': 'foo', 'data': 'bar', 'skip_sid': '123'}
)
@ -277,7 +277,7 @@ class TestPubSubManager(unittest.TestCase):
)
def test_handle_emit_with_remote_callback(self):
with mock.patch.object(base_manager.BaseManager, 'emit') as super_emit:
with mock.patch.object(manager.Manager, 'emit') as super_emit:
self.pm._handle_emit(
{
'event': 'foo',
@ -308,7 +308,7 @@ class TestPubSubManager(unittest.TestCase):
)
def test_handle_emit_with_local_callback(self):
with mock.patch.object(base_manager.BaseManager, 'emit') as super_emit:
with mock.patch.object(manager.Manager, 'emit') as super_emit:
self.pm._handle_emit(
{
'event': 'foo',
@ -397,7 +397,7 @@ class TestPubSubManager(unittest.TestCase):
def test_handle_enter_room(self):
sid = self.pm.connect('123', '/')
with mock.patch.object(
base_manager.BaseManager, 'enter_room'
manager.Manager, 'enter_room'
) as super_enter_room:
self.pm._handle_enter_room({
'method': 'enter_room', 'sid': sid, 'namespace': '/',
@ -410,7 +410,7 @@ class TestPubSubManager(unittest.TestCase):
def test_handle_leave_room(self):
sid = self.pm.connect('123', '/')
with mock.patch.object(
base_manager.BaseManager, 'leave_room'
manager.Manager, 'leave_room'
) as super_leave_room:
self.pm._handle_leave_room({
'method': 'leave_room', 'sid': sid, 'namespace': '/',
@ -422,7 +422,7 @@ class TestPubSubManager(unittest.TestCase):
def test_handle_close_room(self):
with mock.patch.object(
base_manager.BaseManager, 'close_room'
manager.Manager, 'close_room'
) as super_close_room:
self.pm._handle_close_room({'method': 'close_room', 'room': 'foo'})
super_close_room.assert_called_once_with(
@ -431,7 +431,7 @@ class TestPubSubManager(unittest.TestCase):
def test_handle_close_room_with_namespace(self):
with mock.patch.object(
base_manager.BaseManager, 'close_room'
manager.Manager, 'close_room'
) as super_close_room:
self.pm._handle_close_room(
{'method': 'close_room', 'room': 'foo', 'namespace': '/bar'}

Loading…
Cancel
Save