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.
- hammad/__init__.py +169 -56
- hammad/_core/__init__.py +1 -0
- hammad/_core/_utils/__init__.py +4 -0
- hammad/_core/_utils/_import_utils.py +182 -0
- hammad/ai/__init__.py +59 -0
- hammad/ai/_utils.py +142 -0
- hammad/ai/completions/__init__.py +44 -0
- hammad/ai/completions/client.py +729 -0
- hammad/ai/completions/create.py +686 -0
- hammad/ai/completions/types.py +711 -0
- hammad/ai/completions/utils.py +374 -0
- hammad/ai/embeddings/__init__.py +35 -0
- hammad/ai/embeddings/client/__init__.py +1 -0
- hammad/ai/embeddings/client/base_embeddings_client.py +26 -0
- hammad/ai/embeddings/client/fastembed_text_embeddings_client.py +200 -0
- hammad/ai/embeddings/client/litellm_embeddings_client.py +288 -0
- hammad/ai/embeddings/create.py +159 -0
- hammad/ai/embeddings/types.py +69 -0
- hammad/base/__init__.py +35 -0
- hammad/{based → base}/fields.py +23 -23
- hammad/{based → base}/model.py +124 -14
- hammad/base/utils.py +280 -0
- hammad/cache/__init__.py +30 -12
- hammad/cache/base_cache.py +181 -0
- hammad/cache/cache.py +169 -0
- hammad/cache/decorators.py +261 -0
- hammad/cache/file_cache.py +80 -0
- hammad/cache/ttl_cache.py +74 -0
- hammad/cli/__init__.py +10 -2
- hammad/cli/{styles/animations.py → animations.py} +79 -23
- hammad/cli/{plugins/__init__.py → plugins.py} +85 -90
- hammad/cli/styles/__init__.py +50 -0
- hammad/cli/styles/settings.py +4 -0
- hammad/configuration/__init__.py +35 -0
- hammad/{data/types/files → configuration}/configuration.py +96 -7
- hammad/data/__init__.py +14 -26
- hammad/data/collections/__init__.py +4 -2
- hammad/data/collections/collection.py +300 -75
- hammad/data/collections/vector_collection.py +118 -12
- hammad/data/databases/__init__.py +2 -2
- hammad/data/databases/database.py +383 -32
- hammad/json/__init__.py +2 -2
- hammad/logging/__init__.py +13 -5
- hammad/logging/decorators.py +404 -2
- hammad/logging/logger.py +442 -22
- hammad/multimodal/__init__.py +24 -0
- hammad/{data/types/files → multimodal}/audio.py +21 -6
- hammad/{data/types/files → multimodal}/image.py +5 -5
- hammad/multithreading/__init__.py +304 -0
- hammad/pydantic/__init__.py +2 -2
- hammad/pydantic/converters.py +1 -1
- hammad/pydantic/models/__init__.py +2 -2
- hammad/text/__init__.py +59 -14
- hammad/text/converters.py +723 -0
- hammad/text/{utils/markdown/formatting.py → markdown.py} +25 -23
- hammad/text/text.py +12 -14
- hammad/types/__init__.py +11 -0
- hammad/{data/types/files → types}/file.py +18 -18
- hammad/typing/__init__.py +138 -84
- hammad/web/__init__.py +3 -2
- hammad/web/models.py +245 -0
- hammad/web/search/client.py +75 -23
- hammad/web/utils.py +14 -5
- hammad/yaml/__init__.py +2 -2
- hammad/yaml/converters.py +1 -1
- {hammad_python-0.0.11.dist-info → hammad_python-0.0.13.dist-info}/METADATA +4 -1
- hammad_python-0.0.13.dist-info/RECORD +85 -0
- hammad/based/__init__.py +0 -52
- hammad/based/utils.py +0 -455
- hammad/cache/_cache.py +0 -746
- hammad/data/types/__init__.py +0 -33
- hammad/data/types/files/__init__.py +0 -1
- hammad/data/types/files/document.py +0 -195
- hammad/text/utils/__init__.py +0 -1
- hammad/text/utils/converters.py +0 -229
- hammad/text/utils/markdown/__init__.py +0 -1
- hammad/text/utils/markdown/converters.py +0 -506
- hammad_python-0.0.11.dist-info/RECORD +0 -65
- {hammad_python-0.0.11.dist-info → hammad_python-0.0.13.dist-info}/WHEEL +0 -0
- {hammad_python-0.0.11.dist-info → hammad_python-0.0.13.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,4 @@
|
|
1
|
-
"""hammad.text.utils.markdown
|
1
|
+
"""hammad.text.utils.markdown"""
|
2
2
|
|
3
3
|
from typing import (
|
4
4
|
List,
|
@@ -7,60 +7,62 @@ from typing import (
|
|
7
7
|
)
|
8
8
|
|
9
9
|
__all__ = (
|
10
|
-
"
|
11
|
-
"
|
12
|
-
"
|
13
|
-
"
|
14
|
-
"
|
15
|
-
"
|
16
|
-
"
|
17
|
-
"
|
18
|
-
"
|
19
|
-
"
|
20
|
-
"
|
10
|
+
"markdown_bold",
|
11
|
+
"markdown_italic",
|
12
|
+
"markdown_code",
|
13
|
+
"markdown_code_block",
|
14
|
+
"markdown_heading",
|
15
|
+
"markdown_link",
|
16
|
+
"markdown_list_item",
|
17
|
+
"markdown_table_row",
|
18
|
+
"markdown_blockquote",
|
19
|
+
"markdown_horizontal_rule",
|
20
|
+
"markdown_table",
|
21
21
|
)
|
22
22
|
|
23
23
|
|
24
|
-
def
|
24
|
+
def markdown_bold(text: str) -> str:
|
25
25
|
"""Format text as bold in Markdown."""
|
26
26
|
return f"**{text}**"
|
27
27
|
|
28
28
|
|
29
|
-
def
|
29
|
+
def markdown_italic(text: str) -> str:
|
30
30
|
"""Format text as italic in Markdown."""
|
31
31
|
return f"*{text}*"
|
32
32
|
|
33
33
|
|
34
|
-
def
|
34
|
+
def markdown_code(text: str) -> str:
|
35
35
|
"""Format text as inline code in Markdown."""
|
36
36
|
return f"`{text}`"
|
37
37
|
|
38
38
|
|
39
|
-
def
|
39
|
+
def markdown_code_block(text: str, language: str = "") -> str:
|
40
40
|
"""Format text as a code block in Markdown."""
|
41
41
|
return f"```{language}\n{text}\n```"
|
42
42
|
|
43
43
|
|
44
|
-
def
|
44
|
+
def markdown_heading(text: str, level: int = 1) -> str:
|
45
45
|
"""Format text as a heading in Markdown."""
|
46
46
|
if not 1 <= level <= 6:
|
47
47
|
level = 1
|
48
48
|
return f"{'#' * level} {text}"
|
49
49
|
|
50
50
|
|
51
|
-
def
|
51
|
+
def markdown_link(text: str, url: str) -> str:
|
52
52
|
"""Format text as a link in Markdown."""
|
53
53
|
return f"[{text}]({url})"
|
54
54
|
|
55
55
|
|
56
|
-
def
|
56
|
+
def markdown_list_item(
|
57
|
+
text: str, level: int = 0, ordered: bool = False, index: int = 1
|
58
|
+
) -> str:
|
57
59
|
"""Format text as a list item in Markdown."""
|
58
60
|
indent = " " * level
|
59
61
|
marker = f"{index}." if ordered else "-"
|
60
62
|
return f"{indent}{marker} {text}"
|
61
63
|
|
62
64
|
|
63
|
-
def
|
65
|
+
def markdown_table_row(cells: List[str], is_header: bool = False) -> str:
|
64
66
|
"""Format a table row in Markdown."""
|
65
67
|
row = "| " + " | ".join(cells) + " |"
|
66
68
|
if is_header:
|
@@ -69,19 +71,19 @@ def table_row(cells: List[str], is_header: bool = False) -> str:
|
|
69
71
|
return row
|
70
72
|
|
71
73
|
|
72
|
-
def
|
74
|
+
def markdown_blockquote(text: str, level: int = 1) -> str:
|
73
75
|
"""Format text as a blockquote in Markdown."""
|
74
76
|
prefix = ">" * level + " "
|
75
77
|
lines = text.split("\n")
|
76
78
|
return "\n".join(f"{prefix}{line}" for line in lines)
|
77
79
|
|
78
80
|
|
79
|
-
def
|
81
|
+
def markdown_horizontal_rule() -> str:
|
80
82
|
"""Create a horizontal rule in Markdown."""
|
81
83
|
return "---"
|
82
84
|
|
83
85
|
|
84
|
-
def
|
86
|
+
def markdown_table(
|
85
87
|
headers: List[str],
|
86
88
|
rows: List[List[str]],
|
87
89
|
alignment: Optional[List[Literal["left", "center", "right"]]] = None,
|
hammad/text/text.py
CHANGED
@@ -25,13 +25,11 @@ from typing import (
|
|
25
25
|
Generic,
|
26
26
|
)
|
27
27
|
|
28
|
-
from .
|
29
|
-
|
30
|
-
|
31
|
-
)
|
32
|
-
from .utils.markdown.converters import (
|
33
|
-
convert_to_markdown,
|
28
|
+
from .markdown import (
|
29
|
+
markdown_heading,
|
30
|
+
markdown_code_block,
|
34
31
|
)
|
32
|
+
from .converters import convert_to_text
|
35
33
|
|
36
34
|
|
37
35
|
# -----------------------------------------------------------------------------
|
@@ -234,7 +232,7 @@ class BaseText(ABC):
|
|
234
232
|
if isinstance(self.content, str):
|
235
233
|
parts.append(self.content)
|
236
234
|
else:
|
237
|
-
parts.append(
|
235
|
+
parts.append(convert_to_text(self.content, **kwargs))
|
238
236
|
|
239
237
|
# Handle subsections
|
240
238
|
for section in self.sections:
|
@@ -250,7 +248,7 @@ class BaseText(ABC):
|
|
250
248
|
|
251
249
|
# Handle title
|
252
250
|
if self.title:
|
253
|
-
parts.append(
|
251
|
+
parts.append(markdown_heading(self.title, self.heading_level))
|
254
252
|
|
255
253
|
# Handle description
|
256
254
|
if self.description:
|
@@ -261,7 +259,7 @@ class BaseText(ABC):
|
|
261
259
|
if isinstance(self.content, str):
|
262
260
|
parts.append(self.content)
|
263
261
|
else:
|
264
|
-
parts.append(
|
262
|
+
parts.append(convert_to_text(self.content, **kwargs))
|
265
263
|
|
266
264
|
# Add table of contents if requested (only for top-level documents)
|
267
265
|
if kwargs.get("add_toc", False) and self.heading_level == 1:
|
@@ -271,9 +269,9 @@ class BaseText(ABC):
|
|
271
269
|
toc_headings.append((section.heading_level, section.title))
|
272
270
|
|
273
271
|
if toc_headings:
|
274
|
-
from .
|
272
|
+
from .markdown import markdown_table
|
275
273
|
|
276
|
-
parts.append(
|
274
|
+
parts.append(markdown_table(toc_headings))
|
277
275
|
|
278
276
|
# Handle subsections
|
279
277
|
for section in self.sections:
|
@@ -437,13 +435,13 @@ class CodeSection(BaseText):
|
|
437
435
|
parts = []
|
438
436
|
|
439
437
|
if self.title:
|
440
|
-
parts.append(
|
438
|
+
parts.append(markdown_heading(self.title, self.heading_level))
|
441
439
|
|
442
440
|
if self.description:
|
443
441
|
parts.append(self.description)
|
444
442
|
|
445
443
|
if self.content:
|
446
|
-
parts.append(
|
444
|
+
parts.append(markdown_code_block(str(self.content), self.language or ""))
|
447
445
|
|
448
446
|
# Handle subsections
|
449
447
|
for section in self.sections:
|
@@ -466,7 +464,7 @@ class SchemaSection(BaseText):
|
|
466
464
|
def _to_markdown(self, **kwargs) -> str:
|
467
465
|
"""Convert schema to Markdown documentation."""
|
468
466
|
if self.schema_object:
|
469
|
-
return
|
467
|
+
return convert_to_text(
|
470
468
|
self.schema_object,
|
471
469
|
name=self.title,
|
472
470
|
description=self.description,
|
hammad/types/__init__.py
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
"""hammad.types
|
2
|
+
|
3
|
+
Contains functional aliases, types and model-like objects that are used
|
4
|
+
internally within the `hammad` package, as well as usable for
|
5
|
+
various other cases."""
|
6
|
+
|
7
|
+
from typing import TYPE_CHECKING
|
8
|
+
from .._core._utils._import_utils import _auto_create_getattr_loader
|
9
|
+
|
10
|
+
if TYPE_CHECKING:
|
11
|
+
from .file import File, FileSource
|
@@ -1,4 +1,4 @@
|
|
1
|
-
"""hammad.
|
1
|
+
"""hammad.types.file"""
|
2
2
|
|
3
3
|
from pathlib import Path
|
4
4
|
import httpx
|
@@ -6,8 +6,8 @@ from typing import Any, Self
|
|
6
6
|
import mimetypes
|
7
7
|
from urllib.parse import urlparse
|
8
8
|
|
9
|
-
from
|
10
|
-
from
|
9
|
+
from ..base.model import Model
|
10
|
+
from ..base.fields import field
|
11
11
|
|
12
12
|
__all__ = ("File", "FileSource")
|
13
13
|
|
@@ -26,41 +26,41 @@ _mime_cache: dict[str, str] = {}
|
|
26
26
|
"""Cache for MIME types."""
|
27
27
|
|
28
28
|
|
29
|
-
class FileSource(
|
29
|
+
class FileSource(Model, kw_only=True, dict=True, frozen=True):
|
30
30
|
"""Represents the source of a `File` object."""
|
31
31
|
|
32
|
-
is_file: bool =
|
32
|
+
is_file: bool = field(default=False)
|
33
33
|
"""Whether this data represents a file."""
|
34
|
-
is_dir: bool =
|
34
|
+
is_dir: bool = field(default=False)
|
35
35
|
"""Whether this data represents a directory."""
|
36
|
-
is_url: bool =
|
36
|
+
is_url: bool = field(default=False)
|
37
37
|
"""Whether this data originates from a URL."""
|
38
|
-
path: Path | None =
|
38
|
+
path: Path | None = field(default=None)
|
39
39
|
"""The file path if this is file-based data."""
|
40
|
-
url: str | None =
|
40
|
+
url: str | None = field(default=None)
|
41
41
|
"""The URL if this is URL-based data."""
|
42
|
-
size: int | None =
|
42
|
+
size: int | None = field(default=None)
|
43
43
|
"""Size in bytes if available."""
|
44
|
-
encoding: str | None =
|
44
|
+
encoding: str | None = field(default=None)
|
45
45
|
"""Text encoding if applicable."""
|
46
46
|
|
47
47
|
|
48
|
-
class File(
|
48
|
+
class File(Model, kw_only=True, dict=True):
|
49
49
|
"""Base object for all file-like structure types within
|
50
50
|
the `hammad` ecosystem."""
|
51
51
|
|
52
|
-
data: Any | None =
|
52
|
+
data: Any | None = field(default=None)
|
53
53
|
"""The actual data content (bytes, string, path object, etc.)"""
|
54
|
-
type: str | None =
|
54
|
+
type: str | None = field(default=None)
|
55
55
|
"""The MIME type or identifier for the data."""
|
56
56
|
|
57
|
-
source: FileSource =
|
57
|
+
source: FileSource = field(default_factory=FileSource)
|
58
58
|
"""The source of the data. Contains metadata as well."""
|
59
59
|
|
60
60
|
# Private cached attributes
|
61
|
-
_name: str | None =
|
62
|
-
_extension: str | None =
|
63
|
-
_repr: str | None =
|
61
|
+
_name: str | None = field(default=None)
|
62
|
+
_extension: str | None = field(default=None)
|
63
|
+
_repr: str | None = field(default=None)
|
64
64
|
|
65
65
|
@property
|
66
66
|
def name(self) -> str | None:
|
hammad/typing/__init__.py
CHANGED
@@ -1,18 +1,18 @@
|
|
1
|
-
"""hammad.
|
1
|
+
"""hammad.typing
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
import hammad
|
8
|
-
|
9
|
-
hammad.typing.get_type_description(Optional[int])
|
10
|
-
...
|
11
|
-
```
|
12
|
-
"""
|
3
|
+
'Namespace' package extension for various **CORE** typing resources and
|
4
|
+
types. This is not a collection of built types, rather resources from the
|
5
|
+
core `typing` module, `typing_extensions`, `typing_inspect` and other
|
6
|
+
resources."""
|
13
7
|
|
14
8
|
from typing import Any, TYPE_CHECKING
|
15
9
|
import typing_inspect as inspection
|
10
|
+
|
11
|
+
try:
|
12
|
+
from typing_extensions import *
|
13
|
+
except ImportError:
|
14
|
+
from typing import *
|
15
|
+
|
16
16
|
from typing_inspect import (
|
17
17
|
is_callable_type,
|
18
18
|
is_classvar,
|
@@ -40,6 +40,129 @@ from typing_inspection.introspection import (
|
|
40
40
|
from dataclasses import is_dataclass
|
41
41
|
|
42
42
|
__all__ = (
|
43
|
+
# Super-special typing primitives.
|
44
|
+
"Any",
|
45
|
+
"ClassVar",
|
46
|
+
"Concatenate",
|
47
|
+
"Final",
|
48
|
+
"LiteralString",
|
49
|
+
"ParamSpec",
|
50
|
+
"ParamSpecArgs",
|
51
|
+
"ParamSpecKwargs",
|
52
|
+
"Self",
|
53
|
+
"Type",
|
54
|
+
"TypeVar",
|
55
|
+
"TypeVarTuple",
|
56
|
+
"Unpack",
|
57
|
+
# ABCs (from collections.abc).
|
58
|
+
"Awaitable",
|
59
|
+
"AsyncIterator",
|
60
|
+
"AsyncIterable",
|
61
|
+
"Coroutine",
|
62
|
+
"AsyncGenerator",
|
63
|
+
"AsyncContextManager",
|
64
|
+
"Buffer",
|
65
|
+
"ChainMap",
|
66
|
+
# Concrete collection types.
|
67
|
+
"ContextManager",
|
68
|
+
"Counter",
|
69
|
+
"Deque",
|
70
|
+
"DefaultDict",
|
71
|
+
"NamedTuple",
|
72
|
+
"OrderedDict",
|
73
|
+
"TypedDict",
|
74
|
+
# Structural checks, a.k.a. protocols.
|
75
|
+
"SupportsAbs",
|
76
|
+
"SupportsBytes",
|
77
|
+
"SupportsComplex",
|
78
|
+
"SupportsFloat",
|
79
|
+
"SupportsIndex",
|
80
|
+
"SupportsInt",
|
81
|
+
"SupportsRound",
|
82
|
+
"Reader",
|
83
|
+
"Writer",
|
84
|
+
# One-off things.
|
85
|
+
"Annotated",
|
86
|
+
"assert_never",
|
87
|
+
"assert_type",
|
88
|
+
"clear_overloads",
|
89
|
+
"dataclass_transform",
|
90
|
+
"deprecated",
|
91
|
+
"Doc",
|
92
|
+
"evaluate_forward_ref",
|
93
|
+
"get_overloads",
|
94
|
+
"final",
|
95
|
+
"Format",
|
96
|
+
"get_annotations",
|
97
|
+
"get_args",
|
98
|
+
"get_origin",
|
99
|
+
"get_original_bases",
|
100
|
+
"get_protocol_members",
|
101
|
+
"get_type_hints",
|
102
|
+
"IntVar",
|
103
|
+
"is_protocol",
|
104
|
+
"is_typeddict",
|
105
|
+
"Literal",
|
106
|
+
"NewType",
|
107
|
+
"overload",
|
108
|
+
"override",
|
109
|
+
"Protocol",
|
110
|
+
"Sentinel",
|
111
|
+
"reveal_type",
|
112
|
+
"runtime",
|
113
|
+
"runtime_checkable",
|
114
|
+
"Text",
|
115
|
+
"TypeAlias",
|
116
|
+
"TypeAliasType",
|
117
|
+
"TypeForm",
|
118
|
+
"TypeGuard",
|
119
|
+
"TypeIs",
|
120
|
+
"TYPE_CHECKING",
|
121
|
+
"Never",
|
122
|
+
"NoReturn",
|
123
|
+
"ReadOnly",
|
124
|
+
"Required",
|
125
|
+
"NotRequired",
|
126
|
+
"NoDefault",
|
127
|
+
"NoExtraItems",
|
128
|
+
# Pure aliases, have always been in typing
|
129
|
+
"AbstractSet",
|
130
|
+
"AnyStr",
|
131
|
+
"BinaryIO",
|
132
|
+
"Callable",
|
133
|
+
"Collection",
|
134
|
+
"Container",
|
135
|
+
"Dict",
|
136
|
+
"ForwardRef",
|
137
|
+
"FrozenSet",
|
138
|
+
"Generator",
|
139
|
+
"Generic",
|
140
|
+
"Hashable",
|
141
|
+
"IO",
|
142
|
+
"ItemsView",
|
143
|
+
"Iterable",
|
144
|
+
"Iterator",
|
145
|
+
"KeysView",
|
146
|
+
"List",
|
147
|
+
"Mapping",
|
148
|
+
"MappingView",
|
149
|
+
"Match",
|
150
|
+
"MutableMapping",
|
151
|
+
"MutableSequence",
|
152
|
+
"MutableSet",
|
153
|
+
"Optional",
|
154
|
+
"Pattern",
|
155
|
+
"Reversible",
|
156
|
+
"Sequence",
|
157
|
+
"Set",
|
158
|
+
"Sized",
|
159
|
+
"TextIO",
|
160
|
+
"Tuple",
|
161
|
+
"Union",
|
162
|
+
"ValuesView",
|
163
|
+
"cast",
|
164
|
+
"no_type_check",
|
165
|
+
"no_type_check_decorator",
|
43
166
|
"TypingError",
|
44
167
|
"get_type_description",
|
45
168
|
"inspection",
|
@@ -79,7 +202,7 @@ class TypingError(Exception):
|
|
79
202
|
# ------------------------------------------------------------------------
|
80
203
|
|
81
204
|
|
82
|
-
def is_pydantic_basemodel(t: Any) -> bool:
|
205
|
+
def is_pydantic_basemodel(t: "Any") -> bool:
|
83
206
|
"""Check if an object is a Pydantic BaseModel class or instance using duck typing.
|
84
207
|
|
85
208
|
This function uses duck typing to identify Pydantic BaseModel objects by checking
|
@@ -120,7 +243,7 @@ def is_pydantic_basemodel(t: Any) -> bool:
|
|
120
243
|
)
|
121
244
|
|
122
245
|
|
123
|
-
def is_pydantic_basemodel_instance(t: Any) -> bool:
|
246
|
+
def is_pydantic_basemodel_instance(t: "Any") -> bool:
|
124
247
|
"""Check if an object is an instance (not class) of a Pydantic BaseModel using duck typing.
|
125
248
|
|
126
249
|
This function specifically identifies Pydantic BaseModel instances by ensuring
|
@@ -151,7 +274,7 @@ def is_pydantic_basemodel_instance(t: Any) -> bool:
|
|
151
274
|
)
|
152
275
|
|
153
276
|
|
154
|
-
def is_msgspec_struct(t: Any) -> bool:
|
277
|
+
def is_msgspec_struct(t: "Any") -> bool:
|
155
278
|
"""Check if an object is a msgspec Struct class or instance using duck typing.
|
156
279
|
|
157
280
|
This function uses duck typing to identify msgspec Struct objects by checking
|
@@ -179,7 +302,7 @@ def is_msgspec_struct(t: Any) -> bool:
|
|
179
302
|
return hasattr(t, "__struct_fields__") and hasattr(t, "__struct_config__")
|
180
303
|
|
181
304
|
|
182
|
-
def get_type_description(t: Any) -> str:
|
305
|
+
def get_type_description(t: "Any") -> str:
|
183
306
|
"""Creates a human-readable description of a type hint.
|
184
307
|
|
185
308
|
Args:
|
@@ -282,72 +405,3 @@ def get_type_description(t: Any) -> str:
|
|
282
405
|
return f"typevar({t.__name__})"
|
283
406
|
|
284
407
|
return str(t)
|
285
|
-
|
286
|
-
|
287
|
-
if TYPE_CHECKING:
|
288
|
-
pass
|
289
|
-
|
290
|
-
|
291
|
-
def create_lazy_loader(imports_dict: dict[str, str], package: str):
|
292
|
-
"""Create a lazy loader function for __getattr__.
|
293
|
-
|
294
|
-
Args:
|
295
|
-
imports_dict: Dictionary mapping attribute names to their module paths
|
296
|
-
package: The package name for import_module
|
297
|
-
|
298
|
-
Returns:
|
299
|
-
A __getattr__ function that lazily imports modules
|
300
|
-
"""
|
301
|
-
|
302
|
-
def __getattr__(name: str):
|
303
|
-
if name in imports_dict:
|
304
|
-
from importlib import import_module
|
305
|
-
|
306
|
-
module = import_module(imports_dict[name], package)
|
307
|
-
return getattr(module, name)
|
308
|
-
raise AttributeError(f"module '{package}' has no attribute '{name}'")
|
309
|
-
|
310
|
-
return __getattr__
|
311
|
-
|
312
|
-
|
313
|
-
def parse_type_checking_imports(source_code: str) -> dict[str, str]:
|
314
|
-
"""Parse TYPE_CHECKING imports from source code to build import map.
|
315
|
-
|
316
|
-
This is a simple parser that extracts import information from
|
317
|
-
TYPE_CHECKING blocks to automatically build the module map.
|
318
|
-
|
319
|
-
Args:
|
320
|
-
source_code: The source code containing TYPE_CHECKING imports
|
321
|
-
|
322
|
-
Returns:
|
323
|
-
Dictionary mapping imported names to their module paths
|
324
|
-
"""
|
325
|
-
import ast
|
326
|
-
import re
|
327
|
-
|
328
|
-
# Parse the source code
|
329
|
-
tree = ast.parse(source_code)
|
330
|
-
|
331
|
-
imports_map = {}
|
332
|
-
in_type_checking = False
|
333
|
-
|
334
|
-
for node in ast.walk(tree):
|
335
|
-
# Check if we're in a TYPE_CHECKING block
|
336
|
-
if isinstance(node, ast.If):
|
337
|
-
if (
|
338
|
-
isinstance(node.test, ast.Name) and node.test.id == "TYPE_CHECKING"
|
339
|
-
) or (
|
340
|
-
isinstance(node.test, ast.Attribute)
|
341
|
-
and isinstance(node.test.value, ast.Name)
|
342
|
-
and node.test.value.id == "typing"
|
343
|
-
and node.test.attr == "TYPE_CHECKING"
|
344
|
-
):
|
345
|
-
# Process imports in the TYPE_CHECKING block
|
346
|
-
for stmt in node.body:
|
347
|
-
if isinstance(stmt, ast.ImportFrom):
|
348
|
-
module = stmt.module or ""
|
349
|
-
for alias in stmt.names:
|
350
|
-
name = alias.asname or alias.name
|
351
|
-
imports_map[name] = f".{module}"
|
352
|
-
|
353
|
-
return imports_map
|
hammad/web/__init__.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
"""hammad.web"""
|
2
2
|
|
3
3
|
from typing import TYPE_CHECKING
|
4
|
-
from ..
|
4
|
+
from .._core._utils._import_utils import _auto_create_getattr_loader
|
5
5
|
|
6
6
|
if TYPE_CHECKING:
|
7
7
|
from .utils import (
|
@@ -34,7 +34,8 @@ __all__ = (
|
|
34
34
|
"create_search_client",
|
35
35
|
)
|
36
36
|
|
37
|
-
|
37
|
+
|
38
|
+
__getattr__ = _auto_create_getattr_loader(__all__)
|
38
39
|
|
39
40
|
|
40
41
|
def __dir__() -> list[str]:
|