hammad-python 0.0.14__py3-none-any.whl → 0.0.16__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 (122) hide show
  1. hammad/__init__.py +177 -0
  2. hammad/{performance/imports.py → _internal.py} +7 -1
  3. hammad/cache/__init__.py +1 -1
  4. hammad/cli/__init__.py +3 -1
  5. hammad/cli/_runner.py +265 -0
  6. hammad/cli/animations.py +1 -1
  7. hammad/cli/plugins.py +133 -78
  8. hammad/cli/styles/__init__.py +1 -1
  9. hammad/cli/styles/utils.py +149 -3
  10. hammad/data/__init__.py +56 -29
  11. hammad/data/collections/__init__.py +27 -17
  12. hammad/data/collections/collection.py +205 -383
  13. hammad/data/collections/indexes/__init__.py +37 -0
  14. hammad/data/collections/indexes/qdrant/__init__.py +1 -0
  15. hammad/data/collections/indexes/qdrant/index.py +735 -0
  16. hammad/data/collections/indexes/qdrant/settings.py +94 -0
  17. hammad/data/collections/indexes/qdrant/utils.py +220 -0
  18. hammad/data/collections/indexes/tantivy/__init__.py +1 -0
  19. hammad/data/collections/indexes/tantivy/index.py +428 -0
  20. hammad/data/collections/indexes/tantivy/settings.py +51 -0
  21. hammad/data/collections/indexes/tantivy/utils.py +200 -0
  22. hammad/data/configurations/__init__.py +2 -2
  23. hammad/data/configurations/configuration.py +2 -2
  24. hammad/data/models/__init__.py +20 -9
  25. hammad/data/models/extensions/__init__.py +4 -0
  26. hammad/data/models/{pydantic → extensions/pydantic}/__init__.py +6 -19
  27. hammad/data/models/{pydantic → extensions/pydantic}/converters.py +143 -16
  28. hammad/data/models/{base/fields.py → fields.py} +1 -1
  29. hammad/data/models/{base/model.py → model.py} +1 -1
  30. hammad/data/models/{base/utils.py → utils.py} +1 -1
  31. hammad/data/sql/__init__.py +23 -0
  32. hammad/data/sql/database.py +578 -0
  33. hammad/data/sql/types.py +141 -0
  34. hammad/data/types/__init__.py +1 -3
  35. hammad/data/types/file.py +3 -3
  36. hammad/data/types/multimodal/__init__.py +2 -2
  37. hammad/data/types/multimodal/audio.py +2 -2
  38. hammad/data/types/multimodal/image.py +2 -2
  39. hammad/formatting/__init__.py +9 -27
  40. hammad/formatting/json/__init__.py +8 -2
  41. hammad/formatting/json/converters.py +7 -1
  42. hammad/formatting/text/__init__.py +1 -1
  43. hammad/formatting/yaml/__init__.py +1 -1
  44. hammad/genai/__init__.py +78 -0
  45. hammad/genai/agents/__init__.py +1 -0
  46. hammad/genai/agents/types/__init__.py +35 -0
  47. hammad/genai/agents/types/history.py +277 -0
  48. hammad/genai/agents/types/tool.py +490 -0
  49. hammad/genai/embedding_models/__init__.py +41 -0
  50. hammad/{ai/embeddings/client/litellm_embeddings_client.py → genai/embedding_models/embedding_model.py} +47 -142
  51. hammad/genai/embedding_models/embedding_model_name.py +77 -0
  52. hammad/genai/embedding_models/embedding_model_request.py +65 -0
  53. hammad/{ai/embeddings/types.py → genai/embedding_models/embedding_model_response.py} +3 -3
  54. hammad/genai/embedding_models/run.py +161 -0
  55. hammad/genai/language_models/__init__.py +35 -0
  56. hammad/genai/language_models/_streaming.py +622 -0
  57. hammad/genai/language_models/_types.py +276 -0
  58. hammad/genai/language_models/_utils/__init__.py +31 -0
  59. hammad/genai/language_models/_utils/_completions.py +131 -0
  60. hammad/genai/language_models/_utils/_messages.py +89 -0
  61. hammad/genai/language_models/_utils/_requests.py +202 -0
  62. hammad/genai/language_models/_utils/_structured_outputs.py +124 -0
  63. hammad/genai/language_models/language_model.py +734 -0
  64. hammad/genai/language_models/language_model_request.py +135 -0
  65. hammad/genai/language_models/language_model_response.py +219 -0
  66. hammad/genai/language_models/language_model_response_chunk.py +53 -0
  67. hammad/genai/language_models/run.py +530 -0
  68. hammad/genai/multimodal_models.py +48 -0
  69. hammad/genai/rerank_models.py +26 -0
  70. hammad/logging/__init__.py +1 -1
  71. hammad/logging/decorators.py +1 -1
  72. hammad/logging/logger.py +2 -2
  73. hammad/mcp/__init__.py +1 -1
  74. hammad/mcp/client/__init__.py +35 -0
  75. hammad/mcp/client/client.py +105 -4
  76. hammad/mcp/client/client_service.py +10 -3
  77. hammad/mcp/servers/__init__.py +24 -0
  78. hammad/{performance/runtime → runtime}/__init__.py +2 -2
  79. hammad/{performance/runtime → runtime}/decorators.py +1 -1
  80. hammad/{performance/runtime → runtime}/run.py +1 -1
  81. hammad/service/__init__.py +1 -1
  82. hammad/service/create.py +3 -8
  83. hammad/service/decorators.py +8 -8
  84. hammad/typing/__init__.py +28 -0
  85. hammad/web/__init__.py +3 -3
  86. hammad/web/http/client.py +1 -1
  87. hammad/web/models.py +53 -21
  88. hammad/web/search/client.py +99 -52
  89. hammad/web/utils.py +13 -13
  90. hammad_python-0.0.16.dist-info/METADATA +191 -0
  91. hammad_python-0.0.16.dist-info/RECORD +110 -0
  92. hammad/ai/__init__.py +0 -1
  93. hammad/ai/_utils.py +0 -142
  94. hammad/ai/completions/__init__.py +0 -45
  95. hammad/ai/completions/client.py +0 -684
  96. hammad/ai/completions/create.py +0 -710
  97. hammad/ai/completions/settings.py +0 -100
  98. hammad/ai/completions/types.py +0 -792
  99. hammad/ai/completions/utils.py +0 -486
  100. hammad/ai/embeddings/__init__.py +0 -35
  101. hammad/ai/embeddings/client/__init__.py +0 -1
  102. hammad/ai/embeddings/client/base_embeddings_client.py +0 -26
  103. hammad/ai/embeddings/client/fastembed_text_embeddings_client.py +0 -200
  104. hammad/ai/embeddings/create.py +0 -159
  105. hammad/data/collections/base_collection.py +0 -58
  106. hammad/data/collections/searchable_collection.py +0 -556
  107. hammad/data/collections/vector_collection.py +0 -596
  108. hammad/data/databases/__init__.py +0 -21
  109. hammad/data/databases/database.py +0 -902
  110. hammad/data/models/base/__init__.py +0 -35
  111. hammad/data/models/pydantic/models/__init__.py +0 -28
  112. hammad/data/models/pydantic/models/arbitrary_model.py +0 -46
  113. hammad/data/models/pydantic/models/cacheable_model.py +0 -79
  114. hammad/data/models/pydantic/models/fast_model.py +0 -318
  115. hammad/data/models/pydantic/models/function_model.py +0 -176
  116. hammad/data/models/pydantic/models/subscriptable_model.py +0 -63
  117. hammad/performance/__init__.py +0 -36
  118. hammad/py.typed +0 -0
  119. hammad_python-0.0.14.dist-info/METADATA +0 -70
  120. hammad_python-0.0.14.dist-info/RECORD +0 -99
  121. {hammad_python-0.0.14.dist-info → hammad_python-0.0.16.dist-info}/WHEEL +0 -0
  122. {hammad_python-0.0.14.dist-info → hammad_python-0.0.16.dist-info}/licenses/LICENSE +0 -0
