hammad-python 0.0.10__py3-none-any.whl → 0.0.11__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 (74) hide show
  1. hammad/__init__.py +64 -10
  2. hammad/based/__init__.py +52 -0
  3. hammad/based/fields.py +546 -0
  4. hammad/based/model.py +968 -0
  5. hammad/based/utils.py +455 -0
  6. hammad/cache/__init__.py +30 -0
  7. hammad/{cache.py → cache/_cache.py} +83 -12
  8. hammad/cli/__init__.py +25 -0
  9. hammad/cli/plugins/__init__.py +786 -0
  10. hammad/cli/styles/__init__.py +5 -0
  11. hammad/cli/styles/animations.py +548 -0
  12. hammad/cli/styles/settings.py +135 -0
  13. hammad/cli/styles/types.py +358 -0
  14. hammad/cli/styles/utils.py +480 -0
  15. hammad/data/__init__.py +51 -0
  16. hammad/data/collections/__init__.py +32 -0
  17. hammad/data/collections/base_collection.py +58 -0
  18. hammad/data/collections/collection.py +227 -0
  19. hammad/data/collections/searchable_collection.py +556 -0
  20. hammad/data/collections/vector_collection.py +497 -0
  21. hammad/data/databases/__init__.py +21 -0
  22. hammad/data/databases/database.py +551 -0
  23. hammad/data/types/__init__.py +33 -0
  24. hammad/data/types/files/__init__.py +1 -0
  25. hammad/data/types/files/audio.py +81 -0
  26. hammad/data/types/files/configuration.py +475 -0
  27. hammad/data/types/files/document.py +195 -0
  28. hammad/data/types/files/file.py +358 -0
  29. hammad/data/types/files/image.py +80 -0
  30. hammad/json/__init__.py +21 -0
  31. hammad/{utils/json → json}/converters.py +4 -1
  32. hammad/logging/__init__.py +27 -0
  33. hammad/logging/decorators.py +432 -0
  34. hammad/logging/logger.py +534 -0
  35. hammad/pydantic/__init__.py +43 -0
  36. hammad/{utils/pydantic → pydantic}/converters.py +2 -1
  37. hammad/pydantic/models/__init__.py +28 -0
  38. hammad/pydantic/models/arbitrary_model.py +46 -0
  39. hammad/pydantic/models/cacheable_model.py +79 -0
  40. hammad/pydantic/models/fast_model.py +318 -0
  41. hammad/pydantic/models/function_model.py +176 -0
  42. hammad/pydantic/models/subscriptable_model.py +63 -0
  43. hammad/text/__init__.py +37 -0
  44. hammad/text/text.py +1068 -0
  45. hammad/text/utils/__init__.py +1 -0
  46. hammad/{utils/text → text/utils}/converters.py +2 -2
  47. hammad/text/utils/markdown/__init__.py +1 -0
  48. hammad/{utils → text/utils}/markdown/converters.py +3 -3
  49. hammad/{utils → text/utils}/markdown/formatting.py +1 -1
  50. hammad/{utils/typing/utils.py → typing/__init__.py} +75 -2
  51. hammad/web/__init__.py +42 -0
  52. hammad/web/http/__init__.py +1 -0
  53. hammad/web/http/client.py +944 -0
  54. hammad/web/openapi/client.py +740 -0
  55. hammad/web/search/__init__.py +1 -0
  56. hammad/web/search/client.py +936 -0
  57. hammad/web/utils.py +463 -0
  58. hammad/yaml/__init__.py +30 -0
  59. hammad/yaml/converters.py +19 -0
  60. {hammad_python-0.0.10.dist-info → hammad_python-0.0.11.dist-info}/METADATA +14 -8
  61. hammad_python-0.0.11.dist-info/RECORD +65 -0
  62. hammad/database.py +0 -447
  63. hammad/logger.py +0 -273
  64. hammad/types/color.py +0 -951
  65. hammad/utils/json/__init__.py +0 -0
  66. hammad/utils/markdown/__init__.py +0 -0
  67. hammad/utils/pydantic/__init__.py +0 -0
  68. hammad/utils/text/__init__.py +0 -0
  69. hammad/utils/typing/__init__.py +0 -0
  70. hammad_python-0.0.10.dist-info/RECORD +0 -22
  71. /hammad/{types/__init__.py → py.typed} +0 -0
  72. /hammad/{utils → web/openapi}/__init__.py +0 -0
  73. {hammad_python-0.0.10.dist-info → hammad_python-0.0.11.dist-info}/WHEEL +0 -0
  74. {hammad_python-0.0.10.dist-info → hammad_python-0.0.11.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,432 @@
1
+ """hammad.logging.tracers"""
2
+
3
+ from functools import wraps, update_wrapper
4
+ from typing import (
5
+ Any,
6
+ Callable,
7
+ ParamSpec,
8
+ TypeVar,
9
+ List,
10
+ overload,
11
+ Union,
12
+ Type,
13
+ Dict,
14
+ Optional,
15
+ )
16
+
17
+ import logging
18
+ import time
19
+ import inspect
20
+ from .logger import Logger, create_logger, LoggerLevelName
21
+ from ..cli.styles.types import (
22
+ CLIStyleType,
23
+ CLIStyleBackgroundType,
24
+ )
25
+
26
+
27
+ _P = ParamSpec("_P")
28
+ _R = TypeVar("_R")
29
+
30
+ __all__ = (
31
+ "trace_function",
32
+ "trace_cls",
33
+ "trace",
34
+ )
35
+
36
+
37
+ def trace_function(
38
+ fn: Optional[Callable[_P, _R]] = None,
39
+ *,
40
+ parameters: List[str] = [],
41
+ logger: Union[logging.Logger, Logger, None] = None,
42
+ level: Union[LoggerLevelName, str, int] = "debug",
43
+ rich: bool = True,
44
+ style: Union[CLIStyleType, str] = "white",
45
+ bg: Union[CLIStyleBackgroundType, str] = None,
46
+ ) -> Union[Callable[_P, _R], Callable[[Callable[_P, _R]], Callable[_P, _R]]]:
47
+ """
48
+ Tracing decorator that logs the execution of any function, including
49
+ class methods.
50
+
51
+ You can optionally specify specific parameters, that will display
52
+ 'live updates' of the parameter values as they change.
53
+
54
+ Args:
55
+ fn: The function to trace.
56
+ parameters: The parameters to trace.
57
+ logger: The logger to use.
58
+ level: The level to log at.
59
+ rich: Whether to use rich for the logging.
60
+ style: The style to use for the logging. This can be a string, or a dictionary
61
+ of style settings.
62
+ bg: The background to use for the logging. This can be a string, or a dictionary
63
+ of background settings.
64
+
65
+ Returns:
66
+ The decorated function or a decorator function.
67
+ """
68
+
69
+ def decorator(target_fn: Callable[_P, _R]) -> Callable[_P, _R]:
70
+ @wraps(target_fn)
71
+ def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _R:
72
+ # Get or create logger
73
+ if logger is None:
74
+ _logger = create_logger(
75
+ name=f"trace.{target_fn.__module__}.{target_fn.__name__}",
76
+ level=level,
77
+ rich=rich,
78
+ )
79
+ elif isinstance(logger, Logger):
80
+ _logger = logger
81
+ else:
82
+ # It's a standard logging.Logger, wrap it
83
+ _logger = create_logger(name=logger.name)
84
+ _logger._logger = logger
85
+
86
+ # Get function signature for parameter tracking
87
+ sig = inspect.signature(target_fn)
88
+ bound_args = sig.bind(*args, **kwargs)
89
+ bound_args.apply_defaults()
90
+
91
+ # Build entry message
92
+ func_name = target_fn.__name__
93
+ module_name = target_fn.__module__
94
+
95
+ if rich and bg:
96
+ # Create a styled panel for function entry
97
+ entry_msg = f"[{style}]→ Entering {module_name}.{func_name}()[/{style}]"
98
+ else:
99
+ entry_msg = f"→ Entering {module_name}.{func_name}()"
100
+
101
+ # Log parameters if requested
102
+ if parameters:
103
+ param_info = []
104
+ for param in parameters:
105
+ if param in bound_args.arguments:
106
+ value = bound_args.arguments[param]
107
+ param_info.append(f"{param}={repr(value)}")
108
+
109
+ if param_info:
110
+ entry_msg += f"\n Parameters: {', '.join(param_info)}"
111
+
112
+ # Log function entry
113
+ _logger.log(level, entry_msg)
114
+
115
+ # Track execution time
116
+ start_time = time.time()
117
+
118
+ try:
119
+ # Execute the function
120
+ result = target_fn(*args, **kwargs)
121
+
122
+ # Calculate execution time
123
+ exec_time = time.time() - start_time
124
+
125
+ # Build exit message
126
+ if rich and bg:
127
+ exit_msg = f"[{style}]← Exiting {module_name}.{func_name}() [dim](took {exec_time:.3f}s)[/dim][/{style}]"
128
+ else:
129
+ exit_msg = (
130
+ f"← Exiting {module_name}.{func_name}() (took {exec_time:.3f}s)"
131
+ )
132
+
133
+ # Log the result if it's not None
134
+ if result is not None:
135
+ exit_msg += f"\n Result: {repr(result)}"
136
+
137
+ _logger.log(level, exit_msg)
138
+
139
+ return result
140
+
141
+ except Exception as e:
142
+ # Calculate execution time
143
+ exec_time = time.time() - start_time
144
+
145
+ # Build error message
146
+ error_style = "bold red" if rich else None
147
+ if rich:
148
+ error_msg = f"[{error_style}]✗ Exception in {module_name}.{func_name}() [dim](after {exec_time:.3f}s)[/dim][/{error_style}]"
149
+ error_msg += f"\n [red]{type(e).__name__}: {str(e)}[/red]"
150
+ else:
151
+ error_msg = f"✗ Exception in {module_name}.{func_name}() (after {exec_time:.3f}s)"
152
+ error_msg += f"\n {type(e).__name__}: {str(e)}"
153
+
154
+ # Log at error level for exceptions
155
+ _logger.error(error_msg)
156
+
157
+ # Re-raise the exception
158
+ raise
159
+
160
+ return wrapper
161
+
162
+ if fn is None:
163
+ # Called with parameters: @trace_function(parameters=["x"])
164
+ return decorator
165
+ else:
166
+ # Called directly: @trace_function
167
+ return decorator(fn)
168
+
169
+
170
+ def trace_cls(
171
+ cls: Optional[Type[Any]] = None,
172
+ *,
173
+ attributes: List[str] = [],
174
+ functions: List[str] = [],
175
+ logger: Union[logging.Logger, Logger, None] = None,
176
+ level: Union[LoggerLevelName, str, int] = "debug",
177
+ rich: bool = True,
178
+ style: Union[CLIStyleType, str] = "white",
179
+ bg: Union[CLIStyleBackgroundType, str] = None,
180
+ ) -> Union[Type[Any], Callable[[Type[Any]], Type[Any]]]:
181
+ """
182
+ Tracing decorator that logs the execution of any class, including
183
+ class methods.
184
+
185
+ Unlike the `trace_function` decorator, this decorator must take
186
+ in either a list of attributes, or a list of functions to display
187
+ 'live updates' of the attribute values as they change.
188
+
189
+ Args:
190
+ cls: The class to trace.
191
+ attributes: The attributes to trace.
192
+ functions: The functions to trace.
193
+ logger: An optional logger to use.
194
+ level: An optional level to log at.
195
+ rich: Whether to use rich for the logging.
196
+ style: The style to use for the logging. This can be a string, or a dictionary
197
+ of style settings.
198
+ bg: The background to use for the logging. This can be a string, or a dictionary
199
+ of background settings.
200
+
201
+ Returns:
202
+ The traced class or a decorator function.
203
+ """
204
+
205
+ def decorator(target_cls: Type[Any]) -> Type[Any]:
206
+ # Get or create logger for the class
207
+ if logger is None:
208
+ _logger = create_logger(
209
+ name=f"trace.{target_cls.__module__}.{target_cls.__name__}",
210
+ level=level,
211
+ rich=rich,
212
+ )
213
+ elif isinstance(logger, Logger):
214
+ _logger = logger
215
+ else:
216
+ # It's a standard logging.Logger, wrap it
217
+ _logger = create_logger(name=logger.name)
218
+ _logger._logger = logger
219
+
220
+ # Store original __init__ method
221
+ original_init = target_cls.__init__
222
+
223
+ # Create wrapper for __init__ to log instance creation and track attributes
224
+ @wraps(original_init)
225
+ def traced_init(self, *args, **kwargs):
226
+ # Log instance creation
227
+ if rich:
228
+ create_msg = (
229
+ f"[{style}]🏗 Creating instance of {target_cls.__name__}[/{style}]"
230
+ )
231
+ else:
232
+ create_msg = f"Creating instance of {target_cls.__name__}"
233
+
234
+ _logger.log(level, create_msg)
235
+
236
+ # Call original __init__
237
+ original_init(self, *args, **kwargs)
238
+
239
+ # Log initial attribute values if requested
240
+ if attributes:
241
+ attr_info = []
242
+ for attr in attributes:
243
+ if hasattr(self, attr):
244
+ value = getattr(self, attr)
245
+ attr_info.append(f"{attr}={repr(value)}")
246
+
247
+ if attr_info:
248
+ if rich:
249
+ attr_msg = f"[{style}] Initial attributes: {', '.join(attr_info)}[/{style}]"
250
+ else:
251
+ attr_msg = f" Initial attributes: {', '.join(attr_info)}"
252
+ _logger.log(level, attr_msg)
253
+
254
+ # Replace __init__ with traced version
255
+ target_cls.__init__ = traced_init
256
+
257
+ # Create wrapper for __setattr__ to track attribute changes
258
+ if attributes:
259
+ original_setattr = (
260
+ target_cls.__setattr__
261
+ if hasattr(target_cls, "__setattr__")
262
+ else object.__setattr__
263
+ )
264
+
265
+ def traced_setattr(self, name, value):
266
+ # Check if this is a tracked attribute
267
+ if name in attributes:
268
+ # Get old value if it exists
269
+ old_value = getattr(self, name, "<not set>")
270
+
271
+ # Call original __setattr__
272
+ if original_setattr == object.__setattr__:
273
+ object.__setattr__(self, name, value)
274
+ else:
275
+ original_setattr(self, name, value)
276
+
277
+ # Log the change
278
+ if rich:
279
+ change_msg = f"[{style}]{target_cls.__name__}.{name}: {repr(old_value)} → {repr(value)}[/{style}]"
280
+ else:
281
+ change_msg = f"{target_cls.__name__}.{name}: {repr(old_value)} → {repr(value)}"
282
+
283
+ _logger.log(level, change_msg)
284
+ else:
285
+ # Not a tracked attribute, just set it normally
286
+ if original_setattr == object.__setattr__:
287
+ object.__setattr__(self, name, value)
288
+ else:
289
+ original_setattr(self, name, value)
290
+
291
+ target_cls.__setattr__ = traced_setattr
292
+
293
+ # Trace specific functions if requested
294
+ if functions:
295
+ for func_name in functions:
296
+ if hasattr(target_cls, func_name):
297
+ func = getattr(target_cls, func_name)
298
+ if callable(func) and not isinstance(func, type):
299
+ # Apply trace_function decorator to this method
300
+ traced_func = trace_function(
301
+ func,
302
+ logger=_logger,
303
+ level=level,
304
+ rich=rich,
305
+ style=style,
306
+ bg=bg,
307
+ )
308
+ setattr(target_cls, func_name, traced_func)
309
+
310
+ # Log class decoration
311
+ if rich:
312
+ decorate_msg = f"[{style}]✨ Decorated class {target_cls.__name__} with tracing[/{style}]"
313
+ else:
314
+ decorate_msg = f"Decorated class {target_cls.__name__} with tracing"
315
+
316
+ _logger.log(level, decorate_msg)
317
+
318
+ return target_cls
319
+
320
+ if cls is None:
321
+ # Called with parameters: @trace_cls(attributes=["x"])
322
+ return decorator
323
+ else:
324
+ # Called directly: @trace_cls
325
+ return decorator(cls)
326
+
327
+
328
+ # Decorator overloads for better type hints
329
+ @overload
330
+ def trace(
331
+ func_or_cls: Callable[_P, _R],
332
+ ) -> Callable[_P, _R]:
333
+ """Decorator to add log tracing over a function or class."""
334
+
335
+
336
+ @overload
337
+ def trace(
338
+ func_or_cls: Type[Any],
339
+ ) -> Type[Any]:
340
+ """Decorator to add log tracing over a class."""
341
+
342
+
343
+ @overload
344
+ def trace(
345
+ *,
346
+ parameters: List[str] = [],
347
+ attributes: List[str] = [],
348
+ functions: List[str] = [],
349
+ logger: Union[logging.Logger, Logger, None] = None,
350
+ level: Union[LoggerLevelName, str, int] = "debug",
351
+ rich: bool = True,
352
+ style: Union[CLIStyleType, str] = "white",
353
+ bg: Union[CLIStyleBackgroundType, str] = None,
354
+ ) -> Callable[[Union[Callable[_P, _R], Type[Any]]], Union[Callable[_P, _R], Type[Any]]]:
355
+ """Decorator to add log tracing over a function or class."""
356
+
357
+
358
+ def trace(
359
+ func_or_cls: Union[Callable[_P, _R], Type[Any], None] = None,
360
+ *,
361
+ parameters: List[str] = [],
362
+ attributes: List[str] = [],
363
+ functions: List[str] = [],
364
+ logger: Union[logging.Logger, Logger, None] = None,
365
+ level: Union[LoggerLevelName, str, int] = "debug",
366
+ rich: bool = True,
367
+ style: Union[CLIStyleType, str] = "bold blue",
368
+ bg: Union[CLIStyleBackgroundType, str] = None,
369
+ ) -> Union[
370
+ Callable[_P, _R],
371
+ Type[Any],
372
+ Callable[[Union[Callable[_P, _R], Type[Any]]], Union[Callable[_P, _R], Type[Any]]],
373
+ ]:
374
+ """
375
+ Universal tracing decorator that can be applied to both functions and classes.
376
+
377
+ Can be used in three ways:
378
+ 1. @log (direct decoration)
379
+ 2. @log() (parameterized with defaults)
380
+ 3. @log(parameters=["x"], level="info") (parameterized with custom settings)
381
+
382
+ When applied to a function, it logs entry/exit and optionally tracks parameters.
383
+ When applied to a class, it can track attribute changes and log specific methods.
384
+
385
+ Args:
386
+ func_or_cls: The function or class to log (when used directly)
387
+ parameters: For functions, the parameters to log
388
+ attributes: For classes, the attributes to track changes
389
+ functions: For classes, the methods to log
390
+ logger: The logger to use (creates one if not provided)
391
+ level: The logging level
392
+ rich: Whether to use rich formatting
393
+ style: The style for rich formatting
394
+ bg: The background style for rich formatting
395
+
396
+ Returns:
397
+ The decorated function/class or a decorator function
398
+ """
399
+
400
+ def decorator(
401
+ target: Union[Callable[_P, _R], Type[Any]],
402
+ ) -> Union[Callable[_P, _R], Type[Any]]:
403
+ if inspect.isclass(target):
404
+ # It's a class
405
+ return trace_cls(
406
+ target,
407
+ attributes=attributes,
408
+ functions=functions,
409
+ logger=logger,
410
+ level=level,
411
+ rich=rich,
412
+ style=style,
413
+ bg=bg,
414
+ )
415
+ else:
416
+ # It's a function
417
+ return trace_function(
418
+ target,
419
+ parameters=parameters,
420
+ logger=logger,
421
+ level=level,
422
+ rich=rich,
423
+ style=style,
424
+ bg=bg,
425
+ )
426
+
427
+ if func_or_cls is None:
428
+ # Called with parameters: @log(parameters=["x"])
429
+ return decorator
430
+ else:
431
+ # Called directly: @log
432
+ return decorator(func_or_cls)