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.
@@ -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,3 @@
1
+ include README.md
2
+ include LICENSE
3
+ recursive-include phoenix_channels_python_client *.py
@@ -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
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
33
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
+ ]