crazy-workers 1.4.0__tar.gz → 1.4.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.
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/PKG-INFO +1 -1
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/cli/commands/status.py +13 -4
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/cli/discovery.py +13 -1
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/cli/main.py +8 -3
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers.egg-info/PKG-INFO +1 -1
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/pyproject.toml +1 -1
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/LICENSE +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/README.md +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/__init__.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/_bootstrap.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/boot/__init__.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/boot/__main__.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/boot/base.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/boot/detect.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/boot/entry.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/boot/orchestrator.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/boot/systemd.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/boot/windows.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/cli/__init__.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/cli/commands/__init__.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/cli/commands/params.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/cli/commands/starter.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/cli/commands/stopper.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/cli/ui.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/client.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/core/__init__.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/core/backend.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/core/engine.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/core/manager/__init__.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/core/manager/lister.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/core/manager/recoverer.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/core/manager/starter.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/core/manager/stopper.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/core/recovery.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/daemon/__init__.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/daemon/__main__.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/daemon/reconciler.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/daemon/runner.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/database/__init__.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/database/schema.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/database/storage.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/testing/__init__.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/testing/backends.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers/testing/polling.py +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers.egg-info/SOURCES.txt +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers.egg-info/dependency_links.txt +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers.egg-info/entry_points.txt +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers.egg-info/requires.txt +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/crazy_workers.egg-info/top_level.txt +0 -0
- {crazy_workers-1.4.0 → crazy_workers-1.4.2}/setup.cfg +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
3
|
import re
|
|
4
|
+
import sys
|
|
4
5
|
from datetime import datetime
|
|
5
6
|
from rich.panel import Panel
|
|
6
7
|
from rich.table import Table
|
|
@@ -8,11 +9,15 @@ from rich.table import Table
|
|
|
8
9
|
from ..ui import console
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
def show_status(client, workers_dir):
|
|
12
|
+
def show_status(client, workers_dir, json_mode=False):
|
|
12
13
|
"""Observability hub: the target state store plus the worker table (desired vs actual)."""
|
|
13
|
-
console().print(_build_header(workers_dir))
|
|
14
|
-
|
|
15
14
|
workers = _merge_with_filesystem(client.list(), workers_dir)
|
|
15
|
+
|
|
16
|
+
if json_mode:
|
|
17
|
+
sys.stdout.write(json.dumps({'workers': workers}) + '\n')
|
|
18
|
+
return workers
|
|
19
|
+
|
|
20
|
+
console().print(_build_header(workers_dir))
|
|
16
21
|
if not workers:
|
|
17
22
|
console().print('[yellow]No workers found.[/yellow]')
|
|
18
23
|
return workers
|
|
@@ -27,7 +32,8 @@ def _build_header(workers_dir):
|
|
|
27
32
|
target = f'[green]shared DB[/green] [dim]({_redact(db_url)})[/dim]'
|
|
28
33
|
else:
|
|
29
34
|
target = '[dim]self-contained SQLite (.service/workers.db)[/dim]'
|
|
30
|
-
|
|
35
|
+
dir_label = workers_dir if workers_dir else '[dim](not set — scripts not listed)[/dim]'
|
|
36
|
+
body = f'[bold]Workers dir:[/bold] {dir_label}\n[bold]State store:[/bold] {target}'
|
|
31
37
|
return Panel.fit(body, border_style='cyan', title='[bold cyan]Crazy Workers status[/bold cyan]')
|
|
32
38
|
|
|
33
39
|
|
|
@@ -39,6 +45,9 @@ def _redact(db_url):
|
|
|
39
45
|
def _merge_with_filesystem(db_workers, workers_dir):
|
|
40
46
|
"""Append NEVER_STARTED rows for worker scripts that have no DB record yet."""
|
|
41
47
|
results = list(db_workers)
|
|
48
|
+
if not workers_dir:
|
|
49
|
+
# No dir resolved (shared-DB mode without CRAZY_WORKERS_DIR): nothing to scan.
|
|
50
|
+
return results
|
|
42
51
|
registered_types = {w['worker_type'] for w in results}
|
|
43
52
|
try:
|
|
44
53
|
available = sorted({f[:-3] for f in os.listdir(workers_dir) if f.endswith('.py') and f != '__init__.py'})
|
|
@@ -45,7 +45,15 @@ def save_to_env(key, value):
|
|
|
45
45
|
os.replace(tmp, '.env')
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
def resolve_workers_dir(flag_dir):
|
|
48
|
+
def resolve_workers_dir(flag_dir, required=True):
|
|
49
|
+
"""Locate the workers directory (where the ``.py`` scripts live).
|
|
50
|
+
|
|
51
|
+
``required=False`` is used by commands that only talk to a shared DB (e.g.
|
|
52
|
+
``status``/``stop``/``params`` with ``CRAZY_WORKERS_DB_URL`` set): they do not
|
|
53
|
+
need the scripts, so we must never block on the interactive prompt. In that
|
|
54
|
+
mode the dir is best-effort — returned from the flag/env/``workers`` fallback
|
|
55
|
+
if available, otherwise ``None``.
|
|
56
|
+
"""
|
|
49
57
|
load_env()
|
|
50
58
|
|
|
51
59
|
# 1. Flag priority
|
|
@@ -65,6 +73,10 @@ def resolve_workers_dir(flag_dir):
|
|
|
65
73
|
err_console().print(f'[bold red]Error:[/bold red] Directory "{env_dir}" (from CRAZY_WORKERS_DIR) does not exist.')
|
|
66
74
|
sys.exit(1)
|
|
67
75
|
|
|
76
|
+
# When the dir is not needed, never prompt or exit — report it as unknown.
|
|
77
|
+
if not required:
|
|
78
|
+
return 'workers' if os.path.isdir('workers') else None
|
|
79
|
+
|
|
68
80
|
# 3. Interactive Prompt
|
|
69
81
|
if sys.stdin.isatty():
|
|
70
82
|
console().print('[bold yellow]CRAZY_WORKERS_DIR not set in environment.[/bold yellow]')
|
|
@@ -39,7 +39,8 @@ def _build_parser():
|
|
|
39
39
|
|
|
40
40
|
subparsers = parser.add_subparsers(dest='command', help='Commands')
|
|
41
41
|
|
|
42
|
-
subparsers.add_parser('status', help='Show workers (desired vs actual) and the target DB')
|
|
42
|
+
status_parser = subparsers.add_parser('status', help='Show workers (desired vs actual) and the target DB')
|
|
43
|
+
status_parser.add_argument('--json', action='store_true', help='Output workers as JSON to stdout')
|
|
43
44
|
|
|
44
45
|
start_parser = subparsers.add_parser('start', help='Request a worker to run (interactive if type missing)')
|
|
45
46
|
start_parser.add_argument('worker_type', nargs='?', help='The type (filename) of worker to start')
|
|
@@ -72,7 +73,11 @@ def main():
|
|
|
72
73
|
parser.print_help()
|
|
73
74
|
sys.exit(1)
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
# Only `start` (lists/validates the worker scripts) and the daemon (owns them)
|
|
77
|
+
# truly need the workers dir. With a shared DB, status/stop/params work without
|
|
78
|
+
# it, so we must not block on the interactive prompt (see CRAZY_WORKERS_DB_URL).
|
|
79
|
+
needs_dir = args.command in ('start', 'daemon') or _db_url() is None
|
|
80
|
+
workers_dir = resolve_workers_dir(args.workers_dir, required=needs_dir)
|
|
76
81
|
|
|
77
82
|
# The daemon is the process owner, not a client — it builds its own manager.
|
|
78
83
|
if args.command == 'daemon':
|
|
@@ -87,7 +92,7 @@ def main():
|
|
|
87
92
|
try:
|
|
88
93
|
with _build_client(workers_dir) as client:
|
|
89
94
|
if args.command == 'status':
|
|
90
|
-
show_status(client, workers_dir)
|
|
95
|
+
show_status(client, workers_dir, json_mode=getattr(args, 'json', False))
|
|
91
96
|
elif args.command == 'start':
|
|
92
97
|
params = _parse_params(args.params)
|
|
93
98
|
if not start_worker(client, workers_dir, args.worker_type, worker_key=args.key, parameters=params):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "crazy-workers"
|
|
3
|
-
version = "1.4.
|
|
3
|
+
version = "1.4.2"
|
|
4
4
|
description = "A Python library for managing background worker processes with persistent state, automatic recovery, and a CLI."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "GioVanni Colasanto" }]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|