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
hammad/data/types/__init__.py
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
"""hammad.data.types
|
2
|
-
|
3
|
-
Contains various explicit data models and definitions for
|
4
|
-
various file types, data formats, and other data related
|
5
|
-
concepts."""
|
6
|
-
|
7
|
-
from typing import TYPE_CHECKING
|
8
|
-
from ...based.utils import auto_create_lazy_loader
|
9
|
-
|
10
|
-
if TYPE_CHECKING:
|
11
|
-
from .files.audio import Audio
|
12
|
-
from .files.configuration import Configuration
|
13
|
-
from .files.document import Document
|
14
|
-
from .files.file import File, FileSource
|
15
|
-
from .files.image import Image
|
16
|
-
|
17
|
-
|
18
|
-
__all__ = (
|
19
|
-
"Audio",
|
20
|
-
"Configuration",
|
21
|
-
"Document",
|
22
|
-
"File",
|
23
|
-
"FileSource",
|
24
|
-
"Image",
|
25
|
-
)
|
26
|
-
|
27
|
-
|
28
|
-
__getattr__ = auto_create_lazy_loader(__all__)
|
29
|
-
|
30
|
-
|
31
|
-
def __dir__() -> list[str]:
|
32
|
-
"""Get the attributes of the data.types module."""
|
33
|
-
return list(__all__)
|
@@ -1 +0,0 @@
|
|
1
|
-
"""hammad.data.types.files"""
|
@@ -1,195 +0,0 @@
|
|
1
|
-
"""hammad.data.types.files.document"""
|
2
|
-
|
3
|
-
import httpx
|
4
|
-
from typing import Any, Self, Iterator
|
5
|
-
from markdown_it import MarkdownIt
|
6
|
-
|
7
|
-
from .file import File, FileSource
|
8
|
-
from ....based.fields import basedfield
|
9
|
-
|
10
|
-
__all__ = ("Document",)
|
11
|
-
|
12
|
-
|
13
|
-
class Document(File):
|
14
|
-
"""A representation of a document, that is loadable from both a URL, file path
|
15
|
-
or bytes. This document can additionally be used to represent web pages, as well
|
16
|
-
as implement markdown formatting for both documents and web pages."""
|
17
|
-
|
18
|
-
# Cached properties for text processing
|
19
|
-
_lines: list[str] | None = basedfield(default=None)
|
20
|
-
_content: str | None = basedfield(default=None)
|
21
|
-
_md_parser: MarkdownIt | None = basedfield(default=None)
|
22
|
-
metadata: dict[str, Any] = basedfield(default_factory=dict)
|
23
|
-
|
24
|
-
@property
|
25
|
-
def content(self) -> str:
|
26
|
-
"""Get the document content as string."""
|
27
|
-
if self._content is None:
|
28
|
-
data = self.read()
|
29
|
-
self._content = (
|
30
|
-
data
|
31
|
-
if isinstance(data, str)
|
32
|
-
else data.decode(self.source.encoding or "utf-8")
|
33
|
-
)
|
34
|
-
return self._content
|
35
|
-
|
36
|
-
@property
|
37
|
-
def lines(self) -> list[str]:
|
38
|
-
"""Get lines of the document (cached for efficiency)."""
|
39
|
-
if self._lines is None:
|
40
|
-
self._lines = self.content.splitlines(keepends=False)
|
41
|
-
return self._lines
|
42
|
-
|
43
|
-
@property
|
44
|
-
def line_count(self) -> int:
|
45
|
-
"""Get the number of lines in the document."""
|
46
|
-
return len(self.lines)
|
47
|
-
|
48
|
-
@property
|
49
|
-
def word_count(self) -> int:
|
50
|
-
"""Get the word count of the document."""
|
51
|
-
return len(self.content.split())
|
52
|
-
|
53
|
-
@property
|
54
|
-
def char_count(self) -> int:
|
55
|
-
"""Get the character count of the document."""
|
56
|
-
return len(self.content)
|
57
|
-
|
58
|
-
@property
|
59
|
-
def is_markdown(self) -> bool:
|
60
|
-
"""Check if the document is a markdown file."""
|
61
|
-
return self.extension in {".md", ".markdown", ".mdown", ".mkd", ".mdx"}
|
62
|
-
|
63
|
-
@property
|
64
|
-
def md_parser(self) -> MarkdownIt:
|
65
|
-
"""Get the markdown parser (lazy initialization)."""
|
66
|
-
if self._md_parser is None:
|
67
|
-
self._md_parser = MarkdownIt()
|
68
|
-
return self._md_parser
|
69
|
-
|
70
|
-
def iter_lines(self, *, strip: bool = False) -> Iterator[str]:
|
71
|
-
"""Iterate over lines in the document.
|
72
|
-
|
73
|
-
Args:
|
74
|
-
strip: If True, strip whitespace from each line.
|
75
|
-
|
76
|
-
Yields:
|
77
|
-
Lines from the document.
|
78
|
-
"""
|
79
|
-
for line in self.lines:
|
80
|
-
yield line.strip() if strip else line
|
81
|
-
|
82
|
-
def iter_paragraphs(self) -> Iterator[str]:
|
83
|
-
"""Iterate over paragraphs (text blocks separated by empty lines)."""
|
84
|
-
paragraph = []
|
85
|
-
for line in self.lines:
|
86
|
-
if line.strip():
|
87
|
-
paragraph.append(line)
|
88
|
-
elif paragraph:
|
89
|
-
yield "\n".join(paragraph)
|
90
|
-
paragraph = []
|
91
|
-
if paragraph:
|
92
|
-
yield "\n".join(paragraph)
|
93
|
-
|
94
|
-
def search(
|
95
|
-
self, pattern: str, *, case_sensitive: bool = False
|
96
|
-
) -> list[tuple[int, str]]:
|
97
|
-
"""Search for a pattern in the document.
|
98
|
-
|
99
|
-
Args:
|
100
|
-
pattern: The pattern to search for.
|
101
|
-
case_sensitive: If True, search is case-sensitive.
|
102
|
-
|
103
|
-
Returns:
|
104
|
-
List of tuples (line_number, line_content) for matching lines.
|
105
|
-
"""
|
106
|
-
results = []
|
107
|
-
search_pattern = pattern if case_sensitive else pattern.lower()
|
108
|
-
|
109
|
-
for i, line in enumerate(self.lines):
|
110
|
-
search_line = line if case_sensitive else line.lower()
|
111
|
-
if search_pattern in search_line:
|
112
|
-
results.append((i + 1, line)) # 1-indexed line numbers
|
113
|
-
|
114
|
-
return results
|
115
|
-
|
116
|
-
def render_markdown(self) -> str:
|
117
|
-
"""Render markdown content to HTML."""
|
118
|
-
if not self.is_markdown:
|
119
|
-
return self.content
|
120
|
-
return self.md_parser.render(self.content)
|
121
|
-
|
122
|
-
def extract_headers(self) -> list[tuple[int, str]]:
|
123
|
-
"""Extract headers from markdown documents.
|
124
|
-
|
125
|
-
Returns:
|
126
|
-
List of tuples (level, text) for each header.
|
127
|
-
"""
|
128
|
-
headers = []
|
129
|
-
if self.is_markdown:
|
130
|
-
tokens = self.md_parser.parse(self.content)
|
131
|
-
i = 0
|
132
|
-
while i < len(tokens):
|
133
|
-
if tokens[i].type == "heading_open":
|
134
|
-
level = int(tokens[i].tag[1]) # h1 -> 1, h2 -> 2, etc.
|
135
|
-
# Next token should be inline with the content
|
136
|
-
if i + 1 < len(tokens) and tokens[i + 1].type == "inline":
|
137
|
-
headers.append((level, tokens[i + 1].content))
|
138
|
-
i += 1
|
139
|
-
else:
|
140
|
-
# For non-markdown files, look for common header patterns
|
141
|
-
for line in self.lines:
|
142
|
-
stripped = line.strip()
|
143
|
-
if stripped.startswith("#"):
|
144
|
-
level = len(line) - len(line.lstrip("#"))
|
145
|
-
text = line.lstrip("#").strip()
|
146
|
-
headers.append((level, text))
|
147
|
-
return headers
|
148
|
-
|
149
|
-
@classmethod
|
150
|
-
def from_url(
|
151
|
-
cls,
|
152
|
-
url: str,
|
153
|
-
*,
|
154
|
-
lazy: bool = True,
|
155
|
-
timeout: float = 30.0,
|
156
|
-
) -> Self:
|
157
|
-
"""Download and create a document from a URL.
|
158
|
-
|
159
|
-
Args:
|
160
|
-
url: The URL to download from.
|
161
|
-
lazy: If True, defer loading content until needed.
|
162
|
-
timeout: Request timeout in seconds.
|
163
|
-
|
164
|
-
Returns:
|
165
|
-
A new Document instance.
|
166
|
-
"""
|
167
|
-
data = None
|
168
|
-
size = None
|
169
|
-
encoding = None
|
170
|
-
type = None
|
171
|
-
|
172
|
-
if not lazy:
|
173
|
-
with httpx.Client(timeout=timeout) as client:
|
174
|
-
response = client.get(url)
|
175
|
-
response.raise_for_status()
|
176
|
-
|
177
|
-
# Always get text for documents
|
178
|
-
data = response.text
|
179
|
-
size = len(data.encode("utf-8"))
|
180
|
-
encoding = response.encoding
|
181
|
-
|
182
|
-
# Get content type
|
183
|
-
content_type = response.headers.get("content-type", "")
|
184
|
-
type = content_type.split(";")[0] if content_type else "text/plain"
|
185
|
-
|
186
|
-
return cls(
|
187
|
-
data=data,
|
188
|
-
type=type,
|
189
|
-
source=FileSource(
|
190
|
-
is_url=True,
|
191
|
-
url=url,
|
192
|
-
size=size,
|
193
|
-
encoding=encoding,
|
194
|
-
),
|
195
|
-
)
|
hammad/text/utils/__init__.py
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
"""hammad.text.utils"""
|
hammad/text/utils/converters.py
DELETED
@@ -1,229 +0,0 @@
|
|
1
|
-
"""hammad.text.utils.converters"""
|
2
|
-
|
3
|
-
from docstring_parser import parse
|
4
|
-
from typing import (
|
5
|
-
Any,
|
6
|
-
Optional,
|
7
|
-
)
|
8
|
-
|
9
|
-
from ...typing import (
|
10
|
-
inspection,
|
11
|
-
is_pydantic_basemodel,
|
12
|
-
is_msgspec_struct,
|
13
|
-
is_dataclass,
|
14
|
-
)
|
15
|
-
|
16
|
-
__all__ = [
|
17
|
-
"convert_type_to_text",
|
18
|
-
"convert_docstring_to_text",
|
19
|
-
]
|
20
|
-
|
21
|
-
|
22
|
-
def convert_type_to_text(cls: Any) -> str:
|
23
|
-
"""Converts a type into a clean & human readable text representation.
|
24
|
-
|
25
|
-
This function uses `typing_inspect` exclusively to infer nested types
|
26
|
-
within `Optional`, `Union` types, for the cleanest possible string
|
27
|
-
representation of a type.
|
28
|
-
|
29
|
-
Args:
|
30
|
-
cls: The type to convert to a text representation.
|
31
|
-
|
32
|
-
Returns:
|
33
|
-
A clean, human-readable string representation of the type.
|
34
|
-
"""
|
35
|
-
# Handle None type
|
36
|
-
if cls is None or cls is type(None):
|
37
|
-
return "None"
|
38
|
-
|
39
|
-
# Get origin and args using typing_inspect for better type handling
|
40
|
-
origin = inspection.get_origin(cls)
|
41
|
-
args = inspection.get_args(cls)
|
42
|
-
|
43
|
-
if origin is not None:
|
44
|
-
# Handle Optional (Union[T, None])
|
45
|
-
if inspection.is_optional_type(cls):
|
46
|
-
# Recursively get the name of the inner type (the one not None)
|
47
|
-
inner_type = args[0]
|
48
|
-
inner_type_name = convert_type_to_text(inner_type)
|
49
|
-
return f"Optional[{inner_type_name}]"
|
50
|
-
|
51
|
-
# Handle other Union types
|
52
|
-
if inspection.is_union_type(cls):
|
53
|
-
# Recursively get names of all arguments in the Union
|
54
|
-
args_str = ", ".join(convert_type_to_text(arg) for arg in args)
|
55
|
-
return f"Union[{args_str}]"
|
56
|
-
|
57
|
-
# Handle other generic types (List, Dict, Tuple, Set, etc.)
|
58
|
-
# Use origin.__name__ for built-in generics like list, dict, tuple, set
|
59
|
-
origin_name = getattr(origin, "__name__", str(origin).split(".")[-1])
|
60
|
-
if origin_name.startswith("_"): # Handle internal typing names like _List
|
61
|
-
origin_name = origin_name[1:]
|
62
|
-
|
63
|
-
# Convert to lowercase for built-in types to match modern Python style
|
64
|
-
if origin_name in ["List", "Dict", "Tuple", "Set"]:
|
65
|
-
origin_name = origin_name.lower()
|
66
|
-
|
67
|
-
if args: # If there are type arguments
|
68
|
-
# Recursively get names of type arguments
|
69
|
-
args_str = ", ".join(convert_type_to_text(arg) for arg in args)
|
70
|
-
return f"{origin_name}[{args_str}]"
|
71
|
-
else: # Generic without arguments (e.g., typing.List)
|
72
|
-
return origin_name
|
73
|
-
|
74
|
-
# Handle special cases with typing_inspect
|
75
|
-
if inspection.is_typevar(cls):
|
76
|
-
return str(cls)
|
77
|
-
if inspection.is_forward_ref(cls):
|
78
|
-
return str(cls)
|
79
|
-
if inspection.is_literal_type(cls):
|
80
|
-
return f"Literal[{', '.join(str(arg) for arg in args)}]"
|
81
|
-
if inspection.is_final_type(cls):
|
82
|
-
return f"Final[{convert_type_to_text(args[0])}]" if args else "Final"
|
83
|
-
if inspection.is_new_type(cls):
|
84
|
-
return str(cls)
|
85
|
-
|
86
|
-
# Handle Pydantic BaseModel types
|
87
|
-
if is_pydantic_basemodel(cls):
|
88
|
-
if hasattr(cls, "__name__"):
|
89
|
-
return cls.__name__
|
90
|
-
return "BaseModel"
|
91
|
-
|
92
|
-
# Handle msgspec Struct types
|
93
|
-
if is_msgspec_struct(cls):
|
94
|
-
if hasattr(cls, "__name__"):
|
95
|
-
return cls.__name__
|
96
|
-
return "Struct"
|
97
|
-
|
98
|
-
# Handle dataclass types
|
99
|
-
if is_dataclass(cls):
|
100
|
-
if hasattr(cls, "__name__"):
|
101
|
-
return cls.__name__
|
102
|
-
return "dataclass"
|
103
|
-
|
104
|
-
# Handle basic types with __name__ attribute
|
105
|
-
if hasattr(cls, "__name__") and cls.__name__ != "<lambda>":
|
106
|
-
return cls.__name__
|
107
|
-
|
108
|
-
# Special handling for Optional type string representation
|
109
|
-
if str(cls).startswith("typing.Optional"):
|
110
|
-
# Extract the inner type from the string representation
|
111
|
-
inner_type_str = str(cls).replace("typing.Optional[", "").rstrip("]")
|
112
|
-
return f"Optional[{inner_type_str}]"
|
113
|
-
|
114
|
-
# Fallback for any other types
|
115
|
-
# Clean up 'typing.' prefix and handle other common representations
|
116
|
-
return str(cls).replace("typing.", "").replace("__main__.", "")
|
117
|
-
|
118
|
-
|
119
|
-
def convert_docstring_to_text(
|
120
|
-
obj: Any,
|
121
|
-
*,
|
122
|
-
params_override: Optional[str] = None,
|
123
|
-
returns_override: Optional[str] = None,
|
124
|
-
raises_override: Optional[str] = None,
|
125
|
-
examples_override: Optional[str] = None,
|
126
|
-
params_prefix: Optional[str] = None,
|
127
|
-
returns_prefix: Optional[str] = None,
|
128
|
-
raises_prefix: Optional[str] = None,
|
129
|
-
exclude_params: bool = False,
|
130
|
-
exclude_returns: bool = False,
|
131
|
-
exclude_raises: bool = False,
|
132
|
-
exclude_examples: bool = False,
|
133
|
-
) -> str:
|
134
|
-
"""
|
135
|
-
Convert an object's docstring to formatted text using docstring_parser.
|
136
|
-
|
137
|
-
Args:
|
138
|
-
obj: The object to extract docstring from
|
139
|
-
params_override: Override text for parameters section
|
140
|
-
returns_override: Override text for returns section
|
141
|
-
raises_override: Override text for raises section
|
142
|
-
examples_override: Override text for examples section
|
143
|
-
params_prefix: Prefix for parameters section
|
144
|
-
returns_prefix: Prefix for returns section
|
145
|
-
raises_prefix: Prefix for raises section
|
146
|
-
exclude_params: Whether to exclude parameters section
|
147
|
-
exclude_returns: Whether to exclude returns section
|
148
|
-
exclude_raises: Whether to exclude raises section
|
149
|
-
exclude_examples: Whether to exclude examples section
|
150
|
-
|
151
|
-
Returns:
|
152
|
-
Formatted text representation of the docstring
|
153
|
-
"""
|
154
|
-
# Get the raw docstring
|
155
|
-
doc = getattr(obj, "__doc__", None)
|
156
|
-
if not doc:
|
157
|
-
return ""
|
158
|
-
|
159
|
-
try:
|
160
|
-
# Parse the docstring using docstring_parser
|
161
|
-
parsed = parse(doc)
|
162
|
-
|
163
|
-
parts = []
|
164
|
-
|
165
|
-
# Add short description
|
166
|
-
if parsed.short_description:
|
167
|
-
parts.append(parsed.short_description)
|
168
|
-
|
169
|
-
# Add long description
|
170
|
-
if parsed.long_description:
|
171
|
-
parts.append("") # Empty line separator
|
172
|
-
parts.append(parsed.long_description)
|
173
|
-
|
174
|
-
# Add parameters section
|
175
|
-
if not exclude_params and (params_override or parsed.params):
|
176
|
-
parts.append("") # Empty line separator
|
177
|
-
if params_override:
|
178
|
-
parts.append(params_override)
|
179
|
-
else:
|
180
|
-
prefix = params_prefix or "Parameters:"
|
181
|
-
parts.append(prefix)
|
182
|
-
for param in parsed.params:
|
183
|
-
param_line = f" {param.arg_name}"
|
184
|
-
if param.type_name:
|
185
|
-
param_line += f" ({param.type_name})"
|
186
|
-
if param.description:
|
187
|
-
param_line += f": {param.description}"
|
188
|
-
parts.append(param_line)
|
189
|
-
|
190
|
-
# Add returns section
|
191
|
-
if not exclude_returns and (returns_override or parsed.returns):
|
192
|
-
parts.append("") # Empty line separator
|
193
|
-
if returns_override:
|
194
|
-
parts.append(returns_override)
|
195
|
-
else:
|
196
|
-
prefix = returns_prefix or "Returns:"
|
197
|
-
parts.append(prefix)
|
198
|
-
if parsed.returns:
|
199
|
-
return_line = " "
|
200
|
-
if parsed.returns.type_name:
|
201
|
-
return_line += f"{parsed.returns.type_name}: "
|
202
|
-
if parsed.returns.description:
|
203
|
-
return_line += parsed.returns.description
|
204
|
-
parts.append(return_line)
|
205
|
-
|
206
|
-
# Add raises section
|
207
|
-
if not exclude_raises and (raises_override or parsed.raises):
|
208
|
-
parts.append("") # Empty line separator
|
209
|
-
if raises_override:
|
210
|
-
parts.append(raises_override)
|
211
|
-
else:
|
212
|
-
prefix = raises_prefix or "Raises:"
|
213
|
-
parts.append(prefix)
|
214
|
-
for exc in parsed.raises:
|
215
|
-
exc_line = f" {exc.type_name or 'Exception'}"
|
216
|
-
if exc.description:
|
217
|
-
exc_line += f": {exc.description}"
|
218
|
-
parts.append(exc_line)
|
219
|
-
|
220
|
-
# Add examples section (if available in parsed docstring)
|
221
|
-
if not exclude_examples and examples_override:
|
222
|
-
parts.append("") # Empty line separator
|
223
|
-
parts.append(examples_override)
|
224
|
-
|
225
|
-
return "\n".join(parts)
|
226
|
-
|
227
|
-
except Exception:
|
228
|
-
# Fallback to raw docstring if parsing fails
|
229
|
-
return doc.strip()
|
@@ -1 +0,0 @@
|
|
1
|
-
"""hammad.text.utils.markdown"""
|