phoenix-channels-python-client 0.1.2__tar.gz
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.
- phoenix_channels_python_client-0.1.2/LICENSE +21 -0
- phoenix_channels_python_client-0.1.2/MANIFEST.in +3 -0
- phoenix_channels_python_client-0.1.2/PKG-INFO +268 -0
- phoenix_channels_python_client-0.1.2/README.md +239 -0
- phoenix_channels_python_client-0.1.2/phoenix_channels_python_client/__init__.py +22 -0
- phoenix_channels_python_client-0.1.2/phoenix_channels_python_client/client.py +900 -0
- phoenix_channels_python_client-0.1.2/phoenix_channels_python_client/exceptions.py +12 -0
- phoenix_channels_python_client-0.1.2/phoenix_channels_python_client/phx_messages.py +52 -0
- phoenix_channels_python_client-0.1.2/phoenix_channels_python_client/protocol_handler.py +170 -0
- phoenix_channels_python_client-0.1.2/phoenix_channels_python_client/topic_subscription.py +46 -0
- phoenix_channels_python_client-0.1.2/phoenix_channels_python_client/utils.py +65 -0
- phoenix_channels_python_client-0.1.2/phoenix_channels_python_client.egg-info/PKG-INFO +268 -0
- phoenix_channels_python_client-0.1.2/phoenix_channels_python_client.egg-info/SOURCES.txt +16 -0
- phoenix_channels_python_client-0.1.2/phoenix_channels_python_client.egg-info/dependency_links.txt +1 -0
- phoenix_channels_python_client-0.1.2/phoenix_channels_python_client.egg-info/requires.txt +13 -0
- phoenix_channels_python_client-0.1.2/phoenix_channels_python_client.egg-info/top_level.txt +1 -0
- phoenix_channels_python_client-0.1.2/pyproject.toml +52 -0
- phoenix_channels_python_client-0.1.2/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 thenvoi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: phoenix-channels-python-client
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: A Python client library for connecting to Phoenix Channels
|
|
5
|
+
Author: Phoenix Channels Python Client
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Requires-Python: >=3.11
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: websockets>=10.0
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: pytest; extra == "dev"
|
|
20
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
21
|
+
Requires-Dist: pre-commit>=3.0.0; extra == "dev"
|
|
22
|
+
Requires-Dist: commitizen>=3.13.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pyrefly; extra == "dev"
|
|
24
|
+
Requires-Dist: ruff; extra == "dev"
|
|
25
|
+
Provides-Extra: test
|
|
26
|
+
Requires-Dist: pytest; extra == "test"
|
|
27
|
+
Requires-Dist: pytest-asyncio; extra == "test"
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# Phoenix Channels Python Client
|
|
31
|
+
|
|
32
|
+
[](https://www.python.org/downloads/)
|
|
33
|
+
[](https://opensource.org/licenses/MIT)
|
|
34
|
+
|
|
35
|
+
A modern, async Python client library for connecting to [Phoenix Channels](https://hexdocs.pm/phoenix/channels.html) - the real-time WebSocket layer of the Phoenix Framework.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
### For Users
|
|
40
|
+
|
|
41
|
+
Install from source:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git clone https://github.com/your-org/phoenix_channels_python_client.git
|
|
45
|
+
cd phoenix_channels_python_client
|
|
46
|
+
python3 -m venv venv
|
|
47
|
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
|
48
|
+
pip install .
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### For Development
|
|
52
|
+
|
|
53
|
+
Set up development environment:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/your-org/phoenix_channels_python_client.git
|
|
57
|
+
cd phoenix_channels_python_client
|
|
58
|
+
python3 -m venv venv
|
|
59
|
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
|
60
|
+
python3 -m pip install -U pip
|
|
61
|
+
pip install -e ".[dev]"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Run tests:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install -e ".[test]"
|
|
68
|
+
pytest
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Dependencies
|
|
72
|
+
|
|
73
|
+
- Python 3.11+
|
|
74
|
+
- `websockets>=10.0`
|
|
75
|
+
|
|
76
|
+
## Quick Start
|
|
77
|
+
|
|
78
|
+
Here's a minimal example to get you started:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
import asyncio
|
|
82
|
+
from phoenix_channels_python_client import PHXChannelsClient
|
|
83
|
+
|
|
84
|
+
async def main():
|
|
85
|
+
# Connect to Phoenix WebSocket server
|
|
86
|
+
async with PHXChannelsClient(
|
|
87
|
+
websocket_url="ws://localhost:4000/socket/websocket",
|
|
88
|
+
api_key="your-api-key"
|
|
89
|
+
) as client:
|
|
90
|
+
|
|
91
|
+
# Define message handler
|
|
92
|
+
async def handle_message(message):
|
|
93
|
+
print(f"Received: {message}")
|
|
94
|
+
|
|
95
|
+
# Subscribe to a topic
|
|
96
|
+
await client.subscribe_to_topic("room:lobby", handle_message)
|
|
97
|
+
|
|
98
|
+
# Use built-in convenience method to keep connection alive
|
|
99
|
+
await client.run_forever() # Handles Ctrl+C automatically
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
asyncio.run(main())
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Phoenix System Events
|
|
106
|
+
|
|
107
|
+
Phoenix Channels uses several reserved event names for internal protocol communication. The client library automatically handles these system events to manage connections, subscriptions, and message routing:
|
|
108
|
+
|
|
109
|
+
- `phx_join` - Channel join requests
|
|
110
|
+
- `phx_reply` - Server replies to client messages
|
|
111
|
+
- `phx_leave` - Channel leave notifications
|
|
112
|
+
- `phx_close` - Channel close events
|
|
113
|
+
- `phx_error` - Channel error events
|
|
114
|
+
|
|
115
|
+
**Important:** You should avoid using these event names when handling custom events in your application, as they are reserved for Phoenix's internal protocol. The library uses these events to determine message routing and connection state management.
|
|
116
|
+
|
|
117
|
+
If you have a specific use case that requires handling these system events directly, you can do so, but be aware that this may interfere with the library's automatic connection management.
|
|
118
|
+
|
|
119
|
+
## Protocol Versions
|
|
120
|
+
|
|
121
|
+
Phoenix Channels supports two protocol versions. Choose based on your Phoenix server version:
|
|
122
|
+
|
|
123
|
+
### Protocol v2.0 (Default)
|
|
124
|
+
```python
|
|
125
|
+
from phoenix_channels_python_client import PHXChannelsClient
|
|
126
|
+
|
|
127
|
+
async with PHXChannelsClient(
|
|
128
|
+
websocket_url="ws://localhost:4000/socket/websocket",
|
|
129
|
+
api_key="your-api-key"
|
|
130
|
+
# protocol_version defaults to v2.0
|
|
131
|
+
) as client:
|
|
132
|
+
# Your code here
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Protocol v1.0
|
|
136
|
+
```python
|
|
137
|
+
from phoenix_channels_python_client import PHXChannelsClient, PhoenixChannelsProtocolVersion
|
|
138
|
+
|
|
139
|
+
async with PHXChannelsClient(
|
|
140
|
+
websocket_url="ws://localhost:4000/socket/websocket",
|
|
141
|
+
api_key="your-api-key",
|
|
142
|
+
protocol_version=PhoenixChannelsProtocolVersion.V1
|
|
143
|
+
) as client:
|
|
144
|
+
# Your code here
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Usage Examples
|
|
148
|
+
|
|
149
|
+
### Basic Topic Subscription
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
import asyncio
|
|
153
|
+
from phoenix_channels_python_client import PHXChannelsClient
|
|
154
|
+
|
|
155
|
+
async def message_handler(message):
|
|
156
|
+
print(f"Topic: {message.topic}")
|
|
157
|
+
print(f"Event: {message.event}")
|
|
158
|
+
print(f"Payload: {message.payload}")
|
|
159
|
+
|
|
160
|
+
async def main():
|
|
161
|
+
async with PHXChannelsClient("ws://localhost:4000/socket/websocket", "api-key") as client:
|
|
162
|
+
await client.subscribe_to_topic("chat:general", message_handler)
|
|
163
|
+
|
|
164
|
+
# Use built-in method to keep connection alive
|
|
165
|
+
await client.run_forever()
|
|
166
|
+
|
|
167
|
+
asyncio.run(main())
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Message Handlers vs Event-Specific Handlers
|
|
171
|
+
|
|
172
|
+
The library provides two complementary ways to handle incoming messages:
|
|
173
|
+
|
|
174
|
+
**Message Handler** - Receives ALL messages for a topic:
|
|
175
|
+
- Set when subscribing to a topic or separately with `set_message_handler()`
|
|
176
|
+
- Gets the complete message object with topic, event, payload, etc.
|
|
177
|
+
- Good for logging, debugging, or handling any message type
|
|
178
|
+
|
|
179
|
+
**Event-Specific Handlers** - Receive only messages with specific event names:
|
|
180
|
+
- Added with `add_event_handler()` for particular events like "user_join", "chat_message", etc.
|
|
181
|
+
- Only get the payload (data) part of the message
|
|
182
|
+
- Perfect for handling specific application events
|
|
183
|
+
|
|
184
|
+
**Execution order** when you have both types of handlers:
|
|
185
|
+
1. **Message handler runs first** (if set) - receives the full message
|
|
186
|
+
2. **Event-specific handler runs second** (if matching event) - receives just the payload
|
|
187
|
+
|
|
188
|
+
You can add, remove, or change either type of handler at any time during your connection.
|
|
189
|
+
|
|
190
|
+
### Event-Specific Handlers
|
|
191
|
+
|
|
192
|
+
You can register handlers for specific events on a topic:
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
import asyncio
|
|
196
|
+
from phoenix_channels_python_client import PHXChannelsClient
|
|
197
|
+
|
|
198
|
+
async def handle_user_join(payload):
|
|
199
|
+
print(f"User joined: {payload}")
|
|
200
|
+
|
|
201
|
+
async def handle_user_leave(payload):
|
|
202
|
+
print(f"User left: {payload}")
|
|
203
|
+
|
|
204
|
+
async def main():
|
|
205
|
+
async with PHXChannelsClient("ws://localhost:4000/socket/websocket", "api-key") as client:
|
|
206
|
+
# Subscribe to topic first
|
|
207
|
+
await client.subscribe_to_topic("room:lobby")
|
|
208
|
+
|
|
209
|
+
# Add event-specific handlers for custom events
|
|
210
|
+
client.add_event_handler("room:lobby", "user_join", handle_user_join)
|
|
211
|
+
client.add_event_handler("room:lobby", "user_leave", handle_user_leave)
|
|
212
|
+
|
|
213
|
+
# Use built-in method to keep connection alive
|
|
214
|
+
await client.run_forever()
|
|
215
|
+
|
|
216
|
+
asyncio.run(main())
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Unsubscribing from Topics
|
|
220
|
+
|
|
221
|
+
You can unsubscribe from topics to stop receiving messages and clean up resources:
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
import asyncio
|
|
225
|
+
from phoenix_channels_python_client import PHXChannelsClient
|
|
226
|
+
|
|
227
|
+
async def main():
|
|
228
|
+
async with PHXChannelsClient("ws://localhost:4000/socket/websocket", "api-key") as client:
|
|
229
|
+
# Subscribe to a topic
|
|
230
|
+
await client.subscribe_to_topic("room:lobby", lambda msg: print(f"Message: {msg.payload}"))
|
|
231
|
+
|
|
232
|
+
# Do some work...
|
|
233
|
+
await asyncio.sleep(5)
|
|
234
|
+
|
|
235
|
+
# Unsubscribe when no longer needed
|
|
236
|
+
await client.unsubscribe_from_topic("room:lobby")
|
|
237
|
+
print("Unsubscribed from room:lobby")
|
|
238
|
+
|
|
239
|
+
# Continue with other work or subscribe to different topics
|
|
240
|
+
await client.run_forever()
|
|
241
|
+
|
|
242
|
+
asyncio.run(main())
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Notes on unsubscription:**
|
|
246
|
+
- Removes all handlers (both message and event-specific) for that topic
|
|
247
|
+
- Sends a leave message to the Phoenix server
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Security Considerations
|
|
252
|
+
|
|
253
|
+
**API Key in URL**: This client passes the API key as a URL query parameter when connecting to the WebSocket (e.g., `wss://server.com/socket?api_key=xxx`). While the connection uses WSS (encrypted in transit), the API key may be logged by:
|
|
254
|
+
|
|
255
|
+
- Server access logs
|
|
256
|
+
- Reverse proxies and load balancers
|
|
257
|
+
- Network monitoring tools
|
|
258
|
+
|
|
259
|
+
**Recommendations:**
|
|
260
|
+
- Ensure your infrastructure does not log full URLs in production
|
|
261
|
+
- Use short-lived, rotatable tokens rather than long-lived API keys
|
|
262
|
+
- Consider this limitation when evaluating this client for sensitive environments
|
|
263
|
+
|
|
264
|
+
**Note:** The official Phoenix JS client (v1.8+) supports header-based authentication via the `authToken` option, which avoids URL logging. This Python client currently uses the older `params` style for broad compatibility.
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
**Need help?** Open an issue on GitHub or check the [Phoenix Channels documentation](https://hexdocs.pm/phoenix/channels.html) for more information about the Phoenix Channels protocol.
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Phoenix Channels Python Client
|
|
2
|
+
|
|
3
|
+
[](https://www.python.org/downloads/)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
A modern, async Python client library for connecting to [Phoenix Channels](https://hexdocs.pm/phoenix/channels.html) - the real-time WebSocket layer of the Phoenix Framework.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
### For Users
|
|
11
|
+
|
|
12
|
+
Install from source:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
git clone https://github.com/your-org/phoenix_channels_python_client.git
|
|
16
|
+
cd phoenix_channels_python_client
|
|
17
|
+
python3 -m venv venv
|
|
18
|
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
|
19
|
+
pip install .
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### For Development
|
|
23
|
+
|
|
24
|
+
Set up development environment:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
git clone https://github.com/your-org/phoenix_channels_python_client.git
|
|
28
|
+
cd phoenix_channels_python_client
|
|
29
|
+
python3 -m venv venv
|
|
30
|
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
|
31
|
+
python3 -m pip install -U pip
|
|
32
|
+
pip install -e ".[dev]"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Run tests:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install -e ".[test]"
|
|
39
|
+
pytest
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Dependencies
|
|
43
|
+
|
|
44
|
+
- Python 3.11+
|
|
45
|
+
- `websockets>=10.0`
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
Here's a minimal example to get you started:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
import asyncio
|
|
53
|
+
from phoenix_channels_python_client import PHXChannelsClient
|
|
54
|
+
|
|
55
|
+
async def main():
|
|
56
|
+
# Connect to Phoenix WebSocket server
|
|
57
|
+
async with PHXChannelsClient(
|
|
58
|
+
websocket_url="ws://localhost:4000/socket/websocket",
|
|
59
|
+
api_key="your-api-key"
|
|
60
|
+
) as client:
|
|
61
|
+
|
|
62
|
+
# Define message handler
|
|
63
|
+
async def handle_message(message):
|
|
64
|
+
print(f"Received: {message}")
|
|
65
|
+
|
|
66
|
+
# Subscribe to a topic
|
|
67
|
+
await client.subscribe_to_topic("room:lobby", handle_message)
|
|
68
|
+
|
|
69
|
+
# Use built-in convenience method to keep connection alive
|
|
70
|
+
await client.run_forever() # Handles Ctrl+C automatically
|
|
71
|
+
|
|
72
|
+
if __name__ == "__main__":
|
|
73
|
+
asyncio.run(main())
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Phoenix System Events
|
|
77
|
+
|
|
78
|
+
Phoenix Channels uses several reserved event names for internal protocol communication. The client library automatically handles these system events to manage connections, subscriptions, and message routing:
|
|
79
|
+
|
|
80
|
+
- `phx_join` - Channel join requests
|
|
81
|
+
- `phx_reply` - Server replies to client messages
|
|
82
|
+
- `phx_leave` - Channel leave notifications
|
|
83
|
+
- `phx_close` - Channel close events
|
|
84
|
+
- `phx_error` - Channel error events
|
|
85
|
+
|
|
86
|
+
**Important:** You should avoid using these event names when handling custom events in your application, as they are reserved for Phoenix's internal protocol. The library uses these events to determine message routing and connection state management.
|
|
87
|
+
|
|
88
|
+
If you have a specific use case that requires handling these system events directly, you can do so, but be aware that this may interfere with the library's automatic connection management.
|
|
89
|
+
|
|
90
|
+
## Protocol Versions
|
|
91
|
+
|
|
92
|
+
Phoenix Channels supports two protocol versions. Choose based on your Phoenix server version:
|
|
93
|
+
|
|
94
|
+
### Protocol v2.0 (Default)
|
|
95
|
+
```python
|
|
96
|
+
from phoenix_channels_python_client import PHXChannelsClient
|
|
97
|
+
|
|
98
|
+
async with PHXChannelsClient(
|
|
99
|
+
websocket_url="ws://localhost:4000/socket/websocket",
|
|
100
|
+
api_key="your-api-key"
|
|
101
|
+
# protocol_version defaults to v2.0
|
|
102
|
+
) as client:
|
|
103
|
+
# Your code here
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Protocol v1.0
|
|
107
|
+
```python
|
|
108
|
+
from phoenix_channels_python_client import PHXChannelsClient, PhoenixChannelsProtocolVersion
|
|
109
|
+
|
|
110
|
+
async with PHXChannelsClient(
|
|
111
|
+
websocket_url="ws://localhost:4000/socket/websocket",
|
|
112
|
+
api_key="your-api-key",
|
|
113
|
+
protocol_version=PhoenixChannelsProtocolVersion.V1
|
|
114
|
+
) as client:
|
|
115
|
+
# Your code here
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Usage Examples
|
|
119
|
+
|
|
120
|
+
### Basic Topic Subscription
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
import asyncio
|
|
124
|
+
from phoenix_channels_python_client import PHXChannelsClient
|
|
125
|
+
|
|
126
|
+
async def message_handler(message):
|
|
127
|
+
print(f"Topic: {message.topic}")
|
|
128
|
+
print(f"Event: {message.event}")
|
|
129
|
+
print(f"Payload: {message.payload}")
|
|
130
|
+
|
|
131
|
+
async def main():
|
|
132
|
+
async with PHXChannelsClient("ws://localhost:4000/socket/websocket", "api-key") as client:
|
|
133
|
+
await client.subscribe_to_topic("chat:general", message_handler)
|
|
134
|
+
|
|
135
|
+
# Use built-in method to keep connection alive
|
|
136
|
+
await client.run_forever()
|
|
137
|
+
|
|
138
|
+
asyncio.run(main())
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Message Handlers vs Event-Specific Handlers
|
|
142
|
+
|
|
143
|
+
The library provides two complementary ways to handle incoming messages:
|
|
144
|
+
|
|
145
|
+
**Message Handler** - Receives ALL messages for a topic:
|
|
146
|
+
- Set when subscribing to a topic or separately with `set_message_handler()`
|
|
147
|
+
- Gets the complete message object with topic, event, payload, etc.
|
|
148
|
+
- Good for logging, debugging, or handling any message type
|
|
149
|
+
|
|
150
|
+
**Event-Specific Handlers** - Receive only messages with specific event names:
|
|
151
|
+
- Added with `add_event_handler()` for particular events like "user_join", "chat_message", etc.
|
|
152
|
+
- Only get the payload (data) part of the message
|
|
153
|
+
- Perfect for handling specific application events
|
|
154
|
+
|
|
155
|
+
**Execution order** when you have both types of handlers:
|
|
156
|
+
1. **Message handler runs first** (if set) - receives the full message
|
|
157
|
+
2. **Event-specific handler runs second** (if matching event) - receives just the payload
|
|
158
|
+
|
|
159
|
+
You can add, remove, or change either type of handler at any time during your connection.
|
|
160
|
+
|
|
161
|
+
### Event-Specific Handlers
|
|
162
|
+
|
|
163
|
+
You can register handlers for specific events on a topic:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
import asyncio
|
|
167
|
+
from phoenix_channels_python_client import PHXChannelsClient
|
|
168
|
+
|
|
169
|
+
async def handle_user_join(payload):
|
|
170
|
+
print(f"User joined: {payload}")
|
|
171
|
+
|
|
172
|
+
async def handle_user_leave(payload):
|
|
173
|
+
print(f"User left: {payload}")
|
|
174
|
+
|
|
175
|
+
async def main():
|
|
176
|
+
async with PHXChannelsClient("ws://localhost:4000/socket/websocket", "api-key") as client:
|
|
177
|
+
# Subscribe to topic first
|
|
178
|
+
await client.subscribe_to_topic("room:lobby")
|
|
179
|
+
|
|
180
|
+
# Add event-specific handlers for custom events
|
|
181
|
+
client.add_event_handler("room:lobby", "user_join", handle_user_join)
|
|
182
|
+
client.add_event_handler("room:lobby", "user_leave", handle_user_leave)
|
|
183
|
+
|
|
184
|
+
# Use built-in method to keep connection alive
|
|
185
|
+
await client.run_forever()
|
|
186
|
+
|
|
187
|
+
asyncio.run(main())
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Unsubscribing from Topics
|
|
191
|
+
|
|
192
|
+
You can unsubscribe from topics to stop receiving messages and clean up resources:
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
import asyncio
|
|
196
|
+
from phoenix_channels_python_client import PHXChannelsClient
|
|
197
|
+
|
|
198
|
+
async def main():
|
|
199
|
+
async with PHXChannelsClient("ws://localhost:4000/socket/websocket", "api-key") as client:
|
|
200
|
+
# Subscribe to a topic
|
|
201
|
+
await client.subscribe_to_topic("room:lobby", lambda msg: print(f"Message: {msg.payload}"))
|
|
202
|
+
|
|
203
|
+
# Do some work...
|
|
204
|
+
await asyncio.sleep(5)
|
|
205
|
+
|
|
206
|
+
# Unsubscribe when no longer needed
|
|
207
|
+
await client.unsubscribe_from_topic("room:lobby")
|
|
208
|
+
print("Unsubscribed from room:lobby")
|
|
209
|
+
|
|
210
|
+
# Continue with other work or subscribe to different topics
|
|
211
|
+
await client.run_forever()
|
|
212
|
+
|
|
213
|
+
asyncio.run(main())
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Notes on unsubscription:**
|
|
217
|
+
- Removes all handlers (both message and event-specific) for that topic
|
|
218
|
+
- Sends a leave message to the Phoenix server
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Security Considerations
|
|
223
|
+
|
|
224
|
+
**API Key in URL**: This client passes the API key as a URL query parameter when connecting to the WebSocket (e.g., `wss://server.com/socket?api_key=xxx`). While the connection uses WSS (encrypted in transit), the API key may be logged by:
|
|
225
|
+
|
|
226
|
+
- Server access logs
|
|
227
|
+
- Reverse proxies and load balancers
|
|
228
|
+
- Network monitoring tools
|
|
229
|
+
|
|
230
|
+
**Recommendations:**
|
|
231
|
+
- Ensure your infrastructure does not log full URLs in production
|
|
232
|
+
- Use short-lived, rotatable tokens rather than long-lived API keys
|
|
233
|
+
- Consider this limitation when evaluating this client for sensitive environments
|
|
234
|
+
|
|
235
|
+
**Note:** The official Phoenix JS client (v1.8+) supports header-based authentication via the `authToken` option, which avoids URL logging. This Python client currently uses the older `params` style for broad compatibility.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
**Need help?** Open an issue on GitHub or check the [Phoenix Channels documentation](https://hexdocs.pm/phoenix/channels.html) for more information about the Phoenix Channels protocol.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Phoenix Channels Python Client
|
|
3
|
+
|
|
4
|
+
A Python client library for connecting to Phoenix Channels.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from phoenix_channels_python_client.client import PHXChannelsClient
|
|
8
|
+
from phoenix_channels_python_client.protocol_handler import (
|
|
9
|
+
PHXProtocolHandler,
|
|
10
|
+
PhoenixChannelsProtocolVersion,
|
|
11
|
+
)
|
|
12
|
+
from phoenix_channels_python_client.utils import setup_logging
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.2"
|
|
15
|
+
__author__ = "Phoenix Channels Python Client"
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"PHXChannelsClient",
|
|
19
|
+
"PHXProtocolHandler",
|
|
20
|
+
"PhoenixChannelsProtocolVersion",
|
|
21
|
+
"setup_logging",
|
|
22
|
+
]
|