django-cfg 1.2.14__py3-none-any.whl → 1.2.15__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.
django_cfg/__init__.py CHANGED
@@ -32,7 +32,7 @@ Example:
32
32
  default_app_config = "django_cfg.apps.DjangoCfgConfig"
33
33
 
34
34
  # Version information
35
- __version__ = "1.2.14"
35
+ __version__ = "1.2.15"
36
36
  __license__ = "MIT"
37
37
 
38
38
  # Import registry for organized lazy loading
django_cfg/core/config.py CHANGED
@@ -20,6 +20,7 @@ from django_cfg import (
20
20
  DatabaseConfig, CacheConfig, EmailConfig, TelegramConfig,
21
21
  UnfoldConfig, DRFConfig, SpectacularConfig, LimitsConfig
22
22
  )
23
+ from django_cfg.models.tasks import TaskConfig
23
24
 
24
25
  # Default apps
25
26
  DEFAULT_APPS = [
@@ -244,6 +245,12 @@ class DjangoConfig(BaseModel):
244
245
  description="Unfold admin interface configuration",
245
246
  )
246
247
 
248
+ # === Background Task Processing ===
249
+ tasks: Optional[TaskConfig] = Field(
250
+ default=None,
251
+ description="Background task processing configuration (Dramatiq)",
252
+ )
253
+
247
254
  # === API Configuration ===
248
255
  # Note: DRF base configuration is handled by django-revolution
249
256
  # These fields provide additional/extended settings on top of Revolution
@@ -353,6 +360,7 @@ class DjangoConfig(BaseModel):
353
360
  if "default" not in self.databases:
354
361
  raise ConfigurationError("'default' database is required", context={"available_databases": list(self.databases.keys())}, suggestions=["Add a database with alias 'default'"])
355
362
 
363
+
356
364
  # Validate database routing consistency - check migrate_to references
357
365
  referenced_databases = set()
358
366
  for alias, db_config in self.databases.items():
@@ -418,11 +418,17 @@ class SettingsGenerator:
418
418
 
419
419
  # Check for Tasks/Dramatiq configuration
420
420
  try:
421
+ from django_cfg.models.tasks import TaskConfig
421
422
  from django_cfg.modules.django_tasks import generate_dramatiq_settings_from_config
422
- dramatiq_settings = generate_dramatiq_settings_from_config(config)
423
- if dramatiq_settings:
424
- settings.update(dramatiq_settings)
425
- integrations.append("dramatiq")
423
+
424
+ # Auto-initialize TaskConfig if needed and generate settings
425
+ task_config = TaskConfig.auto_initialize_if_needed()
426
+ if task_config is not None:
427
+ dramatiq_settings = generate_dramatiq_settings_from_config()
428
+ if dramatiq_settings:
429
+ settings.update(dramatiq_settings)
430
+ integrations.append("dramatiq")
431
+
426
432
  except ImportError as e:
427
433
  logger.warning(f"Failed to import django_tasks module: {e}")
428
434
  except Exception as e:
@@ -11,6 +11,7 @@ from typing import Optional, List, Literal, Dict, Any
11
11
  from enum import Enum
12
12
  import os
13
13
  import logging
14
+ from django_cfg.models.cfg import BaseCfgAutoModule
14
15
 
15
16
  logger = logging.getLogger(__name__)
16
17
 
@@ -239,7 +240,7 @@ class WorkerConfig(BaseModel):
239
240
  )
240
241
 
241
242
 
