hammad-python 0.0.13__py3-none-any.whl → 0.0.15__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 (87) hide show
  1. hammad_python-0.0.15.dist-info/METADATA +184 -0
  2. hammad_python-0.0.15.dist-info/RECORD +4 -0
  3. hammad/__init__.py +0 -180
  4. hammad/_core/__init__.py +0 -1
  5. hammad/_core/_utils/__init__.py +0 -4
  6. hammad/_core/_utils/_import_utils.py +0 -182
  7. hammad/ai/__init__.py +0 -59
  8. hammad/ai/_utils.py +0 -142
  9. hammad/ai/completions/__init__.py +0 -44
  10. hammad/ai/completions/client.py +0 -729
  11. hammad/ai/completions/create.py +0 -686
  12. hammad/ai/completions/types.py +0 -711
  13. hammad/ai/completions/utils.py +0 -374
  14. hammad/ai/embeddings/__init__.py +0 -35
  15. hammad/ai/embeddings/client/__init__.py +0 -1
  16. hammad/ai/embeddings/client/base_embeddings_client.py +0 -26
  17. hammad/ai/embeddings/client/fastembed_text_embeddings_client.py +0 -200
  18. hammad/ai/embeddings/client/litellm_embeddings_client.py +0 -288
  19. hammad/ai/embeddings/create.py +0 -159
  20. hammad/ai/embeddings/types.py +0 -69
  21. hammad/base/__init__.py +0 -35
  22. hammad/base/fields.py +0 -546
  23. hammad/base/model.py +0 -1078
  24. hammad/base/utils.py +0 -280
  25. hammad/cache/__init__.py +0 -48
  26. hammad/cache/base_cache.py +0 -181
  27. hammad/cache/cache.py +0 -169
  28. hammad/cache/decorators.py +0 -261
  29. hammad/cache/file_cache.py +0 -80
  30. hammad/cache/ttl_cache.py +0 -74
  31. hammad/cli/__init__.py +0 -33
  32. hammad/cli/animations.py +0 -604
  33. hammad/cli/plugins.py +0 -781
  34. hammad/cli/styles/__init__.py +0 -55
  35. hammad/cli/styles/settings.py +0 -139
  36. hammad/cli/styles/types.py +0 -358
  37. hammad/cli/styles/utils.py +0 -480
  38. hammad/configuration/__init__.py +0 -35
  39. hammad/configuration/configuration.py +0 -564
  40. hammad/data/__init__.py +0 -39
  41. hammad/data/collections/__init__.py +0 -34
  42. hammad/data/collections/base_collection.py +0 -58
  43. hammad/data/collections/collection.py +0 -452
  44. hammad/data/collections/searchable_collection.py +0 -556
  45. hammad/data/collections/vector_collection.py +0 -603
  46. hammad/data/databases/__init__.py +0 -21
  47. hammad/data/databases/database.py +0 -902
  48. hammad/json/__init__.py +0 -21
  49. hammad/json/converters.py +0 -152
  50. hammad/logging/__init__.py +0 -35
  51. hammad/logging/decorators.py +0 -834
  52. hammad/logging/logger.py +0 -954
  53. hammad/multimodal/__init__.py +0 -24
  54. hammad/multimodal/audio.py +0 -96
  55. hammad/multimodal/image.py +0 -80
  56. hammad/multithreading/__init__.py +0 -304
  57. hammad/py.typed +0 -0
  58. hammad/pydantic/__init__.py +0 -43
  59. hammad/pydantic/converters.py +0 -623
  60. hammad/pydantic/models/__init__.py +0 -28
  61. hammad/pydantic/models/arbitrary_model.py +0 -46
  62. hammad/pydantic/models/cacheable_model.py +0 -79
  63. hammad/pydantic/models/fast_model.py +0 -318
  64. hammad/pydantic/models/function_model.py +0 -176
  65. hammad/pydantic/models/subscriptable_model.py +0 -63
  66. hammad/text/__init__.py +0 -82
  67. hammad/text/converters.py +0 -723
  68. hammad/text/markdown.py +0 -131
  69. hammad/text/text.py +0 -1066
  70. hammad/types/__init__.py +0 -11
  71. hammad/types/file.py +0 -358
  72. hammad/typing/__init__.py +0 -407
  73. hammad/web/__init__.py +0 -43
  74. hammad/web/http/__init__.py +0 -1
  75. hammad/web/http/client.py +0 -944
  76. hammad/web/models.py +0 -245
  77. hammad/web/openapi/__init__.py +0 -0
  78. hammad/web/openapi/client.py +0 -740
  79. hammad/web/search/__init__.py +0 -1
  80. hammad/web/search/client.py +0 -988
  81. hammad/web/utils.py +0 -472
  82. hammad/yaml/__init__.py +0 -30
  83. hammad/yaml/converters.py +0 -19
  84. hammad_python-0.0.13.dist-info/METADATA +0 -38
  85. hammad_python-0.0.13.dist-info/RECORD +0 -85
  86. {hammad_python-0.0.13.dist-info → hammad_python-0.0.15.dist-info}/WHEEL +0 -0
  87. {hammad_python-0.0.13.dist-info → hammad_python-0.0.15.dist-info}/licenses/LICENSE +0 -0
