hammad-python 0.0.11__py3-none-any.whl → 0.0.13__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.
- hammad/__init__.py +169 -56
- hammad/_core/__init__.py +1 -0
- hammad/_core/_utils/__init__.py +4 -0
- hammad/_core/_utils/_import_utils.py +182 -0
- hammad/ai/__init__.py +59 -0
- hammad/ai/_utils.py +142 -0
- hammad/ai/completions/__init__.py +44 -0
- hammad/ai/completions/client.py +729 -0
- hammad/ai/completions/create.py +686 -0
- hammad/ai/completions/types.py +711 -0
- hammad/ai/completions/utils.py +374 -0
- hammad/ai/embeddings/__init__.py +35 -0
- hammad/ai/embeddings/client/__init__.py +1 -0
- hammad/ai/embeddings/client/base_embeddings_client.py +26 -0
- hammad/ai/embeddings/client/fastembed_text_embeddings_client.py +200 -0
- hammad/ai/embeddings/client/litellm_embeddings_client.py +288 -0
- hammad/ai/embeddings/create.py +159 -0
- hammad/ai/embeddings/types.py +69 -0
- hammad/base/__init__.py +35 -0
- hammad/{based → base}/fields.py +23 -23
- hammad/{based → base}/model.py +124 -14
- hammad/base/utils.py +280 -0
- hammad/cache/__init__.py +30 -12
- hammad/cache/base_cache.py +181 -0
- hammad/cache/cache.py +169 -0
- hammad/cache/decorators.py +261 -0
- hammad/cache/file_cache.py +80 -0
- hammad/cache/ttl_cache.py +74 -0
- hammad/cli/__init__.py +10 -2
- hammad/cli/{styles/animations.py → animations.py} +79 -23
- hammad/cli/{plugins/__init__.py → plugins.py} +85 -90
- hammad/cli/styles/__init__.py +50 -0
- hammad/cli/styles/settings.py +4 -0
- hammad/configuration/__init__.py +35 -0
- hammad/{data/types/files → configuration}/configuration.py +96 -7
- hammad/data/__init__.py +14 -26
- hammad/data/collections/__init__.py +4 -2
- hammad/data/collections/collection.py +300 -75
- hammad/data/collections/vector_collection.py +118 -12
- hammad/data/databases/__init__.py +2 -2
- hammad/data/databases/database.py +383 -32
- hammad/json/__init__.py +2 -2
- hammad/logging/__init__.py +13 -5
- hammad/logging/decorators.py +404 -2
- hammad/logging/logger.py +442 -22
- hammad/multimodal/__init__.py +24 -0
- hammad/{data/types/files → multimodal}/audio.py +21 -6
- hammad/{data/types/files → multimodal}/image.py +5 -5
- hammad/multithreading/__init__.py +304 -0
- hammad/pydantic/__init__.py +2 -2
- hammad/pydantic/converters.py +1 -1
- hammad/pydantic/models/__init__.py +2 -2
- hammad/text/__init__.py +59 -14
- hammad/text/converters.py +723 -0
- hammad/text/{utils/markdown/formatting.py → markdown.py} +25 -23
- hammad/text/text.py +12 -14
- hammad/types/__init__.py +11 -0
- hammad/{data/types/files → types}/file.py +18 -18
- hammad/typing/__init__.py +138 -84
- hammad/web/__init__.py +3 -2
- hammad/web/models.py +245 -0
- hammad/web/search/client.py +75 -23
- hammad/web/utils.py +14 -5
- hammad/yaml/__init__.py +2 -2
- hammad/yaml/converters.py +1 -1
- {hammad_python-0.0.11.dist-info → hammad_python-0.0.13.dist-info}/METADATA +4 -1
- hammad_python-0.0.13.dist-info/RECORD +85 -0
- hammad/based/__init__.py +0 -52
- hammad/based/utils.py +0 -455
- hammad/cache/_cache.py +0 -746
- hammad/data/types/__init__.py +0 -33
- hammad/data/types/files/__init__.py +0 -1
- hammad/data/types/files/document.py +0 -195
- hammad/text/utils/__init__.py +0 -1
- hammad/text/utils/converters.py +0 -229
- hammad/text/utils/markdown/__init__.py +0 -1
- hammad/text/utils/markdown/converters.py +0 -506
- hammad_python-0.0.11.dist-info/RECORD +0 -65
- {hammad_python-0.0.11.dist-info → hammad_python-0.0.13.dist-info}/WHEEL +0 -0
- {hammad_python-0.0.11.dist-info → hammad_python-0.0.13.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
"""hammad.cache.ttl_cache"""
|
2
|
+
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from typing import Any, Literal, OrderedDict, Tuple
|
5
|
+
import time
|
6
|
+
|
7
|
+
from .base_cache import BaseCache
|
8
|
+
|
9
|
+
__all__ = ("TTLCache",)
|
10
|
+
|
11
|
+
|
12
|
+
@dataclass
|
13
|
+
class TTLCache(BaseCache):
|
14
|
+
"""
|
15
|
+
Thread-safe TTL cache implementation with LRU eviction.
|
16
|
+
|
17
|
+
Uses OrderedDict for efficient LRU tracking and automatic cleanup
|
18
|
+
of expired entries on access.
|
19
|
+
"""
|
20
|
+
|
21
|
+
maxsize: int = 1000
|
22
|
+
ttl: int = 3600
|
23
|
+
type: Literal["ttl"] = "ttl"
|
24
|
+
|
25
|
+
def __post_init__(self):
|
26
|
+
"""Initialize TTL cache after dataclass initialization."""
|
27
|
+
super().__post_init__()
|
28
|
+
self._cache: OrderedDict[str, Tuple[Any, float]] = OrderedDict()
|
29
|
+
|
30
|
+
def __contains__(self, key: str) -> bool:
|
31
|
+
"""Check if key exists and is not expired."""
|
32
|
+
if key in self._cache:
|
33
|
+
_value, timestamp = self._cache[key]
|
34
|
+
if time.time() - timestamp <= self.ttl:
|
35
|
+
self._cache.move_to_end(key)
|
36
|
+
return True
|
37
|
+
else:
|
38
|
+
# Expired, remove it
|
39
|
+
del self._cache[key]
|
40
|
+
return False
|
41
|
+
|
42
|
+
def __getitem__(self, key: str) -> Any:
|
43
|
+
"""Get value for key if not expired."""
|
44
|
+
if key in self:
|
45
|
+
return self._cache[key][0]
|
46
|
+
raise KeyError(key)
|
47
|
+
|
48
|
+
def __setitem__(self, key: str, value: Any) -> None:
|
49
|
+
"""Set value with current timestamp."""
|
50
|
+
if len(self._cache) >= self.maxsize and key not in self._cache:
|
51
|
+
self._cleanup_expired()
|
52
|
+
|
53
|
+
if len(self._cache) >= self.maxsize:
|
54
|
+
self._cache.popitem(last=False)
|
55
|
+
|
56
|
+
self._cache[key] = (value, time.time())
|
57
|
+
self._cache.move_to_end(key)
|
58
|
+
|
59
|
+
def _cleanup_expired(self) -> None:
|
60
|
+
"""Remove all expired entries."""
|
61
|
+
current_time = time.time()
|
62
|
+
|
63
|
+
expired_keys = [
|
64
|
+
k
|
65
|
+
for k, (_, ts) in list(self._cache.items())
|
66
|
+
if current_time - ts > self.ttl
|
67
|
+
]
|
68
|
+
for k in expired_keys:
|
69
|
+
if k in self._cache:
|
70
|
+
del self._cache[k]
|
71
|
+
|
72
|
+
def clear(self) -> None:
|
73
|
+
"""Clear all cached items."""
|
74
|
+
self._cache.clear()
|
hammad/cli/__init__.py
CHANGED
@@ -4,20 +4,28 @@ Contains resources for styling rendered CLI content as well
|
|
4
4
|
as extensions / utilities for creating CLI interfaces."""
|
5
5
|
|
6
6
|
from typing import TYPE_CHECKING
|
7
|
-
from ..
|
7
|
+
from .._core._utils._import_utils import _auto_create_getattr_loader
|
8
8
|
|
9
9
|
if TYPE_CHECKING:
|
10
10
|
from .plugins import print, input, animate
|
11
|
+
from .styles.settings import (
|
12
|
+
CLIStyleRenderableSettings,
|
13
|
+
CLIStyleBackgroundSettings,
|
14
|
+
CLIStyleLiveSettings,
|
15
|
+
)
|
11
16
|
|
12
17
|
|
13
18
|
__all__ = (
|
14
19
|
"print",
|
15
20
|
"input",
|
16
21
|
"animate",
|
22
|
+
"CLIStyleRenderableSettings",
|
23
|
+
"CLIStyleBackgroundSettings",
|
24
|
+
"CLIStyleLiveSettings",
|
17
25
|
)
|
18
26
|
|
19
27
|
|
20
|
-
__getattr__ =
|
28
|
+
__getattr__ = _auto_create_getattr_loader(__all__)
|
21
29
|
|
22
30
|
|
23
31
|
def __dir__() -> list[str]:
|
@@ -14,7 +14,7 @@ if TYPE_CHECKING:
|
|
14
14
|
from rich.text import Text
|
15
15
|
from rich.panel import Panel
|
16
16
|
|
17
|
-
from .types import (
|
17
|
+
from .styles.types import (
|
18
18
|
CLIStyleColorName,
|
19
19
|
)
|
20
20
|
|
@@ -133,16 +133,29 @@ class CLIAnimation:
|
|
133
133
|
self,
|
134
134
|
duration: Optional[float] = None,
|
135
135
|
refresh_rate: int = 20,
|
136
|
+
transient: bool = True,
|
137
|
+
auto_refresh: bool = True,
|
138
|
+
console: Optional["Console"] = None,
|
139
|
+
screen: bool = False,
|
140
|
+
vertical_overflow: str = "ellipsis",
|
136
141
|
) -> None:
|
137
142
|
"""Animate this effect for the specified duration using Live."""
|
138
143
|
animate_duration = duration or self.duration or 3.0
|
139
144
|
rich_classes = _get_rich_animation_classes()
|
140
145
|
Console = rich_classes["Console"]
|
141
146
|
Live = rich_classes["Live"]
|
142
|
-
|
147
|
+
|
148
|
+
# Use provided console or create new one
|
149
|
+
live_console = console or Console()
|
143
150
|
|
144
151
|
with Live(
|
145
|
-
self,
|
152
|
+
self,
|
153
|
+
console=live_console,
|
154
|
+
refresh_per_second=refresh_rate,
|
155
|
+
transient=transient,
|
156
|
+
auto_refresh=auto_refresh,
|
157
|
+
screen=screen,
|
158
|
+
vertical_overflow=vertical_overflow,
|
146
159
|
) as live:
|
147
160
|
start = time.time()
|
148
161
|
while time.time() - start < animate_duration:
|
@@ -157,16 +170,17 @@ class CLIFlashingAnimation(CLIAnimation):
|
|
157
170
|
renderable,
|
158
171
|
speed: float = 0.5,
|
159
172
|
colors: Optional[List[CLIStyleColorName]] = None,
|
173
|
+
on_color: CLIStyleColorName = "white",
|
174
|
+
off_color: CLIStyleColorName = "dim white",
|
160
175
|
duration: Optional[float] = None,
|
161
176
|
):
|
162
177
|
super().__init__(renderable, duration)
|
163
178
|
self.speed = speed
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
]
|
179
|
+
# If colors is provided, use it; otherwise use on_color/off_color
|
180
|
+
if colors is not None:
|
181
|
+
self.colors = colors
|
182
|
+
else:
|
183
|
+
self.colors = [on_color, off_color]
|
170
184
|
|
171
185
|
def apply(self, console, options):
|
172
186
|
rich_classes = _get_rich_animation_classes()
|
@@ -273,22 +287,38 @@ class CLITypingAnimation(CLIAnimation):
|
|
273
287
|
"""Typewriter effect."""
|
274
288
|
|
275
289
|
def __init__(
|
276
|
-
self,
|
290
|
+
self,
|
291
|
+
text: str,
|
292
|
+
speed: float = 0.05,
|
293
|
+
typing_speed: Optional[float] = None,
|
294
|
+
cursor: str = "█",
|
295
|
+
show_cursor: bool = True,
|
296
|
+
duration: Optional[float] = None,
|
277
297
|
):
|
278
298
|
super().__init__(text, duration)
|
279
299
|
self.text = text
|
280
|
-
|
300
|
+
# Use typing_speed if provided, otherwise use speed
|
301
|
+
self.speed = typing_speed if typing_speed is not None else speed
|
302
|
+
self.cursor = cursor
|
303
|
+
self.show_cursor = show_cursor
|
281
304
|
|
282
305
|
def apply(self, console: "Console", options: "ConsoleOptions") -> "RenderResult":
|
306
|
+
rich_classes = _get_rich_animation_classes()
|
307
|
+
Text = rich_classes["Text"]
|
308
|
+
|
283
309
|
# Calculate how many characters to show
|
284
310
|
chars_to_show = int(self.time_elapsed / self.speed)
|
285
311
|
chars_to_show = min(chars_to_show, len(self.text))
|
286
312
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
313
|
+
if chars_to_show < len(self.text):
|
314
|
+
# Still typing - show cursor if enabled
|
315
|
+
text_content = self.text[:chars_to_show]
|
316
|
+
if self.show_cursor:
|
317
|
+
text_content += self.cursor
|
318
|
+
yield Text(text_content)
|
319
|
+
else:
|
320
|
+
# Finished typing - show complete text without cursor
|
321
|
+
yield Text(self.text)
|
292
322
|
|
293
323
|
|
294
324
|
class CLISpinningAnimation(CLIAnimation):
|
@@ -396,6 +426,8 @@ def animate_flashing(
|
|
396
426
|
speed: float = 0.5,
|
397
427
|
on_color: CLIStyleColorName = "white",
|
398
428
|
off_color: CLIStyleColorName = "dim white",
|
429
|
+
refresh_rate: int = 20,
|
430
|
+
transient: bool = True,
|
399
431
|
) -> None:
|
400
432
|
"""Create and run a flashing animation on any renderable.
|
401
433
|
|
@@ -405,6 +437,8 @@ def animate_flashing(
|
|
405
437
|
speed: Speed of the flashing effect (defaults to 0.5)
|
406
438
|
on_color: Color when flashing "on" (defaults to "white")
|
407
439
|
off_color: Color when flashing "off" (defaults to "dim white")
|
440
|
+
refresh_rate: Refresh rate per second (defaults to 20)
|
441
|
+
transient: Whether to clear animation after completion (defaults to True)
|
408
442
|
|
409
443
|
Examples:
|
410
444
|
>>> animate_flashing("Alert!", duration=3.0, speed=0.3)
|
@@ -417,7 +451,7 @@ def animate_flashing(
|
|
417
451
|
on_color=on_color,
|
418
452
|
off_color=off_color,
|
419
453
|
)
|
420
|
-
animation.animate()
|
454
|
+
animation.animate(duration=duration, refresh_rate=refresh_rate)
|
421
455
|
|
422
456
|
|
423
457
|
def animate_pulsing(
|
@@ -426,6 +460,8 @@ def animate_pulsing(
|
|
426
460
|
speed: float = 1.0,
|
427
461
|
min_opacity: float = 0.3,
|
428
462
|
max_opacity: float = 1.0,
|
463
|
+
refresh_rate: int = 20,
|
464
|
+
transient: bool = True,
|
429
465
|
) -> None:
|
430
466
|
"""Create and run a pulsing animation on any renderable.
|
431
467
|
|
@@ -435,6 +471,8 @@ def animate_pulsing(
|
|
435
471
|
speed: Speed of the pulsing effect (defaults to 1.0)
|
436
472
|
min_opacity: Minimum opacity during pulse (defaults to 0.3)
|
437
473
|
max_opacity: Maximum opacity during pulse (defaults to 1.0)
|
474
|
+
refresh_rate: Refresh rate per second (defaults to 20)
|
475
|
+
transient: Whether to clear animation after completion (defaults to True)
|
438
476
|
|
439
477
|
Examples:
|
440
478
|
>>> animate_pulsing("Loading...", duration=5.0, speed=2.0)
|
@@ -447,7 +485,7 @@ def animate_pulsing(
|
|
447
485
|
min_opacity=min_opacity,
|
448
486
|
max_opacity=max_opacity,
|
449
487
|
)
|
450
|
-
animation.animate()
|
488
|
+
animation.animate(duration=duration, refresh_rate=refresh_rate)
|
451
489
|
|
452
490
|
|
453
491
|
def animate_shaking(
|
@@ -455,6 +493,8 @@ def animate_shaking(
|
|
455
493
|
duration: Optional[float] = None,
|
456
494
|
intensity: int = 2,
|
457
495
|
speed: float = 10.0,
|
496
|
+
refresh_rate: int = 20,
|
497
|
+
transient: bool = True,
|
458
498
|
) -> None:
|
459
499
|
"""Create and run a shaking animation on any renderable.
|
460
500
|
|
@@ -463,6 +503,8 @@ def animate_shaking(
|
|
463
503
|
duration: Duration of the animation in seconds (defaults to 2.0)
|
464
504
|
intensity: Intensity of the shake effect (defaults to 2)
|
465
505
|
speed: Speed of the shaking motion (defaults to 10.0)
|
506
|
+
refresh_rate: Refresh rate per second (defaults to 20)
|
507
|
+
transient: Whether to clear animation after completion (defaults to True)
|
466
508
|
|
467
509
|
Examples:
|
468
510
|
>>> animate_shaking("Error!", duration=1.5, intensity=3)
|
@@ -471,7 +513,7 @@ def animate_shaking(
|
|
471
513
|
animation = CLIShakingAnimation(
|
472
514
|
renderable, duration=duration, intensity=intensity, speed=speed
|
473
515
|
)
|
474
|
-
animation.animate()
|
516
|
+
animation.animate(duration=duration, refresh_rate=refresh_rate)
|
475
517
|
|
476
518
|
|
477
519
|
def animate_spinning(
|
@@ -480,6 +522,8 @@ def animate_spinning(
|
|
480
522
|
frames: Optional[List[str]] = None,
|
481
523
|
speed: float = 0.1,
|
482
524
|
prefix: bool = True,
|
525
|
+
refresh_rate: int = 20,
|
526
|
+
transient: bool = True,
|
483
527
|
) -> None:
|
484
528
|
"""Create and run a spinning animation on any renderable.
|
485
529
|
|
@@ -489,6 +533,8 @@ def animate_spinning(
|
|
489
533
|
frames: List of spinner frames (defaults to ["⋅", "•", "●", "◉", "●", "•"])
|
490
534
|
speed: Speed between frame changes (defaults to 0.1)
|
491
535
|
prefix: Whether to show spinner before text (defaults to True)
|
536
|
+
refresh_rate: Refresh rate per second (defaults to 20)
|
537
|
+
transient: Whether to clear animation after completion (defaults to True)
|
492
538
|
|
493
539
|
Examples:
|
494
540
|
>>> animate_spinning("Processing...", duration=10.0, speed=0.2)
|
@@ -497,11 +543,15 @@ def animate_spinning(
|
|
497
543
|
animation = CLISpinningAnimation(
|
498
544
|
renderable, duration=duration, frames=frames, speed=speed, prefix=prefix
|
499
545
|
)
|
500
|
-
animation.animate()
|
546
|
+
animation.animate(duration=duration, refresh_rate=refresh_rate)
|
501
547
|
|
502
548
|
|
503
549
|
def animate_rainbow(
|
504
|
-
renderable: "RenderableType",
|
550
|
+
renderable: "RenderableType",
|
551
|
+
duration: Optional[float] = None,
|
552
|
+
speed: float = 0.5,
|
553
|
+
refresh_rate: int = 20,
|
554
|
+
transient: bool = True,
|
505
555
|
) -> None:
|
506
556
|
"""Create and run a rainbow animation on any renderable.
|
507
557
|
|
@@ -509,13 +559,15 @@ def animate_rainbow(
|
|
509
559
|
renderable: The object to animate (text, panel, etc.)
|
510
560
|
duration: Duration of the animation in seconds (defaults to 2.0)
|
511
561
|
speed: Speed of the color cycling effect (defaults to 0.5)
|
562
|
+
refresh_rate: Refresh rate per second (defaults to 20)
|
563
|
+
transient: Whether to clear animation after completion (defaults to True)
|
512
564
|
|
513
565
|
Examples:
|
514
566
|
>>> animate_rainbow("Colorful Text!", duration=4.0, speed=1.0)
|
515
567
|
>>> animate_rainbow(Panel("Rainbow Panel"), speed=0.3)
|
516
568
|
"""
|
517
569
|
animation = CLIRainbowAnimation(renderable, duration=duration, speed=speed)
|
518
|
-
animation.animate()
|
570
|
+
animation.animate(duration=duration, refresh_rate=refresh_rate)
|
519
571
|
|
520
572
|
|
521
573
|
def animate_typing(
|
@@ -524,6 +576,8 @@ def animate_typing(
|
|
524
576
|
typing_speed: float = 0.05,
|
525
577
|
cursor: str = "▌",
|
526
578
|
show_cursor: bool = True,
|
579
|
+
refresh_rate: int = 20,
|
580
|
+
transient: bool = True,
|
527
581
|
) -> None:
|
528
582
|
"""Create and run a typewriter animation.
|
529
583
|
|
@@ -533,6 +587,8 @@ def animate_typing(
|
|
533
587
|
typing_speed: Speed between character reveals (defaults to 0.05)
|
534
588
|
cursor: Cursor character to show (defaults to "▌")
|
535
589
|
show_cursor: Whether to show the typing cursor (defaults to True)
|
590
|
+
refresh_rate: Refresh rate per second (defaults to 20)
|
591
|
+
transient: Whether to clear animation after completion (defaults to True)
|
536
592
|
|
537
593
|
Examples:
|
538
594
|
>>> animate_typing("Hello, World!", typing_speed=0.1)
|
@@ -545,4 +601,4 @@ def animate_typing(
|
|
545
601
|
cursor=cursor,
|
546
602
|
show_cursor=show_cursor,
|
547
603
|
)
|
548
|
-
animation.animate()
|
604
|
+
animation.animate(duration=duration, refresh_rate=refresh_rate)
|
@@ -30,7 +30,7 @@ if TYPE_CHECKING:
|
|
30
30
|
from rich.prompt import Prompt, Confirm
|
31
31
|
from prompt_toolkit import prompt as pt_prompt
|
32
32
|
from prompt_toolkit.completion import WordCompleter
|
33
|
-
from
|
33
|
+
from .animations import (
|
34
34
|
CLIFlashingAnimation,
|
35
35
|
CLIPulsingAnimation,
|
36
36
|
CLIShakingAnimation,
|
@@ -39,17 +39,17 @@ if TYPE_CHECKING:
|
|
39
39
|
CLIRainbowAnimation,
|
40
40
|
RainbowPreset,
|
41
41
|
)
|
42
|
-
from
|
42
|
+
from .styles.types import (
|
43
43
|
CLIStyleType,
|
44
44
|
CLIStyleBackgroundType,
|
45
45
|
CLIStyleColorName,
|
46
46
|
)
|
47
|
-
from
|
47
|
+
from .styles.settings import (
|
48
48
|
CLIStyleRenderableSettings,
|
49
49
|
CLIStyleBackgroundSettings,
|
50
50
|
CLIStyleLiveSettings,
|
51
51
|
)
|
52
|
-
from
|
52
|
+
from .styles.utils import (
|
53
53
|
live_render,
|
54
54
|
style_renderable,
|
55
55
|
)
|
@@ -98,7 +98,7 @@ def _get_prompt_toolkit():
|
|
98
98
|
def _get_style_utils():
|
99
99
|
"""Lazy import for style utilities"""
|
100
100
|
if "style_utils" not in _IMPORT_CACHE:
|
101
|
-
from
|
101
|
+
from .styles.utils import live_render, style_renderable
|
102
102
|
|
103
103
|
_IMPORT_CACHE["style_utils"] = (live_render, style_renderable)
|
104
104
|
return _IMPORT_CACHE["style_utils"]
|
@@ -107,7 +107,7 @@ def _get_style_utils():
|
|
107
107
|
def _get_animation_classes():
|
108
108
|
"""Lazy import for animation classes"""
|
109
109
|
if "animations" not in _IMPORT_CACHE:
|
110
|
-
from
|
110
|
+
from .animations import (
|
111
111
|
CLIFlashingAnimation,
|
112
112
|
CLIPulsingAnimation,
|
113
113
|
CLIShakingAnimation,
|
@@ -220,7 +220,7 @@ def print(
|
|
220
220
|
if live is not None:
|
221
221
|
if isinstance(live, int):
|
222
222
|
# If live is an integer, treat it as duration in seconds
|
223
|
-
from
|
223
|
+
from .styles.settings import CLIStyleLiveSettings
|
224
224
|
|
225
225
|
live_settings: CLIStyleLiveSettings = {
|
226
226
|
"duration": float(live),
|
@@ -433,6 +433,9 @@ def _collect_fields_sequentially(schema: Any, console) -> Dict[str, Any]:
|
|
433
433
|
|
434
434
|
# Handle dataclasses
|
435
435
|
elif hasattr(schema, "__dataclass_fields__"):
|
436
|
+
from ..typing import get_type_description
|
437
|
+
import dataclasses
|
438
|
+
|
436
439
|
fields_info = schema.__dataclass_fields__
|
437
440
|
console.print(
|
438
441
|
f"\n[bold blue]Entering data for {schema.__name__}:[/bold blue]"
|
@@ -443,13 +446,21 @@ def _collect_fields_sequentially(schema: Any, console) -> Dict[str, Any]:
|
|
443
446
|
default = getattr(field_info, "default", None)
|
444
447
|
|
445
448
|
prompt_text = f" {field_name}"
|
446
|
-
if default is not None:
|
449
|
+
if default is not None and default is not dataclasses.MISSING:
|
447
450
|
prompt_text += f" (default: {default})"
|
448
|
-
|
451
|
+
elif hasattr(field_type, "__name__"):
|
452
|
+
prompt_text += f" ({field_type.__name__})"
|
453
|
+
else:
|
454
|
+
prompt_text += f" ({get_type_description(field_type)})"
|
455
|
+
prompt_text += ""
|
449
456
|
|
450
457
|
Prompt, _ = _get_rich_prompts()
|
451
458
|
value = Prompt.ask(prompt_text)
|
452
|
-
if
|
459
|
+
if (
|
460
|
+
not value
|
461
|
+
and default is not None
|
462
|
+
and default is not dataclasses.MISSING
|
463
|
+
):
|
453
464
|
result[field_name] = default
|
454
465
|
else:
|
455
466
|
try:
|
@@ -650,101 +661,80 @@ def input(
|
|
650
661
|
raise InputError(f"Input error: {e}")
|
651
662
|
|
652
663
|
|
653
|
-
@overload
|
654
664
|
def animate(
|
655
|
-
renderable: "RenderableType",
|
656
|
-
type: Literal["flashing"],
|
665
|
+
renderable: "RenderableType | str",
|
666
|
+
type: Literal["flashing", "pulsing", "shaking", "typing", "spinning", "rainbow"],
|
657
667
|
duration: Optional[float] = None,
|
668
|
+
# Flashing animation parameters
|
658
669
|
speed: float = 0.5,
|
659
670
|
colors: "Optional[List[CLIStyleColorName]]" = None,
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
@overload
|
664
|
-
def animate(
|
665
|
-
renderable: "RenderableType",
|
666
|
-
type: Literal["pulsing"],
|
667
|
-
duration: Optional[float] = None,
|
668
|
-
speed: float = 2.0,
|
671
|
+
on_color: "CLIStyleColorName" = "white",
|
672
|
+
off_color: "CLIStyleColorName" = "dim white",
|
673
|
+
# Pulsing animation parameters
|
669
674
|
min_opacity: float = 0.3,
|
670
675
|
max_opacity: float = 1.0,
|
671
676
|
color: "CLIStyleColorName" = "white",
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
@overload
|
676
|
-
def animate(
|
677
|
-
renderable: "RenderableType",
|
678
|
-
type: Literal["shaking"],
|
679
|
-
duration: Optional[float] = None,
|
677
|
+
# Shaking animation parameters
|
680
678
|
intensity: int = 1,
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
def animate(
|
687
|
-
text: str,
|
688
|
-
type: Literal["typing"],
|
689
|
-
duration: Optional[float] = None,
|
690
|
-
speed: float = 0.05,
|
691
|
-
) -> None: ...
|
692
|
-
|
693
|
-
|
694
|
-
@overload
|
695
|
-
def animate(
|
696
|
-
renderable: "RenderableType",
|
697
|
-
type: Literal["spinning"],
|
698
|
-
duration: Optional[float] = None,
|
679
|
+
# Typing animation parameters
|
680
|
+
typing_speed: Optional[float] = None,
|
681
|
+
cursor: str = "█",
|
682
|
+
show_cursor: bool = True,
|
683
|
+
# Spinning animation parameters
|
699
684
|
frames: Optional[List[str]] = None,
|
700
|
-
speed: float = 0.1,
|
701
685
|
prefix: bool = True,
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
duration: Optional[float] = None,
|
710
|
-
speed: float = 0.5,
|
711
|
-
colors: "RainbowPreset | List[CLIStyleColorName] | None" = None,
|
712
|
-
) -> None: ...
|
713
|
-
|
714
|
-
|
715
|
-
def animate(
|
716
|
-
renderable: "RenderableType | str",
|
717
|
-
type: Literal["flashing", "pulsing", "shaking", "typing", "spinning", "rainbow"],
|
718
|
-
duration: Optional[float] = None,
|
719
|
-
**kwargs,
|
686
|
+
# Rich.Live parameters
|
687
|
+
refresh_rate: int = 20,
|
688
|
+
transient: bool = True,
|
689
|
+
auto_refresh: bool = True,
|
690
|
+
console: Optional["Console"] = None,
|
691
|
+
screen: bool = False,
|
692
|
+
vertical_overflow: str = "ellipsis",
|
720
693
|
) -> None:
|
721
694
|
"""Create and run an animation based on the specified type.
|
722
695
|
|
723
696
|
Args:
|
724
|
-
type: The type of animation to create
|
725
697
|
renderable: The object to animate (text, panel, etc.)
|
698
|
+
type: The type of animation to create
|
726
699
|
duration: Duration of the animation in seconds (defaults to 2.0)
|
727
|
-
|
700
|
+
speed: Animation speed (used by flashing, pulsing, shaking, spinning, rainbow)
|
701
|
+
colors: Color list (used by flashing, rainbow)
|
702
|
+
on_color: Color when flashing "on" (used by flashing)
|
703
|
+
off_color: Color when flashing "off" (used by flashing)
|
704
|
+
min_opacity: Minimum opacity for pulsing animation
|
705
|
+
max_opacity: Maximum opacity for pulsing animation
|
706
|
+
color: Color for pulsing animation
|
707
|
+
intensity: Shaking intensity for shaking animation
|
708
|
+
typing_speed: Speed for typing animation (used by typing)
|
709
|
+
cursor: Cursor character for typing animation (used by typing)
|
710
|
+
show_cursor: Whether to show cursor for typing animation (used by typing)
|
711
|
+
frames: Custom frames for spinning animation
|
712
|
+
prefix: Whether to show spinner as prefix for spinning animation
|
713
|
+
refresh_rate: Refresh rate per second for Live rendering
|
714
|
+
transient: Whether to clear animation after completion
|
715
|
+
auto_refresh: Whether to auto-refresh the display
|
716
|
+
console: Console to use for rendering
|
717
|
+
screen: Whether to use alternate screen buffer
|
718
|
+
vertical_overflow: How to handle vertical overflow
|
728
719
|
|
729
720
|
Examples:
|
730
|
-
>>> animate("
|
731
|
-
>>> animate(
|
732
|
-
>>> animate("
|
733
|
-
>>> animate("
|
721
|
+
>>> animate("Hello!", type="flashing", duration=3.0, speed=0.3)
|
722
|
+
>>> animate(Panel("Loading"), type="pulsing", min_opacity=0.1)
|
723
|
+
>>> animate("Hello World!", type="typing", typing_speed=0.1)
|
724
|
+
>>> animate("Colorful!", type="rainbow", colors=["red", "blue"])
|
734
725
|
"""
|
735
726
|
animations = _get_animation_classes()
|
736
727
|
|
737
728
|
if type == "flashing":
|
738
|
-
speed = kwargs.get("speed", 0.5)
|
739
|
-
colors = kwargs.get("colors", None)
|
740
729
|
animation = animations["CLIFlashingAnimation"](
|
741
|
-
renderable,
|
730
|
+
renderable,
|
731
|
+
speed=speed,
|
732
|
+
colors=colors,
|
733
|
+
on_color=on_color,
|
734
|
+
off_color=off_color,
|
735
|
+
duration=duration,
|
742
736
|
)
|
743
737
|
elif type == "pulsing":
|
744
|
-
speed = kwargs.get("speed", 2.0)
|
745
|
-
min_opacity = kwargs.get("min_opacity", 0.3)
|
746
|
-
max_opacity = kwargs.get("max_opacity", 1.0)
|
747
|
-
color = kwargs.get("color", "white")
|
748
738
|
animation = animations["CLIPulsingAnimation"](
|
749
739
|
renderable,
|
750
740
|
speed=speed,
|
@@ -754,33 +744,38 @@ def animate(
|
|
754
744
|
duration=duration,
|
755
745
|
)
|
756
746
|
elif type == "shaking":
|
757
|
-
intensity = kwargs.get("intensity", 1)
|
758
|
-
speed = kwargs.get("speed", 0.1)
|
759
747
|
animation = animations["CLIShakingAnimation"](
|
760
748
|
renderable, intensity=intensity, speed=speed, duration=duration
|
761
749
|
)
|
762
750
|
elif type == "typing":
|
763
|
-
speed = kwargs.get("speed", 0.05)
|
764
751
|
animation = animations["CLITypingAnimation"](
|
765
|
-
renderable,
|
752
|
+
renderable,
|
753
|
+
speed=speed,
|
754
|
+
typing_speed=typing_speed,
|
755
|
+
cursor=cursor,
|
756
|
+
show_cursor=show_cursor,
|
757
|
+
duration=duration,
|
766
758
|
)
|
767
759
|
elif type == "spinning":
|
768
|
-
frames = kwargs.get("frames", None)
|
769
|
-
speed = kwargs.get("speed", 0.1)
|
770
|
-
prefix = kwargs.get("prefix", True)
|
771
760
|
animation = animations["CLISpinningAnimation"](
|
772
761
|
renderable, frames=frames, speed=speed, prefix=prefix, duration=duration
|
773
762
|
)
|
774
763
|
elif type == "rainbow":
|
775
|
-
speed = kwargs.get("speed", 0.5)
|
776
|
-
colors = kwargs.get("colors", None)
|
777
764
|
animation = animations["CLIRainbowAnimation"](
|
778
765
|
renderable, speed=speed, colors=colors, duration=duration
|
779
766
|
)
|
780
767
|
else:
|
781
768
|
raise ValueError(f"Unknown animation type: {type}")
|
782
769
|
|
783
|
-
animation.animate(
|
770
|
+
animation.animate(
|
771
|
+
duration=duration,
|
772
|
+
refresh_rate=refresh_rate,
|
773
|
+
transient=transient,
|
774
|
+
auto_refresh=auto_refresh,
|
775
|
+
console=console,
|
776
|
+
screen=screen,
|
777
|
+
vertical_overflow=vertical_overflow,
|
778
|
+
)
|
784
779
|
|
785
780
|
|
786
781
|
__all__ = ("print", "input", "animate")
|