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.
Files changed (80) hide show
  1. hammad/__init__.py +169 -56
  2. hammad/_core/__init__.py +1 -0
  3. hammad/_core/_utils/__init__.py +4 -0
  4. hammad/_core/_utils/_import_utils.py +182 -0
  5. hammad/ai/__init__.py +59 -0
  6. hammad/ai/_utils.py +142 -0
  7. hammad/ai/completions/__init__.py +44 -0
  8. hammad/ai/completions/client.py +729 -0
  9. hammad/ai/completions/create.py +686 -0
  10. hammad/ai/completions/types.py +711 -0
  11. hammad/ai/completions/utils.py +374 -0
  12. hammad/ai/embeddings/__init__.py +35 -0
  13. hammad/ai/embeddings/client/__init__.py +1 -0
  14. hammad/ai/embeddings/client/base_embeddings_client.py +26 -0
  15. hammad/ai/embeddings/client/fastembed_text_embeddings_client.py +200 -0
  16. hammad/ai/embeddings/client/litellm_embeddings_client.py +288 -0
  17. hammad/ai/embeddings/create.py +159 -0
  18. hammad/ai/embeddings/types.py +69 -0
  19. hammad/base/__init__.py +35 -0
  20. hammad/{based → base}/fields.py +23 -23
  21. hammad/{based → base}/model.py +124 -14
  22. hammad/base/utils.py +280 -0
  23. hammad/cache/__init__.py +30 -12
  24. hammad/cache/base_cache.py +181 -0
  25. hammad/cache/cache.py +169 -0
  26. hammad/cache/decorators.py +261 -0
  27. hammad/cache/file_cache.py +80 -0
  28. hammad/cache/ttl_cache.py +74 -0
  29. hammad/cli/__init__.py +10 -2
  30. hammad/cli/{styles/animations.py → animations.py} +79 -23
  31. hammad/cli/{plugins/__init__.py → plugins.py} +85 -90
  32. hammad/cli/styles/__init__.py +50 -0
  33. hammad/cli/styles/settings.py +4 -0
  34. hammad/configuration/__init__.py +35 -0
  35. hammad/{data/types/files → configuration}/configuration.py +96 -7
  36. hammad/data/__init__.py +14 -26
  37. hammad/data/collections/__init__.py +4 -2
  38. hammad/data/collections/collection.py +300 -75
  39. hammad/data/collections/vector_collection.py +118 -12
  40. hammad/data/databases/__init__.py +2 -2
  41. hammad/data/databases/database.py +383 -32
  42. hammad/json/__init__.py +2 -2
  43. hammad/logging/__init__.py +13 -5
  44. hammad/logging/decorators.py +404 -2
  45. hammad/logging/logger.py +442 -22
  46. hammad/multimodal/__init__.py +24 -0
  47. hammad/{data/types/files → multimodal}/audio.py +21 -6
  48. hammad/{data/types/files → multimodal}/image.py +5 -5
  49. hammad/multithreading/__init__.py +304 -0
  50. hammad/pydantic/__init__.py +2 -2
  51. hammad/pydantic/converters.py +1 -1
  52. hammad/pydantic/models/__init__.py +2 -2
  53. hammad/text/__init__.py +59 -14
  54. hammad/text/converters.py +723 -0
  55. hammad/text/{utils/markdown/formatting.py → markdown.py} +25 -23
  56. hammad/text/text.py +12 -14
  57. hammad/types/__init__.py +11 -0
  58. hammad/{data/types/files → types}/file.py +18 -18
  59. hammad/typing/__init__.py +138 -84
  60. hammad/web/__init__.py +3 -2
  61. hammad/web/models.py +245 -0
  62. hammad/web/search/client.py +75 -23
  63. hammad/web/utils.py +14 -5
  64. hammad/yaml/__init__.py +2 -2
  65. hammad/yaml/converters.py +1 -1
  66. {hammad_python-0.0.11.dist-info → hammad_python-0.0.13.dist-info}/METADATA +4 -1
  67. hammad_python-0.0.13.dist-info/RECORD +85 -0
  68. hammad/based/__init__.py +0 -52
  69. hammad/based/utils.py +0 -455
  70. hammad/cache/_cache.py +0 -746
  71. hammad/data/types/__init__.py +0 -33
  72. hammad/data/types/files/__init__.py +0 -1
  73. hammad/data/types/files/document.py +0 -195
  74. hammad/text/utils/__init__.py +0 -1
  75. hammad/text/utils/converters.py +0 -229
  76. hammad/text/utils/markdown/__init__.py +0 -1
  77. hammad/text/utils/markdown/converters.py +0 -506
  78. hammad_python-0.0.11.dist-info/RECORD +0 -65
  79. {hammad_python-0.0.11.dist-info → hammad_python-0.0.13.dist-info}/WHEEL +0 -0
  80. {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 ..based.utils import auto_create_lazy_loader
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__ = auto_create_lazy_loader(__all__)
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
- console = Console()
147
+
148
+ # Use provided console or create new one
149
+ live_console = console or Console()
143
150
 
144
151
  with Live(
145
- self, console=console, refresh_per_second=refresh_rate, transient=True
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
- self.colors = colors or [
165
- "bright_white",
166
- "bright_yellow",
167
- "bright_cyan",
168
- "bright_magenta",
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, text: str, speed: float = 0.05, duration: Optional[float] = None
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
- self.speed = speed
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
- yield Text(
288
- self.text[:chars_to_show] + "█"
289
- if chars_to_show < len(self.text)
290
- else self.text
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", duration: Optional[float] = None, speed: float = 0.5
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 ..styles.animations import (
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 ..styles.types import (
42
+ from .styles.types import (
43
43
  CLIStyleType,
44
44
  CLIStyleBackgroundType,
45
45
  CLIStyleColorName,
46
46
  )
47
- from ..styles.settings import (
47
+ from .styles.settings import (
48
48
  CLIStyleRenderableSettings,
49
49
  CLIStyleBackgroundSettings,
50
50
  CLIStyleLiveSettings,
51
51
  )
52
- from ..styles.utils import (
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 ..styles.utils import live_render, style_renderable
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 ..styles.animations import (
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 ..styles.settings import CLIStyleLiveSettings
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
- prompt_text += ": "
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 not value and default is not None:
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
- ) -> None: ...
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
- ) -> None: ...
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
- speed: float = 0.1,
682
- ) -> None: ...
683
-
684
-
685
- @overload
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
- ) -> None: ...
703
-
704
-
705
- @overload
706
- def animate(
707
- renderable: "RenderableType",
708
- type: Literal["rainbow"],
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
- **kwargs: Additional parameters specific to each animation type
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("flashing", "Alert!", duration=3.0, speed=0.3)
731
- >>> animate("pulsing", Panel("Loading"), min_opacity=0.1)
732
- >>> animate("typing", "Hello World!", speed=0.1)
733
- >>> animate("rainbow", "Colorful!", colors="bright")
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, speed=speed, colors=colors, duration=duration
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, speed=speed, duration=duration
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")