242
- class TaskConfig(BaseModel):
243
+ class TaskConfig(BaseModel, BaseCfgAutoModule):
243
244
  """
244
245
  High-level task system configuration.
245
246
 
@@ -257,6 +258,12 @@ class TaskConfig(BaseModel):
257
258
  description="Task processing backend"
258
259
  )
259
260
 
261
+ def __init__(self, **data):
262
+ """Initialize TaskConfig with BaseCfgAutoModule support."""
263
+ super().__init__(**data)
264
+ # Initialize _config attribute for BaseCfgAutoModule
265
+ self._config = None
266
+
260
267
  # === Backend-Specific Configuration ===
261
268
  dramatiq: DramatiqConfig = Field(
262
269
  default_factory=DramatiqConfig,
@@ -371,6 +378,53 @@ class TaskConfig(BaseModel):
371
378
  "DRAMATIQ_QUEUES": self.dramatiq.queues,
372
379
  }
373
380
 
381
+ def get_smart_defaults(self):
382
+ """Get smart default configuration for this module."""
383
+ config = self.get_config()
384
+ debug = getattr(config, 'debug', False) if config else False
385
+ return get_default_task_config(debug=debug)
386
+
387
+ def get_module_config(self):
388
+ """Get the final configuration for this module."""
389
+ return self
390
+
391
+ @classmethod
392
+ def auto_initialize_if_needed(cls) -> Optional['TaskConfig']:
393
+ """
394
+ Auto-initialize TaskConfig if needed based on config flags.
395
+
396
+ Returns:
397
+ TaskConfig instance if should be initialized, None otherwise
398
+ """
399
+ # Get config through BaseCfgModule
400
+ from django_cfg.modules import BaseCfgModule
401
+ base_module = BaseCfgModule()
402
+ config = base_module.get_config()
403
+
404
+ if not config:
405
+ return None
406
+
407
+ # Check if TaskConfig already exists
408
+ if hasattr(config, 'tasks') and config.tasks is not None:
409
+ # Set config reference and return existing
410
+ config.tasks.set_config(config)
411
+ return config.tasks
412
+
413
+ # Check if tasks should be enabled
414
+ if base_module.is_tasks_enabled():
415
+ # Auto-initialize with smart defaults
416
+ task_config = cls().get_smart_defaults()
417
+ task_config.set_config(config)
418
+ config.tasks = task_config
419
+
420
+ import logging
421
+ logger = logging.getLogger(__name__)
422
+ logger.info("🚀 Auto-initialized TaskConfig (enabled by knowbase/agents/tasks flags)")
423
+
424
+ return task_config
425
+
426
+ return None
427
+
374
428
 
375
429
  # === Utility Functions ===
376
430
 
@@ -9,16 +9,14 @@ from typing import Optional, Dict, Any, List
9
9
  import logging
10
10
  from urllib.parse import urlparse
11
11
 
12
- from django_cfg.modules.base import BaseCfgModule
12
+ from . import BaseCfgModule
13
13
  from django_cfg.models.tasks import TaskConfig, validate_task_config
14
14
  from django_cfg.models.constance import ConstanceField
15
15
 
16
16
  # Django imports (will be available when Django is configured)
17
17
  try:
18
- from django.conf import settings
19
18
  from django.apps import apps
20
19
  except ImportError:
21
- settings = None
22
20
  apps = None
23
21
 
24
22
  # Optional imports
@@ -426,6 +424,46 @@ def extend_constance_config_with_tasks():
426
424
 
427
425
  # === Exports ===
428
426
 
427
+ def generate_dramatiq_settings_from_config() -> Optional[Dict[str, Any]]:
428
+ """
429
+ Generate Dramatiq settings from auto-discovered DjangoConfig.
430
+
431
+ Returns:
432
+ Dictionary of Dramatiq settings or None if tasks disabled
433
+ """
434
+ try:
435
+ # Get config through BaseCfgModule
436
+ base_module = BaseCfgModule()
437
+ config = base_module.get_config()
438
+
439
+ if not config or not hasattr(config, 'tasks') or not config.tasks or not config.tasks.enabled:
440
+ return None
441
+
442
+ # Get Redis URL from cache config or environment
443
+ redis_url = None
444
+ if hasattr(config, 'cache_default') and config.cache_default:
445
+ redis_url = getattr(config.cache_default, 'redis_url', None)
446
+
447
+ if not redis_url:
448
+ # Fallback to environment or default
449
+ import os
450
+ redis_url = os.getenv('REDIS_URL', 'redis://localhost:6379/1')
451
+
452
+ # Generate Dramatiq settings
453
+ dramatiq_settings = config.tasks.get_dramatiq_settings(redis_url)
454
+
455
+ # Ensure we only use Redis broker (no RabbitMQ)
456
+ if 'DRAMATIQ_BROKER' in dramatiq_settings:
457
+ dramatiq_settings['DRAMATIQ_BROKER']['BROKER'] = 'dramatiq.brokers.redis.RedisBroker'
458
+
459
+ logger.info(f"✅ Generated Dramatiq settings with Redis broker and {len(config.tasks.dramatiq.queues)} queues")
460
+ return dramatiq_settings
461
+
462
+ except Exception as e:
463
+ logger.error(f"Failed to generate Dramatiq settings: {e}")
464
+ return None
465
+
466
+
429
467
  __all__ = [
430
468
  "DjangoTasks",
431
469
  "get_task_service",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-cfg
3
- Version: 1.2.14
3
+ Version: 1.2.15
4
4
  Summary: 🚀 Next-gen Django configuration: type-safety, AI features, blazing-fast setup, and automated best practices — all in one.
5
5
  Project-URL: Homepage, https://djangocfg.com
6
6
  Project-URL: Documentation, https://docs.djangocfg.com
@@ -1,5 +1,5 @@
1
1
  django_cfg/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- django_cfg/__init__.py,sha256=g_flQ-9-LV1gfDgs04khEhR0zXfBFiXDQk50mNX438M,1631
2
+ django_cfg/__init__.py,sha256=WO5eQC0F4xNgcT1pvxC4u4-1_fGdLq1oolFLlGmuRKE,1631
3
3
  django_cfg/apps.py,sha256=k84brkeXJI7EgKZLEpTkM9YFZofKI4PzhFOn1cl9Msc,1656
4
4
  django_cfg/config.py,sha256=0cuRJVEnf03WzvEqhwzLvn9Zi1805C5KG1yk27ekABA,1190
5
5
  django_cfg/urls.py,sha256=bpRFjMonQuk4UCUMxx4ueBX3YDNB7HXKFwEghQ3KR3o,793
@@ -285,10 +285,10 @@ django_cfg/cli/commands/__init__.py,sha256=EKLXDAx-QttnGmdjsmVANAfhxWplxl2V_2I0S
285
285
  django_cfg/cli/commands/create_project.py,sha256=iuf965j8Yg7zxHcPb0GtFHEj73CYXC45ZJRmd6RbA9E,21025
286
286
  django_cfg/cli/commands/info.py,sha256=o4S1xPJSHv2oEVqmH0X9RTF5f-8Wy9579yHkyd_PC3E,4923
287
287
  django_cfg/core/__init__.py,sha256=eVK57qFOok9kTeHoNEMQ1BplkUOaQ7NB9kP9eQK1vg0,358
288
- django_cfg/core/config.py,sha256=XZmW4tqHYi8zUOywpJ_8DqeFUzwwflMshZnwrpddjtw,26302
288
+ django_cfg/core/config.py,sha256=y0rvqiO5l_MMqDhQCiyA9c3GHYJlJwRpdOgvN_MfcWU,26536
289
289
  django_cfg/core/environment.py,sha256=MAoEPqIPsLVhSANT2Bz4nnus2wmbMW0RCOQxhQfDrDc,9106
290
290
  django_cfg/core/exceptions.py,sha256=RTQEoU3PfR8lqqNNv5ayd_HY2yJLs3eioqUy8VM6AG4,10378
291
- django_cfg/core/generation.py,sha256=RDxVGheBYopFbMPYBpR6Zz4c8RIjk6-T0R5zVe9gy3A,24701
291
+ django_cfg/core/generation.py,sha256=zSVb3VIv588wzqSF1-wxOb15CW5hX2tf9fq_FRXSOzk,24990
292
292
  django_cfg/core/integration.py,sha256=5kzkZSd45ij0rfrBdeNUYnDalObqvK__XpoP31xFYKo,5224
293
293
  django_cfg/core/validation.py,sha256=PhwDBTs24nuM3xqfIT3hXmN88VrhMT-Bk8Yb8FxjmPk,6590
294
294
  django_cfg/management/__init__.py,sha256=NrLAhiS59hqjy-bipOC1abNuRiNm5BpKXmjN05VzKbM,28
@@ -337,14 +337,14 @@ django_cfg/models/ngrok.py,sha256=MVgcKWx0DRSW0QcwhiSx2vVwTSG49vbVrzPkZqDK-zw,35
337
337
  django_cfg/models/revolution.py,sha256=gaAhdicd1ji-XiJPqUFQaUmDcgfwFHofSYS3JtLcz4A,7540
338
338
  django_cfg/models/security.py,sha256=Xv19ZVOIenB_-f0wB6fm-Ap4j9kA43bSFaT2XenpSqc,4685
339
339
  django_cfg/models/services.py,sha256=fj9JjrJFrlL4DMnMbx_D8JiiZpz4E5uBqmhquAxau7c,13159
340
- django_cfg/models/tasks.py,sha256=QdYtbwxcQgJ1Qh0tPydSRW_OrflOfm2AzvvBb7CKtbQ,13556
340
+ django_cfg/models/tasks.py,sha256=G06kEMaJ9mZhW525y-r33q4rHswAwnvPgMt7lpbgiak,15526
341
341
  django_cfg/modules/__init__.py,sha256=Ip9WMpzImEwIAywpFwU056_v0O9oIGG7nCT1YSArxkw,316
342
342
  django_cfg/modules/base.py,sha256=ALlsylzO1rYsrLNwL3MhEK_dqYSZV0BBZAObR6wlGEA,5343
343
343
  django_cfg/modules/django_email.py,sha256=2XXlIKzD6Jao3CT4_zIE2eaM9Cc9ROA1tjp2bJ9z5Lo,16592
344
344
  django_cfg/modules/django_health.py,sha256=7QzuQ6WyjWYj6lecd4auwRvEyrMUL7N6hiAp-tLyoY4,8923
345
345
  django_cfg/modules/django_logger.py,sha256=3oP9jev0lOcFUJ1tYcpbFnK524zIGA2xIOrrAiTwpb8,6331
346
346
  django_cfg/modules/django_ngrok.py,sha256=LjlAIJprbFhFYwDG93acas8XaB6iFQGu4fWvZiDNhyQ,10482
347
- django_cfg/modules/django_tasks.py,sha256=Ij8AR5RswTJ7oxWC7L3BN9WAO_JaZOtdFQoLzrSJLfA,15862
347
+ django_cfg/modules/django_tasks.py,sha256=SBas2juL6RfDL7ttm4xAxJ7bvS052WmUzixNHPlzJVo,17328
348
348
  django_cfg/modules/django_telegram.py,sha256=qz3EOJnCR_YTJVIu2-Iq8--cla6mU-WPohquQ26eHDQ,16367
349
349
  django_cfg/modules/dramatiq_setup.py,sha256=Jke4aO_L1t6F3OAc4pl12zppKzL0gb1p6ilfQ3zUIZ8,454
350
350
  django_cfg/modules/logger.py,sha256=4_zeasNehr8LGz8r_ckv15-fQS63zCodiqD4CYIEyFI,10546
@@ -464,8 +464,8 @@ django_cfg/utils/path_resolution.py,sha256=C9As6p4Q9l3VeoVkFDRPQWGrzAWf8O8UxLVka
464
464
  django_cfg/utils/smart_defaults.py,sha256=b6A1z7VO1NJGq0oUQXN5P97c3k_Ssgw6qUi0mK-4TlM,19786
465
465
  django_cfg/utils/toolkit.py,sha256=Td8_iXNaftonF_xdZP4Y3uO65nuA_4_zditn5Q_Pfcw,23310
466
466
  django_cfg/utils/version_check.py,sha256=jI4v3YMdQriUEeb_TvRl511sDghy6I75iKRDUaNpucs,4800
467
- django_cfg-1.2.14.dist-info/METADATA,sha256=qvEWenNbuvgOJy4EBPRaLJXYFUfUm062ILx64DlqcCA,36406
468
- django_cfg-1.2.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
469
- django_cfg-1.2.14.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
470
- django_cfg-1.2.14.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
471
- django_cfg-1.2.14.dist-info/RECORD,,
467
+ django_cfg-1.2.15.dist-info/METADATA,sha256=QcWw3jNJ4GU7JwFC9Ya3UdOT-9UFo2Au_7XeCUT3KLE,36406
468
+ django_cfg-1.2.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
469
+ django_cfg-1.2.15.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
470
+ django_cfg-1.2.15.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
471
+ django_cfg-1.2.15.dist-info/RECORD,,