horsies 0.1.0a5__py3-none-any.whl → 0.1.0a6__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.
- horsies/core/banner.py +144 -30
- {horsies-0.1.0a5.dist-info → horsies-0.1.0a6.dist-info}/METADATA +1 -1
- {horsies-0.1.0a5.dist-info → horsies-0.1.0a6.dist-info}/RECORD +6 -6
- {horsies-0.1.0a5.dist-info → horsies-0.1.0a6.dist-info}/WHEEL +0 -0
- {horsies-0.1.0a5.dist-info → horsies-0.1.0a6.dist-info}/entry_points.txt +0 -0
- {horsies-0.1.0a5.dist-info → horsies-0.1.0a6.dist-info}/top_level.txt +0 -0
horsies/core/banner.py
CHANGED
|
@@ -9,6 +9,33 @@ if TYPE_CHECKING:
|
|
|
9
9
|
from horsies.core.app import Horsies
|
|
10
10
|
|
|
11
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
|
+
|
|
12
39
|
# Braille art of galloping horse - converted from the golden horse image
|
|
13
40
|
# Each braille character represents a 2x4 pixel block for higher resolution
|
|
14
41
|
HORSE_BRAILLE = """
|
|
@@ -28,13 +55,13 @@ HORSE_BRAILLE = """
|
|
|
28
55
|
⠚⠒⠒⠒⡶⠚⠒⠒⠒⡰⠓⠒⠒⠒⢲⠓⠒⠒⠒⢻⠿⠀⠀⠚⢿⡷⠐⠒⠒⠚⡆⠒⠒⠒⠚⣖⠒⠒⠒⠚⢶⠒⠒⠒⠚⢶⠂⠐⠒⠛
|
|
29
56
|
"""
|
|
30
57
|
|
|
31
|
-
# Figlet-style "horsies" text
|
|
58
|
+
# Figlet-style "horsies" text with version and tagline
|
|
32
59
|
LOGO_TEXT = r"""
|
|
33
60
|
__ _
|
|
34
61
|
/ /_ ____ __________(_)__ _____
|
|
35
|
-
/ __ \/ __ \/ ___/ ___/ / _ \/ ___/
|
|
36
|
-
/ / / / /_/ / / (__ ) / __(__ )
|
|
37
|
-
/_/ /_/\____/_/ /____/_/\___/____/
|
|
62
|
+
/ __ \/ __ \/ ___/ ___/ / _ \/ ___/ {version}
|
|
63
|
+
/ / / / /_/ / / (__ ) / __(__ ) distributed task queue
|
|
64
|
+
/_/ /_/\____/_/ /____/_/\___/____/ and workflow engine
|
|
38
65
|
"""
|
|
39
66
|
|
|
40
67
|
# Full banner combining horse and logo
|
|
@@ -51,14 +78,12 @@ BANNER = (
|
|
|
51
78
|
|
|
52
79
|
|
|
53
80
|
def get_version() -> str:
|
|
54
|
-
"""Get horsies version."""
|
|
81
|
+
"""Get horsies version from package metadata."""
|
|
55
82
|
try:
|
|
56
|
-
import
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return
|
|
60
|
-
except ImportError:
|
|
61
|
-
return '0.1.0'
|
|
83
|
+
from importlib.metadata import version
|
|
84
|
+
return version('horsies')
|
|
85
|
+
except Exception:
|
|
86
|
+
return 'dev'
|
|
62
87
|
|
|
63
88
|
|
|
64
89
|
def format_banner(version: str | None = None) -> str:
|
|
@@ -68,6 +93,48 @@ def format_banner(version: str | None = None) -> str:
|
|
|
68
93
|
return BANNER.format(version=f'v{version}')
|
|
69
94
|
|
|
70
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
|
+
|
|
71
138
|
def print_banner(
|
|
72
139
|
app: 'Horsies',
|
|
73
140
|
role: str = 'worker',
|
|
@@ -91,45 +158,82 @@ def print_banner(
|
|
|
91
158
|
# Build the banner
|
|
92
159
|
lines: list[str] = []
|
|
93
160
|
|
|
94
|
-
# ASCII art header
|
|
95
|
-
|
|
96
|
-
lines.append(
|
|
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)
|
|
97
169
|
|
|
98
|
-
#
|
|
99
|
-
lines
|
|
100
|
-
lines
|
|
101
|
-
lines
|
|
102
|
-
lines
|
|
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())
|
|
103
175
|
|
|
104
176
|
# Queue info
|
|
105
177
|
if app.config.queue_mode.name == 'CUSTOM' and app.config.custom_queues:
|
|
106
178
|
queues_str = ', '.join(q.name for q in app.config.custom_queues)
|
|
107
|
-
lines.append(f' .> queues: {queues_str}')
|
|
108
179
|
else:
|
|
109
|
-
|
|
180
|
+
queues_str = 'default'
|
|
181
|
+
_write_kv(lines, 'queues', queues_str)
|
|
110
182
|
|
|
111
|
-
# Broker info
|
|
183
|
+
# Broker info (mask password)
|
|
112
184
|
broker_url = app.config.broker.database_url
|
|
113
|
-
# Mask password in URL
|
|
114
185
|
if '@' in broker_url:
|
|
115
186
|
pre, post = broker_url.split('@', 1)
|
|
116
187
|
if ':' in pre:
|
|
117
188
|
scheme_user = pre.rsplit(':', 1)[0]
|
|
118
189
|
broker_url = f'{scheme_user}:****@{post}'
|
|
119
|
-
lines
|
|
190
|
+
_write_kv(lines, 'broker', broker_url)
|
|
120
191
|
|
|
121
|
-
#
|
|
192
|
+
# Cluster-wide cap
|
|
122
193
|
if hasattr(app.config, 'cluster_wide_cap') and app.config.cluster_wide_cap:
|
|
123
|
-
lines
|
|
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))
|
|
124
199
|
|
|
125
200
|
lines.append('')
|
|
126
201
|
|
|
127
|
-
#
|
|
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
|
|
128
229
|
if show_tasks:
|
|
129
230
|
task_names = app.list_tasks()
|
|
130
|
-
|
|
231
|
+
tasks_header = _color('tasks', Colors.BRIGHT_CYAN, Colors.BOLD)
|
|
232
|
+
lines.append(f'[{tasks_header}] ({len(task_names)} registered)')
|
|
131
233
|
for task_name in sorted(task_names):
|
|
132
|
-
|
|
234
|
+
bullet = _color('.', Colors.DIMMED)
|
|
235
|
+
name = _color(task_name, Colors.WHITE)
|
|
236
|
+
lines.append(f' {bullet} {name}')
|
|
133
237
|
lines.append('')
|
|
134
238
|
|
|
135
239
|
# Print everything
|
|
@@ -141,4 +245,14 @@ def print_simple_banner(file: TextIO | None = None) -> None:
|
|
|
141
245
|
"""Print just the ASCII art banner without config."""
|
|
142
246
|
if file is None:
|
|
143
247
|
file = sys.stdout
|
|
144
|
-
|
|
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)
|
|
@@ -2,7 +2,7 @@ horsies/__init__.py,sha256=rLxes5IuyzggohtXKRsHoddeyqEy0LsgwDlk09itKXk,2769
|
|
|
2
2
|
horsies/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
horsies/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
horsies/core/app.py,sha256=5HkGil3sZvJAZAerRuPulKUJO-U-vgtrmQxtrVGKc2w,22218
|
|
5
|
-
horsies/core/banner.py,sha256=
|
|
5
|
+
horsies/core/banner.py,sha256=UuPylHfAutn_Vmxso2qGT_mR0iTeq6NzwBr34iOCmHE,9547
|
|
6
6
|
horsies/core/cli.py,sha256=irlRO9pbpGPZT7r0hSi1d0mEQkAB5M54zLb-6oB6GnY,21078
|
|
7
7
|
horsies/core/errors.py,sha256=3jcu4TcI1P9d2w-Tgz1yTsh4NFPOqvrd5S58cnUEKjE,16768
|
|
8
8
|
horsies/core/logging.py,sha256=p2utHDeOHgvwtSzKTKXh0RwUNCk38ODmgQQkmsD1tWA,2934
|
|
@@ -35,8 +35,8 @@ horsies/core/workflows/__init__.py,sha256=JA-8wcYUHp-wF5OCEqJwKElT8PaZZB1G7iglDZ
|
|
|
35
35
|
horsies/core/workflows/engine.py,sha256=ByIpZnPhQvjydVZSAJuV3Yj6eLzDfZQ3KHtPw93VojI,85894
|
|
36
36
|
horsies/core/workflows/recovery.py,sha256=x-jhYwIZpBxByYyFb5g2vDMtjmUFt33HgOB2J6mgPfs,19377
|
|
37
37
|
horsies/core/workflows/registry.py,sha256=ItpjTN8yK9NNSV8Q8OwDnHdQSO-hxDzxeWAyGvExpRk,3194
|
|
38
|
-
horsies-0.1.
|
|
39
|
-
horsies-0.1.
|
|
40
|
-
horsies-0.1.
|
|
41
|
-
horsies-0.1.
|
|
42
|
-
horsies-0.1.
|
|
38
|
+
horsies-0.1.0a6.dist-info/METADATA,sha256=W3o2wh8r9BWMryc0t_Reo5D8_OwUhLtCuCaee88PCsI,1321
|
|
39
|
+
horsies-0.1.0a6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
40
|
+
horsies-0.1.0a6.dist-info/entry_points.txt,sha256=6b_OhuNbJ1ky9WSOt3UfNQXETG2YuH0lKimcibILrEE,50
|
|
41
|
+
horsies-0.1.0a6.dist-info/top_level.txt,sha256=ZQ_NurgT-yr3pE4a1svlO_VAhGJ6NFA31vJDLT1yyOA,8
|
|
42
|
+
horsies-0.1.0a6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|