horsies 0.1.0a5__tar.gz → 0.1.0a6__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.
Files changed (49) hide show
  1. {horsies-0.1.0a5 → horsies-0.1.0a6}/PKG-INFO +1 -1
  2. horsies-0.1.0a6/horsies/core/banner.py +258 -0
  3. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies.egg-info/PKG-INFO +1 -1
  4. {horsies-0.1.0a5 → horsies-0.1.0a6}/pyproject.toml +1 -1
  5. horsies-0.1.0a5/horsies/core/banner.py +0 -144
  6. {horsies-0.1.0a5 → horsies-0.1.0a6}/README.md +0 -0
  7. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/__init__.py +0 -0
  8. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/__init__.py +0 -0
  9. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/app.py +0 -0
  10. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/brokers/__init__.py +0 -0
  11. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/brokers/listener.py +0 -0
  12. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/brokers/postgres.py +0 -0
  13. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/cli.py +0 -0
  14. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/codec/serde.py +0 -0
  15. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/errors.py +0 -0
  16. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/logging.py +0 -0
  17. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/models/__init__.py +0 -0
  18. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/models/app.py +0 -0
  19. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/models/broker.py +0 -0
  20. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/models/queues.py +0 -0
  21. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/models/recovery.py +0 -0
  22. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/models/schedule.py +0 -0
  23. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/models/task_pg.py +0 -0
  24. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/models/tasks.py +0 -0
  25. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/models/workflow.py +0 -0
  26. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/models/workflow_pg.py +0 -0
  27. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/registry/tasks.py +0 -0
  28. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/scheduler/__init__.py +0 -0
  29. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/scheduler/calculator.py +0 -0
  30. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/scheduler/service.py +0 -0
  31. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/scheduler/state.py +0 -0
  32. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/task_decorator.py +0 -0
  33. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/types/status.py +0 -0
  34. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/utils/imports.py +0 -0
  35. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/utils/loop_runner.py +0 -0
  36. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/worker/current.py +0 -0
  37. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/worker/worker.py +0 -0
  38. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/workflows/__init__.py +0 -0
  39. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/workflows/engine.py +0 -0
  40. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/workflows/recovery.py +0 -0
  41. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/core/workflows/registry.py +0 -0
  42. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies/py.typed +0 -0
  43. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies.egg-info/SOURCES.txt +0 -0
  44. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies.egg-info/dependency_links.txt +0 -0
  45. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies.egg-info/entry_points.txt +0 -0
  46. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies.egg-info/requires.txt +0 -0
  47. {horsies-0.1.0a5 → horsies-0.1.0a6}/horsies.egg-info/top_level.txt +0 -0
  48. {horsies-0.1.0a5 → horsies-0.1.0a6}/setup.cfg +0 -0
  49. {horsies-0.1.0a5 → horsies-0.1.0a6}/tests/test_issue_fixes.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: horsies
3
- Version: 0.1.0a5
3
+ Version: 0.1.0a6
4
4
  Summary: A Python library for distributed task execution
5
5
  Author: Suleyman Ozkeskin
6
6
  License-Expression: MIT
