import itertools
import logging
import random
import signal
import threading
import engineio
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.
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 [ : ] :
client . _reconnect_abort . set ( )
if callable ( original_signal_handler ) :
return original_signal_handler ( sig , frame )
else : # pragma: no cover
# Handle case where no original SIGINT handler was present.
return signal . default_int_handler ( sig , frame )
original_signal_handler = None
class Client ( object ) :
""" A Socket.IO client.
This class implements a fully compliant Socket . IO web client with support
for websocket and long - polling transports .
: param reconnection : ` ` True ` ` if the client should automatically attempt to
reconnect to the server after an interruption , or
` ` False ` ` to not reconnect . The default is ` ` True ` ` .
: param reconnection_attempts : How many reconnection attempts to issue
before giving up , or 0 for infinite attempts .
The default is 0.
: param reconnection_delay : How long to wait in seconds before the first
reconnection attempt . Each successive attempt
doubles this delay .
: param reconnection_delay_max : The maximum delay between reconnection
attempts .
: param randomization_factor : Randomization amount for each delay between
reconnection attempts . The default is 0.5 ,
which means that each delay is randomly
adjusted by + / - 50 % .
: param logger : To enable logging set to ` ` True ` ` or pass a logger object to
use . To disable logging set to ` ` False ` ` . The default is
` ` False ` ` . Note that fatal errors are logged even when
` ` logger ` ` is ` ` False ` ` .
: param serializer : The serialization method to use when transmitting
packets . Valid values are ` ` ' default ' ` ` , ` ` ' pickle ' ` ` ,
` ` ' msgpack ' ` ` and ` ` ' cbor ' ` ` . Alternatively , a subclass
of the : class : ` Packet ` class with custom implementations
of the ` ` encode ( ) ` ` and ` ` decode ( ) ` ` methods can be
provided . Client and server must use compatible
serializers .
: param json : An alternative json module to use for encoding and decoding
packets . Custom json modules must have ` ` dumps ` ` and ` ` loads ` `
functions that are compatible with the standard library
versions .
: param handle_sigint : Set to ` ` True ` ` to automatically handle disconnection
when the process is interrupted , or to ` ` False ` ` to
leave interrupt handling to the calling application .
Interrupt handling can only be enabled when the
client instance is created in the main thread .
The Engine . IO configuration supports the following settings :
: param request_timeout : A timeout in seconds for requests . The default is
5 seconds .
: param http_session : an initialized ` ` requests . Session ` ` object to be used
when sending requests to the server . Use it if you
need to add special client options such as proxy
servers , SSL certificates , etc .
: param ssl_verify : ` ` True ` ` to verify SSL certificates , or ` ` False ` ` to
skip SSL certificate verification , allowing
connections to servers with self signed certificates .
The default is ` ` True ` ` .
: param engineio_logger : To enable Engine . IO logging set to ` ` True ` ` or pass
a logger object to use . To disable logging set to
` ` False ` ` . The default is ` ` False ` ` . Note that
fatal errors are logged even when
` ` engineio_logger ` ` is ` ` False ` ` .
"""
reserved_events = [ ' connect ' , ' connect_error ' , ' disconnect ' ]
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 ) :
global original_signal_handler
if handle_sigint and original_signal_handler is None and \
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
def connect ( self , url , headers = { } , auth = None , transports = None ,
namespaces = None , socketio_path = ' socket.io ' , wait = True ,
wait_timeout = 1 ) :
""" Connect to a Socket.IO server.
: param url : The URL of the Socket . IO server . It can include custom
query string parameters if required by the server . If a
function is provided , the client will invoke it to obtain
the URL each time a connection or reconnection is
attempted .
: param headers : A dictionary with custom headers to send with the
connection request . If a function is provided , the
client will invoke it to obtain the headers dictionary
each time a connection or reconnection is attempted .
: param auth : Authentication data passed to the server with the
connection request , normally a dictionary with one or
more string key / value pairs . If a function is provided ,
the client will invoke it to obtain the authentication
data each time a connection or reconnection is attempted .
: param transports : The list of allowed transports . Valid transports
are ` ` ' polling ' ` ` and ` ` ' websocket ' ` ` . If not
given , the polling transport is connected first ,
then an upgrade to websocket is attempted .
: param namespaces : The namespaces to connect as a string or list of
strings . If not given , the namespaces that have
registered event handlers are connected .
: param socketio_path : The endpoint where the Socket . IO server is
installed . The default value is appropriate for
most cases .
: param wait : if set to ` ` True ` ` ( the default ) the call only returns
when all the namespaces are connected . If set to
` ` False ` ` , the call returns as soon as the Engine . IO
transport is connected , and the namespaces will connect
in the background .
: param wait_timeout : How long the client should wait for the
connection . The default is 1 second . This
argument is only considered when ` ` wait ` ` is set
to ` ` True ` ` .
Example usage : :
sio = socketio . Client ( )
sio . connect ( ' http://localhost:5000 ' )
"""
if self . connected :
raise exceptions . ConnectionError ( ' Already connected ' )
self . connection_url = url
self . connection_headers = headers
self . connection_auth = auth
self . connection_transports = transports
self . connection_namespaces = namespaces
self . socketio_path = socketio_path
if namespaces is None :
namespaces = list ( set ( self . handlers . keys ( ) ) . union (
set ( self . namespace_handlers . keys ( ) ) ) )
if len ( namespaces ) == 0 :
namespaces = [ ' / ' ]
elif isinstance ( namespaces , str ) :
namespaces = [ namespaces ]
self . connection_namespaces = namespaces
self . namespaces = { }
if self . _connect_event is None :
self . _connect_event = self . eio . create_event ( )
else :
self . _connect_event . clear ( )
real_url = self . _get_real_value ( self . connection_url )
real_headers = self . _get_real_value ( self . connection_headers )
try :
self . eio . connect ( real_url , headers = real_headers ,
transports = transports ,
engineio_path = socketio_path )
except engineio . exceptions . ConnectionError as exc :
self . _trigger_event (
' connect_error ' , ' / ' ,
exc . args [ 1 ] if len ( exc . args ) > 1 else exc . args [ 0 ] )
raise exceptions . ConnectionError ( exc . args [ 0 ] ) from None
if wait :
while self . _connect_event . wait ( timeout = wait_timeout ) :
self . _connect_event . clear ( )
if set ( self . namespaces ) == set ( self . connection_namespaces ) :
break
if set ( self . namespaces ) != set ( self . connection_namespaces ) :
self . disconnect ( )
raise exceptions . ConnectionError (
' One or more namespaces failed to connect ' )
self . connected = True
def wait ( self ) :
""" Wait until the connection with the server ends.
Client applications can use this function to block the main thread
during the life of the connection .
"""
while True :
self . eio . wait ( )
self . sleep ( 1 ) # give the reconnect task time to start up
if not self . _reconnect_task :
break
self . _reconnect_task . join ( )
if self . eio . state != ' connected ' :
break
def emit ( self , event , data = None , namespace = None , callback = None ) :
""" Emit a custom event to one or more connected clients.
: 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 data : The data to send to the server . Data can be of
type ` ` str ` ` , ` ` bytes ` ` , ` ` list ` ` or ` ` dict ` ` . To send
multiple arguments , use a tuple where each element is of
one of the types indicated above .
: param namespace : The Socket . IO namespace for the event . If this
argument is omitted the event is emitted to the
default namespace .
: param callback : If given , this function will be called to acknowledge
the the server has received the message . The arguments
that will be passed to the function are those provided
by the server .
Note : this method is not thread safe . If multiple threads are emitting
at the same time on the same client connection , messages composed of
multiple packets may end up being sent in an incorrect sequence . Use
standard concurrency solutions ( such as a Lock object ) to prevent this
situation .
"""
namespace = namespace or ' / '
if namespace not in self . namespaces :
raise exceptions . BadNamespaceError (
namespace + ' is not a connected namespace. ' )
self . logger . info ( ' Emitting event " %s " [ %s ] ' , event , namespace )
if callback is not None :
id = self . _generate_ack_id ( namespace , callback )
else :
id = None
# tuples are expanded to multiple arguments, everything else is sent
# as a single argument
if isinstance ( data , tuple ) :
data = list ( data )
elif data is not None :
data = [ data ]
else :
data = [ ]
self . _send_packet ( self . packet_class ( packet . EVENT , namespace = namespace ,
data = [ event ] + data , id = id ) )
def send ( self , data , namespace = None , callback = None ) :
""" Send a message to one or more connected clients.
This function emits an event with the name ` ` ' message ' ` ` . Use
: func : ` emit ` to issue custom event names .
: param data : The data to send to the server . Data can be of
type ` ` str ` ` , ` ` bytes ` ` , ` ` list ` ` or ` ` dict ` ` . To send
multiple arguments , use a tuple where each element is of
one of the types indicated above .
: param namespace : The Socket . IO namespace for the event . If this
argument is omitted the event is emitted to the
default namespace .
: param callback : If given , this function will be called to acknowledge
the the server has received the message . The arguments
that will be passed to the function are those provided
by the server .
"""
self . emit ( ' message ' , data = data , namespace = namespace ,
callback = callback )
def call ( self , event , data = None , namespace = None , timeout = 60 ) :
""" Emit a custom event to a client and wait for the response.
This method issues an emit with a callback and waits for the callback
to be invoked before returning . If the callback isn ' t invoked before
the timeout , then a ` ` TimeoutError ` ` exception is raised . If the
Socket . IO connection drops during the wait , this method still waits
until the specified timeout .
: 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 data : The data to send to the server . Data can be of
type ` ` str ` ` , ` ` bytes ` ` , ` ` list ` ` or ` ` dict ` ` . To send
multiple arguments , use a tuple where each element is of
one of the types indicated above .
: param namespace : The Socket . IO namespace for the event . If this
argument is omitted the event is emitted to the
default namespace .
: param timeout : The waiting timeout . If the timeout is reached before
the client acknowledges the event , then a
` ` TimeoutError ` ` exception is raised .
Note : this method is not thread safe . If multiple threads are emitting
at the same time on the same client connection , messages composed of
multiple packets may end up being sent in an incorrect sequence . Use
standard concurrency solutions ( such as a Lock object ) to prevent this
situation .
"""
callback_event = self . eio . create_event ( )
callback_args = [ ]
def event_callback ( * args ) :
callback_args . append ( args )
callback_event . set ( )
self . emit ( event , data = data , namespace = namespace ,
callback = event_callback )
if not callback_event . wait ( timeout = timeout ) :
raise exceptions . TimeoutError ( )
return callback_args [ 0 ] if len ( callback_args [ 0 ] ) > 1 \
else callback_args [ 0 ] [ 0 ] if len ( callback_args [ 0 ] ) == 1 \
else None
def disconnect ( self ) :
""" Disconnect from the server. """
# here we just request the disconnection
# later in _handle_eio_disconnect we invoke the disconnect handler
for n in self . namespaces :
self . _send_packet ( self . packet_class (
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.
This is a utility function that applications can use to start a
background task using the method that is compatible with the
selected async mode .
: param target : the target function to execute .
: param args : arguments to pass to the function .
: param kwargs : keyword arguments to pass to the function .
This function returns an object that represents the background task ,
on which the ` ` join ( ) ` ` methond can be invoked to wait for the task to
complete .
"""
return self . eio . start_background_task ( target , * args , * * kwargs )
def sleep ( self , seconds = 0 ) :
""" Sleep for the requested amount of time using the appropriate async
model .
This is a utility function that applications can use to put a task to
sleep without having to worry about using the correct call for the
selected async mode .
"""
return self . eio . sleep ( seconds )
def _get_real_value ( self , value ) :
""" Return the actual value, for parameters that can also be given as
callables . """
if not callable ( value ) :
return value
return value ( )
def _send_packet ( self , pkt ) :
""" Send a Socket.IO packet to the server. """
encoded_packet = pkt . encode ( )
if isinstance ( encoded_packet , list ) :
for ep in encoded_packet :
self . eio . send ( ep )
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 :
self . logger . info ( ' Namespace {} is connected ' . format ( namespace ) )
self . namespaces [ namespace ] = ( data or { } ) . get ( ' sid ' , self . sid )
self . _trigger_event ( ' connect ' , namespace = namespace )
self . _connect_event . set ( )
def _handle_disconnect ( self , namespace ) :
if not self . connected :
return
namespace = namespace or ' / '
self . _trigger_event ( ' disconnect ' , namespace = namespace )
if namespace in self . namespaces :
del self . namespaces [ namespace ]
if not self . namespaces :
self . connected = False
self . eio . disconnect ( abort = True )
def _handle_event ( self , namespace , id , data ) :
namespace = namespace or ' / '
self . logger . info ( ' Received event " %s " [ %s ] ' , data [ 0 ] , namespace )
r = self . _trigger_event ( data [ 0 ] , namespace , * data [ 1 : ] )
if id is not None :
# send ACK packet with the response returned by the handler
# tuples are expanded as multiple arguments
if r is None :
data = [ ]
elif isinstance ( r , tuple ) :
data = list ( r )
else :
data = [ r ]
self . _send_packet ( self . packet_class (
packet . ACK , namespace = namespace , id = id , data = data ) )
def _handle_ack ( self , namespace , id , data ) :
namespace = namespace or ' / '
self . logger . info ( ' Received ack [ %s ] ' , namespace )
callback = None
try :
callback = self . callbacks [ namespace ] [ id ]
except KeyError :
# if we get an unknown callback we just ignore it
self . logger . warning ( ' Unknown callback received, ignoring. ' )
else :
del self . callbacks [ namespace ] [ id ]
if callback is not None :
callback ( * data )
def _handle_error ( self , namespace , data ) :
namespace = namespace or ' / '
self . logger . info ( ' Connection to namespace {} was rejected ' . format (
namespace ) )
if data is None :
data = tuple ( )
elif not isinstance ( data , ( tuple , list ) ) :
data = ( data , )
self . _trigger_event ( ' connect_error ' , namespace , * data )
self . _connect_event . set ( )
if namespace in self . namespaces :
del self . namespaces [ namespace ]
if namespace == ' / ' :
self . namespaces = { }
self . connected = False
def _trigger_event ( self , event , namespace , * args ) :
""" Invoke an application event handler. """
# first see if we have an explicit handler for the event
if namespace in self . handlers :
if event in self . handlers [ namespace ] :
return self . handlers [ namespace ] [ event ] ( * args )
elif event not in self . reserved_events and \
' * ' in self . handlers [ namespace ] :
return self . handlers [ namespace ] [ ' * ' ] ( event , * args )
# or else, forward the event to a namespace handler if one exists
elif namespace in self . namespace_handlers :
return self . namespace_handlers [ namespace ] . trigger_event (
event , * args )
def _handle_reconnect ( self ) :
if self . _reconnect_abort is None : # pragma: no cover
self . _reconnect_abort = self . eio . create_event ( )
self . _reconnect_abort . clear ( )
reconnecting_clients . append ( self )
attempt_count = 0
current_delay = self . reconnection_delay
while True :
delay = current_delay
current_delay * = 2
if delay > self . reconnection_delay_max :
delay = self . reconnection_delay_max
delay + = self . randomization_factor * ( 2 * random . random ( ) - 1 )
self . logger . info (
' Connection failed, new attempt in {:.02f} seconds ' . format (
delay ) )
if self . _reconnect_abort . wait ( delay ) :
self . logger . info ( ' Reconnect task aborted ' )
break
attempt_count + = 1
try :
self . connect ( self . connection_url ,
headers = self . connection_headers ,
auth = self . connection_auth ,
transports = self . connection_transports ,
namespaces = self . connection_namespaces ,
socketio_path = self . socketio_path )
except ( exceptions . ConnectionError , ValueError ) :
pass
else :
self . logger . info ( ' Reconnection successful ' )
self . _reconnect_task = None
break
if self . reconnection_attempts and \
attempt_count > = self . reconnection_attempts :
self . logger . info (
' Maximum reconnection attempts reached, giving up ' )
break
reconnecting_clients . remove ( self )
def _handle_eio_connect ( self ) :
""" Handle the Engine.IO connection event. """
self . logger . info ( ' Engine.IO connection established ' )
self . sid = self . eio . sid
real_auth = self . _get_real_value ( self . connection_auth ) or { }
for n in self . connection_namespaces :
self . _send_packet ( self . packet_class (
packet . CONNECT , data = real_auth , namespace = n ) )
def _handle_eio_message ( self , data ) :
""" Dispatch Engine.IO messages. """
if self . _binary_packet :
pkt = self . _binary_packet
if pkt . add_attachment ( data ) :
self . _binary_packet = None
if pkt . packet_type == packet . BINARY_EVENT :
self . _handle_event ( pkt . namespace , pkt . id , pkt . data )
else :
self . _handle_ack ( pkt . namespace , pkt . id , pkt . data )
else :
pkt = self . packet_class ( encoded_packet = data )
if pkt . packet_type == packet . CONNECT :
self . _handle_connect ( pkt . namespace , pkt . data )
elif pkt . packet_type == packet . DISCONNECT :
self . _handle_disconnect ( pkt . namespace )
elif pkt . packet_type == packet . EVENT :
self . _handle_event ( pkt . namespace , pkt . id , pkt . data )
elif pkt . packet_type == packet . ACK :
self . _handle_ack ( pkt . namespace , pkt . id , pkt . data )
elif pkt . packet_type == packet . BINARY_EVENT or \
pkt . packet_type == packet . BINARY_ACK :
self . _binary_packet = pkt
elif pkt . packet_type == packet . CONNECT_ERROR :
self . _handle_error ( pkt . namespace , pkt . data )
else :
raise ValueError ( ' Unknown packet type. ' )
def _handle_eio_disconnect ( self ) :
""" Handle the Engine.IO disconnection event. """
self . logger . info ( ' Engine.IO connection dropped ' )
if self . connected :
for n in self . namespaces :
self . _trigger_event ( ' disconnect ' , namespace = n )
self . namespaces = { }
self . connected = False
self . callbacks = { }
self . _binary_packet = None
self . sid = None
if self . eio . state == ' connected ' and self . reconnection :
self . _reconnect_task = self . start_background_task (
self . _handle_reconnect )
def _engineio_client_class ( self ) :
return engineio . Client