django-cfg 1.5.14__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 (118) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/business/accounts/serializers/profile.py +42 -0
  3. django_cfg/apps/business/support/serializers.py +3 -2
  4. django_cfg/apps/integrations/centrifugo/__init__.py +2 -0
  5. django_cfg/apps/integrations/centrifugo/apps.py +2 -1
  6. django_cfg/apps/integrations/centrifugo/codegen/generators/typescript_thin/templates/rpc-client.ts.j2 +151 -12
  7. django_cfg/apps/integrations/centrifugo/management/commands/generate_centrifugo_clients.py +2 -2
  8. django_cfg/apps/integrations/centrifugo/services/__init__.py +6 -0
  9. django_cfg/apps/integrations/centrifugo/services/client/__init__.py +6 -1
  10. django_cfg/apps/integrations/centrifugo/services/client/client.py +1 -1
  11. django_cfg/apps/integrations/centrifugo/services/client/direct_client.py +282 -0
  12. django_cfg/apps/integrations/centrifugo/services/logging.py +47 -0
  13. django_cfg/apps/integrations/centrifugo/services/publisher.py +371 -0
  14. django_cfg/apps/integrations/centrifugo/services/token_generator.py +122 -0
  15. django_cfg/apps/integrations/centrifugo/urls.py +8 -0
  16. django_cfg/apps/integrations/centrifugo/views/__init__.py +2 -0
  17. django_cfg/apps/integrations/centrifugo/views/admin_api.py +29 -32
  18. django_cfg/apps/integrations/centrifugo/views/testing_api.py +31 -116
  19. django_cfg/apps/integrations/centrifugo/views/token_api.py +101 -0
  20. django_cfg/apps/integrations/centrifugo/views/wrapper.py +259 -0
  21. django_cfg/apps/integrations/grpc/auth/api_key_auth.py +11 -10
  22. django_cfg/apps/integrations/grpc/management/commands/compile_proto.py +105 -0
  23. django_cfg/apps/integrations/grpc/management/commands/generate_protos.py +56 -1
  24. django_cfg/apps/integrations/grpc/management/commands/rungrpc.py +315 -26
  25. django_cfg/apps/integrations/grpc/management/proto/__init__.py +3 -0
  26. django_cfg/apps/integrations/grpc/management/proto/compiler.py +194 -0
  27. django_cfg/apps/integrations/grpc/managers/grpc_request_log.py +84 -0
  28. django_cfg/apps/integrations/grpc/managers/grpc_server_status.py +126 -3
  29. django_cfg/apps/integrations/grpc/models/grpc_api_key.py +7 -1
  30. django_cfg/apps/integrations/grpc/models/grpc_server_status.py +22 -3
  31. django_cfg/apps/integrations/grpc/services/__init__.py +102 -17
  32. django_cfg/apps/integrations/grpc/services/centrifugo/__init__.py +29 -0
  33. django_cfg/apps/integrations/grpc/services/centrifugo/bridge.py +469 -0
  34. django_cfg/apps/integrations/grpc/services/centrifugo/config.py +167 -0
  35. django_cfg/apps/integrations/grpc/services/centrifugo/demo.py +626 -0
  36. django_cfg/apps/integrations/grpc/services/centrifugo/test_publish.py +229 -0
  37. django_cfg/apps/integrations/grpc/services/centrifugo/transformers.py +89 -0
  38. django_cfg/apps/integrations/grpc/services/client/__init__.py +26 -0
  39. django_cfg/apps/integrations/grpc/services/commands/IMPLEMENTATION.md +456 -0
  40. django_cfg/apps/integrations/grpc/services/commands/README.md +252 -0
  41. django_cfg/apps/integrations/grpc/services/commands/__init__.py +93 -0
  42. django_cfg/apps/integrations/grpc/services/commands/base.py +243 -0
  43. django_cfg/apps/integrations/grpc/services/commands/examples/__init__.py +22 -0
  44. django_cfg/apps/integrations/grpc/services/commands/examples/base_client.py +228 -0
  45. django_cfg/apps/integrations/grpc/services/commands/examples/client.py +272 -0
  46. django_cfg/apps/integrations/grpc/services/commands/examples/config.py +177 -0
  47. django_cfg/apps/integrations/grpc/services/commands/examples/start.py +125 -0
  48. django_cfg/apps/integrations/grpc/services/commands/examples/stop.py +101 -0
  49. django_cfg/apps/integrations/grpc/services/commands/registry.py +170 -0
  50. django_cfg/apps/integrations/grpc/services/discovery/__init__.py +39 -0
  51. django_cfg/apps/integrations/grpc/services/{discovery.py → discovery/discovery.py} +67 -54
  52. django_cfg/apps/integrations/grpc/services/{service_registry.py → discovery/registry.py} +215 -5
  53. django_cfg/apps/integrations/grpc/{interceptors → services/interceptors}/__init__.py +3 -1
  54. django_cfg/apps/integrations/grpc/services/interceptors/centrifugo.py +541 -0
  55. django_cfg/apps/integrations/grpc/{interceptors → services/interceptors}/metrics.py +3 -3
  56. django_cfg/apps/integrations/grpc/{interceptors → services/interceptors}/request_logger.py +10 -13
  57. django_cfg/apps/integrations/grpc/services/management/__init__.py +37 -0
  58. django_cfg/apps/integrations/grpc/services/monitoring/__init__.py +38 -0
  59. django_cfg/apps/integrations/grpc/services/{monitoring_service.py → monitoring/monitoring.py} +2 -2
  60. django_cfg/apps/integrations/grpc/services/{testing_service.py → monitoring/testing.py} +5 -5
  61. django_cfg/apps/integrations/grpc/services/rendering/__init__.py +27 -0
  62. django_cfg/apps/integrations/grpc/services/{chart_generator.py → rendering/charts.py} +1 -1
  63. django_cfg/apps/integrations/grpc/services/routing/__init__.py +59 -0
  64. django_cfg/apps/integrations/grpc/services/routing/config.py +76 -0
  65. django_cfg/apps/integrations/grpc/services/routing/router.py +430 -0
  66. django_cfg/apps/integrations/grpc/services/streaming/__init__.py +117 -0
  67. django_cfg/apps/integrations/grpc/services/streaming/config.py +451 -0
  68. django_cfg/apps/integrations/grpc/services/streaming/service.py +651 -0
  69. django_cfg/apps/integrations/grpc/services/streaming/types.py +367 -0
  70. django_cfg/apps/integrations/grpc/utils/SERVER_LOGGING.md +164 -0
  71. django_cfg/apps/integrations/grpc/utils/__init__.py +58 -1
  72. django_cfg/apps/integrations/grpc/utils/converters.py +565 -0
  73. django_cfg/apps/integrations/grpc/utils/handlers.py +242 -0
  74. django_cfg/apps/integrations/grpc/utils/proto_gen.py +1 -1
  75. django_cfg/apps/integrations/grpc/utils/streaming_logger.py +261 -13
  76. django_cfg/apps/integrations/grpc/views/charts.py +1 -1
  77. django_cfg/apps/integrations/grpc/views/config.py +1 -1
  78. django_cfg/apps/system/dashboard/serializers/config.py +95 -9
  79. django_cfg/apps/system/dashboard/serializers/statistics.py +9 -4
  80. django_cfg/apps/system/frontend/views.py +87 -6
  81. django_cfg/core/base/config_model.py +11 -0
  82. django_cfg/core/builders/middleware_builder.py +5 -0
  83. django_cfg/core/builders/security_builder.py +1 -0
  84. django_cfg/core/generation/integration_generators/api.py +2 -0
  85. django_cfg/management/commands/pool_status.py +153 -0
  86. django_cfg/middleware/pool_cleanup.py +261 -0
  87. django_cfg/models/api/grpc/config.py +2 -2
  88. django_cfg/models/infrastructure/database/config.py +16 -0
  89. django_cfg/models/infrastructure/database/converters.py +2 -0
  90. django_cfg/modules/django_admin/utils/html/composition.py +57 -13
  91. django_cfg/modules/django_admin/utils/html_builder.py +1 -0
  92. django_cfg/modules/django_client/core/generator/typescript/generator.py +26 -0
  93. django_cfg/modules/django_client/core/generator/typescript/hooks_generator.py +7 -1
  94. django_cfg/modules/django_client/core/generator/typescript/models_generator.py +5 -0
  95. django_cfg/modules/django_client/core/generator/typescript/schemas_generator.py +11 -0
  96. django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/fetchers.ts.jinja +1 -0
  97. django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/function.ts.jinja +29 -1
  98. django_cfg/modules/django_client/core/generator/typescript/templates/hooks/hooks.ts.jinja +4 -0
  99. django_cfg/modules/django_client/core/groups/manager.py +25 -18
  100. django_cfg/modules/django_client/core/ir/schema.py +15 -1
  101. django_cfg/modules/django_client/core/parser/base.py +12 -0
  102. django_cfg/modules/django_client/management/commands/generate_client.py +9 -5
  103. django_cfg/modules/django_logging/django_logger.py +58 -19
  104. django_cfg/pyproject.toml +3 -3
  105. django_cfg/static/frontend/admin.zip +0 -0
  106. django_cfg/templates/admin/index.html +0 -39
  107. django_cfg/utils/pool_monitor.py +320 -0
  108. django_cfg/utils/smart_defaults.py +233 -7
  109. {django_cfg-1.5.14.dist-info → django_cfg-1.5.29.dist-info}/METADATA +75 -5
  110. {django_cfg-1.5.14.dist-info → django_cfg-1.5.29.dist-info}/RECORD +118 -74
  111. /django_cfg/apps/integrations/grpc/services/{grpc_client.py → client/client.py} +0 -0
  112. /django_cfg/apps/integrations/grpc/{interceptors → services/interceptors}/errors.py +0 -0
  113. /django_cfg/apps/integrations/grpc/{interceptors → services/interceptors}/logging.py +0 -0
  114. /django_cfg/apps/integrations/grpc/services/{config_helper.py → management/config_helper.py} +0 -0
  115. /django_cfg/apps/integrations/grpc/services/{proto_files_manager.py → management/proto_manager.py} +0 -0
  116. {django_cfg-1.5.14.dist-info → django_cfg-1.5.29.dist-info}/WHEEL +0 -0
  117. {django_cfg-1.5.14.dist-info → django_cfg-1.5.29.dist-info}/entry_points.txt +0 -0
  118. {django_cfg-1.5.14.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
+ ]
@@ -0,0 +1,164 @@
1
+ # Server Lifecycle Logging Utilities
2
+
3
+ Reusable functions for logging server startup and shutdown with timestamps and uptime tracking.
4
+
5
+ **Uses Rich** for beautiful output with panels, tables, and colors! 🎨
6
+
7
+ ## 📦 Functions
8
+
9
+ ### `log_server_start()`
10
+
11
+ Logs server startup with timestamp and configuration.
12
+
13
+ ```python
14
+ from django_cfg.apps.integrations.grpc.utils import log_server_start
15
+
16
+ start_time = log_server_start(
17
+ logger,
18
+ server_type="gRPC Server", # Server type
19
+ mode="Development", # Production/Development
20
+ hotreload_enabled=True, # Is hotreload enabled?
21
+ host="0.0.0.0", # Additional parameters
22
+ port=50051
23
+ )
24
+ ```
25
+
26
+ **Output (Rich Panel):**
27
+ ```
28
+ ╭────────────── 🚀 gRPC Server Starting ───────────────╮
29
+ │ │
30
+ │ ⏰ Started at 2025-11-05 14:30:15 │
31
+ │ Mode Development │
32
+ │ Hotreload Enabled ⚡ │
33
+ │ Host 0.0.0.0 │
34
+ │ Port 50051 │
35
+ │ │
36
+ ╰──────────────────────────────────────────────────────╯
37
+ ⚠️ Hotreload active - connections may be dropped on code changes
38
+ ```
39
+ (with green border and colors!)
40
+
41
+ **Returns:** `datetime` object of start time (for use in `log_server_shutdown`)
42
+
43
+ ---
44
+
45
+ ### `log_server_shutdown()`
46
+
47
+ Logs server shutdown with uptime calculation.
48
+
49
+ ```python
50
+ from django_cfg.apps.integrations.grpc.utils import log_server_shutdown
51
+
52
+ log_server_shutdown(
53
+ logger,
54
+ start_time, # datetime from log_server_start()
55
+ server_type="gRPC Server",
56
+ reason="Hotreload triggered", # Shutdown reason
57
+ active_connections=5 # Additional parameters
58
+ )
59
+ ```
60
+
61
+ **Output (Rich Panel):**
62
+ ```
63
+ ╭───────────── 🧹 Shutting down gRPC Server ──────────────╮
64
+ │ │
65
+ │ 📋 Reason Hotreload triggered │
66
+ │ ⏱️ Uptime 0h 2m 35s │
67
+ │ 🕐 Stopped at 2025-11-05 14:32:50 │
68
+ │ Active Connections 5 │
69
+ │ │
70
+ ╰──────────────────────────────────────────────────────────╯
71
+ ✅ Server shutdown complete
72
+ ```
73
+ (with red border and colors!)
74
+
75
+ ---
76
+
77
+ ## 🎯 Complete Usage Example
78
+
79
+ ```python
80
+ from django_cfg.apps.integrations.grpc.utils import (
81
+ setup_streaming_logger,
82
+ log_server_start,
83
+ log_server_shutdown,
84
+ )
85
+
86
+ # Create logger
87
+ logger = setup_streaming_logger('my_server')
88
+
89
+ # Log server start
90
+ start_time = log_server_start(
91
+ logger,
92
+ server_type="WebSocket Server",
93
+ mode="Production",
94
+ hotreload_enabled=False,
95
+ host="0.0.0.0",
96
+ port=8000
97
+ )
98
+
99
+ try:
100
+ # Server work...
101
+ await server.serve_forever()
102
+ shutdown_reason = "Normal termination"
103
+ except KeyboardInterrupt:
104
+ shutdown_reason = "Keyboard interrupt"
105
+ finally:
106
+ # Log server shutdown
107
+ log_server_shutdown(
108
+ logger,
109
+ start_time,
110
+ server_type="WebSocket Server",
111
+ reason=shutdown_reason,
112
+ total_connections=1250
113
+ )
114
+ ```
115
+
116
+ ---
117
+
118
+ ## 📊 Used in:
119
+
120
+ - ✅ `rungrpc` - gRPC server command
121
+ - ✅ Can be used for any servers (WebSocket, HTTP, etc.)
122
+
123
+ ---
124
+
125
+ ## 🔧 Parameters
126
+
127
+ ### `log_server_start()`
128
+
129
+ | Parameter | Type | Required | Description |
130
+ |----------|-----|----------|-------------|
131
+ | `logger` | `logging.Logger` | ✅ Yes | Logger instance |
132
+ | `server_type` | `str` | ❌ No | Server type (default: "Server") |
133
+ | `mode` | `str` | ❌ No | Running mode (default: "Development") |
134
+ | `hotreload_enabled` | `bool` | ❌ No | Is hotreload enabled (default: False) |
135
+ | `use_rich` | `bool` | ❌ No | Use Rich for output (default: True) |
136
+ | `**extra_info` | `dict` | ❌ No | Additional key-value pairs to log |
137
+
138
+ ### `log_server_shutdown()`
139
+
140
+ | Parameter | Type | Required | Description |
141
+ |----------|-----|----------|-------------|
142
+ | `logger` | `logging.Logger` | ✅ Yes | Logger instance |
143
+ | `start_time` | `datetime` | ✅ Yes | Start time from `log_server_start()` |
144
+ | `server_type` | `str` | ❌ No | Server type (default: "Server") |
145
+ | `reason` | `str` | ❌ No | Shutdown reason |
146
+ | `use_rich` | `bool` | ❌ No | Use Rich for output (default: True) |
147
+ | `**extra_info` | `dict` | ❌ No | Additional key-value pairs to log |
148
+
149
+ ---
150
+
151
+ ## 💡 Benefits
152
+
153
+ 1. ✅ **DRY** - single code for all servers
154
+ 2. ✅ **Consistency** - uniform log format
155
+ 3. ✅ **Rich UI** - beautiful panels, tables, colors 🎨
156
+ 4. ✅ **Uptime tracking** - automatic time calculation
157
+ 5. ✅ **Flexible** - can add arbitrary parameters via `**extra_info`
158
+ 6. ✅ **Hotreload aware** - special warning for hotreload mode
159
+ 7. ✅ **Fallback** - works without Rich (if `use_rich=False`)
160
+
161
+ ---
162
+
163
+ **Author:** django-cfg team
164
+ **Date:** 2025-11-05
@@ -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
+ ]