@@ -1,24 +0,0 @@
1
- """hammad.multimodal
2
-
3
- Contains types and model like objects for working with various
4
- types of multimodal data."""
5
-
6
- from typing import TYPE_CHECKING
7
- from .._core._utils._import_utils import _auto_create_getattr_loader
8
-
9
- if TYPE_CHECKING:
10
- from .image import Image
11
- from .audio import Audio
12
-
13
-
14
- __all__ = (
15
- "Image",
16
- "Audio",
17
- )
18
-
19
-
20
- __getattr__ = _auto_create_getattr_loader(__all__)
21
-
22
-
23
- def __dir__() -> list[str]:
24
- return list(__all__)
@@ -1,96 +0,0 @@
1
- """hammad.data.types.files.audio"""
2
-
3
- import httpx
4
- from typing import Self
5
-
6
- from ..types.file import File, FileSource
7
- from ..base.fields import field
8
-
9
- __all__ = ("Audio",)
10
-
11
-
12
- class Audio(File):
13
- """A representation of an audio file, that is loadable from both a URL, file path
14
- or bytes."""
15
-
16
- # Audio-specific metadata
17
- _duration: float | None = field(default=None)
18
- _sample_rate: int | None = field(default=None)
19
- _channels: int | None = field(default=None)
20
- _format: str | None = field(default=None)
21
-
22
- @property
23
- def is_valid_audio(self) -> bool:
24
- """Check if this is a valid audio file based on MIME type."""
25
- return self.type is not None and self.type.startswith("audio/")
26
-
27
- @property
28
- def format(self) -> str | None:
29
- """Get the audio format from MIME type."""
30
- if self._format is None and self.type:
31
- # Extract format from MIME type (e.g., 'audio/mp3' -> 'mp3')
32
- self._format = self.type.split("/")[-1].upper()
33
- return self._format
34
-
35
- @property
36
- def duration(self) -> float | None:
37
- """Get the duration of the audio file in seconds."""
38
- return self._duration
39
-
40
- @property
41
- def sample_rate(self) -> int | None:
42
- """Get the sample rate of the audio file in Hz."""
43
- return self._sample_rate
44
-
45
- @property
46
- def channels(self) -> int | None:
47
- """Get the number of channels in the audio file."""
48
- return self._channels
49
-
50
- @classmethod
51
- def from_url(
52
- cls,
53
- url: str,
54
- *,
55
- lazy: bool = True,
56
- timeout: float = 30.0,
57
- ) -> Self:
58
- """Download and create an audio file from a URL.
59
-
60
- Args:
61
- url: The URL to download from.
62
- lazy: If True, defer loading content until needed.
63
- timeout: Request timeout in seconds.
64
-
65
- Returns:
66
- A new Audio instance.
67
- """
68
- data = None
69
- size = None
70
- type = None
71
-
72
- if not lazy:
73
- with httpx.Client(timeout=timeout) as client:
74
- response = client.get(url)
75
- response.raise_for_status()
76
-
77
- data = response.content
78
- size = len(data)
79
-
80
- # Get content type
81
- content_type = response.headers.get("content-type", "")
82
- type = content_type.split(";")[0] if content_type else None
83
-
84
- # Validate it's audio
85
- if type and not type.startswith("audio/"):
86
- raise ValueError(f"URL does not point to an audio file: {type}")
87
-
88
- return cls(
89
- data=data,
90
- type=type,
91
- source=FileSource(
92
- is_url=True,
93
- url=url,
94
- size=size,
95
- ),
96
- )
@@ -1,80 +0,0 @@
1
- """hammad.data.types.files.image"""
2
-
3
- import httpx
4
- from typing import Self
5
-
6
- from ..types.file import File, FileSource
7
- from ..base.fields import field
8
-
9
- __all__ = ("Image",)
10
-
11
-
12
- class Image(File):
13
- """A representation of an image, that is loadable from both a URL, file path
14
- or bytes."""
15
-
16
- # Image-specific metadata
17
- _width: int | None = field(default=None)
18
- _height: int | None = field(default=None)
19
- _format: str | None = field(default=None)
20
-
21
- @property
22
- def is_valid_image(self) -> bool:
23
- """Check if this is a valid image based on MIME type."""
24
- return self.type is not None and self.type.startswith("image/")
25
-
26
- @property
27
- def format(self) -> str | None:
28
- """Get the image format from MIME type."""
29
- if self._format is None and self.type:
30
- # Extract format from MIME type (e.g., 'image/png' -> 'png')
31
- self._format = self.type.split("/")[-1].upper()
32
- return self._format
33
-
34
- @classmethod
35
- def from_url(
36
- cls,
37
- url: str,
38
- *,
39
- lazy: bool = True,
40
- timeout: float = 30.0,
41
- ) -> Self:
42
- """Download and create an image from a URL.
43
-
44
- Args:
45
- url: The URL to download from.
46
- lazy: If True, defer loading content until needed.
47
- timeout: Request timeout in seconds.
48
-
49
- Returns:
50
- A new Image instance.
51
- """
52
- data = None
53
- size = None
54
- type = None
55
-
56
- if not lazy:
57
- with httpx.Client(timeout=timeout) as client:
58
- response = client.get(url)
59
- response.raise_for_status()
60
-
61
- data = response.content
62
- size = len(data)
63
-
64
- # Get content type
65
- content_type = response.headers.get("content-type", "")
66
- type = content_type.split(";")[0] if content_type else None
67
-
68
- # Validate it's an image
69
- if type and not type.startswith("image/"):
70
- raise ValueError(f"URL does not point to an image: {type}")
71
-
72
- return cls(
73
- data=data,
74
- type=type,
75
- source=FileSource(
76
- is_url=True,
77
- url=url,
78
- size=size,
79
- ),
80
- )
@@ -1,304 +0,0 @@
1
- """hammad.multithreading"""
2
-
3
- import concurrent.futures
4
- import functools
5
- import time
6
- from typing import (
7
- Callable,
8
- Iterable,
9
- List,
10
- Any,
11
- TypeVar,
12
- Tuple,
13
- Optional,
14
- Union,
15
- Type,
16
- cast,
17
- overload,
18
- )
19
- from tenacity import (
20
- retry,
21
- wait_exponential,
22
- stop_after_attempt,
23
- retry_if_exception_type,
24
- retry_if_exception_message,
25
- retry_if_exception_type,
26
- )
27
-
28
- T_Arg = TypeVar("T_Arg")
29
- R_Out = TypeVar("R_Out")
30
-
31
- SingleTaskArgs = Union[T_Arg, Tuple[Any, ...]]
32
-
33
-
34
- __all__ = (
35
- "run_sequentially",
36
- "run_parallel",
37
- "sequentialize",
38
- "parallelize",
39
- "typed_batch",
40
- "run_with_retry",
41
- "retry",
42
- "wait_exponential",
43
- "stop_after_attempt",
44
- "retry_if_exception_type",
45
- "retry_if_exception_message",
46
- "retry_if_exception_type",
47
- )
48
-
49
-
50
- def run_sequentially(
51
- func: Callable[..., R_Out], args_list: Iterable[SingleTaskArgs]
52
- ) -> List[R_Out]:
53
- """
54
- Executes a function sequentially for each set of arguments in args_list.
55
- If the function raises an exception during any call, the execution stops
56
- and the exception is propagated.
57
-
58
- Args:
59
- func: The function to execute.
60
- args_list: An iterable of arguments (or argument tuples) to pass to func.
61
- - If func takes multiple arguments (e.g., func(a, b)),
62
- each item in args_list should be a tuple (e.g., [(val1_a, val1_b), (val2_a, val2_b)]).
63
- - If func takes one argument (e.g., func(a)),
64
- each item can be the argument itself (e.g., [val1, val2]).
65
- - If func takes no arguments (e.g., func()),
66
- each item should be an empty tuple (e.g., [(), ()]).
67
-
68
- Returns:
69
- A list of results from the sequential execution.
70
- """
71
- results: List[R_Out] = []
72
- for args_item in args_list:
73
- if isinstance(args_item, tuple):
74
- results.append(func(*args_item))
75
- else:
76
- # This branch handles single arguments.
77
- # If func expects no arguments, args_item should be `()` and caught by `isinstance(tuple)`.
78
- # If func expects one argument, args_item is that argument.
79
- results.append(func(args_item))
80
- return results
81
-
82
-
83
- def run_parallel(
84
- func: Callable[..., R_Out],
85
- args_list: Iterable[SingleTaskArgs],
86
- max_workers: Optional[int] = None,
87
- task_timeout: Optional[float] = None,
88
- ) -> List[Union[R_Out, Exception]]:
89
- """
90
- Executes a function in parallel for each set of arguments in args_list
91
- using a ThreadPoolExecutor. Results are returned in the same order as the input args_list.
92
-
93
- Args:
94
- func: The function to execute.
95
- args_list: An iterable of arguments (or argument tuples) to pass to func.
96
- (See `run_sequentially` for formatting details).
97
- max_workers: The maximum number of worker threads. If None, it defaults
98
- to ThreadPoolExecutor's default (typically based on CPU cores).
99
- task_timeout: The maximum number of seconds to wait for each individual task
100
- to complete. If a task exceeds this timeout, a
101
- concurrent.futures.TimeoutError will be stored as its result.
102
- If None, tasks will wait indefinitely for completion.
103
-
104
- Returns:
105
- A list where each element corresponds to the respective item in args_list.
106
- - If a task executed successfully, its return value (R_Out) is stored.
107
- - If a task raised an exception (including TimeoutError due to task_timeout),
108
- the exception object itself is stored.
109
- """
110
- # Materialize args_list to ensure consistent ordering and count, especially if it's a generator.
111
- materialized_args_list = list(args_list)
112
- if not materialized_args_list:
113
- return []
114
-
115
- with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
116
- futures: List[concurrent.futures.Future] = []
117
- for args_item in materialized_args_list:
118
- if isinstance(args_item, tuple):
119
- future = executor.submit(func, *args_item)
120
- else:
121
- future = executor.submit(func, args_item)
122
- futures.append(future)
123
-
124
- # Initialize results list. Type ignore is used because None is a placeholder.
125
- results: List[Union[R_Out, Exception]] = [None] * len(futures) # type: ignore
126
- for i, future in enumerate(futures):
127
- try:
128
- results[i] = future.result(timeout=task_timeout)
129
- except Exception as e: # Catches TimeoutError from future.result and any exception from func
130
- results[i] = e
131
- return results
132
-
133
-
134
- def sequentialize():
135
- """
136
- Decorator to make a function that processes a single item (or argument set)
137
- able to process an iterable of items (or argument sets) sequentially.
138
-
139
- The decorated function will expect an iterable of argument sets as its
140
- primary argument and will return a list of results. If the underlying
141
- function raises an error, execution stops and the error propagates.
142
-
143
- Example:
144
- @sequentialize()
145
- def process_single(data, factor):
146
- return data * factor
147
-
148
- # Now call it with a list of argument tuples
149
- results = process_single([(1, 2), (3, 4)])
150
- # results will be [2, 12]
151
- """
152
-
153
- def decorator(
154
- func_to_process_single_item: Callable[..., R_Out],
155
- ) -> Callable[[Iterable[SingleTaskArgs]], List[R_Out]]:
156
- @functools.wraps(func_to_process_single_item)
157
- def wrapper(args_list_for_func: Iterable[SingleTaskArgs]) -> List[R_Out]:
158
- return run_sequentially(func_to_process_single_item, args_list_for_func)
159
-
160
- return wrapper
161
-
162
- return decorator
163
-
164
-
165
- def parallelize(
166
- max_workers: Optional[int] = None, task_timeout: Optional[float] = None
167
- ):
168
- """
169
- Decorator to make a function that processes a single item (or argument set)
170
- able to process an iterable of items (or argument sets) in parallel.
171
-
172
- The decorated function will expect an iterable of argument sets as its
173
- primary argument and will return a list of results or exceptions,
174
- maintaining the original order.
175
-
176
- Args:
177
- max_workers (Optional[int]): Max worker threads for parallel execution.
178
- task_timeout (Optional[float]): Timeout for each individual task.
179
-
180
- Example:
181
- @parallelize(max_workers=4, task_timeout=5.0)
182
- def fetch_url_content(url: str) -> str:
183
- # ... implementation to fetch url ...
184
- return "content"
185
-
186
- # Now call it with a list of URLs
187
- results = fetch_url_content(["http://example.com", "http://example.org"])
188
- # results will be a list of contents or Exception objects.
189
- """
190
-
191
- def decorator(
192
- func_to_process_single_item: Callable[..., R_Out],
193
- ) -> Callable[[Iterable[SingleTaskArgs]], List[Union[R_Out, Exception]]]:
194
- @functools.wraps(func_to_process_single_item)
195
- def wrapper(
196
- args_list_for_func: Iterable[SingleTaskArgs],
197
- ) -> List[Union[R_Out, Exception]]:
198
- return run_parallel(
199
- func_to_process_single_item,
200
- args_list_for_func,
201
- max_workers=max_workers,
202
- task_timeout=task_timeout,
203
- )
204
-
205
- return wrapper
206
-
207
- return decorator
208
-
209
-
210
- def typed_batch():
211
- """
212
- Decorator that provides better IDE type hinting for functions converted from
213
- single-item to batch processing. This helps IDEs understand the transformation
214
- and provide accurate autocomplete and type checking.
215
-
216
- The decorated function maintains proper type information showing it transforms
217
- from Callable[[T], R] to Callable[[Iterable[T]], List[R]].
218
-
219
- Example:
220
- @typed_batch()
221
- def process_url(url: str) -> dict:
222
- return {"url": url, "status": "ok"}
223
-
224
- # IDE will now correctly understand:
225
- # process_url: (Iterable[str]) -> List[dict]
226
- results = process_url(["http://example.com", "http://test.com"])
227
- """
228
-
229
- def decorator(
230
- func: Callable[..., R_Out],
231
- ) -> Callable[[Iterable[SingleTaskArgs]], List[R_Out]]:
232
- @functools.wraps(func)
233
- def wrapper(args_list: Iterable[SingleTaskArgs]) -> List[R_Out]:
234
- return run_sequentially(func, args_list)
235
-
236
- # Preserve original function's type info while updating signature
237
- wrapper.__annotations__ = {
238
- "args_list": Iterable[SingleTaskArgs],
239
- "return": List[R_Out],
240
- }
241
-
242
- return cast(Callable[[Iterable[SingleTaskArgs]], List[R_Out]], wrapper)
243
-
244
- return decorator
245
-
246
-
247
- def run_with_retry(
248
- max_attempts: int = 3,
249
- delay: float = 1.0,
250
- backoff: float = 2.0,
251
- exceptions: Optional[Tuple[Type[Exception], ...]] = None,
252
- ):
253
- """
254
- Decorator that adds retry logic to functions. Essential for robust parallel
255
- processing when dealing with network calls, database operations, or other
256
- operations that might fail transiently.
257
-
258
- Args:
259
- max_attempts: Maximum number of attempts (including the first try).
260
- delay: Initial delay between retries in seconds.
261
- backoff: Multiplier for delay after each failed attempt.
262
- exceptions: Tuple of exception types to retry on. If None, retries on all exceptions.
263
-
264
- Example:
265
- @with_retry(max_attempts=3, delay=0.5, backoff=2.0, exceptions=(ConnectionError, TimeoutError))
266
- def fetch_data(url: str) -> dict:
267
- # This will retry up to 3 times with exponential backoff
268
- # only for ConnectionError and TimeoutError
269
- return requests.get(url).json()
270
-
271
- @parallelize(max_workers=10)
272
- @with_retry(max_attempts=2)
273
- def robust_fetch(url: str) -> str:
274
- return fetch_url_content(url)
275
- """
276
- if exceptions is None:
277
- exceptions = (Exception,)
278
-
279
- def decorator(func: Callable[..., R_Out]) -> Callable[..., R_Out]:
280
- @functools.wraps(func)
281
- def wrapper(*args, **kwargs) -> R_Out:
282
- last_exception = None
283
- current_delay = delay
284
-
285
- for attempt in range(max_attempts):
286
- try:
287
- return func(*args, **kwargs)
288
- except exceptions as e:
289
- last_exception = e
290
- if attempt == max_attempts - 1: # Last attempt
291
- break
292
-
293
- print(
294
- f"Attempt {attempt + 1} failed for {func.__name__}: {e}. Retrying in {current_delay:.2f}s..."
295
- )
296
- time.sleep(current_delay)
297
- current_delay *= backoff
298
-
299
- # If we get here, all attempts failed
300
- raise last_exception
301
-
302
- return wrapper
303
-
304
- return decorator
hammad/py.typed DELETED
File without changes
@@ -1,43 +0,0 @@
1
- """hammad.pydantic
2
-
3
- Contains both models and pydantic **specific** utiltiies / resources
4
- meant for general case usage."""
5
-
6
- from typing import TYPE_CHECKING
7
- from .._core._utils._import_utils import _auto_create_getattr_loader
8
-
9
- if TYPE_CHECKING:
10
- from .converters import (
11
- convert_to_pydantic_model,
12
- convert_to_pydantic_field,
13
- create_confirmation_pydantic_model,
14
- create_selection_pydantic_model,
15
- )
16
- from .models import (
17
- FastModel,
18
- FunctionModel,
19
- ArbitraryModel,
20
- CacheableModel,
21
- SubscriptableModel,
22
- )
23
-
24
-
25
- __all__ = (
26
- "convert_to_pydantic_model",
27
- "convert_to_pydantic_field",
28
- "create_confirmation_pydantic_model",
29
- "create_selection_pydantic_model",
30
- "FastModel",
31
- "FunctionModel",
32
- "ArbitraryModel",
33
- "CacheableModel",
34
- "SubscriptableModel",
35
- )
36
-
37
-
38
- __getattr__ = _auto_create_getattr_loader(__all__)
39
-
40
-
41
- def __dir__() -> list[str]:
42
- """Get the attributes of the pydantic module."""
43
- return list(__all__)