@@ -0,0 +1,258 @@
1
+ """Startup banner for horsies."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from typing import TYPE_CHECKING, TextIO
7
+
8
+ if TYPE_CHECKING:
9
+ from horsies.core.app import Horsies
10
+
11
+
12
+ # ---------------------------------------------------------------------------
13
+ # ANSI color codes (matching Rust owo-colors output)
14
+ # ---------------------------------------------------------------------------
15
+
16
+ class Colors:
17
+ """ANSI escape codes for terminal colors."""
18
+
19
+ RESET = '\033[0m'
20
+ BOLD = '\033[1m'
21
+ DIMMED = '\033[2m'
22
+
23
+ # Standard colors
24
+ WHITE = '\033[37m'
25
+ BRIGHT_WHITE = '\033[97m'
26
+ CYAN = '\033[36m'
27
+ BRIGHT_CYAN = '\033[96m'
28
+ YELLOW = '\033[33m'
29
+ BRIGHT_YELLOW = '\033[93m'
30
+
31
+
32
+ def _color(text: str, *codes: str) -> str:
33
+ """Apply ANSI color codes to text."""
34
+ if not codes:
35
+ return text
36
+ return ''.join(codes) + text + Colors.RESET
37
+
38
+
39
+ # Braille art of galloping horse - converted from the golden horse image
40
+ # Each braille character represents a 2x4 pixel block for higher resolution
41
+ HORSE_BRAILLE = """
42
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣶⣾⣿⣿⣿⣿⣷⣯⡀⠀⠀⠀⠀⠀⠀
43
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣟⣿⣆⠀⠀⠀⠀
44
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⣿⣿⣿⣿⣿⡟⠹⣿⣿⣿⣿⣿⣦⡀⠀⠀⠀
45
+ ⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣀⣠⣴⣾⣿⣿⣿⣶⣶⣶⣤⣤⣤⣤⣤⣶⣾⣿⣿⣿⣿⣿⣿⣿⡟⠀⠀⠈⠉⠛⠻⡿⣿⣿⠂⠀
46
+ ⠀⠀⢀⣀⠀⢀⣀⣠⣶⣿⣿⠟⢛⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⡐⠀⠀⠀⠀⠀⠀⠈⠋⡿⠁⠀⠀
47
+ ⠀⠀⠀⢹⣿⣿⣿⣿⣿⡿⠁⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
48
+ ⠀⠀⠀⠀⠛⠻⠿⠛⠉⠀⠀⠀⠈⢯⡻⣿⣿⣿⣿⣿⢿⣿⣿⣿⣿⣿⣿⡿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
49
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⣷⣿⣿⣿⡟⠀⠙⠻⠿⠿⣿⣿⠃⣿⣿⣿⣿⣿⣿⣿⣿⡁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
50
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡿⠁⢀⠀⠀⠀⠀⠀⠂⠀⢿⣿⣿⣿⡍⠈⢁⣙⣿⢦⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
51
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⠏⠀⠀⣼⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣿⣷⠀⠀⠀⠀⠁⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
52
+ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣿⣧⣀⢄⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣿⣧⠀⠀⢀⣼⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
53
+ ⠀⠀⠀⣀⠀⠀⠀⢀⡀⠀⠀⠀⡀⠀⠀⠀⣀⠛⣿⣷⣍⠛⣦⡀⠀⠂⢠⣷⣶⣾⡿⠟⠛⢃⠀⢠⣾⡟⠀⠀⠀⠀⡀⠀⠀⠀⡀⠀⠀⠀
54
+ ⠄⡤⠦⠤⠤⢤⡤⠡⠤⠤⢤⠬⠤⠤⠤⢤⠅⠀⠤⣿⣷⠄⠎⢻⣤⡦⠄⠀⠤⢵⠄⠠⠤⠬⣾⠏⠁⠀⠥⡤⠄⠠⠬⢦⡤⠀⠠⠵⢤⠠
55
+ ⠚⠒⠒⠒⡶⠚⠒⠒⠒⡰⠓⠒⠒⠒⢲⠓⠒⠒⠒⢻⠿⠀⠀⠚⢿⡷⠐⠒⠒⠚⡆⠒⠒⠒⠚⣖⠒⠒⠒⠚⢶⠒⠒⠒⠚⢶⠂⠐⠒⠛
56
+ """
57
+
58
+ # Figlet-style "horsies" text with version and tagline
59
+ LOGO_TEXT = r"""
60
+ __ _
61
+ / /_ ____ __________(_)__ _____
62
+ / __ \/ __ \/ ___/ ___/ / _ \/ ___/ {version}
63
+ / / / / /_/ / / (__ ) / __(__ ) distributed task queue
64
+ /_/ /_/\____/_/ /____/_/\___/____/ and workflow engine
65
+ """
66
+
67
+ # Full banner combining horse and logo
68
+ BANNER = (
69
+ HORSE_BRAILLE
70
+ + r"""
71
+ __ _
72
+ / /_ ____ __________(_)__ _____
73
+ / __ \/ __ \/ ___/ ___/ / _ \/ ___/ {version}
74
+ / / / / /_/ / / (__ ) / __(__ ) distributed task queue
75
+ /_/ /_/\____/_/ /____/_/\___/____/ and workflow engine
76
+ """
77
+ )
78
+
79
+
80
+ def get_version() -> str:
81
+ """Get horsies version from package metadata."""
82
+ try:
83
+ from importlib.metadata import version
84
+ return version('horsies')
85
+ except Exception:
86
+ return 'dev'
87
+
88
+
89
+ def format_banner(version: str | None = None) -> str:
90
+ """Format the banner string with version."""
91
+ if version is None:
92
+ version = get_version()
93
+ return BANNER.format(version=f'v{version}')
94
+
95
+
96
+ # ---------------------------------------------------------------------------
97
+ # Formatting helpers (matching Rust banner.rs)
98
+ # ---------------------------------------------------------------------------
99
+
100
+ def _format_ms(ms: int) -> str:
101
+ """Format milliseconds into a human-readable string."""
102
+ if ms >= 60_000:
103
+ mins = ms // 60_000
104
+ remainder_s = (ms % 60_000) // 1_000
105
+ if remainder_s > 0:
106
+ return f'{mins}m{remainder_s}s'
107
+ return f'{mins}m'
108
+ elif ms >= 1_000:
109
+ secs = ms // 1_000
110
+ remainder_ms = ms % 1_000
111
+ if remainder_ms > 0:
112
+ return f'{secs}.{remainder_ms // 100}s'
113
+ return f'{secs}s'
114
+ return f'{ms}ms'
115
+
116
+
117
+ def _format_bool(val: bool) -> str:
118
+ """Format a boolean for display."""
119
+ return 'yes' if val else 'no'
120
+
121
+
122
+ def _write_section_header(lines: list[str], name: str) -> None:
123
+ """Write a colored [section] header line."""
124
+ header = _color(name, Colors.BRIGHT_CYAN, Colors.BOLD)
125
+ lines.append(f'[{header}]')
126
+
127
+
128
+ def _write_kv(lines: list[str], key: str, value: str) -> None:
129
+ """Write a colored '.> key: value' line with consistent alignment."""
130
+ prefix = _color('.>', Colors.DIMMED)
131
+ # Pad key before coloring to maintain alignment
132
+ padded_key = f'{key}:'.ljust(22)
133
+ colored_key = _color(padded_key, Colors.WHITE, Colors.BOLD)
134
+ colored_value = _color(value, Colors.BRIGHT_WHITE)
135
+ lines.append(f' {prefix} {colored_key} {colored_value}')
136
+
137
+
138
+ def print_banner(
139
+ app: 'Horsies',
140
+ role: str = 'worker',
141
+ show_tasks: bool = True,
142
+ file: TextIO | None = None,
143
+ ) -> None:
144
+ """
145
+ Print startup banner with configuration and task list.
146
+
147
+ Args:
148
+ app: The Horsies app instance
149
+ role: The role (worker, scheduler, producer)
150
+ show_tasks: Whether to list discovered tasks
151
+ file: Output file (default: sys.stdout)
152
+ """
153
+ if file is None:
154
+ file = sys.stdout
155
+
156
+ version = get_version()
157
+
158
+ # Build the banner
159
+ lines: list[str] = []
160
+
161
+ # ASCII art header - cyan colored
162
+ colored_horse = _color(HORSE_BRAILLE, Colors.CYAN)
163
+ lines.append(colored_horse)
164
+
165
+ # Logo with version - bright yellow bold
166
+ logo = LOGO_TEXT.replace('{version}', f'v{version}')
167
+ colored_logo = _color(logo, Colors.BRIGHT_YELLOW, Colors.BOLD)
168
+ lines.append(colored_logo)
169
+
170
+ # [config] section
171
+ _write_section_header(lines, 'config')
172
+ _write_kv(lines, 'app', app.__class__.__name__)
173
+ _write_kv(lines, 'role', role)
174
+ _write_kv(lines, 'queue_mode', app.config.queue_mode.name.lower())
175
+
176
+ # Queue info
177
+ if app.config.queue_mode.name == 'CUSTOM' and app.config.custom_queues:
178
+ queues_str = ', '.join(q.name for q in app.config.custom_queues)
179
+ else:
180
+ queues_str = 'default'
181
+ _write_kv(lines, 'queues', queues_str)
182
+
183
+ # Broker info (mask password)
184
+ broker_url = app.config.broker.database_url
185
+ if '@' in broker_url:
186
+ pre, post = broker_url.split('@', 1)
187
+ if ':' in pre:
188
+ scheme_user = pre.rsplit(':', 1)[0]
189
+ broker_url = f'{scheme_user}:****@{post}'
190
+ _write_kv(lines, 'broker', broker_url)
191
+
192
+ # Cluster-wide cap
193
+ if hasattr(app.config, 'cluster_wide_cap') and app.config.cluster_wide_cap:
194
+ _write_kv(lines, 'cluster_cap', f'{app.config.cluster_wide_cap} (cluster-wide)')
195
+
196
+ # Prefetch buffer
197
+ if hasattr(app.config, 'prefetch_buffer') and app.config.prefetch_buffer > 0:
198
+ _write_kv(lines, 'prefetch', str(app.config.prefetch_buffer))
199
+
200
+ lines.append('')
201
+
202
+ # [recovery] section
203
+ if hasattr(app.config, 'recovery') and app.config.recovery:
204
+ recovery = app.config.recovery
205
+ _write_section_header(lines, 'recovery')
206
+ _write_kv(
207
+ lines,
208
+ 'requeue_stale_claimed',
209
+ _format_bool(recovery.auto_requeue_stale_claimed),
210
+ )
211
+ _write_kv(
212
+ lines,
213
+ 'fail_stale_running',
214
+ _format_bool(recovery.auto_fail_stale_running),
215
+ )
216
+ _write_kv(
217
+ lines,
218
+ 'check_interval',
219
+ _format_ms(recovery.check_interval_ms),
220
+ )
221
+ _write_kv(
222
+ lines,
223
+ 'heartbeat_interval',
224
+ _format_ms(recovery.runner_heartbeat_interval_ms),
225
+ )
226
+ lines.append('')
227
+
228
+ # [tasks] section
229
+ if show_tasks:
230
+ task_names = app.list_tasks()
231
+ tasks_header = _color('tasks', Colors.BRIGHT_CYAN, Colors.BOLD)
232
+ lines.append(f'[{tasks_header}] ({len(task_names)} registered)')
233
+ for task_name in sorted(task_names):
234
+ bullet = _color('.', Colors.DIMMED)
235
+ name = _color(task_name, Colors.WHITE)
236
+ lines.append(f' {bullet} {name}')
237
+ lines.append('')
238
+
239
+ # Print everything
240
+ output = '\n'.join(lines)
241
+ print(output, file=file)
242
+
243
+
244
+ def print_simple_banner(file: TextIO | None = None) -> None:
245
+ """Print just the ASCII art banner without config."""
246
+ if file is None:
247
+ file = sys.stdout
248
+
249
+ version = get_version()
250
+
251
+ # Horse art - cyan
252
+ colored_horse = _color(HORSE_BRAILLE, Colors.CYAN)
253
+ print(colored_horse, file=file)
254
+
255
+ # Logo with version - bright yellow bold
256
+ logo = LOGO_TEXT.replace('{version}', f'v{version}')
257
+ colored_logo = _color(logo, Colors.BRIGHT_YELLOW, Colors.BOLD)
258
+ print(colored_logo, file=file)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: horsies
3
- Version: 0.1.0a5
3
+ Version: 0.1.0a6
4
4
  Summary: A Python library for distributed task execution
5
5
  Author: Suleyman Ozkeskin
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "horsies"
7
- version = "0.1.0a5"
7
+ version = "0.1.0a6"
8
8
  description = "A Python library for distributed task execution"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1,144 +0,0 @@
1
- """Startup banner for horsies."""
2
-
3
- from __future__ import annotations
4
-
5
- import sys
6
- from typing import TYPE_CHECKING, TextIO
7
-
8
- if TYPE_CHECKING:
9
- from horsies.core.app import Horsies
10
-
11
-
12
- # Braille art of galloping horse - converted from the golden horse image
13
- # Each braille character represents a 2x4 pixel block for higher resolution
14
- HORSE_BRAILLE = """
15
- ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣶⣾⣿⣿⣿⣿⣷⣯⡀⠀⠀⠀⠀⠀⠀
16
- ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣟⣿⣆⠀⠀⠀⠀
17
- ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⣿⣿⣿⣿⣿⡟⠹⣿⣿⣿⣿⣿⣦⡀⠀⠀⠀
18
- ⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣀⣠⣴⣾⣿⣿⣿⣶⣶⣶⣤⣤⣤⣤⣤⣶⣾⣿⣿⣿⣿⣿⣿⣿⡟⠀⠀⠈⠉⠛⠻⡿⣿⣿⠂⠀
19
- ⠀⠀⢀⣀⠀⢀⣀⣠⣶⣿⣿⠟⢛⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⡐⠀⠀⠀⠀⠀⠀⠈⠋⡿⠁⠀⠀
20
- ⠀⠀⠀⢹⣿⣿⣿⣿⣿⡿⠁⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
21
- ⠀⠀⠀⠀⠛⠻⠿⠛⠉⠀⠀⠀⠈⢯⡻⣿⣿⣿⣿⣿⢿⣿⣿⣿⣿⣿⣿⡿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
22
- ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⣷⣿⣿⣿⡟⠀⠙⠻⠿⠿⣿⣿⠃⣿⣿⣿⣿⣿⣿⣿⣿⡁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
23
- ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡿⠁⢀⠀⠀⠀⠀⠀⠂⠀⢿⣿⣿⣿⡍⠈⢁⣙⣿⢦⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
24
- ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⠏⠀⠀⣼⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣿⣷⠀⠀⠀⠀⠁⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
25
- ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣿⣧⣀⢄⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣿⣧⠀⠀⢀⣼⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
26
- ⠀⠀⠀⣀⠀⠀⠀⢀⡀⠀⠀⠀⡀⠀⠀⠀⣀⠛⣿⣷⣍⠛⣦⡀⠀⠂⢠⣷⣶⣾⡿⠟⠛⢃⠀⢠⣾⡟⠀⠀⠀⠀⡀⠀⠀⠀⡀⠀⠀⠀
27
- ⠄⡤⠦⠤⠤⢤⡤⠡⠤⠤⢤⠬⠤⠤⠤⢤⠅⠀⠤⣿⣷⠄⠎⢻⣤⡦⠄⠀⠤⢵⠄⠠⠤⠬⣾⠏⠁⠀⠥⡤⠄⠠⠬⢦⡤⠀⠠⠵⢤⠠
28
- ⠚⠒⠒⠒⡶⠚⠒⠒⠒⡰⠓⠒⠒⠒⢲⠓⠒⠒⠒⢻⠿⠀⠀⠚⢿⡷⠐⠒⠒⠚⡆⠒⠒⠒⠚⣖⠒⠒⠒⠚⢶⠒⠒⠒⠚⢶⠂⠐⠒⠛
29
- """
30
-
31
- # Figlet-style "horsies" text
32
- LOGO_TEXT = r"""
33
- __ _
34
- / /_ ____ __________(_)__ _____
35
- / __ \/ __ \/ ___/ ___/ / _ \/ ___/
36
- / / / / /_/ / / (__ ) / __(__ )
37
- /_/ /_/\____/_/ /____/_/\___/____/
38
- """
39
-
40
- # Full banner combining horse and logo
41
- BANNER = (
42
- HORSE_BRAILLE
43
- + r"""
44
- __ _
45
- / /_ ____ __________(_)__ _____
46
- / __ \/ __ \/ ___/ ___/ / _ \/ ___/ {version}
47
- / / / / /_/ / / (__ ) / __(__ ) distributed task queue
48
- /_/ /_/\____/_/ /____/_/\___/____/ and workflow engine
49
- """
50
- )
51
-
52
-
53
- def get_version() -> str:
54
- """Get horsies version."""
55
- try:
56
- import horsies
57
-
58
- version = getattr(horsies, '__version__', None)
59
- return str(version) if version else '0.1.0'
60
- except ImportError:
61
- return '0.1.0'
62
-
63
-
64
- def format_banner(version: str | None = None) -> str:
65
- """Format the banner string with version."""
66
- if version is None:
67
- version = get_version()
68
- return BANNER.format(version=f'v{version}')
69
-
70
-
71
- def print_banner(
72
- app: 'Horsies',
73
- role: str = 'worker',
74
- show_tasks: bool = True,
75
- file: TextIO | None = None,
76
- ) -> None:
77
- """
78
- Print startup banner with configuration and task list.
79
-
80
- Args:
81
- app: The Horsies app instance
82
- role: The role (worker, scheduler, producer)
83
- show_tasks: Whether to list discovered tasks
84
- file: Output file (default: sys.stdout)
85
- """
86
- if file is None:
87
- file = sys.stdout
88
-
89
- version = get_version()
90
-
91
- # Build the banner
92
- lines: list[str] = []
93
-
94
- # ASCII art header
95
- banner = format_banner(version)
96
- lines.append(banner)
97
-
98
- # Configuration section
99
- lines.append('[config]')
100
- lines.append(f' .> app: {app.__class__.__name__}')
101
- lines.append(f' .> role: {role}')
102
- lines.append(f' .> queue_mode: {app.config.queue_mode.name}')
103
-
104
- # Queue info
105
- if app.config.queue_mode.name == 'CUSTOM' and app.config.custom_queues:
106
- queues_str = ', '.join(q.name for q in app.config.custom_queues)
107
- lines.append(f' .> queues: {queues_str}')
108
- else:
109
- lines.append(f' .> queues: default')
110
-
111
- # Broker info
112
- broker_url = app.config.broker.database_url
113
- # Mask password in URL
114
- if '@' in broker_url:
115
- pre, post = broker_url.split('@', 1)
116
- if ':' in pre:
117
- scheme_user = pre.rsplit(':', 1)[0]
118
- broker_url = f'{scheme_user}:****@{post}'
119
- lines.append(f' .> broker: {broker_url}')
120
-
121
- # Concurrency info (if available)
122
- if hasattr(app.config, 'cluster_wide_cap') and app.config.cluster_wide_cap:
123
- lines.append(f' .> cap: {app.config.cluster_wide_cap} (cluster-wide)')
124
-
125
- lines.append('')
126
-
127
- # Tasks section
128
- if show_tasks:
129
- task_names = app.list_tasks()
130
- lines.append(f'[tasks] ({len(task_names)} registered)')
131
- for task_name in sorted(task_names):
132
- lines.append(f' . {task_name}')
133
- lines.append('')
134
-
135
- # Print everything
136
- output = '\n'.join(lines)
137
- print(output, file=file)
138
-
139
-
140
- def print_simple_banner(file: TextIO | None = None) -> None:
141
- """Print just the ASCII art banner without config."""
142
- if file is None:
143
- file = sys.stdout
144
- print(format_banner(), file=file)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes