hammad-python 0.0.14__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.
- hammad_python-0.0.15.dist-info/METADATA +184 -0
- hammad_python-0.0.15.dist-info/RECORD +4 -0
- hammad/__init__.py +0 -1
- hammad/ai/__init__.py +0 -1
- hammad/ai/_utils.py +0 -142
- hammad/ai/completions/__init__.py +0 -45
- hammad/ai/completions/client.py +0 -684
- hammad/ai/completions/create.py +0 -710
- hammad/ai/completions/settings.py +0 -100
- hammad/ai/completions/types.py +0 -792
- hammad/ai/completions/utils.py +0 -486
- hammad/ai/embeddings/__init__.py +0 -35
- hammad/ai/embeddings/client/__init__.py +0 -1
- hammad/ai/embeddings/client/base_embeddings_client.py +0 -26
- hammad/ai/embeddings/client/fastembed_text_embeddings_client.py +0 -200
- hammad/ai/embeddings/client/litellm_embeddings_client.py +0 -288
- hammad/ai/embeddings/create.py +0 -159
- hammad/ai/embeddings/types.py +0 -69
- hammad/cache/__init__.py +0 -40
- hammad/cache/base_cache.py +0 -181
- hammad/cache/cache.py +0 -169
- hammad/cache/decorators.py +0 -261
- hammad/cache/file_cache.py +0 -80
- hammad/cache/ttl_cache.py +0 -74
- hammad/cli/__init__.py +0 -33
- hammad/cli/animations.py +0 -573
- hammad/cli/plugins.py +0 -781
- hammad/cli/styles/__init__.py +0 -55
- hammad/cli/styles/settings.py +0 -139
- hammad/cli/styles/types.py +0 -358
- hammad/cli/styles/utils.py +0 -480
- hammad/data/__init__.py +0 -56
- hammad/data/collections/__init__.py +0 -34
- hammad/data/collections/base_collection.py +0 -58
- hammad/data/collections/collection.py +0 -452
- hammad/data/collections/searchable_collection.py +0 -556
- hammad/data/collections/vector_collection.py +0 -596
- hammad/data/configurations/__init__.py +0 -35
- hammad/data/configurations/configuration.py +0 -564
- hammad/data/databases/__init__.py +0 -21
- hammad/data/databases/database.py +0 -902
- hammad/data/models/__init__.py +0 -44
- hammad/data/models/base/__init__.py +0 -35
- hammad/data/models/base/fields.py +0 -546
- hammad/data/models/base/model.py +0 -1078
- hammad/data/models/base/utils.py +0 -280
- hammad/data/models/pydantic/__init__.py +0 -55
- hammad/data/models/pydantic/converters.py +0 -632
- hammad/data/models/pydantic/models/__init__.py +0 -28
- hammad/data/models/pydantic/models/arbitrary_model.py +0 -46
- hammad/data/models/pydantic/models/cacheable_model.py +0 -79
- hammad/data/models/pydantic/models/fast_model.py +0 -318
- hammad/data/models/pydantic/models/function_model.py +0 -176
- hammad/data/models/pydantic/models/subscriptable_model.py +0 -63
- hammad/data/types/__init__.py +0 -41
- hammad/data/types/file.py +0 -358
- hammad/data/types/multimodal/__init__.py +0 -24
- hammad/data/types/multimodal/audio.py +0 -96
- hammad/data/types/multimodal/image.py +0 -80
- hammad/data/types/text.py +0 -1066
- hammad/formatting/__init__.py +0 -38
- hammad/formatting/json/__init__.py +0 -21
- hammad/formatting/json/converters.py +0 -152
- hammad/formatting/text/__init__.py +0 -63
- hammad/formatting/text/converters.py +0 -723
- hammad/formatting/text/markdown.py +0 -131
- hammad/formatting/yaml/__init__.py +0 -26
- hammad/formatting/yaml/converters.py +0 -5
- hammad/logging/__init__.py +0 -35
- hammad/logging/decorators.py +0 -834
- hammad/logging/logger.py +0 -954
- hammad/mcp/__init__.py +0 -50
- hammad/mcp/client/__init__.py +0 -1
- hammad/mcp/client/client.py +0 -523
- hammad/mcp/client/client_service.py +0 -393
- hammad/mcp/client/settings.py +0 -178
- hammad/mcp/servers/__init__.py +0 -1
- hammad/mcp/servers/launcher.py +0 -1161
- hammad/performance/__init__.py +0 -36
- hammad/performance/imports.py +0 -231
- hammad/performance/runtime/__init__.py +0 -32
- hammad/performance/runtime/decorators.py +0 -142
- hammad/performance/runtime/run.py +0 -299
- hammad/py.typed +0 -0
- hammad/service/__init__.py +0 -49
- hammad/service/create.py +0 -532
- hammad/service/decorators.py +0 -285
- hammad/typing/__init__.py +0 -407
- hammad/web/__init__.py +0 -43
- hammad/web/http/__init__.py +0 -1
- hammad/web/http/client.py +0 -944
- hammad/web/models.py +0 -245
- hammad/web/openapi/__init__.py +0 -1
- hammad/web/openapi/client.py +0 -740
- hammad/web/search/__init__.py +0 -1
- hammad/web/search/client.py +0 -988
- hammad/web/utils.py +0 -472
- hammad_python-0.0.14.dist-info/METADATA +0 -70
- hammad_python-0.0.14.dist-info/RECORD +0 -99
- {hammad_python-0.0.14.dist-info → hammad_python-0.0.15.dist-info}/WHEEL +0 -0
- {hammad_python-0.0.14.dist-info → hammad_python-0.0.15.dist-info}/licenses/LICENSE +0 -0
hammad/service/decorators.py
DELETED
@@ -1,285 +0,0 @@
|
|
1
|
-
"""hammad.service.decorators"""
|
2
|
-
|
3
|
-
from typing import (
|
4
|
-
Any,
|
5
|
-
Callable,
|
6
|
-
Dict,
|
7
|
-
List,
|
8
|
-
Literal,
|
9
|
-
Optional,
|
10
|
-
Type,
|
11
|
-
Union,
|
12
|
-
ParamSpec,
|
13
|
-
TypeVar,
|
14
|
-
)
|
15
|
-
|
16
|
-
|
17
|
-
ServiceFunctionParams = ParamSpec("ServiceFunctionParams")
|
18
|
-
ServiceFunctionReturn = TypeVar("ServiceFunctionReturn")
|
19
|
-
|
20
|
-
|
21
|
-
def serve(
|
22
|
-
func: Optional[Callable[ServiceFunctionParams, ServiceFunctionReturn]] = None,
|
23
|
-
*,
|
24
|
-
# Overrides
|
25
|
-
name: Optional[str] = None,
|
26
|
-
method: Literal[
|
27
|
-
"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"
|
28
|
-
] = "POST",
|
29
|
-
path: str = "/",
|
30
|
-
# Server configuration
|
31
|
-
host: str = "0.0.0.0",
|
32
|
-
port: int = 8000,
|
33
|
-
log_level: str = "info",
|
34
|
-
reload: bool = False,
|
35
|
-
workers: int = 1,
|
36
|
-
timeout_keep_alive: int = 5,
|
37
|
-
access_log: bool = True,
|
38
|
-
use_colors: bool = True,
|
39
|
-
auto_start: bool = True,
|
40
|
-
# FastAPI
|
41
|
-
include_in_schema: bool = True,
|
42
|
-
dependencies: Optional[List[Callable[..., Any]]] = None,
|
43
|
-
tags: Optional[List[str]] = None,
|
44
|
-
description: Optional[str] = None,
|
45
|
-
) -> Union[Callable[ServiceFunctionParams, ServiceFunctionReturn], Callable]:
|
46
|
-
"""Decorator to serve a function as a FastAPI endpoint.
|
47
|
-
|
48
|
-
Can be used as a decorator (@serve) or as a function (serve(func)).
|
49
|
-
|
50
|
-
Args:
|
51
|
-
func: Function to serve (when used as decorator, this is None initially)
|
52
|
-
name: Service name (defaults to function name)
|
53
|
-
method: HTTP method to use
|
54
|
-
path: API endpoint path
|
55
|
-
host: Host to bind to
|
56
|
-
port: Port to bind to
|
57
|
-
log_level: Uvicorn log level
|
58
|
-
reload: Enable auto-reload
|
59
|
-
workers: Number of worker processes
|
60
|
-
timeout_keep_alive: Keep-alive timeout
|
61
|
-
access_log: Enable access logging
|
62
|
-
use_colors: Use colored logs
|
63
|
-
auto_start: Automatically start the server
|
64
|
-
include_in_schema: Include in OpenAPI schema
|
65
|
-
dependencies: FastAPI dependencies
|
66
|
-
tags: API tags
|
67
|
-
description: API description
|
68
|
-
|
69
|
-
Returns:
|
70
|
-
The original function (when used as decorator)
|
71
|
-
"""
|
72
|
-
from .create import create_service
|
73
|
-
from ..mcp.servers.launcher import find_next_free_port
|
74
|
-
|
75
|
-
def decorator(
|
76
|
-
f: Callable[ServiceFunctionParams, ServiceFunctionReturn],
|
77
|
-
) -> Callable[ServiceFunctionParams, ServiceFunctionReturn]:
|
78
|
-
# Find next available port if auto_start is True
|
79
|
-
actual_port = port
|
80
|
-
if auto_start:
|
81
|
-
actual_port = find_next_free_port(port, host)
|
82
|
-
|
83
|
-
# Handle dependencies - convert raw functions to FastAPI Depends
|
84
|
-
processed_dependencies = None
|
85
|
-
if dependencies is not None:
|
86
|
-
from fastapi import Depends
|
87
|
-
|
88
|
-
processed_dependencies = [
|
89
|
-
Depends(dep) if callable(dep) else dep for dep in dependencies
|
90
|
-
]
|
91
|
-
|
92
|
-
# Store the service configuration on the function
|
93
|
-
f._service_config = {
|
94
|
-
"name": name or f.__name__,
|
95
|
-
"method": method,
|
96
|
-
"path": path,
|
97
|
-
"host": host,
|
98
|
-
"port": actual_port,
|
99
|
-
"log_level": log_level,
|
100
|
-
"reload": reload,
|
101
|
-
"workers": workers,
|
102
|
-
"timeout_keep_alive": timeout_keep_alive,
|
103
|
-
"access_log": access_log,
|
104
|
-
"use_colors": use_colors,
|
105
|
-
"auto_start": auto_start,
|
106
|
-
"include_in_schema": include_in_schema,
|
107
|
-
"dependencies": processed_dependencies,
|
108
|
-
"tags": tags,
|
109
|
-
"description": description,
|
110
|
-
}
|
111
|
-
|
112
|
-
# Create and start the service immediately if auto_start is True
|
113
|
-
if auto_start:
|
114
|
-
create_service(f, **f._service_config)
|
115
|
-
|
116
|
-
return f
|
117
|
-
|
118
|
-
if func is None:
|
119
|
-
# Called as @serve(...)
|
120
|
-
return decorator
|
121
|
-
else:
|
122
|
-
# Called as @serve (without parentheses)
|
123
|
-
return decorator(func)
|
124
|
-
|
125
|
-
|
126
|
-
def serve_mcp(
|
127
|
-
func_or_funcs: Optional[Union[Callable, List[Callable]]] = None,
|
128
|
-
*,
|
129
|
-
# MCP Server configuration
|
130
|
-
name: Optional[str] = None,
|
131
|
-
instructions: Optional[str] = None,
|
132
|
-
transport: Literal["stdio", "sse", "streamable-http"] = "stdio",
|
133
|
-
# Server settings (for sse/http transports)
|
134
|
-
host: str = "127.0.0.1",
|
135
|
-
port: int = 8000,
|
136
|
-
mount_path: str = "/",
|
137
|
-
sse_path: str = "/sse",
|
138
|
-
message_path: str = "/messages/",
|
139
|
-
streamable_http_path: str = "/mcp",
|
140
|
-
json_response: bool = False,
|
141
|
-
stateless_http: bool = False,
|
142
|
-
warn_on_duplicate_resources: bool = True,
|
143
|
-
warn_on_duplicate_tools: bool = True,
|
144
|
-
# FastMCP settings
|
145
|
-
dependencies: Optional[List[str]] = None,
|
146
|
-
log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO",
|
147
|
-
debug_mode: bool = False,
|
148
|
-
cwd: Optional[str] = None,
|
149
|
-
# Launch settings
|
150
|
-
auto_restart: bool = False,
|
151
|
-
check_interval: float = 1.0,
|
152
|
-
# Function-specific parameters (only when single function)
|
153
|
-
single_func_name: Optional[str] = None,
|
154
|
-
single_func_description: Optional[str] = None,
|
155
|
-
) -> Union[Callable, List[Callable]]:
|
156
|
-
"""Decorator/function to serve functions as MCP server tools.
|
157
|
-
|
158
|
-
Can be used in multiple ways:
|
159
|
-
1. As a decorator: @serve_mcp
|
160
|
-
2. As a decorator with params: @serve_mcp(name="MyServer")
|
161
|
-
3. As a function with single function: serve_mcp(my_func)
|
162
|
-
4. As a function with multiple functions: serve_mcp([func1, func2])
|
163
|
-
|
164
|
-
Args:
|
165
|
-
func_or_funcs: Function or list of functions to serve
|
166
|
-
name: MCP server name
|
167
|
-
instructions: Server instructions
|
168
|
-
transport: Transport type (stdio, sse, streamable-http)
|
169
|
-
host: Host for HTTP transports
|
170
|
-
port: Starting port for HTTP transports
|
171
|
-
mount_path: Mount path for HTTP servers
|
172
|
-
sse_path: SSE endpoint path
|
173
|
-
message_path: Message endpoint path
|
174
|
-
streamable_http_path: StreamableHTTP endpoint path
|
175
|
-
json_response: Use JSON responses for HTTP
|
176
|
-
stateless_http: Use stateless HTTP mode
|
177
|
-
warn_on_duplicate_resources: Warn on duplicate resources
|
178
|
-
warn_on_duplicate_tools: Warn on duplicate tools
|
179
|
-
dependencies: FastMCP dependencies
|
180
|
-
log_level: Logging level
|
181
|
-
debug_mode: Enable debug mode
|
182
|
-
cwd: Working directory
|
183
|
-
auto_restart: Automatically restart failed servers
|
184
|
-
check_interval: Health check interval in seconds
|
185
|
-
single_func_name: Name override for single function
|
186
|
-
single_func_description: Description for single function
|
187
|
-
|
188
|
-
Returns:
|
189
|
-
Original function(s) unchanged
|
190
|
-
"""
|
191
|
-
from ..mcp.servers.launcher import (
|
192
|
-
launch_mcp_servers,
|
193
|
-
MCPServerStdioSettings,
|
194
|
-
MCPServerSseSettings,
|
195
|
-
MCPServerStreamableHttpSettings,
|
196
|
-
)
|
197
|
-
|
198
|
-
def _create_server_config(
|
199
|
-
tools: List[Callable], server_name: str, server_instructions: Optional[str]
|
200
|
-
):
|
201
|
-
"""Create the appropriate server configuration based on transport type."""
|
202
|
-
base_config = {
|
203
|
-
"name": server_name,
|
204
|
-
"instructions": server_instructions,
|
205
|
-
"tools": tools,
|
206
|
-
"dependencies": dependencies or [],
|
207
|
-
"log_level": log_level,
|
208
|
-
"debug_mode": debug_mode,
|
209
|
-
"cwd": cwd,
|
210
|
-
}
|
211
|
-
|
212
|
-
if transport == "stdio":
|
213
|
-
return MCPServerStdioSettings(**base_config)
|
214
|
-
elif transport == "sse":
|
215
|
-
return MCPServerSseSettings(
|
216
|
-
**base_config,
|
217
|
-
host=host,
|
218
|
-
start_port=port,
|
219
|
-
mount_path=mount_path,
|
220
|
-
sse_path=sse_path,
|
221
|
-
message_path=message_path,
|
222
|
-
json_response=json_response,
|
223
|
-
stateless_http=stateless_http,
|
224
|
-
warn_on_duplicate_resources=warn_on_duplicate_resources,
|
225
|
-
warn_on_duplicate_tools=warn_on_duplicate_tools,
|
226
|
-
)
|
227
|
-
elif transport == "streamable-http":
|
228
|
-
return MCPServerStreamableHttpSettings(
|
229
|
-
**base_config,
|
230
|
-
host=host,
|
231
|
-
start_port=port,
|
232
|
-
mount_path=mount_path,
|
233
|
-
streamable_http_path=streamable_http_path,
|
234
|
-
json_response=json_response,
|
235
|
-
stateless_http=stateless_http,
|
236
|
-
warn_on_duplicate_resources=warn_on_duplicate_resources,
|
237
|
-
warn_on_duplicate_tools=warn_on_duplicate_tools,
|
238
|
-
)
|
239
|
-
else:
|
240
|
-
raise ValueError(f"Unsupported transport: {transport}")
|
241
|
-
|
242
|
-
def _launch_server(server_config):
|
243
|
-
"""Launch the MCP server with the given configuration."""
|
244
|
-
launch_mcp_servers(
|
245
|
-
servers=[server_config],
|
246
|
-
check_interval=check_interval,
|
247
|
-
auto_restart=auto_restart,
|
248
|
-
)
|
249
|
-
|
250
|
-
def decorator(f: Callable) -> Callable:
|
251
|
-
"""Decorator for single function."""
|
252
|
-
func_name = single_func_name or name or f.__name__
|
253
|
-
func_instructions = single_func_description or instructions or f.__doc__
|
254
|
-
|
255
|
-
# Create server configuration and launch
|
256
|
-
server_config = _create_server_config([f], func_name, func_instructions)
|
257
|
-
_launch_server(server_config)
|
258
|
-
|
259
|
-
return f
|
260
|
-
|
261
|
-
def handle_multiple_functions(funcs: List[Callable]) -> List[Callable]:
|
262
|
-
"""Handle multiple functions."""
|
263
|
-
server_name = name or "MCPServer"
|
264
|
-
server_instructions = instructions or f"MCP server with {len(funcs)} tools"
|
265
|
-
|
266
|
-
# Create server configuration and launch
|
267
|
-
server_config = _create_server_config(funcs, server_name, server_instructions)
|
268
|
-
_launch_server(server_config)
|
269
|
-
|
270
|
-
return funcs
|
271
|
-
|
272
|
-
# Handle different call patterns
|
273
|
-
if func_or_funcs is None:
|
274
|
-
# Called as @serve_mcp(...) - return decorator
|
275
|
-
return decorator
|
276
|
-
elif callable(func_or_funcs):
|
277
|
-
# Called as @serve_mcp (no parentheses) or serve_mcp(single_func)
|
278
|
-
return decorator(func_or_funcs)
|
279
|
-
elif isinstance(func_or_funcs, list):
|
280
|
-
# Called as serve_mcp([func1, func2, ...])
|
281
|
-
return handle_multiple_functions(func_or_funcs)
|
282
|
-
else:
|
283
|
-
raise TypeError(
|
284
|
-
f"Expected callable or list of callables, got {type(func_or_funcs)}"
|
285
|
-
)
|
hammad/typing/__init__.py
DELETED
@@ -1,407 +0,0 @@
|
|
1
|
-
"""hammad.typing
|
2
|
-
|
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."""
|
7
|
-
|
8
|
-
from typing import Any, TYPE_CHECKING
|
9
|
-
import typing_inspect as inspection
|
10
|
-
|
11
|
-
try:
|
12
|
-
from typing_extensions import *
|
13
|
-
except ImportError:
|
14
|
-
from typing import *
|
15
|
-
|
16
|
-
from typing_inspect import (
|
17
|
-
is_callable_type,
|
18
|
-
is_classvar,
|
19
|
-
is_final_type,
|
20
|
-
is_forward_ref,
|
21
|
-
is_generic_type,
|
22
|
-
is_literal_type,
|
23
|
-
is_new_type,
|
24
|
-
is_optional_type,
|
25
|
-
is_union_type,
|
26
|
-
is_typevar,
|
27
|
-
is_tuple_type,
|
28
|
-
get_origin,
|
29
|
-
get_args,
|
30
|
-
get_last_args,
|
31
|
-
get_last_origin,
|
32
|
-
get_generic_bases,
|
33
|
-
typed_dict_keys as get_typed_dict_keys,
|
34
|
-
)
|
35
|
-
from typing_inspection.introspection import (
|
36
|
-
is_union_origin,
|
37
|
-
inspect_annotation,
|
38
|
-
get_literal_values,
|
39
|
-
)
|
40
|
-
from dataclasses import is_dataclass
|
41
|
-
|
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",
|
166
|
-
"TypingError",
|
167
|
-
"get_type_description",
|
168
|
-
"inspection",
|
169
|
-
"is_pydantic_basemodel",
|
170
|
-
"is_pydantic_basemodel_instance",
|
171
|
-
"is_msgspec_struct",
|
172
|
-
"is_dataclass",
|
173
|
-
"is_callable_type",
|
174
|
-
"is_classvar",
|
175
|
-
"is_final_type",
|
176
|
-
"is_forward_ref",
|
177
|
-
"is_generic_type",
|
178
|
-
"is_literal_type",
|
179
|
-
"is_new_type",
|
180
|
-
"is_optional_type",
|
181
|
-
"is_union_type",
|
182
|
-
"is_typevar",
|
183
|
-
"is_tuple_type",
|
184
|
-
"get_origin",
|
185
|
-
"get_args",
|
186
|
-
"is_union_origin",
|
187
|
-
"inspect_annotation",
|
188
|
-
"get_literal_values",
|
189
|
-
"get_last_args",
|
190
|
-
"get_last_origin",
|
191
|
-
"get_generic_bases",
|
192
|
-
"get_typed_dict_keys",
|
193
|
-
)
|
194
|
-
|
195
|
-
|
196
|
-
class TypingError(Exception):
|
197
|
-
"""An exception raised when a type utility raises an error."""
|
198
|
-
|
199
|
-
|
200
|
-
# ------------------------------------------------------------------------
|
201
|
-
# Inspection Extensions
|
202
|
-
# ------------------------------------------------------------------------
|
203
|
-
|
204
|
-
|
205
|
-
def is_pydantic_basemodel(t: "Any") -> bool:
|
206
|
-
"""Check if an object is a Pydantic BaseModel class or instance using duck typing.
|
207
|
-
|
208
|
-
This function uses duck typing to identify Pydantic BaseModel objects by checking
|
209
|
-
for the presence of characteristic attributes (`model_fields` and `model_dump`)
|
210
|
-
without requiring direct imports of Pydantic.
|
211
|
-
|
212
|
-
Args:
|
213
|
-
t: The object to check. Can be a class, instance, or any other type.
|
214
|
-
|
215
|
-
Returns:
|
216
|
-
True if the object appears to be a Pydantic BaseModel (class or instance),
|
217
|
-
False otherwise.
|
218
|
-
|
219
|
-
Example:
|
220
|
-
>>> from pydantic import BaseModel
|
221
|
-
>>> class User(BaseModel):
|
222
|
-
... name: str
|
223
|
-
>>> is_pydantic_basemodel(User)
|
224
|
-
True
|
225
|
-
>>> is_pydantic_basemodel(User(name="John"))
|
226
|
-
True
|
227
|
-
>>> is_pydantic_basemodel(dict)
|
228
|
-
False
|
229
|
-
"""
|
230
|
-
# Check if it's a class first
|
231
|
-
if isinstance(t, type):
|
232
|
-
return (
|
233
|
-
hasattr(t, "model_fields")
|
234
|
-
and hasattr(t, "model_dump")
|
235
|
-
and callable(getattr(t, "model_dump", None))
|
236
|
-
)
|
237
|
-
|
238
|
-
# For instances, check the class instead of the instance to avoid deprecation warning
|
239
|
-
return (
|
240
|
-
hasattr(t.__class__, "model_fields")
|
241
|
-
and hasattr(t, "model_dump")
|
242
|
-
and callable(getattr(t, "model_dump", None))
|
243
|
-
)
|
244
|
-
|
245
|
-
|
246
|
-
def is_pydantic_basemodel_instance(t: "Any") -> bool:
|
247
|
-
"""Check if an object is an instance (not class) of a Pydantic BaseModel using duck typing.
|
248
|
-
|
249
|
-
This function specifically identifies Pydantic BaseModel instances by ensuring
|
250
|
-
the object is not a type/class itself and has the characteristic Pydantic attributes.
|
251
|
-
|
252
|
-
Args:
|
253
|
-
t: The object to check.
|
254
|
-
|
255
|
-
Returns:
|
256
|
-
True if the object is a Pydantic BaseModel instance (not the class itself),
|
257
|
-
False otherwise.
|
258
|
-
|
259
|
-
Example:
|
260
|
-
>>> from pydantic import BaseModel
|
261
|
-
>>> class User(BaseModel):
|
262
|
-
... name: str
|
263
|
-
>>> user = User(name="John")
|
264
|
-
>>> is_pydantic_basemodel_instance(user)
|
265
|
-
True
|
266
|
-
>>> is_pydantic_basemodel_instance(User) # Class, not instance
|
267
|
-
False
|
268
|
-
"""
|
269
|
-
return (
|
270
|
-
not isinstance(t, type)
|
271
|
-
and hasattr(t.__class__, "model_fields")
|
272
|
-
and hasattr(t, "model_dump")
|
273
|
-
and callable(getattr(t, "model_dump", None))
|
274
|
-
)
|
275
|
-
|
276
|
-
|
277
|
-
def is_msgspec_struct(t: "Any") -> bool:
|
278
|
-
"""Check if an object is a msgspec Struct class or instance using duck typing.
|
279
|
-
|
280
|
-
This function uses duck typing to identify msgspec Struct objects by checking
|
281
|
-
for the presence of characteristic attributes (`__struct_fields__` and
|
282
|
-
`__struct_config__`) without requiring direct imports of msgspec.
|
283
|
-
|
284
|
-
Args:
|
285
|
-
t: The object to check. Can be a class, instance, or any other type.
|
286
|
-
|
287
|
-
Returns:
|
288
|
-
True if the object appears to be a msgspec Struct (class or instance),
|
289
|
-
False otherwise.
|
290
|
-
|
291
|
-
Example:
|
292
|
-
>>> import msgspec
|
293
|
-
>>> class User(msgspec.Struct):
|
294
|
-
... name: str
|
295
|
-
>>> is_msgspec_struct(User)
|
296
|
-
True
|
297
|
-
>>> is_msgspec_struct(User(name="John"))
|
298
|
-
True
|
299
|
-
>>> is_msgspec_struct(dict)
|
300
|
-
False
|
301
|
-
"""
|
302
|
-
return hasattr(t, "__struct_fields__") and hasattr(t, "__struct_config__")
|
303
|
-
|
304
|
-
|
305
|
-
def get_type_description(t: "Any") -> str:
|
306
|
-
"""Creates a human-readable description of a type hint.
|
307
|
-
|
308
|
-
Args:
|
309
|
-
t : The type hint to create a description for.
|
310
|
-
|
311
|
-
Returns:
|
312
|
-
A human-readable description of the type hint.
|
313
|
-
"""
|
314
|
-
origin = inspection.get_origin(t)
|
315
|
-
args = inspection.get_args(t)
|
316
|
-
|
317
|
-
if origin is None:
|
318
|
-
# Handle basic types that should have special names
|
319
|
-
if t is list:
|
320
|
-
return "array"
|
321
|
-
elif t is dict:
|
322
|
-
return "object"
|
323
|
-
elif t is tuple:
|
324
|
-
return "tuple"
|
325
|
-
elif hasattr(t, "__name__"):
|
326
|
-
return t.__name__
|
327
|
-
return str(t)
|
328
|
-
|
329
|
-
if origin is list:
|
330
|
-
if args:
|
331
|
-
return f"array of {get_type_description(args[0])}"
|
332
|
-
return "array"
|
333
|
-
|
334
|
-
if origin is dict:
|
335
|
-
if len(args) == 2:
|
336
|
-
return f"object with {get_type_description(args[0])} keys and {get_type_description(args[1])} values"
|
337
|
-
return "object"
|
338
|
-
|
339
|
-
if origin is tuple:
|
340
|
-
if args:
|
341
|
-
arg_descriptions = [get_type_description(arg) for arg in args]
|
342
|
-
return f"tuple of ({', '.join(arg_descriptions)})"
|
343
|
-
return "tuple"
|
344
|
-
|
345
|
-
if inspection.is_literal_type(t):
|
346
|
-
if args:
|
347
|
-
values = [repr(arg) for arg in args]
|
348
|
-
return f"one of: {', '.join(values)}"
|
349
|
-
return "literal"
|
350
|
-
|
351
|
-
# Handle Union types (including Optional)
|
352
|
-
if inspection.is_union_type(t):
|
353
|
-
if inspection.is_optional_type(t):
|
354
|
-
# This is Optional[T]
|
355
|
-
non_none_args = [arg for arg in args if arg is not type(None)]
|
356
|
-
if non_none_args:
|
357
|
-
return f"optional {get_type_description(non_none_args[0])}"
|
358
|
-
else:
|
359
|
-
# This is Union[T1, T2, ...]
|
360
|
-
arg_descriptions = [get_type_description(arg) for arg in args]
|
361
|
-
return f"one of: {', '.join(arg_descriptions)}"
|
362
|
-
|
363
|
-
# Handle callable types
|
364
|
-
if inspection.is_callable_type(t):
|
365
|
-
if args and len(args) >= 2:
|
366
|
-
param_types_arg = args[0] # First arg is the parameter types
|
367
|
-
return_type = args[1] # Second arg is the return type
|
368
|
-
|
369
|
-
# param_types_arg is either a list of types or ... (Ellipsis)
|
370
|
-
if param_types_arg is ...:
|
371
|
-
return f"function(...) -> {get_type_description(return_type)}"
|
372
|
-
elif isinstance(param_types_arg, (list, tuple)):
|
373
|
-
if param_types_arg:
|
374
|
-
param_descriptions = [
|
375
|
-
get_type_description(param) for param in param_types_arg
|
376
|
-
]
|
377
|
-
return f"function({', '.join(param_descriptions)}) -> {get_type_description(return_type)}"
|
378
|
-
else:
|
379
|
-
return f"function() -> {get_type_description(return_type)}"
|
380
|
-
return "function"
|
381
|
-
|
382
|
-
# Handle generic types
|
383
|
-
if inspection.is_generic_type(t):
|
384
|
-
if args:
|
385
|
-
arg_descriptions = [get_type_description(arg) for arg in args]
|
386
|
-
return f"{origin.__name__}[{', '.join(arg_descriptions)}]"
|
387
|
-
return str(origin)
|
388
|
-
|
389
|
-
# Handle final types
|
390
|
-
if inspection.is_final_type(t):
|
391
|
-
if args:
|
392
|
-
return f"final {get_type_description(args[0])}"
|
393
|
-
return "final"
|
394
|
-
|
395
|
-
# Handle forward references
|
396
|
-
if inspection.is_forward_ref(t):
|
397
|
-
return f"forward_ref({t.__forward_arg__})"
|
398
|
-
|
399
|
-
# Handle new types
|
400
|
-
if inspection.is_new_type(t):
|
401
|
-
return f"new_type({t.__name__})"
|
402
|
-
|
403
|
-
# Handle type variables
|
404
|
-
if inspection.is_typevar(t):
|
405
|
-
return f"typevar({t.__name__})"
|
406
|
-
|
407
|
-
return str(t)
|