django-cfg 1.5.20__py3-none-any.whl → 1.5.29__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.

Potentially problematic release.


This version of django-cfg might be problematic. Click here for more details.

Files changed (88) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/integrations/centrifugo/__init__.py +2 -0
  3. django_cfg/apps/integrations/centrifugo/services/client/client.py +1 -1
  4. django_cfg/apps/integrations/centrifugo/services/logging.py +47 -0
  5. django_cfg/apps/integrations/centrifugo/views/admin_api.py +29 -32
  6. django_cfg/apps/integrations/centrifugo/views/testing_api.py +31 -37
  7. django_cfg/apps/integrations/centrifugo/views/wrapper.py +25 -23
  8. django_cfg/apps/integrations/grpc/auth/api_key_auth.py +11 -10
  9. django_cfg/apps/integrations/grpc/management/commands/generate_protos.py +1 -1
  10. django_cfg/apps/integrations/grpc/management/commands/rungrpc.py +21 -36
  11. django_cfg/apps/integrations/grpc/managers/grpc_request_log.py +84 -0
  12. django_cfg/apps/integrations/grpc/managers/grpc_server_status.py +126 -3
  13. django_cfg/apps/integrations/grpc/models/grpc_api_key.py +7 -1
  14. django_cfg/apps/integrations/grpc/models/grpc_server_status.py +22 -3
  15. django_cfg/apps/integrations/grpc/services/__init__.py +102 -17
  16. django_cfg/apps/integrations/grpc/services/centrifugo/bridge.py +469 -0
  17. django_cfg/apps/integrations/grpc/{centrifugo → services/centrifugo}/demo.py +1 -1
  18. django_cfg/apps/integrations/grpc/{centrifugo → services/centrifugo}/test_publish.py +4 -4
  19. django_cfg/apps/integrations/grpc/services/client/__init__.py +26 -0
  20. django_cfg/apps/integrations/grpc/services/commands/IMPLEMENTATION.md +456 -0
  21. django_cfg/apps/integrations/grpc/services/commands/README.md +252 -0
  22. django_cfg/apps/integrations/grpc/services/commands/__init__.py +93 -0
  23. django_cfg/apps/integrations/grpc/services/commands/base.py +243 -0
  24. django_cfg/apps/integrations/grpc/services/commands/examples/__init__.py +22 -0
  25. django_cfg/apps/integrations/grpc/services/commands/examples/base_client.py +228 -0
  26. django_cfg/apps/integrations/grpc/services/commands/examples/client.py +272 -0
  27. django_cfg/apps/integrations/grpc/services/commands/examples/config.py +177 -0
  28. django_cfg/apps/integrations/grpc/services/commands/examples/start.py +125 -0
  29. django_cfg/apps/integrations/grpc/services/commands/examples/stop.py +101 -0
  30. django_cfg/apps/integrations/grpc/services/commands/registry.py +170 -0
  31. django_cfg/apps/integrations/grpc/services/discovery/__init__.py +39 -0
  32. django_cfg/apps/integrations/grpc/services/{discovery.py → discovery/discovery.py} +62 -55
  33. django_cfg/apps/integrations/grpc/services/{service_registry.py → discovery/registry.py} +215 -5
  34. django_cfg/apps/integrations/grpc/{interceptors → services/interceptors}/metrics.py +3 -3
  35. django_cfg/apps/integrations/grpc/{interceptors → services/interceptors}/request_logger.py +10 -13
  36. django_cfg/apps/integrations/grpc/services/management/__init__.py +37 -0
  37. django_cfg/apps/integrations/grpc/services/monitoring/__init__.py +38 -0
  38. django_cfg/apps/integrations/grpc/services/{monitoring_service.py → monitoring/monitoring.py} +2 -2
  39. django_cfg/apps/integrations/grpc/services/{testing_service.py → monitoring/testing.py} +5 -5
  40. django_cfg/apps/integrations/grpc/services/rendering/__init__.py +27 -0
  41. django_cfg/apps/integrations/grpc/services/{chart_generator.py → rendering/charts.py} +1 -1
  42. django_cfg/apps/integrations/grpc/services/routing/__init__.py +59 -0
  43. django_cfg/apps/integrations/grpc/services/routing/config.py +76 -0
  44. django_cfg/apps/integrations/grpc/services/routing/router.py +430 -0
  45. django_cfg/apps/integrations/grpc/services/streaming/__init__.py +117 -0
  46. django_cfg/apps/integrations/grpc/services/streaming/config.py +451 -0
  47. django_cfg/apps/integrations/grpc/services/streaming/service.py +651 -0
  48. django_cfg/apps/integrations/grpc/services/streaming/types.py +367 -0
  49. django_cfg/apps/integrations/grpc/utils/__init__.py +58 -1
  50. django_cfg/apps/integrations/grpc/utils/converters.py +565 -0
  51. django_cfg/apps/integrations/grpc/utils/handlers.py +242 -0
  52. django_cfg/apps/integrations/grpc/utils/proto_gen.py +1 -1
  53. django_cfg/apps/integrations/grpc/utils/streaming_logger.py +55 -8
  54. django_cfg/apps/integrations/grpc/views/charts.py +1 -1
  55. django_cfg/apps/integrations/grpc/views/config.py +1 -1
  56. django_cfg/core/base/config_model.py +11 -0
  57. django_cfg/core/builders/middleware_builder.py +5 -0
  58. django_cfg/management/commands/pool_status.py +153 -0
  59. django_cfg/middleware/pool_cleanup.py +261 -0
  60. django_cfg/models/api/grpc/config.py +2 -2
  61. django_cfg/models/infrastructure/database/config.py +16 -0
  62. django_cfg/models/infrastructure/database/converters.py +2 -0
  63. django_cfg/modules/django_admin/utils/html/composition.py +57 -13
  64. django_cfg/modules/django_admin/utils/html_builder.py +1 -0
  65. django_cfg/modules/django_client/core/groups/manager.py +25 -18
  66. django_cfg/modules/django_client/management/commands/generate_client.py +9 -5
  67. django_cfg/modules/django_logging/django_logger.py +58 -19
  68. django_cfg/pyproject.toml +3 -3
  69. django_cfg/static/frontend/admin.zip +0 -0
  70. django_cfg/templates/admin/index.html +0 -39
  71. django_cfg/utils/pool_monitor.py +320 -0
  72. django_cfg/utils/smart_defaults.py +233 -7
  73. {django_cfg-1.5.20.dist-info → django_cfg-1.5.29.dist-info}/METADATA +75 -5
  74. {django_cfg-1.5.20.dist-info → django_cfg-1.5.29.dist-info}/RECORD +87 -59
  75. django_cfg/apps/integrations/grpc/centrifugo/bridge.py +0 -277
  76. /django_cfg/apps/integrations/grpc/{centrifugo → services/centrifugo}/__init__.py +0 -0
  77. /django_cfg/apps/integrations/grpc/{centrifugo → services/centrifugo}/config.py +0 -0
  78. /django_cfg/apps/integrations/grpc/{centrifugo → services/centrifugo}/transformers.py +0 -0
  79. /django_cfg/apps/integrations/grpc/services/{grpc_client.py → client/client.py} +0 -0
  80. /django_cfg/apps/integrations/grpc/{interceptors → services/interceptors}/__init__.py +0 -0
  81. /django_cfg/apps/integrations/grpc/{interceptors → services/interceptors}/centrifugo.py +0 -0
  82. /django_cfg/apps/integrations/grpc/{interceptors → services/interceptors}/errors.py +0 -0
  83. /django_cfg/apps/integrations/grpc/{interceptors → services/interceptors}/logging.py +0 -0
  84. /django_cfg/apps/integrations/grpc/services/{config_helper.py → management/config_helper.py} +0 -0
  85. /django_cfg/apps/integrations/grpc/services/{proto_files_manager.py → management/proto_manager.py} +0 -0
  86. {django_cfg-1.5.20.dist-info → django_cfg-1.5.29.dist-info}/WHEEL +0 -0
  87. {django_cfg-1.5.20.dist-info → django_cfg-1.5.29.dist-info}/entry_points.txt +0 -0
  88. {django_cfg-1.5.20.dist-info → django_cfg-1.5.29.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,367 @@
1
+ """
2
+ Protocol type definitions for type-safe gRPC bidirectional streaming.
3
+
4
+ This module defines Protocol types used for generic, reusable gRPC streaming components.
5
+ All protocols are fully typed and support generic message/command types.
6
+
7
+ **Design Goals**:
8
+ - 100% type-safe callbacks
9
+ - Zero runtime overhead (protocols are compile-time only)
10
+ - Compatible with any protobuf message types
11
+ - Enables IDE autocomplete and mypy validation
12
+
13
+ **Usage Example**:
14
+ ```python
15
+ # Define your processor
16
+ async def process_signal_message(
17
+ client_id: str,
18
+ message: SignalCommand,
19
+ output_queue: asyncio.Queue[SignalMessage]
20
+ ) -> None:
21
+ # Your logic here
22
+ await output_queue.put(response)
23
+
24
+ # Type checker validates signature matches MessageProcessor protocol
25
+ service = BidirectionalStreamingService(
26
+ message_processor=process_signal_message, # ✅ Type-safe
27
+ ...
28
+ )
29
+ ```
30
+
31
+ Created: 2025-11-07
32
+ Status: %%PRODUCTION%%
33
+ Phase: Phase 1 - Universal Components
34
+ """
35
+
36
+ from typing import Protocol, TypeVar, Any
37
+ import asyncio
38
+
39
+
40
+ # ============================================================================
41
+ # Generic Type Variables
42
+ # ============================================================================
43
+
44
+ TMessage = TypeVar('TMessage', contravariant=True)
45
+ """
46
+ Generic type for incoming gRPC messages (commands from client).
47
+
48
+ **Contravariant**: Allows passing more specific message types where generic is expected.
49
+
50
+ Example:
51
+ - Protocol expects: Message (base)
52
+ - Can pass: SignalCommand (subclass)
53
+ """
54
+
55
+ TCommand = TypeVar('TCommand', covariant=True)
56
+ """
57
+ Generic type for outgoing gRPC commands (responses to client).
58
+
59
+ **Covariant**: Allows returning more general command types where specific is expected.
60
+
61
+ Example:
62
+ - Protocol returns: BotResponse (specific)
63
+ - Can be treated as: Message (base)
64
+ """
65
+
66
+
67
+ # ============================================================================
68
+ # Core Processing Protocols
69
+ # ============================================================================
70
+
71
+ class MessageProcessor(Protocol[TMessage, TCommand]):
72
+ """
73
+ Protocol for processing incoming gRPC messages and generating responses.
74
+
75
+ This is the core business logic handler that processes each message from the client
76
+ and optionally enqueues response commands.
77
+
78
+ **Type Parameters**:
79
+ TMessage: Type of incoming messages (e.g., SignalCommand, BotCommand)
80
+ TCommand: Type of outgoing commands (e.g., SignalMessage, BotResponse)
81
+
82
+ **Signature**:
83
+ async def(client_id: str, message: TMessage, output_queue: Queue[TCommand]) -> None
84
+
85
+ **Parameters**:
86
+ client_id: Unique identifier for the connected client
87
+ message: Incoming message from client (generic type TMessage)
88
+ output_queue: Queue to enqueue response commands (generic type TCommand)
89
+
90
+ **Example Implementation**:
91
+ ```python
92
+ async def process_bot_command(
93
+ client_id: str,
94
+ message: BotCommand,
95
+ output_queue: asyncio.Queue[BotResponse]
96
+ ) -> None:
97
+ logger.info(f"Processing command for {client_id}")
98
+
99
+ if message.command_type == CommandType.START:
100
+ response = BotResponse(status="started")
101
+ await output_queue.put(response)
102
+ ```
103
+ """
104
+ async def __call__(
105
+ self,
106
+ client_id: str,
107
+ message: TMessage,
108
+ output_queue: asyncio.Queue[TCommand]
109
+ ) -> None:
110
+ """Process incoming message and optionally enqueue responses."""
111
+ ...
112
+
113
+
114
+ class ClientIdExtractor(Protocol[TMessage]):
115
+ """
116
+ Protocol for extracting client ID from incoming gRPC messages.
117
+
118
+ Different services may store client IDs in different message fields.
119
+ This protocol allows type-safe client ID extraction.
120
+
121
+ **Type Parameters**:
122
+ TMessage: Type of incoming messages
123
+
124
+ **Signature**:
125
+ def(message: TMessage) -> str
126
+
127
+ **Example Implementation**:
128
+ ```python
129
+ def extract_bot_client_id(message: BotCommand) -> str:
130
+ return str(message.bot_id)
131
+
132
+ def extract_signal_client_id(message: SignalCommand) -> str:
133
+ return message.client_id
134
+ ```
135
+ """
136
+ def __call__(self, message: TMessage) -> str:
137
+ """Extract client ID from message."""
138
+ ...
139
+
140
+
141
+ class PingMessageCreator(Protocol[TCommand]):
142
+ """
143
+ Protocol for creating ping/keepalive messages.
144
+
145
+ Bidirectional streams need periodic ping messages to keep connections alive.
146
+ This protocol allows type-safe ping message creation.
147
+
148
+ **Type Parameters**:
149
+ TCommand: Type of outgoing commands
150
+
151
+ **Signature**:
152
+ def() -> TCommand
153
+
154
+ **Example Implementation**:
155
+ ```python
156
+ def create_bot_ping() -> BotResponse:
157
+ return BotResponse(
158
+ message_type=MessageType.PING,
159
+ timestamp=int(time.time())
160
+ )
161
+
162
+ def create_signal_ping() -> SignalMessage:
163
+ return SignalMessage(is_ping=True)
164
+ ```
165
+ """
166
+ def __call__(self) -> TCommand:
167
+ """Create a ping message."""
168
+ ...
169
+
170
+
171
+ # ============================================================================
172
+ # Connection Management Protocols
173
+ # ============================================================================
174
+
175
+ class ConnectionCallback(Protocol):
176
+ """
177
+ Protocol for connection lifecycle callbacks.
178
+
179
+ **Signature**:
180
+ async def(client_id: str) -> None
181
+
182
+ **Use Cases**:
183
+ - on_connect: Initialize resources, log connection
184
+ - on_disconnect: Cleanup resources, update database
185
+ - on_error: Handle connection errors
186
+
187
+ **Example Implementation**:
188
+ ```python
189
+ async def on_client_connected(client_id: str) -> None:
190
+ logger.info(f"Client {client_id} connected")
191
+ await db.mark_client_active(client_id)
192
+
193
+ async def on_client_disconnected(client_id: str) -> None:
194
+ logger.info(f"Client {client_id} disconnected")
195
+ await db.mark_client_inactive(client_id)
196
+ ```
197
+ """
198
+ async def __call__(self, client_id: str) -> None:
199
+ """Handle connection lifecycle event."""
200
+ ...
201
+
202
+
203
+ class ErrorHandler(Protocol):
204
+ """
205
+ Protocol for handling errors during streaming.
206
+
207
+ **Signature**:
208
+ async def(client_id: str, error: Exception) -> None
209
+
210
+ **Example Implementation**:
211
+ ```python
212
+ async def handle_stream_error(client_id: str, error: Exception) -> None:
213
+ logger.error(f"Error for {client_id}: {error}")
214
+
215
+ if isinstance(error, asyncio.CancelledError):
216
+ # Normal cancellation, just log
217
+ pass
218
+ elif isinstance(error, grpc.RpcError):
219
+ # gRPC-specific error handling
220
+ await notify_admin(client_id, error)
221
+ else:
222
+ # Unexpected error, escalate
223
+ raise
224
+ ```
225
+ """
226
+ async def __call__(self, client_id: str, error: Exception) -> None:
227
+ """Handle streaming error."""
228
+ ...
229
+
230
+
231
+ # ============================================================================
232
+ # Type Aliases for Common Patterns
233
+ # ============================================================================
234
+
235
+ MessageProcessorType = MessageProcessor[Any, Any]
236
+ """Type alias for MessageProcessor without generic constraints."""
237
+
238
+ ClientIdExtractorType = ClientIdExtractor[Any]
239
+ """Type alias for ClientIdExtractor without generic constraints."""
240
+
241
+ PingMessageCreatorType = PingMessageCreator[Any]
242
+ """Type alias for PingMessageCreator without generic constraints."""
243
+
244
+
245
+ # ============================================================================
246
+ # Type Guards and Validation
247
+ # ============================================================================
248
+
249
+ def is_valid_message_processor(func: Any) -> bool:
250
+ """
251
+ Runtime check if function matches MessageProcessor protocol.
252
+
253
+ **Parameters**:
254
+ func: Function to validate
255
+
256
+ **Returns**:
257
+ True if function signature matches protocol
258
+
259
+ **Example**:
260
+ ```python
261
+ async def my_processor(client_id: str, msg: Command, queue: Queue) -> None:
262
+ pass
263
+
264
+ assert is_valid_message_processor(my_processor) # ✅
265
+ ```
266
+ """
267
+ if not callable(func):
268
+ return False
269
+
270
+ import inspect
271
+ sig = inspect.signature(func)
272
+ params = list(sig.parameters.values())
273
+
274
+ return (
275
+ len(params) == 3 and
276
+ params[0].annotation in (str, inspect.Parameter.empty) and
277
+ inspect.iscoroutinefunction(func)
278
+ )
279
+
280
+
281
+ def is_valid_client_id_extractor(func: Any) -> bool:
282
+ """
283
+ Runtime check if function matches ClientIdExtractor protocol.
284
+
285
+ **Parameters**:
286
+ func: Function to validate
287
+
288
+ **Returns**:
289
+ True if function signature matches protocol
290
+
291
+ **Example**:
292
+ ```python
293
+ def extract_id(msg: Command) -> str:
294
+ return msg.client_id
295
+
296
+ assert is_valid_client_id_extractor(extract_id) # ✅
297
+ ```
298
+ """
299
+ if not callable(func):
300
+ return False
301
+
302
+ import inspect
303
+ sig = inspect.signature(func)
304
+ params = list(sig.parameters.values())
305
+
306
+ return (
307
+ len(params) == 1 and
308
+ sig.return_annotation in (str, inspect.Parameter.empty)
309
+ )
310
+
311
+
312
+ def is_valid_ping_creator(func: Any) -> bool:
313
+ """
314
+ Runtime check if function matches PingMessageCreator protocol.
315
+
316
+ **Parameters**:
317
+ func: Function to validate
318
+
319
+ **Returns**:
320
+ True if function signature matches protocol
321
+
322
+ **Example**:
323
+ ```python
324
+ def create_ping() -> Message:
325
+ return Message(is_ping=True)
326
+
327
+ assert is_valid_ping_creator(create_ping) # ✅
328
+ ```
329
+ """
330
+ if not callable(func):
331
+ return False
332
+
333
+ import inspect
334
+ sig = inspect.signature(func)
335
+ params = list(sig.parameters.values())
336
+
337
+ return len(params) == 0
338
+
339
+
340
+ # ============================================================================
341
+ # Exports
342
+ # ============================================================================
343
+
344
+ __all__ = [
345
+ # Type variables
346
+ 'TMessage',
347
+ 'TCommand',
348
+
349
+ # Core protocols
350
+ 'MessageProcessor',
351
+ 'ClientIdExtractor',
352
+ 'PingMessageCreator',
353
+
354
+ # Connection protocols
355
+ 'ConnectionCallback',
356
+ 'ErrorHandler',
357
+
358
+ # Type aliases
359
+ 'MessageProcessorType',
360
+ 'ClientIdExtractorType',
361
+ 'PingMessageCreatorType',
362
+
363
+ # Validation
364
+ 'is_valid_message_processor',
365
+ 'is_valid_client_id_extractor',
366
+ 'is_valid_ping_creator',
367
+ ]
@@ -2,8 +2,65 @@
2
2
  Utilities for gRPC Integration.
3
3
 
4
4
  Reusable utilities for gRPC services in django-cfg.
5
+
6
+ **Available Modules**:
7
+ - streaming_logger: Rich logging for gRPC streams
8
+ - converters: Protobuf ↔ Python conversions (Pydantic configured)
9
+ - handlers: gRPC handler factory utilities
10
+
11
+ **Quick Imports**:
12
+ ```python
13
+ from django_cfg.apps.integrations.grpc.utils import (
14
+ # Logging
15
+ setup_streaming_logger,
16
+ get_streaming_logger,
17
+
18
+ # Converters
19
+ ProtobufConverterMixin,
20
+ ConverterConfig,
21
+
22
+ # Handlers
23
+ create_grpc_handler,
24
+ )
25
+ ```
5
26
  """
6
27
 
7
28
  from .streaming_logger import setup_streaming_logger, get_streaming_logger
29
+ from .converters import (
30
+ ConverterConfig,
31
+ ProtobufConverterMixin,
32
+ datetime_to_timestamp,
33
+ timestamp_to_datetime,
34
+ dict_to_struct,
35
+ struct_to_dict,
36
+ )
37
+ from .handlers import (
38
+ create_grpc_handler,
39
+ create_multiple_grpc_handlers,
40
+ validate_grpc_handler,
41
+ validate_grpc_handlers,
42
+ )
43
+
44
+ __all__ = [
45
+ # Logging
46
+ "setup_streaming_logger",
47
+ "get_streaming_logger",
48
+
49
+ # Converters - Config
50
+ "ConverterConfig",
51
+
52
+ # Converters - Mixin
53
+ "ProtobufConverterMixin",
54
+
55
+ # Converters - Standalone functions
56
+ "datetime_to_timestamp",
57
+ "timestamp_to_datetime",
58
+ "dict_to_struct",
59
+ "struct_to_dict",
8
60
 
9
- __all__ = ["setup_streaming_logger", "get_streaming_logger"]
61
+ # Handlers
62
+ "create_grpc_handler",
63
+ "create_multiple_grpc_handlers",
64
+ "validate_grpc_handler",
65
+ "validate_grpc_handlers",
66
+ ]