1import asyncio
2
3from socketio import namespace
4
5
6class AsyncNamespace(namespace.Namespace):
7    """Base class for asyncio server-side class-based namespaces.
8
9    A class-based namespace is a class that contains all the event handlers
10    for a Socket.IO namespace. The event handlers are methods of the class
11    with the prefix ``on_``, such as ``on_connect``, ``on_disconnect``,
12    ``on_message``, ``on_json``, and so on. These can be regular functions or
13    coroutines.
14
15    :param namespace: The Socket.IO namespace to be used with all the event
16                      handlers defined in this class. If this argument is
17                      omitted, the default namespace is used.
18    """
19    def is_asyncio_based(self):
20        return True
21
22    async def trigger_event(self, event, *args):
23        """Dispatch an event to the proper handler method.
24
25        In the most common usage, this method is not overloaded by subclasses,
26        as it performs the routing of events to methods. However, this
27        method can be overridden if special dispatching rules are needed, or if
28        having a single method that catches all events is desired.
29
30        Note: this method is a coroutine.
31        """
32        handler_name = 'on_' + event
33        if hasattr(self, handler_name):
34            handler = getattr(self, handler_name)
35            if asyncio.iscoroutinefunction(handler) is True:
36                try:
37                    ret = await handler(*args)
38                except asyncio.CancelledError:  # pragma: no cover
39                    ret = None
40            else:
41                ret = handler(*args)
42            return ret
43
44    async def emit(self, event, data=None, room=None, skip_sid=None,
45                   namespace=None, callback=None):
46        """Emit a custom event to one or more connected clients.
47
48        The only difference with the :func:`socketio.Server.emit` method is
49        that when the ``namespace`` argument is not given the namespace
50        associated with the class is used.
51
52        Note: this method is a coroutine.
53        """
54        return await self.server.emit(event, data=data, room=room,
55                                      skip_sid=skip_sid,
56                                      namespace=namespace or self.namespace,
57                                      callback=callback)
58
59    async def send(self, data, room=None, skip_sid=None, namespace=None,
60                   callback=None):
61        """Send a message to one or more connected clients.
62
63        The only difference with the :func:`socketio.Server.send` method is
64        that when the ``namespace`` argument is not given the namespace
65        associated with the class is used.
66
67        Note: this method is a coroutine.
68        """
69        return await self.server.send(data, room=room, skip_sid=skip_sid,
70                                      namespace=namespace or self.namespace,
71                                      callback=callback)
72
73    async def close_room(self, room, namespace=None):
74        """Close a room.
75
76        The only difference with the :func:`socketio.Server.close_room` method
77        is that when the ``namespace`` argument is not given the namespace
78        associated with the class is used.
79
80        Note: this method is a coroutine.
81        """
82        return await self.server.close_room(
83            room, namespace=namespace or self.namespace)
84
85    async def get_session(self, sid, namespace=None):
86        """Return the user session for a client.
87
88        The only difference with the :func:`socketio.Server.get_session`
89        method is that when the ``namespace`` argument is not given the
90        namespace associated with the class is used.
91
92        Note: this method is a coroutine.
93        """
94        return await self.server.get_session(
95            sid, namespace=namespace or self.namespace)
96
97    async def save_session(self, sid, session, namespace=None):
98        """Store the user session for a client.
99
100        The only difference with the :func:`socketio.Server.save_session`
101        method is that when the ``namespace`` argument is not given the
102        namespace associated with the class is used.
103
104        Note: this method is a coroutine.
105        """
106        return await self.server.save_session(
107            sid, session, namespace=namespace or self.namespace)
108
109    def session(self, sid, namespace=None):
110        """Return the user session for a client with context manager syntax.
111
112        The only difference with the :func:`socketio.Server.session` method is
113        that when the ``namespace`` argument is not given the namespace
114        associated with the class is used.
115        """
116        return self.server.session(sid, namespace=namespace or self.namespace)
117
118    async def disconnect(self, sid, namespace=None):
119        """Disconnect a client.
120
121        The only difference with the :func:`socketio.Server.disconnect` method
122        is that when the ``namespace`` argument is not given the namespace
123        associated with the class is used.
124
125        Note: this method is a coroutine.
126        """
127        return await self.server.disconnect(
128            sid, namespace=namespace or self.namespace)
129
130
131class AsyncClientNamespace(namespace.ClientNamespace):
132    """Base class for asyncio client-side class-based namespaces.
133
134    A class-based namespace is a class that contains all the event handlers
135    for a Socket.IO namespace. The event handlers are methods of the class
136    with the prefix ``on_``, such as ``on_connect``, ``on_disconnect``,
137    ``on_message``, ``on_json``, and so on. These can be regular functions or
138    coroutines.
139
140    :param namespace: The Socket.IO namespace to be used with all the event
141                      handlers defined in this class. If this argument is
142                      omitted, the default namespace is used.
143    """
144    def is_asyncio_based(self):
145        return True
146
147    async def trigger_event(self, event, *args):
148        """Dispatch an event to the proper handler method.
149
150        In the most common usage, this method is not overloaded by subclasses,
151        as it performs the routing of events to methods. However, this
152        method can be overridden if special dispatching rules are needed, or if
153        having a single method that catches all events is desired.
154
155        Note: this method is a coroutine.
156        """
157        handler_name = 'on_' + event
158        if hasattr(self, handler_name):
159            handler = getattr(self, handler_name)
160            if asyncio.iscoroutinefunction(handler) is True:
161                try:
162                    ret = await handler(*args)
163                except asyncio.CancelledError:  # pragma: no cover
164                    ret = None
165            else:
166                ret = handler(*args)
167            return ret
168
169    async def emit(self, event, data=None, namespace=None, callback=None):
170        """Emit a custom event to the server.
171
172        The only difference with the :func:`socketio.Client.emit` method is
173        that when the ``namespace`` argument is not given the namespace
174        associated with the class is used.
175
176        Note: this method is a coroutine.
177        """
178        return await self.client.emit(event, data=data,
179                                      namespace=namespace or self.namespace,
180                                      callback=callback)
181
182    async def send(self, data, namespace=None, callback=None):
183        """Send a message to the server.
184
185        The only difference with the :func:`socketio.Client.send` method is
186        that when the ``namespace`` argument is not given the namespace
187        associated with the class is used.
188
189        Note: this method is a coroutine.
190        """
191        return await self.client.send(data,
192                                      namespace=namespace or self.namespace,
193                                      callback=callback)
194
195    async def disconnect(self):
196        """Disconnect a client.
197
198        The only difference with the :func:`socketio.Client.disconnect` method
199        is that when the ``namespace`` argument is not given the namespace
200        associated with the class is used.
201
202        Note: this method is a coroutine.
203        """
204        return await self.client.disconnect()
205