hammad/__init__.py CHANGED
@@ -1 +1,178 @@
1
1
  """hammad-python"""
2
+
3
+ from typing import TYPE_CHECKING
4
+ from ._internal import create_getattr_importer as __hammad_importer__
5
+
6
+ if TYPE_CHECKING:
7
+
8
+ # hammad.cache
9
+ from .cache import (
10
+ cached,
11
+ Cache
12
+ )
13
+
14
+ # hammad.cli
15
+ from .cli import (
16
+ print,
17
+ animate,
18
+ input
19
+ )
20
+
21
+ # hammad.data
22
+ from .data.configurations import (
23
+ Configuration,
24
+ read_configuration_from_file,
25
+ read_configuration_from_url,
26
+ read_configuration_from_os_vars,
27
+ read_configuration_from_os_prefix,
28
+ read_configuration_from_dotenv
29
+ )
30
+ from .data.collections.collection import (
31
+ Collection,
32
+ create_collection
33
+ )
34
+ from .data.models import (
35
+ Model,
36
+ field,
37
+ validator,
38
+ model_settings,
39
+ convert_to_pydantic_model,
40
+ convert_to_pydantic_field,
41
+ )
42
+ from .data.types import (
43
+ Audio,
44
+ Image,
45
+ Text
46
+ )
47
+
48
+ # hammad.formatting
49
+ from .formatting.json import (
50
+ convert_to_json_schema
51
+ )
52
+ from .formatting.text import (
53
+ convert_to_text,
54
+ convert_type_to_text
55
+ )
56
+
57
+ # hammad.genai
58
+ from .genai.embedding_models import (
59
+ EmbeddingModel,
60
+ run_embedding_model,
61
+ async_run_embedding_model
62
+ )
63
+ from .genai.language_models import (
64
+ LanguageModel,
65
+ run_language_model,
66
+ async_run_language_model
67
+ )
68
+
69
+ # hammad.logging
70
+ from .logging.logger import (
71
+ Logger,
72
+ create_logger
73
+ )
74
+ from .logging.decorators import (
75
+ trace,
76
+ trace_cls,
77
+ trace_function,
78
+ trace_http
79
+ )
80
+
81
+ # hammad.service
82
+ from .service.decorators import (
83
+ serve as serve_function,
84
+ serve_mcp as serve_function_as_mcp
85
+ )
86
+
87
+ # hammad.web
88
+ from .web.http.client import (
89
+ create_http_client
90
+ )
91
+ from .web.openapi.client import (
92
+ create_openapi_client
93
+ )
94
+ from .web.search.client import (
95
+ create_search_client
96
+ )
97
+ from .web.utils import (
98
+ run_web_request,
99
+ read_web_page,
100
+ read_web_pages,
101
+ run_news_search,
102
+ run_web_search,
103
+ extract_web_page_links,
104
+ )
105
+
106
+
107
+ __all__ = (
108
+ # hammad.cache
109
+ "cached",
110
+ "Cache",
111
+
112
+ # hammad.cli
113
+ "print",
114
+ "animate",
115
+ "input",
116
+
117
+ # hammad.data
118
+ "Configuration",
119
+ "read_configuration_from_file",
120
+ "read_configuration_from_url",
121
+ "read_configuration_from_os_vars",
122
+ "read_configuration_from_os_prefix",
123
+ "read_configuration_from_dotenv",
124
+ "Collection",
125
+ "create_collection",
126
+ "Model",
127
+ "field",
128
+ "validator",
129
+ "model_settings",
130
+ "convert_to_pydantic_model",
131
+ "convert_to_pydantic_field",
132
+ "Audio",
133
+ "Image",
134
+ "Text",
135
+
136
+ # hammad.formatting
137
+ "convert_to_json_schema",
138
+ "convert_to_text",
139
+ "convert_type_to_text",
140
+
141
+ # hammad.genai
142
+ "EmbeddingModel",
143
+ "run_embedding_model",
144
+ "async_run_embedding_model",
145
+ "LanguageModel",
146
+ "run_language_model",
147
+ "async_run_language_model",
148
+
149
+ # hammad.logging
150
+ "Logger",
151
+ "create_logger",
152
+ "trace",
153
+ "trace_cls",
154
+ "trace_function",
155
+ "trace_http",
156
+
157
+ # hammad.service
158
+ "serve_function",
159
+ "serve_function_as_mcp",
160
+
161
+ # hammad.web
162
+ "create_http_client",
163
+ "create_openapi_client",
164
+ "create_search_client",
165
+ "run_web_request",
166
+ "read_web_page",
167
+ "read_web_pages",
168
+ "run_web_search",
169
+ "run_news_search",
170
+ "extract_web_page_links",
171
+ )
172
+
173
+
174
+ __getattr__ = __hammad_importer__(__all__)
175
+
176
+
177
+ def __dir__() -> list[str]:
178
+ return list(__all__)
@@ -1,10 +1,16 @@
1
- """hammad.performance.runtime.imports"""
1
+ """hammad._internal
2
+
3
+ Internal utilities"""
2
4
 
