hammad-python 0.0.15__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 (111) hide show
  1. hammad/__init__.py +178 -0
  2. hammad/_internal.py +237 -0
  3. hammad/cache/__init__.py +40 -0
  4. hammad/cache/base_cache.py +181 -0
  5. hammad/cache/cache.py +169 -0
  6. hammad/cache/decorators.py +261 -0
  7. hammad/cache/file_cache.py +80 -0
  8. hammad/cache/ttl_cache.py +74 -0
  9. hammad/cli/__init__.py +35 -0
  10. hammad/cli/_runner.py +265 -0
  11. hammad/cli/animations.py +573 -0
  12. hammad/cli/plugins.py +836 -0
  13. hammad/cli/styles/__init__.py +55 -0
  14. hammad/cli/styles/settings.py +139 -0
  15. hammad/cli/styles/types.py +358 -0
  16. hammad/cli/styles/utils.py +626 -0
  17. hammad/data/__init__.py +83 -0
  18. hammad/data/collections/__init__.py +44 -0
  19. hammad/data/collections/collection.py +274 -0
  20. hammad/data/collections/indexes/__init__.py +37 -0
  21. hammad/data/collections/indexes/qdrant/__init__.py +1 -0
  22. hammad/data/collections/indexes/qdrant/index.py +735 -0
  23. hammad/data/collections/indexes/qdrant/settings.py +94 -0
  24. hammad/data/collections/indexes/qdrant/utils.py +220 -0
  25. hammad/data/collections/indexes/tantivy/__init__.py +1 -0
  26. hammad/data/collections/indexes/tantivy/index.py +428 -0
  27. hammad/data/collections/indexes/tantivy/settings.py +51 -0
  28. hammad/data/collections/indexes/tantivy/utils.py +200 -0
  29. hammad/data/configurations/__init__.py +35 -0
  30. hammad/data/configurations/configuration.py +564 -0
  31. hammad/data/models/__init__.py +55 -0
  32. hammad/data/models/extensions/__init__.py +4 -0
  33. hammad/data/models/extensions/pydantic/__init__.py +42 -0
  34. hammad/data/models/extensions/pydantic/converters.py +759 -0
  35. hammad/data/models/fields.py +546 -0
  36. hammad/data/models/model.py +1078 -0
  37. hammad/data/models/utils.py +280 -0
  38. hammad/data/sql/__init__.py +23 -0
  39. hammad/data/sql/database.py +578 -0
  40. hammad/data/sql/types.py +141 -0
  41. hammad/data/types/__init__.py +39 -0
  42. hammad/data/types/file.py +358 -0
  43. hammad/data/types/multimodal/__init__.py +24 -0
  44. hammad/data/types/multimodal/audio.py +96 -0
  45. hammad/data/types/multimodal/image.py +80 -0
  46. hammad/data/types/text.py +1066 -0
  47. hammad/formatting/__init__.py +20 -0
  48. hammad/formatting/json/__init__.py +27 -0
  49. hammad/formatting/json/converters.py +158 -0
  50. hammad/formatting/text/__init__.py +63 -0
  51. hammad/formatting/text/converters.py +723 -0
  52. hammad/formatting/text/markdown.py +131 -0
  53. hammad/formatting/yaml/__init__.py +26 -0
  54. hammad/formatting/yaml/converters.py +5 -0
  55. hammad/genai/__init__.py +78 -0
  56. hammad/genai/agents/__init__.py +1 -0
  57. hammad/genai/agents/types/__init__.py +35 -0
  58. hammad/genai/agents/types/history.py +277 -0
  59. hammad/genai/agents/types/tool.py +490 -0
  60. hammad/genai/embedding_models/__init__.py +41 -0
  61. hammad/genai/embedding_models/embedding_model.py +193 -0
  62. hammad/genai/embedding_models/embedding_model_name.py +77 -0
  63. hammad/genai/embedding_models/embedding_model_request.py +65 -0
  64. hammad/genai/embedding_models/embedding_model_response.py +69 -0
  65. hammad/genai/embedding_models/run.py +161 -0
  66. hammad/genai/language_models/__init__.py +35 -0
  67. hammad/genai/language_models/_streaming.py +622 -0
  68. hammad/genai/language_models/_types.py +276 -0
  69. hammad/genai/language_models/_utils/__init__.py +31 -0
  70. hammad/genai/language_models/_utils/_completions.py +131 -0
  71. hammad/genai/language_models/_utils/_messages.py +89 -0
  72. hammad/genai/language_models/_utils/_requests.py +202 -0
  73. hammad/genai/language_models/_utils/_structured_outputs.py +124 -0
  74. hammad/genai/language_models/language_model.py +734 -0
  75. hammad/genai/language_models/language_model_request.py +135 -0
  76. hammad/genai/language_models/language_model_response.py +219 -0
  77. hammad/genai/language_models/language_model_response_chunk.py +53 -0
  78. hammad/genai/language_models/run.py +530 -0
  79. hammad/genai/multimodal_models.py +48 -0
  80. hammad/genai/rerank_models.py +26 -0
  81. hammad/logging/__init__.py +35 -0
  82. hammad/logging/decorators.py +834 -0
  83. hammad/logging/logger.py +954 -0
  84. hammad/mcp/__init__.py +50 -0
  85. hammad/mcp/client/__init__.py +36 -0
  86. hammad/mcp/client/client.py +624 -0
  87. hammad/mcp/client/client_service.py +400 -0
  88. hammad/mcp/client/settings.py +178 -0
  89. hammad/mcp/servers/__init__.py +25 -0
  90. hammad/mcp/servers/launcher.py +1161 -0
  91. hammad/runtime/__init__.py +32 -0
  92. hammad/runtime/decorators.py +142 -0
  93. hammad/runtime/run.py +299 -0
  94. hammad/service/__init__.py +49 -0
  95. hammad/service/create.py +527 -0
  96. hammad/service/decorators.py +285 -0
  97. hammad/typing/__init__.py +435 -0
  98. hammad/web/__init__.py +43 -0
  99. hammad/web/http/__init__.py +1 -0
  100. hammad/web/http/client.py +944 -0
  101. hammad/web/models.py +277 -0
  102. hammad/web/openapi/__init__.py +1 -0
  103. hammad/web/openapi/client.py +740 -0
  104. hammad/web/search/__init__.py +1 -0
  105. hammad/web/search/client.py +1035 -0
  106. hammad/web/utils.py +472 -0
  107. {hammad_python-0.0.15.dist-info → hammad_python-0.0.16.dist-info}/METADATA +8 -1
  108. hammad_python-0.0.16.dist-info/RECORD +110 -0
  109. hammad_python-0.0.15.dist-info/RECORD +0 -4
  110. {hammad_python-0.0.15.dist-info → hammad_python-0.0.16.dist-info}/WHEEL +0 -0
  111. {hammad_python-0.0.15.dist-info → hammad_python-0.0.16.dist-info}/licenses/LICENSE +0 -0
