rrq 0.5.0__py3-none-any.whl → 0.7.1__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.
rrq/cli.py CHANGED
@@ -363,6 +363,38 @@ def rrq():
363
363
  pass
364
364
 
365
365
 
366
+ # Register modular commands
367
+ try:
368
+ # Import new command classes
369
+ from .cli_commands.commands.queues import QueueCommands
370
+ from .cli_commands.commands.jobs import JobCommands
371
+ from .cli_commands.commands.monitor import MonitorCommands
372
+ from .cli_commands.commands.debug import DebugCommands
373
+ from .cli_commands.commands.dlq import DLQCommands
374
+
375
+ # Register new commands with existing CLI
376
+ command_classes = [
377
+ QueueCommands(),
378
+ JobCommands(),
379
+ MonitorCommands(),
380
+ DebugCommands(),
381
+ DLQCommands(),
382
+ ]
383
+
384
+ for command_instance in command_classes:
385
+ try:
386
+ command_instance.register(rrq)
387
+ except Exception as e:
388
+ click.echo(
389
+ f"Warning: Failed to register command {command_instance.__class__.__name__}: {e}",
390
+ err=True,
391
+ )
392
+
393
+ except ImportError as e:
394
+ # Fall back to original CLI if new modules aren't available
395
+ click.echo(f"Warning: Enhanced CLI features not available: {e}", err=True)
396
+
397
+
366
398
  @rrq.group("worker")
367
399
  def worker_cli():
368
400
  """Manage RRQ workers (run, watch)."""
