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 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 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'
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
- banner = format_banner(version)
96
- lines.append(banner)
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
- # 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}')
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
- lines.append(f' .> queues: default')
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.append(f' .> broker: {broker_url}')
190
+ _write_kv(lines, 'broker', broker_url)
120
191
 
121
- # Concurrency info (if available)
192
+ # Cluster-wide cap
122
193
  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)')
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
- # Tasks section
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
- lines.append(f'[tasks] ({len(task_names)} registered)')
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
- lines.append(f' . {task_name}')
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
- print(format_banner(), file=file)
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
@@ -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=lgdM5x9aa7kfD0a91wLCSOh61zXZdlA4phq5HagPHZQ,5716
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.0a5.dist-info/METADATA,sha256=rCIhp6_TP-PZEhX630Sv970eChRIyCEldmlVXEQPtFw,1321
39
- horsies-0.1.0a5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
40
- horsies-0.1.0a5.dist-info/entry_points.txt,sha256=6b_OhuNbJ1ky9WSOt3UfNQXETG2YuH0lKimcibILrEE,50
41
- horsies-0.1.0a5.dist-info/top_level.txt,sha256=ZQ_NurgT-yr3pE4a1svlO_VAhGJ6NFA31vJDLT1yyOA,8
42
- horsies-0.1.0a5.dist-info/RECORD,,
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,,