zndraw-socketio 0.1.0__py3-none-any.whl → 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zndraw_socketio/__init__.py +23 -1
- zndraw_socketio/wrapper.py +0 -5
- zndraw_socketio-0.1.1.dist-info/METADATA +114 -0
- zndraw_socketio-0.1.1.dist-info/RECORD +8 -0
- {zndraw_socketio-0.1.0.dist-info → zndraw_socketio-0.1.1.dist-info}/licenses/LICENSE +1 -1
- zndraw_socketio-0.1.0.dist-info/METADATA +0 -212
- zndraw_socketio-0.1.0.dist-info/RECORD +0 -8
- {zndraw_socketio-0.1.0.dist-info → zndraw_socketio-0.1.1.dist-info}/WHEEL +0 -0
zndraw_socketio/__init__.py
CHANGED
|
@@ -1,13 +1,35 @@
|
|
|
1
1
|
from .wrapper import (
|
|
2
2
|
AsyncClientWrapper as AsyncClientWrapper,
|
|
3
|
+
)
|
|
4
|
+
from .wrapper import (
|
|
3
5
|
AsyncServerWrapper as AsyncServerWrapper,
|
|
6
|
+
)
|
|
7
|
+
from .wrapper import (
|
|
4
8
|
AsyncSimpleClientWrapper as AsyncSimpleClientWrapper,
|
|
5
|
-
|
|
9
|
+
)
|
|
10
|
+
from .wrapper import (
|
|
6
11
|
EventContext as EventContext,
|
|
12
|
+
)
|
|
13
|
+
from .wrapper import (
|
|
7
14
|
SimpleClientWrapper as SimpleClientWrapper,
|
|
15
|
+
)
|
|
16
|
+
from .wrapper import (
|
|
8
17
|
SioRequest as SioRequest,
|
|
18
|
+
)
|
|
19
|
+
from .wrapper import (
|
|
9
20
|
SyncClientWrapper as SyncClientWrapper,
|
|
21
|
+
)
|
|
22
|
+
from .wrapper import (
|
|
10
23
|
SyncServerWrapper as SyncServerWrapper,
|
|
24
|
+
)
|
|
25
|
+
from .wrapper import (
|
|
11
26
|
get_event_name as get_event_name,
|
|
27
|
+
)
|
|
28
|
+
from .wrapper import (
|
|
12
29
|
wrap as wrap,
|
|
13
30
|
)
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
from fastapi import Depends as Depends
|
|
34
|
+
except ImportError:
|
|
35
|
+
from zndraw_socketio.params import Depends as Depends
|
zndraw_socketio/wrapper.py
CHANGED
|
@@ -50,11 +50,6 @@ try:
|
|
|
50
50
|
except ImportError:
|
|
51
51
|
from zndraw_socketio.params import _DependsBase as _DependsClass
|
|
52
52
|
|
|
53
|
-
try:
|
|
54
|
-
from fastapi import Depends
|
|
55
|
-
except ImportError:
|
|
56
|
-
from zndraw_socketio.params import Depends
|
|
57
|
-
|
|
58
53
|
try:
|
|
59
54
|
from fastapi import Request
|
|
60
55
|
except ImportError:
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zndraw-socketio
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Typed wrapper for python-socketio with Pydantic validation and dependency injection.
|
|
5
|
+
Project-URL: homepage, https://github.com/pythonFZ/zndraw-socketio
|
|
6
|
+
Project-URL: issues, https://github.com/pythonFZ/zndraw-socketio/issues
|
|
7
|
+
Author-email: Atomie CHEN <atomic_cwh@163.com>, Fabian Zills <fabian.zills@web.de>
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: pydantic,socketio,typed,wrapper,zndraw
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Requires-Dist: pydantic>=2.10.6
|
|
15
|
+
Requires-Dist: python-socketio>=5.12.1
|
|
16
|
+
Requires-Dist: typing-extensions>=4.13.0
|
|
17
|
+
Provides-Extra: asyncio-client
|
|
18
|
+
Requires-Dist: python-socketio[asyncio-client]>=5.12.1; extra == 'asyncio-client'
|
|
19
|
+
Provides-Extra: client
|
|
20
|
+
Requires-Dist: python-socketio[client]>=5.12.1; extra == 'client'
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# ZnDraw SocketIO
|
|
24
|
+
This package provides an opinionated typed interface to the python-socketio library using pydantic models.
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from zndraw_socketio import Wrapper
|
|
28
|
+
from pydantic import BaseModel
|
|
29
|
+
import socketio
|
|
30
|
+
|
|
31
|
+
sio = Wrapper(socketio.AsyncClient()) # can be server as well
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Emit Pattern
|
|
35
|
+
```python
|
|
36
|
+
class Ping(BaseModel):
|
|
37
|
+
message: str
|
|
38
|
+
|
|
39
|
+
# kwargs are passed to socketio's emit method
|
|
40
|
+
# emits {"message": "Hello, World!"} to "ping"
|
|
41
|
+
await sio.emit(Ping(message="Hello, World!"), **kwargs)
|
|
42
|
+
# emits {"message": "Hello, World!"} to "my-ping"
|
|
43
|
+
await sio.emit("my-ping", Ping(message="Hello, World!"), **kwargs)
|
|
44
|
+
# standard sio behaviour
|
|
45
|
+
await sio.emit("event", {"payload": ...})
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Call / RPC Pattern
|
|
49
|
+
```python
|
|
50
|
+
class Pong(BaseModel):
|
|
51
|
+
reply: str
|
|
52
|
+
|
|
53
|
+
# emits {"message": "Hello, World!"} to "ping" and receives Pong(reply=...) in return
|
|
54
|
+
response = await sio.call(Ping(message="Hello, World!"), response_model=Pong)
|
|
55
|
+
assert isinstance(response, Pong)
|
|
56
|
+
# emits {"message": "Hello, World!"} to "my-ping" and receives Pong(reply=...) in return
|
|
57
|
+
response = await sio.call("my-ping", Ping(message="Hello, World!"), response_model=Pong)
|
|
58
|
+
assert isinstance(response, Pong)
|
|
59
|
+
# standard sio behaviour
|
|
60
|
+
response = await sio.call("event", {"payload": ...})
|
|
61
|
+
# standard response obj, typically dict
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Event Names
|
|
65
|
+
By default, the event name is the class name in snake_case. You can customize it by setting the `event_name` attribute.
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
class CustomEvent(Event):
|
|
69
|
+
...
|
|
70
|
+
|
|
71
|
+
assert CustomEvent.event_name == "custom_event"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
you can override it like this:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from typing import ClassVar
|
|
78
|
+
|
|
79
|
+
class CustomEvent(Event):
|
|
80
|
+
event_name: ClassVar[str] = "my_custom_event"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Union Return Types
|
|
84
|
+
You might want to return `Response|ErrorResponse` from an event handler.
|
|
85
|
+
|
|
86
|
+
> [!NOTE]
|
|
87
|
+
> If your responses share fields, it is recommended to add a discriminator field to avoid ambiguity.
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
class ProblemDetail(BaseModel):
|
|
92
|
+
"""RFC 9457 Problem Details.
|
|
93
|
+
|
|
94
|
+
https://www.rfc-editor.org/rfc/rfc9457.html
|
|
95
|
+
"""
|
|
96
|
+
kind: Literal["error"] = "error" # The discriminator (nod needed in this example)
|
|
97
|
+
type: str = "about:blank"
|
|
98
|
+
title: str
|
|
99
|
+
status: int
|
|
100
|
+
detail: str | None = None
|
|
101
|
+
instance: str | None = None
|
|
102
|
+
|
|
103
|
+
class Response(BaseModel):
|
|
104
|
+
kind: Literal["response"] = "response" # The discriminator (not needed in this example)
|
|
105
|
+
data: str
|
|
106
|
+
|
|
107
|
+
class ServerRequest(BaseModel):
|
|
108
|
+
query: str
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
response_model = ... # do we need typing Annotated here?
|
|
112
|
+
|
|
113
|
+
response = await sio.call(ServerRequest(query="..."), response_model=response_model)
|
|
114
|
+
```
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
zndraw_socketio/__init__.py,sha256=KER2gbnWyWq_OfjcZ7EEWJwFxLKgCmPp4e2IPL2EDtM,783
|
|
2
|
+
zndraw_socketio/params.py,sha256=kQvr0rLz5-ug0bGKlvhxPHfLiDIoMvUVWEGJotpPCjM,1338
|
|
3
|
+
zndraw_socketio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
zndraw_socketio/wrapper.py,sha256=sfhbFv2tzu47xV8T5rSVHLQFSqtAaa39-uk9q6MgQ9E,50662
|
|
5
|
+
zndraw_socketio-0.1.1.dist-info/METADATA,sha256=42ZNuCbjRUpDR6wW2IoIgdhrZycM7gsReiEWC5IkY0o,3533
|
|
6
|
+
zndraw_socketio-0.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
7
|
+
zndraw_socketio-0.1.1.dist-info/licenses/LICENSE,sha256=Ry21YmIoh82aAQa8oBStENUpHqywNGoW6f5Q8ahU5HE,1085
|
|
8
|
+
zndraw_socketio-0.1.1.dist-info/RECORD,,
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: zndraw-socketio
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: Typed wrapper for python-socketio with Pydantic validation and dependency injection.
|
|
5
|
-
Project-URL: homepage, https://github.com/pythonFZ/zndraw-socketio
|
|
6
|
-
Project-URL: issues, https://github.com/pythonFZ/zndraw-socketio/issues
|
|
7
|
-
Author-email: Atomie CHEN <atomic_cwh@163.com>, Fabian Zills <fabian.zills@web.de>
|
|
8
|
-
License-File: LICENSE
|
|
9
|
-
Keywords: pydantic,socketio,typed,wrapper,zndraw
|
|
10
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
-
Classifier: Operating System :: OS Independent
|
|
12
|
-
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Requires-Python: >=3.10
|
|
14
|
-
Requires-Dist: pydantic>=2.10.6
|
|
15
|
-
Requires-Dist: python-socketio>=5.12.1
|
|
16
|
-
Requires-Dist: typing-extensions>=4.13.0
|
|
17
|
-
Provides-Extra: asyncio-client
|
|
18
|
-
Requires-Dist: python-socketio[asyncio-client]>=5.12.1; extra == 'asyncio-client'
|
|
19
|
-
Provides-Extra: client
|
|
20
|
-
Requires-Dist: python-socketio[client]>=5.12.1; extra == 'client'
|
|
21
|
-
Description-Content-Type: text/markdown
|
|
22
|
-
|
|
23
|
-
# zndraw-socketio
|
|
24
|
-
|
|
25
|
-
Typed wrapper for [python-socketio](https://github.com/miguelgrinberg/python-socketio) with Pydantic validation and dependency injection.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
## Installation
|
|
29
|
-
|
|
30
|
-
```sh
|
|
31
|
-
pip install zndraw-socketio
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
Optional extras for client transports:
|
|
35
|
-
|
|
36
|
-
```sh
|
|
37
|
-
pip install zndraw-socketio[client]
|
|
38
|
-
pip install zndraw-socketio[asyncio-client]
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
## Usage
|
|
43
|
-
|
|
44
|
-
### Wrap any socketio instance
|
|
45
|
-
|
|
46
|
-
```python
|
|
47
|
-
import socketio
|
|
48
|
-
from pydantic import BaseModel
|
|
49
|
-
from zndraw_socketio import wrap
|
|
50
|
-
|
|
51
|
-
class Ping(BaseModel):
|
|
52
|
-
message: str
|
|
53
|
-
|
|
54
|
-
class Pong(BaseModel):
|
|
55
|
-
reply: str
|
|
56
|
-
|
|
57
|
-
# Wrap any existing socketio instance
|
|
58
|
-
tsio = wrap(socketio.AsyncClient())
|
|
59
|
-
|
|
60
|
-
# Emit with automatic event name derivation (Ping -> "ping")
|
|
61
|
-
await tsio.emit(Ping(message="Hello, World!"))
|
|
62
|
-
|
|
63
|
-
# Emit with explicit event name
|
|
64
|
-
await tsio.emit("my-ping", Ping(message="Hello, World!"))
|
|
65
|
-
|
|
66
|
-
# Call with typed response
|
|
67
|
-
response = await tsio.call(Ping(message="Hello"), response_model=Pong)
|
|
68
|
-
# response is typed as Pong
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### Handler registration with validation
|
|
72
|
-
|
|
73
|
-
```python
|
|
74
|
-
# Event name derived from model class (Ping -> "ping")
|
|
75
|
-
@tsio.on(Ping)
|
|
76
|
-
async def handle_ping(data: Ping) -> Pong:
|
|
77
|
-
return Pong(reply=data.message)
|
|
78
|
-
|
|
79
|
-
# Or use function name as event name
|
|
80
|
-
@tsio.event
|
|
81
|
-
async def ping(data: Ping) -> Pong:
|
|
82
|
-
return Pong(reply=data.message)
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### Union response types
|
|
86
|
-
|
|
87
|
-
```python
|
|
88
|
-
from typing import Annotated, Literal
|
|
89
|
-
from pydantic import BaseModel, Discriminator
|
|
90
|
-
|
|
91
|
-
class Success(BaseModel):
|
|
92
|
-
kind: Literal["success"] = "success"
|
|
93
|
-
data: str
|
|
94
|
-
|
|
95
|
-
class Error(BaseModel):
|
|
96
|
-
kind: Literal["error"] = "error"
|
|
97
|
-
message: str
|
|
98
|
-
|
|
99
|
-
# Simple union
|
|
100
|
-
response = await tsio.call(request, response_model=Success | Error)
|
|
101
|
-
|
|
102
|
-
# Discriminated union
|
|
103
|
-
ResponseType = Annotated[Success | Error, Discriminator("kind")]
|
|
104
|
-
response = await tsio.call(request, response_model=ResponseType)
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Custom event names
|
|
108
|
-
|
|
109
|
-
```python
|
|
110
|
-
from typing import ClassVar
|
|
111
|
-
|
|
112
|
-
class CustomEvent(BaseModel):
|
|
113
|
-
event_name: ClassVar[str] = "my_custom_event"
|
|
114
|
-
data: str
|
|
115
|
-
|
|
116
|
-
# Uses "my_custom_event" instead of "custom_event"
|
|
117
|
-
await tsio.emit(CustomEvent(data="hello"))
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### Exception handlers
|
|
121
|
-
|
|
122
|
-
```python
|
|
123
|
-
from zndraw_socketio import wrap, EventContext
|
|
124
|
-
|
|
125
|
-
tsio = wrap(socketio.AsyncServer(async_mode="asgi"))
|
|
126
|
-
|
|
127
|
-
@tsio.exception_handler(ValueError)
|
|
128
|
-
async def handle_value_error(ctx: EventContext, exc: ValueError):
|
|
129
|
-
return {"error": "value_error", "message": str(exc)}
|
|
130
|
-
|
|
131
|
-
# Namespace-specific
|
|
132
|
-
@tsio.exception_handler(ValueError, namespace="/chat")
|
|
133
|
-
async def handle_chat_error(ctx: EventContext, exc: ValueError):
|
|
134
|
-
return {"error": "chat_error", "message": str(exc)}
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### Dependency injection
|
|
138
|
-
|
|
139
|
-
```python
|
|
140
|
-
from typing import Annotated
|
|
141
|
-
from fastapi import Depends
|
|
142
|
-
from zndraw_socketio import wrap
|
|
143
|
-
|
|
144
|
-
async def get_redis():
|
|
145
|
-
return my_redis_pool
|
|
146
|
-
|
|
147
|
-
RedisDep = Annotated[AsyncRedis, Depends(get_redis)]
|
|
148
|
-
|
|
149
|
-
tsio = wrap(socketio.AsyncServer(async_mode="asgi"))
|
|
150
|
-
|
|
151
|
-
@tsio.on(RoomLeave)
|
|
152
|
-
async def room_leave(sid: str, data: RoomLeave, redis: RedisDep) -> RoomLeaveResponse:
|
|
153
|
-
await redis.delete(f"presence:{data.room_id}:{sid}")
|
|
154
|
-
return RoomLeaveResponse(status="ok")
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
When FastAPI is not installed, import `Depends` from `zndraw_socketio`:
|
|
158
|
-
|
|
159
|
-
```python
|
|
160
|
-
from zndraw_socketio import Depends
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
### FastAPI integration
|
|
164
|
-
|
|
165
|
-
```python
|
|
166
|
-
import socketio
|
|
167
|
-
from typing import Annotated
|
|
168
|
-
from fastapi import FastAPI, Depends
|
|
169
|
-
from zndraw_socketio import wrap, AsyncServerWrapper
|
|
170
|
-
|
|
171
|
-
app = FastAPI()
|
|
172
|
-
tsio = wrap(socketio.AsyncServer(async_mode="asgi"))
|
|
173
|
-
|
|
174
|
-
SioServer = Annotated[AsyncServerWrapper, Depends(tsio)]
|
|
175
|
-
|
|
176
|
-
@app.post("/notify")
|
|
177
|
-
async def notify(server: SioServer):
|
|
178
|
-
await server.emit("notification", {"msg": "hello"})
|
|
179
|
-
return {"status": "sent"}
|
|
180
|
-
|
|
181
|
-
combined_app = socketio.ASGIApp(tsio, app)
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
### SimpleClient support
|
|
185
|
-
|
|
186
|
-
```python
|
|
187
|
-
tsio = wrap(socketio.SimpleClient())
|
|
188
|
-
tsio.connect("http://localhost:5000")
|
|
189
|
-
|
|
190
|
-
tsio.emit(Ping(message="Hello"))
|
|
191
|
-
response = tsio.call(Ping(message="Hello"), response_model=Pong)
|
|
192
|
-
event_name, data = tsio.receive(response_model=Pong)
|
|
193
|
-
tsio.disconnect()
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
## Supported socketio types
|
|
197
|
-
|
|
198
|
-
`wrap()` auto-detects the socketio instance type:
|
|
199
|
-
|
|
200
|
-
| socketio type | Wrapper class |
|
|
201
|
-
|---|---|
|
|
202
|
-
| `AsyncClient` | `AsyncClientWrapper` |
|
|
203
|
-
| `AsyncServer` | `AsyncServerWrapper` |
|
|
204
|
-
| `Client` | `SyncClientWrapper` |
|
|
205
|
-
| `Server` | `SyncServerWrapper` |
|
|
206
|
-
| `SimpleClient` | `SimpleClientWrapper` |
|
|
207
|
-
| `AsyncSimpleClient` | `AsyncSimpleClientWrapper` |
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
## License
|
|
211
|
-
|
|
212
|
-
MIT License
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
zndraw_socketio/__init__.py,sha256=eRrJNRHjGEBsBFUc1gOBE4p9j9-g-K5o4Ao5whz6OLU,455
|
|
2
|
-
zndraw_socketio/params.py,sha256=kQvr0rLz5-ug0bGKlvhxPHfLiDIoMvUVWEGJotpPCjM,1338
|
|
3
|
-
zndraw_socketio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
zndraw_socketio/wrapper.py,sha256=Lsjopx8X2ikCfg4NIXAmBvRmhcgcBqSVNmao8lI4QXM,50767
|
|
5
|
-
zndraw_socketio-0.1.0.dist-info/METADATA,sha256=r0uoZMxmNAylGJOHDm4u8w_8qr-9gEJgVbMKifpX5_Y,5338
|
|
6
|
-
zndraw_socketio-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
7
|
-
zndraw_socketio-0.1.0.dist-info/licenses/LICENSE,sha256=SpSDGxJCTumVhMVOPkFg_x6q01lPcU6KsYh_7DhwJg0,1068
|
|
8
|
-
zndraw_socketio-0.1.0.dist-info/RECORD,,
|
|
File without changes
|