@@ -562,8 +594,13 @@ def _run_multiple_workers(
562
594
  # Pass the RRQ_SETTINGS env var as explicit parameter to subprocess
563
595
  cmd.extend(["--settings", os.getenv("RRQ_SETTINGS")])
564
596
  else:
565
- # Default to app.config.rrq.rrq_settings for ResQ
566
- cmd.extend(["--settings", "app.config.rrq.rrq_settings"])
597
+ # Error: No settings provided for multi-worker mode
598
+ click.echo(
599
+ "Error: Multi-worker mode requires explicit settings. "
600
+ "Please provide either --settings option or set RRQ_SETTINGS environment variable.",
601
+ err=True,
602
+ )
603
+ sys.exit(1)
567
604
  if queues:
568
605
  for q_name in queues:
569
606
  cmd.extend(["--queue", q_name])
@@ -742,65 +779,3 @@ def check_command(settings_object_path: str):
742
779
  else:
743
780
  click.echo(click.style("Health check FAILED.", fg="red"))
744
781
  sys.exit(1)
745
-
746
-
747
- @rrq.group("dlq")
748
- def dlq_cli():
749
- """Manage the Dead Letter Queue (DLQ)."""
750
- pass
751
-
752
-
753
- @dlq_cli.command("requeue")
754
- @click.option(
755
- "--settings",
756
- "settings_object_path",
757
- type=str,
758
- required=False,
759
- default=None,
760
- help=(
761
- "Python settings path for application worker settings "
762
- "(e.g., myapp.worker_config.rrq_settings). "
763
- "Must include `job_registry: JobRegistry` if requeueing requires handler resolution."
764
- ),
765
- )
766
- @click.option(
767
- "--dlq-name",
768
- "dlq_name",
769
- type=str,
770
- required=False,
771
- default=None,
772
- help="Name of the DLQ (without prefix). Defaults to settings.default_dlq_name.",
773
- )
774
- @click.option(
775
- "--queue",
776
- "target_queue",
777
- type=str,
778
- required=False,
779
- default=None,
780
- help="Name of the target queue (without prefix). Defaults to settings.default_queue_name.",
781
- )
782
- @click.option(
783
- "--limit",
784
- type=int,
785
- required=False,
786
- default=None,
787
- help="Maximum number of DLQ jobs to requeue; all if not set.",
788
- )
789
- def dlq_requeue_command(
790
- settings_object_path: str,
791
- dlq_name: str,
792
- target_queue: str,
793
- limit: int,
794
- ):
795
- """Requeue jobs from the dead letter queue back into a live queue."""
796
- rrq_settings = _load_app_settings(settings_object_path)
797
- dlq_to_use = dlq_name or rrq_settings.default_dlq_name
798
- queue_to_use = target_queue or rrq_settings.default_queue_name
799
- job_store = JobStore(settings=rrq_settings)
800
- click.echo(
801
- f"Requeuing jobs from DLQ '{dlq_to_use}' to queue '{queue_to_use}' (limit: {limit or 'all'})..."
802
- )
803
- count = asyncio.run(job_store.requeue_dlq(dlq_to_use, queue_to_use, limit))
804
- click.echo(
805
- f"Requeued {count} job(s) from DLQ '{dlq_to_use}' to queue '{queue_to_use}'."
806
- )
@@ -0,0 +1 @@
1
+ """RRQ CLI module"""
@@ -0,0 +1,102 @@
1
+ """Base classes and utilities for RRQ CLI commands"""
2
+
3
+ import asyncio
4
+ import importlib
5
+ import os
6
+ import pkgutil
7
+ from abc import ABC, abstractmethod
8
+ from typing import Callable
9
+
10
+ import click
11
+
12
+ from ..settings import RRQSettings
13
+ from ..store import JobStore
14
+
15
+
16
+ class BaseCommand(ABC):
17
+ """Base class for all RRQ CLI commands"""
18
+
19
+ @abstractmethod
20
+ def register(self, cli_group: click.Group) -> None:
21
+ """Register the command with the CLI group"""
22
+ pass
23
+
24
+
25
+ class AsyncCommand(BaseCommand):
26
+ """Base class for async CLI commands"""
27
+
28
+ def make_async(self, func: Callable) -> Callable:
29
+ """Wrapper to run async functions in click commands"""
30
+
31
+ def wrapper(*args, **kwargs):
32
+ return asyncio.run(func(*args, **kwargs))
33
+
34
+ return wrapper
35
+
36
+
37
+ def load_app_settings(settings_object_path: str | None = None) -> RRQSettings:
38
+ """Load the settings object from the given path.
39
+
40
+ If not provided, the RRQ_SETTINGS environment variable will be used.
41
+ If the environment variable is not set, will create a default settings object.
42
+ """
43
+ # Import the original function from cli.py
44
+ from ..cli import _load_app_settings
45
+
46
+ return _load_app_settings(settings_object_path)
47
+
48
+
49
+ def resolve_settings_source(
50
+ settings_object_path: str | None = None,
51
+ ) -> tuple[str | None, str]:
52
+ """Resolve the settings path and its source."""
53
+ # Import the original function from cli.py
54
+ from ..cli import _resolve_settings_source
55
+
56
+ return _resolve_settings_source(settings_object_path)
57
+
58
+
59
+ def auto_discover_commands(package_path: str) -> list[type[BaseCommand]]:
60
+ """Auto-discover command classes in the given package"""
61
+ commands = []
62
+
63
+ # Get the package module
64
+ try:
65
+ package = importlib.import_module(package_path)
66
+ package_dir = os.path.dirname(package.__file__)
67
+ except ImportError:
68
+ # Return empty list for non-existent packages
69
+ return commands
70
+
71
+ # Iterate through all modules in the package
72
+ for _, module_name, is_pkg in pkgutil.iter_modules([package_dir]):
73
+ if is_pkg:
74
+ continue
75
+
76
+ # Import the module
77
+ module_path = f"{package_path}.{module_name}"
78
+ try:
79
+ module = importlib.import_module(module_path)
80
+
81
+ # Look for BaseCommand subclasses
82
+ for attr_name in dir(module):
83
+ attr = getattr(module, attr_name)
84
+ if (
85
+ isinstance(attr, type)
86
+ and issubclass(attr, BaseCommand)
87
+ and attr not in (BaseCommand, AsyncCommand)
88
+ ):
89
+ commands.append(attr)
90
+ except ImportError:
91
+ # Skip modules that can't be imported
92
+ continue
93
+
94
+ return commands
95
+
96
+
97
+ async def get_job_store(settings: RRQSettings) -> JobStore:
98
+ """Create and return a JobStore instance"""
99
+ job_store = JobStore(settings=settings)
100
+ # Test connection
101
+ await job_store.redis.ping()
102
+ return job_store
@@ -0,0 +1 @@
1
+ """RRQ CLI commands module"""