3
5
  from typing import Any, Callable, List, Tuple, Union
4
6
  import inspect
5
7
  import ast
6
8
  import hashlib
7
9
 
10
+ # pretty
11
+ from rich.traceback import install
12
+ install()
13
+
8
14
  __all__ = ("create_getattr_importer",)
9
15
 
10
16
 
hammad/cache/__init__.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """hammad.cache"""
2
2
 
3
3
  from typing import TYPE_CHECKING
4
- from ..performance.imports import create_getattr_importer
4
+ from .._internal import create_getattr_importer
5
5
 
6
6
 
7
7
  if TYPE_CHECKING:
hammad/cli/__init__.py CHANGED
@@ -4,10 +4,11 @@ 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 ..performance.imports import create_getattr_importer
7
+ from .._internal import create_getattr_importer
8
8
 
9
9
  if TYPE_CHECKING:
10
10
  from .plugins import print, input, animate
11
+ from ._runner import CLIRunner
11
12
  from .styles.settings import (
12
13
  CLIStyleRenderableSettings,
13
14
  CLIStyleBackgroundSettings,
@@ -19,6 +20,7 @@ __all__ = (
19
20
  "print",
20
21
  "input",
21
22
  "animate",
23
+ "CLIRunner",
22
24
  "CLIStyleRenderableSettings",
23
25
  "CLIStyleBackgroundSettings",
24
26
  "CLIStyleLiveSettings",
hammad/cli/_runner.py ADDED
@@ -0,0 +1,265 @@
1
+ """hammad.cli._runner"""
2
+
3
+ from typing import (
4
+ overload,
5
+ TYPE_CHECKING,
6
+ Optional,
7
+ Any,
8
+ Dict,
9
+ List,
10
+ Union,
11
+ Literal,
12
+ IO,
13
+ )
14
+
15
+ if TYPE_CHECKING:
16
+ from rich.console import Console, RenderableType
17
+ from ..cli.animations import (
18
+ CLIFlashingAnimation,
19
+ CLIPulsingAnimation,
20
+ CLIShakingAnimation,
21
+ CLITypingAnimation,
22
+ CLISpinningAnimation,
23
+ CLIRainbowAnimation,
24
+ RainbowPreset,
25
+ )
26
+ from ..cli.styles.types import (
27
+ CLIStyleType,
28
+ CLIStyleBackgroundType,
29
+ CLIStyleColorName,
30
+ )
31
+ from ..cli.styles.settings import (
32
+ CLIStyleRenderableSettings,
33
+ CLIStyleBackgroundSettings,
34
+ CLIStyleLiveSettings,
35
+ )
36
+
37
+
38
+ __all__ = (
39
+ "CLIRunner",
40
+ )
41
+
42
+
43
+
44
+ class CLIRunner:
45
+ """Runner subclass for various CLI-based operations."""
46
+
47
+ @overload
48
+ @staticmethod
49
+ def print(
50
+ *values: object,
51
+ sep: str = " ",
52
+ end: str = "\n",
53
+ file: Optional[IO[str]] = None,
54
+ flush: bool = False,
55
+ ) -> None: ...
56
+
57
+
58
+ @overload
59
+ @staticmethod
60
+ def print(
61
+ *values: object,
62
+ sep: str = " ",
63
+ end: str = "\n",
64
+ file: Optional[IO[str]] = None,
65
+ flush: bool = False,
66
+ style: "CLIStyleType | None" = None,
67
+ style_settings: "CLIStyleRenderableSettings | None" = None,
68
+ bg: "CLIStyleBackgroundType | None" = None,
69
+ bg_settings: "CLIStyleBackgroundSettings | None" = None,
70
+ live: "CLIStyleLiveSettings | int | None" = None,
71
+ ) -> None: ...
72
+
73
+
74
+ @staticmethod
75
+ def print(
76
+ *values: object,
77
+ sep: str = " ",
78
+ end: str = "\n",
79
+ file: Optional[IO[str]] = None,
80
+ flush: bool = False,
81
+ style: "CLIStyleType | None" = None,
82
+ style_settings: "CLIStyleRenderableSettings | None" = None,
83
+ bg: "CLIStyleBackgroundType | None" = None,
84
+ bg_settings: "CLIStyleBackgroundSettings | None" = None,
85
+ live: "CLIStyleLiveSettings | int | None" = None,
86
+ ) -> Optional["CLIStyleLiveSettings"]:
87
+ """Print values to the console with optional styling and live updates.
88
+
89
+ This function extends Python's built-in print() with additional styling
90
+ capabilities including backgrounds and live updating displays.
91
+
92
+ Args:
93
+ *values: Values to print (similar to built-in print())
94
+ sep: String inserted between values (default: " ")
95
+ end: String appended after the last value (default: "\n")
96
+ file: File object to write to (default: sys.stdout)
97
+ flush: Whether to forcibly flush the stream
98
+ console: Rich Console instance to use
99
+ style: Style to apply to the content
100
+ color: Color to apply to the content
101
+ bg: Background style to apply
102
+ live: Whether to enable live updating
103
+ live_settings: Configuration for live display
104
+ settings: General rendering settings
105
+ bg_settings: Background styling settings
106
+ **kwargs: Additional keyword arguments
107
+
108
+ Returns:
109
+ Live settings object if live=True, otherwise None
110
+ """
111
+ from ..cli import print as _run_cli_print_fn
112
+ return _run_cli_print_fn(
113
+ *values,
114
+ sep=sep,
115
+ end=end,
116
+ file=file,
117
+ flush=flush,
118
+ style=style,
119
+ style_settings=style_settings,
120
+ bg=bg,
121
+ bg_settings=bg_settings,
122
+ live=live,
123
+ )
124
+
125
+ @overload
126
+ @staticmethod
127
+ def input(
128
+ prompt: str = "",
129
+ schema: Any = None,
130
+ sequential: bool = True,
131
+ style: "CLIStyleType | None" = None,
132
+ style_settings: "CLIStyleRenderableSettings | None" = None,
133
+ bg: "CLIStyleBackgroundType | None" = None,
134
+ bg_settings: "CLIStyleBackgroundSettings | None" = None,
135
+ multiline: bool = False,
136
+ password: bool = False,
137
+ complete: Optional[List[str]] = None,
138
+ validate: Optional[callable] = None,
139
+ ) -> Any: ...
140
+
141
+ @staticmethod
142
+ def input(
143
+ prompt: str = "",
144
+ schema: Any = None,
145
+ sequential: bool = True,
146
+ style: "CLIStyleType | None" = None,
147
+ style_settings: "CLIStyleRenderableSettings | None" = None,
148
+ bg: "CLIStyleBackgroundType | None" = None,
149
+ bg_settings: "CLIStyleBackgroundSettings | None" = None,
150
+ multiline: bool = False,
151
+ password: bool = False,
152
+ complete: Optional[List[str]] = None,
153
+ validate: Optional[callable] = None,
154
+ ) -> Any:
155
+ """Get input from the user with optional validation and styling.
156
+
157
+ Args:
158
+ prompt: The prompt message to display.
159
+ schema: Optional schema (dataclass, TypedDict, Pydantic model) for structured input.
160
+ sequential: If schema is provided, collect fields sequentially (default: True).
161
+ style: A color or style name to apply to the prompt.
162
+ style_settings: A dictionary of style settings to apply to the prompt.
163
+ bg: A color or box name to apply to the background of the prompt.
164
+ bg_settings: A dictionary of background settings to apply to the prompt.
165
+ multiline: Allow multiline input (default: False).
166
+ password: Hide input (default: False).
167
+ complete: List of strings for autocompletion.
168
+ validate: A callable to validate the input.
169
+
170
+ Returns:
171
+ The user's input, potentially validated and converted according to the schema.
172
+ """
173
+ from ..cli import input as _run_cli_input_fn
174
+ return _run_cli_input_fn(
175
+ prompt=prompt,
176
+ schema=schema,
177
+ sequential=sequential,
178
+ style=style,
179
+ style_settings=style_settings,
180
+ bg=bg,
181
+ bg_settings=bg_settings,
182
+ multiline=multiline,
183
+ password=password,
184
+ complete=complete,
185
+ validate=validate,
186
+ )
187
+
188
+ @staticmethod
189
+ def animate(
190
+ renderable: "RenderableType | str",
191
+ type: Literal["flashing", "pulsing", "shaking", "typing", "spinning", "rainbow"],
192
+ duration: Optional[float] = None,
193
+ # Animation parameters (defaults are handled by the specific animation classes)
194
+ speed: Optional[float] = None,
195
+ colors: "Optional[List[CLIStyleColorName]]" = None,
196
+ on_color: "Optional[CLIStyleColorName]" = None,
197
+ off_color: "Optional[CLIStyleColorName]" = None,
198
+ min_opacity: Optional[float] = None,
199
+ max_opacity: Optional[float] = None,
200
+ color: "Optional[CLIStyleColorName]" = None,
201
+ intensity: Optional[int] = None,
202
+ typing_speed: Optional[float] = None,
203
+ cursor: Optional[str] = None,
204
+ show_cursor: Optional[bool] = None,
205
+ frames: Optional[List[str]] = None,
206
+ prefix: Optional[bool] = None,
207
+ # Rich.Live parameters
208
+ refresh_rate: int = 20,
209
+ transient: bool = True,
210
+ auto_refresh: bool = True,
211
+ console: Optional["Console"] = None,
212
+ screen: bool = False,
213
+ vertical_overflow: str = "ellipsis",
214
+ ) -> None:
215
+ """Create and run an animation based on the specified type.
216
+
217
+ Args:
218
+ renderable: The object to animate (text, panel, etc.)
219
+ type: The type of animation to create
220
+ duration: Duration of the animation in seconds (defaults to 2.0)
221
+ speed: Animation speed (used by flashing, pulsing, shaking, spinning, rainbow)
222
+ colors: Color list (used by flashing, rainbow)
223
+ on_color: Color when flashing "on" (used by flashing)
224
+ off_color: Color when flashing "off" (used by flashing)
225
+ min_opacity: Minimum opacity for pulsing animation
226
+ max_opacity: Maximum opacity for pulsing animation
227
+ color: Color for pulsing animation
228
+ intensity: Shaking intensity for shaking animation
229
+ typing_speed: Speed for typing animation (used by typing)
230
+ cursor: Cursor character for typing animation (used by typing)
231
+ show_cursor: Whether to show cursor for typing animation (used by typing)
232
+ frames: Custom frames for spinning animation
233
+ prefix: Whether to show spinner as prefix for spinning animation
234
+ refresh_rate: Refresh rate per second for Live rendering
235
+ transient: Whether to clear animation after completion
236
+ auto_refresh: Whether to auto-refresh the display
237
+ console: Console to use for rendering
238
+ screen: Whether to use alternate screen buffer
239
+ vertical_overflow: How to handle vertical overflow
240
+ """
241
+ from ..cli import animate as _run_cli_animate_fn
242
+ _run_cli_animate_fn(
243
+ renderable=renderable,
244
+ type=type,
245
+ duration=duration,
246
+ speed=speed,
247
+ colors=colors,
248
+ on_color=on_color,
249
+ off_color=off_color,
250
+ min_opacity=min_opacity,
251
+ max_opacity=max_opacity,
252
+ color=color,
253
+ intensity=intensity,
254
+ typing_speed=typing_speed,
255
+ cursor=cursor,
256
+ show_cursor=show_cursor,
257
+ frames=frames,
258
+ prefix=prefix,
259
+ refresh_rate=refresh_rate,
260
+ transient=transient,
261
+ auto_refresh=auto_refresh,
262
+ console=console,
263
+ screen=screen,
264
+ vertical_overflow=vertical_overflow,
265
+ )
hammad/cli/animations.py CHANGED
@@ -1,4 +1,4 @@
1
- """hammad.cli.styles.animations"""
1
+ """hammad.cli.animations"""
2
2
 
3
3
  import time
4
4
  import math