hammad/__init__.py ADDED
@@ -0,0 +1,178 @@
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__)
hammad/_internal.py ADDED
@@ -0,0 +1,237 @@
1
+ """hammad._internal
2
+
3
+ Internal utilities"""
4
+
5
+ from typing import Any, Callable, List, Tuple, Union
6
+ import inspect
7
+ import ast
8
+ import hashlib
9
+
10
+ # pretty
11
+ from rich.traceback import install
12
+ install()
13
+
14
+ __all__ = ("create_getattr_importer",)
15
+
16
+
17
+ class GetAttrImporterError(Exception):
18
+ """An error that occurs when the `create_getattr_importer` function
19
+ fails to create a lazy loader function."""
20
+
21
+
22
+ class GetAttrImporterCache:
23
+ """Minimal cache implementation for internal use only
24
+ within the `create_getattr_importer` function."""
25
+
26
+ def __init__(self, maxsize: int = 128):
27
+ self.maxsize = maxsize
28
+ self._cache: dict[str, Any] = {}
29
+
30
+ def _make_key(self, data: str) -> str:
31
+ """Create a simple hash key from string data."""
32
+ return hashlib.sha256(data.encode("utf-8")).hexdigest()[:16]
33
+
34
+ def get(self, key: str, default: Any = None) -> Any:
35
+ """Get value from cache."""
36
+ return self._cache.get(key, default)
37
+
38
+ def set(self, key: str, value: Any) -> None:
39
+ """Set value in cache with basic LRU eviction."""
40
+ if len(self._cache) >= self.maxsize and key not in self._cache:
41
+ # Simple eviction: remove oldest (first) item
42
+ oldest_key = next(iter(self._cache))
43
+ del self._cache[oldest_key]
44
+ self._cache[key] = value
45
+
46
+ def cached_call(self, func: Callable[[str], Any]) -> Callable[[str], Any]:
47
+ """Decorator to cache function calls."""
48
+
49
+ def wrapper(arg: str) -> Any:
50
+ key = self._make_key(arg)
51
+ result = self.get(key)
52
+ if result is None:
53
+ result = func(arg)
54
+ self.set(key, result)
55
+ return result
56
+
57
+ return wrapper
58
+
59
+
60
+ # NOTE:
61
+ # SINGLETON
62
+ GETATTR_IMPORTER_PARSE_CACHE = GetAttrImporterCache(maxsize=64)
63
+ """Library-wide singleton instance providing caching for the
64
+ `_parse_type_checking_imports` function."""
65
+
66
+
67
+ GETATTR_IMPORTER_TYPE_CHECKING_CACHE = {}
68
+ """Cache for the `_parse_type_checking_imports` function."""
69
+
70
+
71
+ def _parse_type_checking_imports(source_code: str) -> dict[str, tuple[str, str]]:
72
+ """Parses the TYPE_CHECKING imports from a source code file, to create
73
+ a dictionary of local names to (module_path, original_name) tuples.
74
+
75
+ This is used to create the mapping used within the `_create_getattr_importer_from_import_dict`
76
+ function.
77
+
78
+ Args:
79
+ source_code : The source code to parse
80
+
81
+ Returns:
82
+ A dictionary mapping local names to (module_path, original_name) tuples
83
+ """
84
+
85
+ @GETATTR_IMPORTER_PARSE_CACHE.cached_call
86
+ def _exec(source_code: str) -> dict[str, tuple[str, str]]:
87
+ tree = ast.parse(source_code)
88
+ imports = {}
89
+
90
+ # Walk through the AST and find TYPE_CHECKING blocks
91
+ for node in ast.walk(tree):
92
+ if isinstance(node, ast.If):
93
+ # Check if this is a TYPE_CHECKING block
94
+ is_type_checking = False
95
+
96
+ if isinstance(node.test, ast.Name) and node.test.id == "TYPE_CHECKING":
97
+ is_type_checking = True
98
+ elif isinstance(node.test, ast.Attribute):
99
+ if (
100
+ isinstance(node.test.value, ast.Name)
101
+ and node.test.value.id == "typing"
102
+ and node.test.attr == "TYPE_CHECKING"
103
+ ):
104
+ is_type_checking = True
105
+
106
+ if is_type_checking:
107
+ # Process imports in this block
108
+ for stmt in node.body:
109
+ if isinstance(stmt, ast.ImportFrom) and stmt.module:
110
+ module_path = f".{stmt.module}"
111
+ for alias in stmt.names:
112
+ original_name = alias.name
113
+ local_name = alias.asname or original_name
114
+ imports[local_name] = (module_path, original_name)
115
+
116
+ return imports
117
+
118
+ return _exec(source_code)
119
+
120
+
121
+ def _create_getattr_importer_from_import_dict(
122
+ imports_dict: dict[str, tuple[str, str]],
123
+ package: str,
124
+ all_attrs: Union[Tuple[str, ...], List[str]],
125
+ ) -> Callable[[str], Any]:
126
+ """Creates a lazy loader function for the `__getattr__` method
127
+ within `__init__.py` modules in Python packages.
128
+
129
+ Args:
130
+ imports_dict : Dictionary mapping attribute names to (module_path, original_name) tuples
131
+ package : The package name for import_module
132
+ all_attrs : List of all valid attributes for this module
133
+
134
+ Returns:
135
+ A __getattr__ function that lazily imports modules
136
+ """
137
+ from importlib import import_module
138
+
139
+ cache = {}
140
+
141
+ def __getattr__(name: str) -> Any:
142
+ if name in cache:
143
+ return cache[name]
144
+
145
+ if name in imports_dict:
146
+ module_path, original_name = imports_dict[name]
147
+ module = import_module(module_path, package)
148
+ result = getattr(module, original_name)
149
+ cache[name] = result
150
+ return result
151
+
152
+ # Try to import as a submodule
153
+ try:
154
+ module_path = f".{name}"
155
+ module = import_module(module_path, package)
156
+ cache[name] = module
157
+ return module
158
+ except ImportError:
159
+ pass
160
+
161
+ raise GetAttrImporterError(f"module '{package}' has no attribute '{name}'")
162
+
163
+ return __getattr__
164
+
165
+
166
+ def create_getattr_importer(
167
+ all: Union[Tuple[str, ...], List[str]],
168
+ ) -> Callable[[str], Any]:
169
+ """Loader used internally within the `hammad` package to create lazy
170
+ loaders within `__init__.py` modules using the `TYPE_CHECKING` and
171
+ `all` source code within files.
172
+
173
+ This function is meant to be set as the `__getattr__` method / var
174
+ within modules to allow for direct lazy loading of attributes.
175
+
176
+ Example:
177
+
178
+ ```
179
+ # Create a module that contains some imports and TYPE_CHECKING
180
+ from typing import TYPE_CHECKING
181
+ from hammad.performance.imports import create_getattr_importer
182
+
183
+ if TYPE_CHECKING:
184
+ from functools import wraps
185
+
186
+ all = ("wraps")
187
+
188
+ __getattr__ = create_getattr_importer(all)
189
+
190
+ # Now, when you import this module, the `wraps` attribute will be
191
+ # lazily loaded when it is first accessed.
192
+ ```
193
+
194
+ Args:
195
+ all : The `all` tuple from the calling module
196
+
197
+ Returns:
198
+ A __getattr__ function that lazily imports modules
199
+ """
200
+ # Get the calling module's frame
201
+ frame = inspect.currentframe()
202
+ if frame is None or frame.f_back is None:
203
+ raise RuntimeError("Cannot determine calling module")
204
+
205
+ calling_frame = frame.f_back
206
+ module_name = calling_frame.f_globals.get("__name__", "")
207
+ package = calling_frame.f_globals.get("__package__", "")
208
+ filename = calling_frame.f_globals.get("__file__", "")
209
+
210
+ # Check cache first
211
+ cache_key = (filename, tuple(all))
212
+ if cache_key in GETATTR_IMPORTER_TYPE_CHECKING_CACHE:
213
+ return GETATTR_IMPORTER_TYPE_CHECKING_CACHE[cache_key]
214
+
215
+ # Read the source file
216
+ try:
217
+ with open(filename, "r") as f:
218
+ source_code = f.read()
219
+ except (IOError, OSError):
220
+ # Fallback: try to get source from the module
221
+ import sys
222
+
223
+ module = sys.modules.get(module_name)
224
+ if module:
225
+ source_code = inspect.getsource(module)
226
+ else:
227
+ raise RuntimeError(f"Cannot read source for module {module_name}")
228
+
229
+ # Parse the source to extract TYPE_CHECKING imports
230
+ imports_map = _parse_type_checking_imports(source_code)
231
+
232
+ # Filter to only include exports that are in __all__
233
+ filtered_map = {name: path for name, path in imports_map.items() if name in all}
234
+
235
+ loader = _create_getattr_importer_from_import_dict(filtered_map, package, all)
236
+ GETATTR_IMPORTER_TYPE_CHECKING_CACHE[cache_key] = loader
237
+ return loader
@@ -0,0 +1,40 @@
1
+ """hammad.cache"""
2
+
3
+ from typing import TYPE_CHECKING
4
+ from .._internal import create_getattr_importer
5
+
6
+
7
+ if TYPE_CHECKING:
8
+ from .base_cache import BaseCache, CacheParams, CacheReturn, CacheType
9
+ from .file_cache import FileCache, FileCacheLocation
10
+ from .ttl_cache import TTLCache
11
+ from .cache import Cache, create_cache
12
+ from .decorators import cached, auto_cached, clear_decorator_cache
13
+
14
+
15
+ __all__ = (
16
+ # hammad.performance.cache.base_cache
17
+ "BaseCache",
18
+ "CacheParams",
19
+ "CacheReturn",
20
+ "CacheType",
21
+ # hammad.performance.cache.file_cache
22
+ "FileCache",
23
+ "FileCacheLocation",
24
+ # hammad.performance.cache.ttl_cache
25
+ "TTLCache",
26
+ # hammad.performance.cache.cache
27
+ "Cache",
28
+ "create_cache",
29
+ # hammad.performance.cache.decorators
30
+ "cached",
31
+ "auto_cached",
32
+ "clear_decorator_cache",
33
+ )
34
+
35
+
36
+ __getattr__ = create_getattr_importer(__all__)
37
+
38
+
39
+ def __dir__() -> list[str]:
40
+ return sorted(__all__)
@@ -0,0 +1,181 @@
1
+ """hammad.cache.base_cache"""
2
+
3
+ from dataclasses import dataclass
4
+ import hashlib
5
+ import inspect
6
+ from typing import Any, Literal, ParamSpec, TypeAlias, TypeVar, get_args
7
+
8
+ __all__ = (
9
+ "BaseCache",
10
+ "CacheType",
11
+ "CacheParams",
12
+ "CacheReturn",
13
+ )
14
+
15
+
16
+ CacheType: TypeAlias = Literal["ttl", "file"]
17
+ """Type of caches that can be created using `hammad`.
18
+
19
+ - `"ttl"`: Time-to-live cache.
20
+ - `"file"`: File-based cache.
21
+ """
22
+
23
+ CacheParams = ParamSpec("CacheParams")
24
+ """Parameter specification for cache functions."""
25
+
26
+ CacheReturn = TypeVar("CacheReturn")
27
+ """Return type for cache functions."""
28
+
29
+
30
+ @dataclass
31
+ class BaseCache:
32
+ """Base class for all caches created using `hammad`."""
33
+
34
+ type: CacheType
35
+ """Type of cache."""
36
+
37
+ def __post_init__(self) -> None:
38
+ """Post-initialization hook."""
39
+ if self.type not in get_args(CacheType):
40
+ raise ValueError(f"Invalid cache type: {self.type}")
41
+
42
+ def __contains__(self, key: str) -> bool:
43
+ """Check if key exists in cache."""
44
+ raise NotImplementedError("Subclasses must implement __contains__")
45
+
46
+ def __getitem__(self, key: str) -> Any:
47
+ """Get value for key."""
48
+ raise NotImplementedError("Subclasses must implement __getitem__")
49
+
50
+ def __setitem__(self, key: str, value: Any) -> None:
51
+ """Set value for key."""
52
+ raise NotImplementedError("Subclasses must implement __setitem__")
53
+
54
+ def get(self, key: str, default: Any = None) -> Any:
55
+ """Get value with default if key doesn't exist."""
56
+ try:
57
+ return self[key]
58
+ except KeyError:
59
+ return default
60
+
61
+ def clear(self) -> None:
62
+ """Clear all cached items."""
63
+ raise NotImplementedError("Subclasses must implement clear")
64
+
65
+ def make_hashable(self, obj: Any) -> str:
66
+ """
67
+ Convert any object to a stable hash string.
68
+
69
+ Uses SHA-256 to generate consistent hash representations.
70
+ Handles nested structures recursively.
71
+
72
+ Args:
73
+ obj: Object to hash
74
+
75
+ Returns:
76
+ Hexadecimal hash string
77
+ """
78
+
79
+ def _hash_obj(data: Any) -> str:
80
+ """Internal recursive hashing function with memoization."""
81
+ # Handle None first
82
+ if data is None:
83
+ return "null"
84
+
85
+ if isinstance(data, bool):
86
+ return f"bool:{data}"
87
+ elif isinstance(data, int):
88
+ return f"int:{data}"
89
+ elif isinstance(data, float):
90
+ if data != data: # NaN
91
+ return "float:nan"
92
+ elif data == float("inf"):
93
+ return "float:inf"
94
+ elif data == float("-inf"):
95
+ return "float:-inf"
96
+ else:
97
+ return f"float:{data}"
98
+ elif isinstance(data, str):
99
+ return f"str:{data}"
100
+ elif isinstance(data, bytes):
101
+ return f"bytes:{data.hex()}"
102
+
103
+ # Handle collections
104
+ elif isinstance(data, (list, tuple)):
105
+ collection_type = "list" if isinstance(data, list) else "tuple"
106
+ items = [_hash_obj(item) for item in data]
107
+ return f"{collection_type}:[{','.join(items)}]"
108
+
109
+ elif isinstance(data, set):
110
+ try:
111
+ sorted_items = sorted(data, key=lambda x: str(x))
112
+ except TypeError:
113
+ sorted_items = sorted(
114
+ data, key=lambda x: (type(x).__name__, str(x))
115
+ )
116
+ items = [_hash_obj(item) for item in sorted_items]
117
+ return f"set:{{{','.join(items)}}}"
118
+
119
+ elif isinstance(data, dict):
120
+ try:
121
+ sorted_items = sorted(data.items(), key=lambda x: str(x[0]))
122
+ except TypeError:
123
+ # Fallback for non-comparable keys
124
+ sorted_items = sorted(
125
+ data.items(), key=lambda x: (type(x[0]).__name__, str(x[0]))
126
+ )
127
+ pairs = [f"{_hash_obj(k)}:{_hash_obj(v)}" for k, v in sorted_items]
128
+ return f"dict:{{{','.join(pairs)}}}"
129
+
130
+ elif isinstance(data, type):
131
+ module = getattr(data, "__module__", "builtins")
132
+ qualname = getattr(data, "__qualname__", data.__name__)
133
+ return f"type:{module}.{qualname}"
134
+
135
+ elif callable(data):
136
+ module = getattr(data, "__module__", "unknown")
137
+ qualname = getattr(
138
+ data, "__qualname__", getattr(data, "__name__", "unknown_callable")
139
+ )
140
+
141
+ try:
142
+ source = inspect.getsource(data)
143
+ normalized_source = " ".join(source.split())
144
+ return f"callable:{module}.{qualname}:{hash(normalized_source)}"
145
+ except (OSError, TypeError, IndentationError):
146
+ return f"callable:{module}.{qualname}"
147
+
148
+ elif hasattr(data, "__dict__"):
149
+ class_info = (
150
+ f"{data.__class__.__module__}.{data.__class__.__qualname__}"
151
+ )
152
+ obj_dict = {"__class__": class_info, **data.__dict__}
153
+ return f"object:{_hash_obj(obj_dict)}"
154
+
155
+ elif hasattr(data, "__slots__"):
156
+ class_info = (
157
+ f"{data.__class__.__module__}.{data.__class__.__qualname__}"
158
+ )
159
+ slot_dict = {
160
+ slot: getattr(data, slot, None)
161
+ for slot in data.__slots__
162
+ if hasattr(data, slot)
163
+ }
164
+ obj_dict = {"__class__": class_info, **slot_dict}
165
+ return f"slotted_object:{_hash_obj(obj_dict)}"
166
+
167
+ else:
168
+ try:
169
+ repr_str = repr(data)
170
+ return f"repr:{type(data).__name__}:{repr_str}"
171
+ except Exception:
172
+ # Ultimate fallback
173
+ return f"unknown:{type(data).__name__}:{id(data)}"
174
+
175
+ # Generate the hash representation
176
+ hash_representation = _hash_obj(obj)
177
+
178
+ # Create final SHA-256 hash
179
+ return hashlib.sha256(
180
+ hash_representation.encode("utf-8", errors="surrogatepass")
181
+ ).hexdigest()