schemez 1.2.0__py3-none-any.whl → 1.2.2__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.
schemez/convert.py CHANGED
@@ -64,7 +64,7 @@ def get_function_model(func: AnyCallable, *, name: str | None = None) -> type[Sc
64
64
 
65
65
  for param_name, param in sig.parameters.items():
66
66
  # Skip self/cls for methods
67
- if param_name in ("self", "cls"):
67
+ if param_name in {"self", "cls"}:
68
68
  continue
69
69
 
70
70
  type_hint = hints.get(param_name, Any)
schemez/functionschema.py CHANGED
@@ -37,7 +37,7 @@ if typing.TYPE_CHECKING:
37
37
  logger = logging.getLogger(__name__)
38
38
 
39
39
 
40
- class FunctionType(str, enum.Enum):
40
+ class FunctionType(enum.StrEnum):
41
41
  """Enum representing different function types."""
42
42
 
43
43
  SYNC = "sync"
@@ -393,7 +393,7 @@ def _is_optional_type(typ: type) -> TypeGuard[type]:
393
393
  True if the type is Optional, False otherwise
394
394
  """
395
395
  origin = typing.get_origin(typ)
396
- if origin not in (typing.Union, types.UnionType): # pyright: ignore
396
+ if origin not in {typing.Union, types.UnionType}: # pyright: ignore
397
397
  return False
398
398
  args = typing.get_args(typ)
399
399
  # Check if any of the union members is None or NoneType
@@ -448,7 +448,7 @@ def _resolve_type_annotation(
448
448
  args = typing.get_args(typ)
449
449
 
450
450
  # Handle Union types (including Optional)
451
- if origin in (typing.Union, types.UnionType): # pyright: ignore
451
+ if origin in {typing.Union, types.UnionType}: # pyright: ignore
452
452
  # For Optional (union with None), filter out None type
453
453
  non_none_types = [t for t in args if t is not type(None)]
454
454
  if non_none_types:
@@ -495,7 +495,7 @@ def _resolve_type_annotation(
495
495
  schema["required"] = required
496
496
  # Handle mappings - updated check
497
497
  elif (
498
- origin in (dict, typing.Dict) # noqa: UP006
498
+ origin in {dict, typing.Dict} # noqa: UP006
499
499
  or (origin is not None and isinstance(origin, type) and issubclass(origin, dict))
500
500
  ):
501
501
  schema["type"] = "object"
@@ -503,14 +503,14 @@ def _resolve_type_annotation(
503
503
  schema["additionalProperties"] = True
504
504
 
505
505
  # Handle sequences
506
- elif origin in (
506
+ elif origin in {
507
507
  list,
508
508
  set,
509
509
  tuple,
510
510
  frozenset,
511
511
  typing.List, # noqa: UP006 # pyright: ignore
512
512
  typing.Set, # noqa: UP006 # pyright: ignore
513
- ) or (
513
+ } or (
514
514
  origin is not None
515
515
  and origin.__module__ == "collections.abc"
516
516
  and origin.__name__ in {"Sequence", "MutableSequence", "Collection"}
@@ -534,11 +534,11 @@ def _resolve_type_annotation(
534
534
  schema["enum"] = [e.value for e in typ]
535
535
 
536
536
  # Basic types
537
- elif typ in (str, Path, UUID, re.Pattern):
537
+ elif typ in {str, Path, UUID, re.Pattern}:
538
538
  schema["type"] = "string"
539
539
  elif typ is int:
540
540
  schema["type"] = "integer"
541
- elif typ in (float, decimal.Decimal):
541
+ elif typ in {float, decimal.Decimal}:
542
542
  schema["type"] = "number"
543
543
  elif typ is bool:
544
544
  schema["type"] = "boolean"
@@ -696,10 +696,10 @@ def create_schema(
696
696
  # Skip the first parameter for bound methods
697
697
  if skip_first and i == 0:
698
698
  continue
699
- if param.kind in (
699
+ if param.kind in {
700
700
  inspect.Parameter.VAR_POSITIONAL,
701
701
  inspect.Parameter.VAR_KEYWORD,
702
- ):
702
+ }:
703
703
  continue
704
704
 
705
705
  param_doc = next(
@@ -726,7 +726,7 @@ def create_schema(
726
726
  function_type = _determine_function_type(func)
727
727
  return_hint = hints.get("return", Any)
728
728
 
729
- if function_type in (FunctionType.SYNC_GENERATOR, FunctionType.ASYNC_GENERATOR):
729
+ if function_type in {FunctionType.SYNC_GENERATOR, FunctionType.ASYNC_GENERATOR}:
730
730
  element_type = next(
731
731
  (t for t in typing.get_args(return_hint) if t is not type(None)),
732
732
  Any,
schemez/helpers.py CHANGED
@@ -214,16 +214,16 @@ async def _detect_command(command: str, *, test_flag: str = "--version") -> list
214
214
 
215
215
 
216
216
  async def model_to_python_code(
217
- model: type[BaseModel],
217
+ model: type[BaseModel] | dict[str, Any],
218
218
  *,
219
219
  class_name: str | None = None,
220
220
  target_python_version: PythonVersion | None = None,
221
221
  model_type: str = "pydantic.BaseModel",
222
222
  ) -> str:
223
- """Convert a BaseModel to Python code asynchronously.
223
+ """Convert a BaseModel or schema dict to Python code asynchronously.
224
224
 
225
225
  Args:
226
- model: The BaseModel class to convert
226
+ model: The BaseModel class or schema dictionary to convert
227
227
  class_name: Optional custom class name for the generated code
228
228
  target_python_version: Target Python version for code generation.
229
229
  Defaults to current system Python version.
@@ -238,8 +238,12 @@ async def model_to_python_code(
238
238
  """
239
239
  working_prefix = await _detect_command("datamodel-codegen")
240
240
 
241
- schema = model.model_json_schema()
242
- name = class_name or model.__name__
241
+ if isinstance(model, dict):
242
+ schema = model
243
+ name = class_name or "GeneratedModel"
244
+ else:
245
+ schema = model.model_json_schema()
246
+ name = class_name or model.__name__
243
247
  py = target_python_version or f"{sys.version_info.major}.{sys.version_info.minor}"
244
248
  with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
245
249
  # Use pydantic_core.to_json for proper schema serialization
schemez/log.py ADDED
@@ -0,0 +1,17 @@
1
+ """Logging configuration for schemez."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+
7
+
8
+ def get_logger(name: str) -> logging.Logger:
9
+ """Get a logger for the given name.
10
+
11
+ Args:
12
+ name: The name of the logger, will be prefixed with 'llmling_agent.'
13
+
14
+ Returns:
15
+ A logger instance
16
+ """
17
+ return logging.getLogger(f"schemez.{name}")
@@ -0,0 +1,8 @@
1
+ """Tool executor module for HTTP tool generation and execution."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from schemez.tool_executor.executor import HttpToolExecutor
6
+ from schemez.tool_executor.types import ToolHandler
7
+
8
+ __all__ = ["HttpToolExecutor", "ToolHandler"]
@@ -0,0 +1,322 @@
1
+ """HTTP tool executor for managing tool generation and execution."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import logging
7
+ from pathlib import Path
8
+ import time
9
+ from typing import TYPE_CHECKING, Any
10
+
11
+ from pydantic import BaseModel
12
+ from pydantic_core import from_json
13
+ from upath import UPath
14
+
15
+ from schemez.functionschema import FunctionSchema
16
+ from schemez.tool_executor.helpers import clean_generated_code, generate_input_model
17
+
18
+
19
+ if TYPE_CHECKING:
20
+ from collections.abc import Callable, Sequence
21
+
22
+ from fastapi import FastAPI
23
+
24
+ from schemez.tool_executor.types import ToolHandler
25
+
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+
30
+ class HttpToolExecutor:
31
+ """Manages HTTP tool generation and execution."""
32
+
33
+ def __init__(
34
+ self,
35
+ schemas: Sequence[dict[str, Any] | Path],
36
+ handler: ToolHandler,
37
+ base_url: str = "http://localhost:8000",
38
+ ):
39
+ """Initialize the tool executor.
40
+
41
+ Args:
42
+ schemas: List of tool schema dictionaries or file paths
43
+ handler: User-provided tool handler function
44
+ base_url: Base URL for the tool server
45
+ """
46
+ self.schemas = schemas
47
+ self.handler = handler
48
+ self.base_url = base_url
49
+
50
+ # Cached artifacts
51
+ self._tool_mappings: dict[str, str] | None = None
52
+ self._tools_code: str | None = None
53
+ self._server_app: FastAPI | None = None
54
+ self._tool_functions: dict[str, Callable] | None = None
55
+
56
+ async def _load_schemas(self) -> list[dict]:
57
+ """Load and normalize schemas from various sources."""
58
+ loaded_schemas = []
59
+
60
+ for schema in self.schemas:
61
+ match schema:
62
+ case dict():
63
+ loaded_schemas.append(schema)
64
+ case str() | Path():
65
+ text = UPath(schema).read_text("utf-8")
66
+ loaded_schemas.append(from_json(text))
67
+ case _:
68
+ msg = f"Invalid schema type: {type(schema)}"
69
+ raise TypeError(msg)
70
+
71
+ return loaded_schemas
72
+
73
+ async def _get_tool_mappings(self) -> dict[str, str]:
74
+ """Get tool name to input class mappings."""
75
+ if self._tool_mappings is None:
76
+ self._tool_mappings = {}
77
+ schemas = await self._load_schemas()
78
+
79
+ for schema_dict in schemas:
80
+ function_schema = FunctionSchema.from_dict(schema_dict)
81
+ name = "".join(word.title() for word in function_schema.name.split("_"))
82
+ input_class_name = f"{name}Input"
83
+ self._tool_mappings[function_schema.name] = input_class_name
84
+
85
+ return self._tool_mappings
86
+
87
+ async def _generate_http_wrapper(
88
+ self, schema_dict: dict, input_class_name: str
89
+ ) -> str:
90
+ """Generate HTTP wrapper function."""
91
+ name = schema_dict["name"]
92
+ description = schema_dict.get("description", "")
93
+
94
+ return f'''
95
+ async def {name}(input: {input_class_name}) -> str:
96
+ """{description}
97
+
98
+ Args:
99
+ input: Function parameters
100
+
101
+ Returns:
102
+ String response from the tool server
103
+ """
104
+ import httpx
105
+ async with httpx.AsyncClient() as client:
106
+ response = await client.post(
107
+ "{self.base_url}/tools/{name}",
108
+ json=input.model_dump(),
109
+ timeout=30.0
110
+ )
111
+ response.raise_for_status()
112
+ return response.text
113
+ '''
114
+
115
+ async def generate_tools_code(self) -> str:
116
+ """Generate HTTP wrapper tools as Python code."""
117
+ if self._tools_code is not None:
118
+ return self._tools_code
119
+
120
+ start_time = time.time()
121
+ logger.info("Starting tools code generation")
122
+
123
+ schemas = await self._load_schemas()
124
+ code_parts: list[str] = []
125
+ await self._get_tool_mappings()
126
+
127
+ # Module header
128
+ header = '''"""Generated HTTP wrapper tools."""
129
+
130
+ from __future__ import annotations
131
+
132
+ from pydantic import BaseModel, Field
133
+ from typing import Literal, List, Any
134
+ from datetime import datetime
135
+
136
+ '''
137
+ code_parts.append(header)
138
+
139
+ # Generate models and wrappers for each tool
140
+ all_exports = []
141
+ for schema_dict in schemas:
142
+ function_schema = FunctionSchema.from_dict(schema_dict)
143
+
144
+ schema_data = {
145
+ "name": function_schema.name,
146
+ "description": function_schema.description,
147
+ "parameters": function_schema.parameters,
148
+ }
149
+
150
+ # Generate input model (strip future imports from generated code)
151
+ input_code, input_class_name = await generate_input_model(schema_data)
152
+ # Remove future imports and datamodel-codegen header from individual models
153
+ cleaned_input_code = clean_generated_code(input_code)
154
+ code_parts.append(cleaned_input_code)
155
+ wrapper_code = await self._generate_http_wrapper(
156
+ schema_data, input_class_name
157
+ )
158
+ code_parts.append(wrapper_code)
159
+
160
+ all_exports.extend([input_class_name, function_schema.name])
161
+
162
+ # Add exports
163
+ code_parts.append(f"\n__all__ = {all_exports}\n")
164
+
165
+ self._tools_code = "\n".join(code_parts)
166
+ elapsed = time.time() - start_time
167
+ logger.info(f"Tools code generation completed in {elapsed:.2f}s") # noqa: G004
168
+ return self._tools_code
169
+
170
+ async def generate_server_app(self) -> FastAPI:
171
+ """Create configured FastAPI server."""
172
+ from fastapi import FastAPI, HTTPException
173
+
174
+ if self._server_app is not None:
175
+ return self._server_app
176
+
177
+ tool_mappings = await self._get_tool_mappings()
178
+ app = FastAPI(title="Tool Server", version="1.0.0")
179
+
180
+ @app.post("/tools/{tool_name}")
181
+ async def handle_tool_call(tool_name: str, input_data: dict) -> str:
182
+ """Generic endpoint that routes all tool calls to user handler."""
183
+ # Validate tool exists
184
+ if tool_name not in tool_mappings:
185
+ tools = list(tool_mappings.keys())
186
+ detail = f"Tool '{tool_name}' not found. Available: {tools}"
187
+ raise HTTPException(status_code=404, detail=detail)
188
+
189
+ # Create a simple BaseModel for validation
190
+ class DynamicInput(BaseModel):
191
+ pass
192
+
193
+ # Add fields dynamically (basic validation only)
194
+ dynamic_input = DynamicInput(**input_data)
195
+
196
+ # Call user's handler
197
+ try:
198
+ return await self.handler(tool_name, dynamic_input)
199
+ except Exception as e:
200
+ msg = f"Tool execution failed: {e}"
201
+ raise HTTPException(status_code=500, detail=msg) from e
202
+
203
+ self._server_app = app
204
+ return app
205
+
206
+ async def get_tool_functions(self) -> dict[str, Callable]:
207
+ """Get ready-to-use tool functions."""
208
+ if self._tool_functions is not None:
209
+ return self._tool_functions
210
+
211
+ start_time = time.time()
212
+ logger.info("Starting tool functions generation")
213
+
214
+ # Generate and execute the tools code
215
+ tools_code = await self.generate_tools_code()
216
+ logger.debug("Generated %s characters of code", len(tools_code))
217
+
218
+ # Create namespace and execute
219
+ namespace = {
220
+ "BaseModel": BaseModel,
221
+ "Field": BaseModel.model_fields_set,
222
+ "Literal": Any,
223
+ "List": list,
224
+ "datetime": __import__("datetime").datetime,
225
+ }
226
+
227
+ logger.debug("Executing generated tools code...")
228
+ exec_start = time.time()
229
+ exec(tools_code, namespace)
230
+ exec_elapsed = time.time() - exec_start
231
+ logger.debug("Code execution completed in %.2fs", exec_elapsed)
232
+
233
+ # Extract tool functions
234
+ tool_mappings = await self._get_tool_mappings()
235
+ self._tool_functions = {
236
+ tool_name: namespace[tool_name]
237
+ for tool_name in tool_mappings
238
+ if tool_name in namespace
239
+ }
240
+
241
+ elapsed = time.time() - start_time
242
+ logger.info(f"Tool functions generation completed in {elapsed:.2f}s") # noqa: G004
243
+ return self._tool_functions
244
+
245
+ async def start_server(
246
+ self, host: str = "0.0.0.0", port: int = 8000, background: bool = False
247
+ ) -> None | asyncio.Task:
248
+ """Start the FastAPI server.
249
+
250
+ Args:
251
+ host: Host to bind to
252
+ port: Port to bind to
253
+ background: If True, run server in background task
254
+ """
255
+ import uvicorn
256
+
257
+ app = await self.generate_server_app()
258
+
259
+ if background:
260
+ config = uvicorn.Config(app, host=host, port=port)
261
+ server = uvicorn.Server(config)
262
+ return asyncio.create_task(server.serve())
263
+
264
+ uvicorn.run(app, host=host, port=port)
265
+ return None
266
+
267
+ async def save_to_files(self, output_dir: Path) -> dict[str, Path]:
268
+ """Save generated code to files.
269
+
270
+ Args:
271
+ output_dir: Directory to save files to
272
+
273
+ Returns:
274
+ Dictionary mapping file types to paths
275
+ """
276
+ output_dir = Path(output_dir)
277
+ output_dir.mkdir(parents=True, exist_ok=True)
278
+
279
+ saved_files = {}
280
+
281
+ # Save tools module
282
+ tools_code = await self.generate_tools_code()
283
+ tools_file = output_dir / "generated_tools.py"
284
+ tools_file.write_text(tools_code)
285
+ saved_files["tools"] = tools_file
286
+
287
+ # Save server code (as template/example)
288
+ server_template = f'''"""FastAPI server using HttpToolExecutor."""
289
+
290
+ import asyncio
291
+ from pathlib import Path
292
+
293
+ from schemez.tool_executor import HttpToolExecutor, ToolHandler
294
+ from pydantic import BaseModel
295
+
296
+
297
+ async def my_tool_handler(method_name: str, input_props: BaseModel) -> str:
298
+ """Implement your tool logic here."""
299
+ match method_name:
300
+ case _:
301
+ return f"Mock result for {{method_name}}: {{input_props}}"
302
+
303
+
304
+ async def main():
305
+ """Start the server with your handler."""
306
+ executor = HttpToolExecutor(
307
+ schemas=[], # Add your schema files/dicts here
308
+ handler=my_tool_handler,
309
+ base_url="{self.base_url}"
310
+ )
311
+
312
+ await executor.start_server()
313
+
314
+
315
+ if __name__ == "__main__":
316
+ asyncio.run(main())
317
+ '''
318
+
319
+ server_file = output_dir / "server_example.py"
320
+ server_file.write_text(server_template)
321
+ saved_files["server_example"] = server_file
322
+ return saved_files
@@ -0,0 +1,46 @@
1
+ """Helper functions for the tool executor."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import time
7
+
8
+ from schemez.helpers import model_to_python_code
9
+
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ async def generate_input_model(schema_dict: dict) -> tuple[str, str]:
15
+ """Generate input model code from schema."""
16
+ start_time = time.time()
17
+ logger.debug("Generating input model for %s", schema_dict["name"])
18
+
19
+ words = [word.title() for word in schema_dict["name"].split("_")]
20
+ cls_name = f"{''.join(words)}Input"
21
+ code = await model_to_python_code(schema_dict["parameters"], class_name=cls_name)
22
+ elapsed = time.time() - start_time
23
+ logger.debug("Generated input model for %s in %.2fs", schema_dict["name"], elapsed)
24
+ return code, cls_name
25
+
26
+
27
+ def clean_generated_code(code: str) -> str:
28
+ """Clean generated code by removing future imports and headers."""
29
+ lines = code.split("\n")
30
+ cleaned_lines = []
31
+ skip_until_class = True
32
+
33
+ for line in lines:
34
+ # Skip lines until we find a class or other meaningful content
35
+ if skip_until_class:
36
+ if line.strip().startswith("class ") or (
37
+ line.strip()
38
+ and not line.startswith("#")
39
+ and not line.startswith("from __future__")
40
+ ):
41
+ skip_until_class = False
42
+ cleaned_lines.append(line)
43
+ continue
44
+ cleaned_lines.append(line)
45
+
46
+ return "\n".join(cleaned_lines)
@@ -0,0 +1,28 @@
1
+ """Type definitions for tool executor."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Protocol
6
+
7
+
8
+ if TYPE_CHECKING:
9
+ from pydantic import BaseModel
10
+
11
+
12
+ class ToolHandler(Protocol):
13
+ """Protocol for user-provided tool handler."""
14
+
15
+ async def __call__(self, method_name: str, input_props: BaseModel) -> str:
16
+ """Process a tool call.
17
+
18
+ Args:
19
+ method_name: Name of the tool being called (e.g., "get_weather")
20
+ input_props: Validated input model instance
21
+
22
+ Returns:
23
+ String result from the tool execution
24
+
25
+ Raises:
26
+ Exception: If tool execution fails
27
+ """
28
+ ...
schemez/typedefs.py CHANGED
@@ -163,7 +163,7 @@ def _convert_complex_property(
163
163
 
164
164
  # Get first non-null type or default to string
165
165
  first_type = next((t for t in types if t != "null"), "string")
166
- if first_type not in ("string", "number", "integer", "boolean"):
166
+ if first_type not in {"string", "number", "integer", "boolean"}:
167
167
  first_type = "string"
168
168
 
169
169
  return _create_simple_property(
@@ -193,7 +193,7 @@ def _convert_complex_property(
193
193
  )
194
194
 
195
195
  type_str = prop.get("type", "string")
196
- if type_str not in ("string", "number", "integer", "boolean"):
196
+ if type_str not in {"string", "number", "integer", "boolean"}:
197
197
  type_str = "string"
198
198
 
199
199
  return _create_simple_property(
@@ -0,0 +1,340 @@
1
+ Metadata-Version: 2.4
2
+ Name: schemez
3
+ Version: 1.2.2
4
+ Summary: Pydantic shim for config stuff
5
+ Keywords:
6
+ Author: Philipp Temminghoff
7
+ Author-email: Philipp Temminghoff <philipptemminghoff@googlemail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Framework :: Pydantic
12
+ Classifier: Framework :: Pydantic :: 2
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Documentation
21
+ Classifier: Topic :: Software Development
22
+ Classifier: Topic :: Utilities
23
+ Classifier: Typing :: Typed
24
+ Requires-Dist: docstring-parser>=0.17.0
25
+ Requires-Dist: griffe>=1.7.3
26
+ Requires-Dist: pydantic
27
+ Requires-Dist: universal-pathlib>=0.2.6
28
+ Requires-Dist: llmling-agent ; extra == 'ai'
29
+ Requires-Dist: anyenv>=0.4.14 ; extra == 'ai'
30
+ Requires-Dist: datamodel-code-generator ; extra == 'codegen'
31
+ Requires-Dist: fastapi>=0.118.2 ; extra == 'tool-execution'
32
+ Requires-Dist: yamling ; extra == 'yaml'
33
+ Requires-Python: >=3.13
34
+ Project-URL: Code coverage, https://app.codecov.io/gh/phil65/schemez
35
+ Project-URL: Discussions, https://github.com/phil65/schemez/discussions
36
+ Project-URL: Documentation, https://phil65.github.io/schemez/
37
+ Project-URL: Issues, https://github.com/phil65/schemez/issues
38
+ Project-URL: Source, https://github.com/phil65/schemez
39
+ Provides-Extra: ai
40
+ Provides-Extra: codegen
41
+ Provides-Extra: tool-execution
42
+ Provides-Extra: yaml
43
+ Description-Content-Type: text/markdown
44
+
45
+ # Schemez
46
+
47
+ [![PyPI License](https://img.shields.io/pypi/l/schemez.svg)](https://pypi.org/project/schemez/)
48
+ [![Package status](https://img.shields.io/pypi/status/schemez.svg)](https://pypi.org/project/schemez/)
49
+ [![Monthly downloads](https://img.shields.io/pypi/dm/schemez.svg)](https://pypi.org/project/schemez/)
50
+ [![Distribution format](https://img.shields.io/pypi/format/schemez.svg)](https://pypi.org/project/schemez/)
51
+ [![Wheel availability](https://img.shields.io/pypi/wheel/schemez.svg)](https://pypi.org/project/schemez/)
52
+ [![Python version](https://img.shields.io/pypi/pyversions/schemez.svg)](https://pypi.org/project/schemez/)
53
+ [![Implementation](https://img.shields.io/pypi/implementation/schemez.svg)](https://pypi.org/project/schemez/)
54
+ [![Releases](https://img.shields.io/github/downloads/phil65/schemez/total.svg)](https://github.com/phil65/schemez/releases)
55
+ [![Github Contributors](https://img.shields.io/github/contributors/phil65/schemez)](https://github.com/phil65/schemez/graphs/contributors)
56
+ [![Github Discussions](https://img.shields.io/github/discussions/phil65/schemez)](https://github.com/phil65/schemez/discussions)
57
+ [![Github Forks](https://img.shields.io/github/forks/phil65/schemez)](https://github.com/phil65/schemez/forks)
58
+ [![Github Issues](https://img.shields.io/github/issues/phil65/schemez)](https://github.com/phil65/schemez/issues)
59
+ [![Github Issues](https://img.shields.io/github/issues-pr/phil65/schemez)](https://github.com/phil65/schemez/pulls)
60
+ [![Github Watchers](https://img.shields.io/github/watchers/phil65/schemez)](https://github.com/phil65/schemez/watchers)
61
+ [![Github Stars](https://img.shields.io/github/stars/phil65/schemez)](https://github.com/phil65/schemez/stars)
62
+ [![Github Repository size](https://img.shields.io/github/repo-size/phil65/schemez)](https://github.com/phil65/schemez)
63
+ [![Github last commit](https://img.shields.io/github/last-commit/phil65/schemez)](https://github.com/phil65/schemez/commits)
64
+ [![Github release date](https://img.shields.io/github/release-date/phil65/schemez)](https://github.com/phil65/schemez/releases)
65
+ [![Github language count](https://img.shields.io/github/languages/count/phil65/schemez)](https://github.com/phil65/schemez)
66
+ [![Github commits this month](https://img.shields.io/github/commit-activity/m/phil65/schemez)](https://github.com/phil65/schemez)
67
+ [![Package status](https://codecov.io/gh/phil65/schemez/branch/main/graph/badge.svg)](https://codecov.io/gh/phil65/schemez/)
68
+ [![PyUp](https://pyup.io/repos/github/phil65/schemez/shield.svg)](https://pyup.io/repos/github/phil65/schemez/)
69
+
70
+ [Read the documentation!](https://phil65.github.io/schemez/)
71
+
72
+
73
+
74
+ # OpenAI Function Schema Generator
75
+
76
+ Convert Python functions to OpenAI-compatible function schemas automatically.
77
+
78
+ ## Installation
79
+
80
+ ```bash
81
+ pip install schemez
82
+ ```
83
+
84
+ ## Basic Usage
85
+
86
+ ```python
87
+ from schemez import create_schema
88
+ from typing import Literal
89
+
90
+ def get_weather(
91
+ location: str,
92
+ unit: Literal["C", "F"] = "C",
93
+ detailed: bool = False,
94
+ ) -> dict[str, str | float]:
95
+ """Get the weather for a location.
96
+
97
+ Args:
98
+ location: City or address to get weather for
99
+ unit: Temperature unit (Celsius or Fahrenheit)
100
+ detailed: Include extended forecast
101
+ """
102
+ return {"temp": 22.5, "conditions": "sunny"}
103
+
104
+ # Create schema
105
+ schema = create_schema(get_weather)
106
+
107
+ # The schema.model_dump_openai() returns a TypedDict with the complete OpenAI tool definition:
108
+ # OpenAIFunctionTool = TypedDict({
109
+ # "type": Literal["function"],
110
+ # "function": OpenAIFunctionDefinition
111
+ # })
112
+
113
+ # Use with OpenAI
114
+ from openai import OpenAI
115
+
116
+ client = OpenAI()
117
+ response = client.chat.completions.create(
118
+ model="gpt-4",
119
+ messages=[{"role": "user", "content": "What's the weather in London?"}],
120
+ tools=[schema.model_dump_openai()], # Schema includes the type: "function" wrapper
121
+ tool_choice="auto"
122
+ )
123
+ ```
124
+
125
+ > **Note**: This library supports the OpenAI API v1 format (openai>=1.0.0). For older
126
+ > versions of the OpenAI package that use the legacy functions API, you'll need to
127
+ > unwrap the function definition using `schema.model_dump_openai()["function"]`.
128
+ ```
129
+
130
+ ## Supported Types
131
+
132
+ ### Basic Types
133
+ ```python
134
+ def func(
135
+ text: str, # -> "type": "string"
136
+ number: int, # -> "type": "integer"
137
+ amount: float, # -> "type": "number"
138
+ enabled: bool, # -> "type": "boolean"
139
+ anything: Any, # -> "type": "string"
140
+ ) -> None: ...
141
+ ```
142
+
143
+ ### Container Types
144
+ ```python
145
+ def func(
146
+ items: list[str], # -> "type": "array", "items": {"type": "string"}
147
+ numbers: set[int], # -> same as list
148
+ mapping: dict[str, Any], # -> "type": "object", "additionalProperties": true
149
+ nested: list[dict[str, int]], # -> nested array/object types
150
+ sequence: Sequence[str], # -> "type": "array"
151
+ collection: Collection[int], # -> "type": "array"
152
+ ) -> None: ...
153
+ ```
154
+
155
+ ### Enums and Literals
156
+ ```python
157
+ class Color(Enum):
158
+ RED = "red"
159
+ BLUE = "blue"
160
+
161
+ def func(
162
+ color: Color, # -> "type": "string", "enum": ["red", "blue"]
163
+ mode: Literal["fast", "slow"], # -> "type": "string", "enum": ["fast", "slow"]
164
+ ) -> None: ...
165
+ ```
166
+
167
+ ### Optional and Union Types
168
+ ```python
169
+ def func(
170
+ opt1: str | None, # -> "type": "string"
171
+ opt2: int | None, # -> "type": "integer"
172
+ union: str | int, # -> "type": "string" (first type)
173
+ ) -> None: ...
174
+ ```
175
+
176
+ ### Custom Types
177
+ ```python
178
+ @dataclass
179
+ class User:
180
+ name: str
181
+ age: int
182
+
183
+ def func(
184
+ user: User, # -> "type": "object"
185
+ data: JsonDict, # -> "type": "object"
186
+ ) -> None: ...
187
+ ```
188
+
189
+ ### Type Aliases
190
+ ```python
191
+ JsonValue = dict[str, Any] | list[Any] | str | int | float | bool | None
192
+ JsonDict = dict[str, JsonValue]
193
+
194
+ def func(
195
+ data: JsonDict, # -> "type": "object"
196
+ values: list[JsonValue], # -> "type": "array"
197
+ ) -> None: ...
198
+ ```
199
+
200
+ ### Recursive Types
201
+ ```python
202
+ def func(
203
+ tree: dict[str, "dict[str, Any] | str"], # -> "type": "object"
204
+ nested: dict[str, list["dict[str, Any]"]], # -> "type": "object"
205
+ ) -> None: ...
206
+ ```
207
+
208
+ ## Generated Schema Example
209
+
210
+ ```python
211
+ {
212
+ "type": "function",
213
+ "function": {
214
+ "name": "get_weather",
215
+ "description": "Get the weather for a location.",
216
+ "parameters": {
217
+ "type": "object",
218
+ "properties": {
219
+ "location": {
220
+ "type": "string",
221
+ "description": "City or address to get weather for"
222
+ },
223
+ "unit": {
224
+ "type": "string",
225
+ "enum": ["C", "F"],
226
+ "description": "Temperature unit (Celsius or Fahrenheit)",
227
+ "default": "C"
228
+ },
229
+ "detailed": {
230
+ "type": "boolean",
231
+ "description": "Include extended forecast",
232
+ "default": false
233
+ }
234
+ },
235
+ "required": ["location"]
236
+ }
237
+ }
238
+ }
239
+ ```
240
+
241
+ ## Schema Generators
242
+
243
+ ### Module Schemas
244
+
245
+ You can generate schemas for all public functions in a module using `create_schemas_from_module`:
246
+
247
+ ```python
248
+ from schemez import create_schemas_from_module
249
+ import math
250
+
251
+ # Generate schemas for all public functions
252
+ schemas = create_schemas_from_module(math)
253
+
254
+ # Generate schemas for specific functions only
255
+ schemas = create_schemas_from_module(math, include_functions=['sin', 'cos'])
256
+
257
+ # Import module by string name
258
+ schemas = create_schemas_from_module('math')
259
+ ```
260
+
261
+ ### Class Schemas
262
+
263
+ Generate schemas for all public methods in a class using `create_schemas_from_class`:
264
+
265
+ ```python
266
+ from schemez import create_schemas_from_class
267
+
268
+ class Calculator:
269
+ def add(self, x: int, y: int) -> int:
270
+ """Add two numbers.
271
+
272
+ Args:
273
+ x: First number
274
+ y: Second number
275
+
276
+ Returns:
277
+ Sum of x and y
278
+ """
279
+ return x + y
280
+
281
+ @classmethod
282
+ def multiply(cls, x: int, y: int) -> int:
283
+ """Multiply two numbers.
284
+
285
+ Args:
286
+ x: First number
287
+ y: Second number
288
+
289
+ Returns:
290
+ Product of x and y
291
+ """
292
+ return x * y
293
+
294
+ @staticmethod
295
+ def divide(x: float, y: float) -> float:
296
+ """Divide two numbers.
297
+
298
+ Args:
299
+ x: Numerator
300
+ y: Denominator
301
+
302
+ Returns:
303
+ Result of x divided by y
304
+ """
305
+ return x / y
306
+
307
+ # Generate schemas for all public methods
308
+ schemas = create_schemas_from_class(Calculator)
309
+
310
+ # Access individual method schemas
311
+ add_schema = schemas['Calculator.add']
312
+ multiply_schema = schemas['Calculator.multiply']
313
+ divide_schema = schemas['Calculator.divide']
314
+ ```
315
+
316
+ The schema generators support:
317
+
318
+ - Regular functions
319
+ - Regular instance methods (bound and unbound)
320
+ - Class methods
321
+ - Static methods
322
+ - Decorated functions / methods
323
+ - Async functions / methods
324
+ - Property methods
325
+ - Basically all stdlib typing features as well as many stdlib types
326
+ - Method docstrings for descriptions
327
+ - Default values
328
+ - Return type hints
329
+
330
+
331
+ ## Diferences to pydantic schema generation
332
+
333
+ While Pydantics schema generation preserves detailed type information, `schema.model_dump_openai()`
334
+ simplifies types to match OpenAI's function calling format. Most special types
335
+ (datetime, UUID, Path, etc.) are handled similarly by both (we only strip unused information), but we handle enums
336
+ differently: Instead of preserving enum class information, we extract just the values
337
+ as a string enum. Union types and Optionals are also handled differently - we typically
338
+ pick the first type to keep the schema simple and practical for AI interaction.
339
+ This ensures compatibility with OpenAI's function calling API while maintaining enough
340
+ type information for the AI to understand the function signature.
@@ -0,0 +1,25 @@
1
+ schemez/__init__.py,sha256=KkwJF8pfbzw_4cBxwXuUyXIExqdxS8iumvyAL-JUED4,1669
2
+ schemez/bind_kwargs.py,sha256=ChyArgNa5R8VdwSJmmrQItMH9Ld6hStWBISw-T1wyws,6228
3
+ schemez/code.py,sha256=usZLov9i5KpK1W2VJxngUzeetgrINtodiooG_AxN-y4,2072
4
+ schemez/convert.py,sha256=3sOxOgDaFzV7uiOUSM6_Sy0YlafIlZRSevs5y2vT1Kw,4403
5
+ schemez/create_type.py,sha256=wrdqdzXtfxZfsgp9IroldoYGTJs_Rdli8TiscqhV2bI,11647
6
+ schemez/docstrings.py,sha256=kmd660wcomXzKac0SSNYxPRNbVCUovrpmE9jwnVRS6c,4115
7
+ schemez/executable.py,sha256=YM4WcmRyJ9CpzKpNgS0A-Ri0Jd7mAzfHmoaXONI-mIs,7134
8
+ schemez/functionschema.py,sha256=kAV3W8_ory2vIlJELaSajQUgo8RcbEdu7ACkYpgWniI,26125
9
+ schemez/helpers.py,sha256=6PnZfbwiQPZGiuHScqkpT3kA2Wm4O3O79gsGA95l7ow,8888
10
+ schemez/log.py,sha256=i0SDbIfWmuC_nfJdQOYAdUYaR0TBk3Mhu-K3M-lnAM4,364
11
+ schemez/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ schemez/pydantic_types.py,sha256=8vgSl8i2z9n0fB-8AJj-D3TBByEWE5IxItBxQ0XwXFI,1640
13
+ schemez/schema.py,sha256=u6SDhYDtfCjgy2Aa-_MDLLNcUfEXbeye4T-W6s3AED8,9558
14
+ schemez/schema_generators.py,sha256=Gze7S7dQkTsl_1ckeHLXPxx4jQo7RB6hHQM-5fpAsrA,6973
15
+ schemez/schemadef/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ schemez/schemadef/schemadef.py,sha256=FtD7TOnYxiuYOIfadRHKkkbZn98mWFb0_lKfPsPR-hI,14393
17
+ schemez/tool_executor/__init__.py,sha256=7wLjhA1NGekTMsiIfWLAv6J7qYhWDlanH9DKU4v1c6c,263
18
+ schemez/tool_executor/executor.py,sha256=lQFnVO5J64nFNEsy82jRS-q52PaU3fWsnuQddaLOo6c,10410
19
+ schemez/tool_executor/helpers.py,sha256=jxBv1G_R8rQU3ACHGgfVEecqO0DSZQ6qUIHcn25Ucc4,1470
20
+ schemez/tool_executor/types.py,sha256=l2DxUIEHP9bjLnEaXZ6X428cSviicTDJsc3wfSNqKxg,675
21
+ schemez/typedefs.py,sha256=3OAUQ1nin9nlsOcTPAO5xrsOqVUfwsH_7_cexQYREus,6091
22
+ schemez-1.2.2.dist-info/licenses/LICENSE,sha256=AteGCH9r177TxxrOFEiOARrastASsf7yW6MQxlAHdwA,1078
23
+ schemez-1.2.2.dist-info/WHEEL,sha256=ZbtZh9LqsQoZs-WmwRO6z-tavdkb5LzNxvrOv2F_OXE,78
24
+ schemez-1.2.2.dist-info/METADATA,sha256=hIMu_x6gj7dGIFYuILQvUcMb_IvE10OPP_nLcXKWXSA,11616
25
+ schemez-1.2.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.9.0
2
+ Generator: uv 0.9.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024, Philipp Temminghoff
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -1,86 +0,0 @@
1
- Metadata-Version: 2.3
2
- Name: schemez
3
- Version: 1.2.0
4
- Summary: Pydantic shim for config stuff
5
- Keywords:
6
- Author: Philipp Temminghoff
7
- Author-email: Philipp Temminghoff <philipptemminghoff@googlemail.com>
8
- License: MIT License
9
-
10
- Copyright (c) 2024, Philipp Temminghoff
11
-
12
- Permission is hereby granted, free of charge, to any person obtaining a copy
13
- of this software and associated documentation files (the "Software"), to deal
14
- in the Software without restriction, including without limitation the rights
15
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
- copies of the Software, and to permit persons to whom the Software is
17
- furnished to do so, subject to the following conditions:
18
-
19
- The above copyright notice and this permission notice shall be included in all
20
- copies or substantial portions of the Software.
21
-
22
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
- SOFTWARE.
29
-
30
- Classifier: Development Status :: 4 - Beta
31
- Classifier: Framework :: Pydantic
32
- Classifier: Framework :: Pydantic :: 2
33
- Classifier: Intended Audience :: Developers
34
- Classifier: Operating System :: OS Independent
35
- Classifier: Programming Language :: Python :: 3
36
- Classifier: Programming Language :: Python :: 3 :: Only
37
- Classifier: Programming Language :: Python :: 3.12
38
- Classifier: Programming Language :: Python :: 3.13
39
- Classifier: Programming Language :: Python :: 3.14
40
- Classifier: Topic :: Documentation
41
- Classifier: Topic :: Software Development
42
- Classifier: Topic :: Utilities
43
- Classifier: Typing :: Typed
44
- Requires-Dist: docstring-parser>=0.17.0
45
- Requires-Dist: griffe>=1.7.3
46
- Requires-Dist: pydantic
47
- Requires-Dist: universal-pathlib>=0.2.6
48
- Requires-Dist: llmling-agent ; extra == 'ai'
49
- Requires-Dist: anyenv>=0.4.14 ; extra == 'ai'
50
- Requires-Dist: yamling ; extra == 'yaml'
51
- Requires-Python: >=3.13
52
- Project-URL: Code coverage, https://app.codecov.io/gh/phil65/schemez
53
- Project-URL: Discussions, https://github.com/phil65/schemez/discussions
54
- Project-URL: Documentation, https://phil65.github.io/schemez/
55
- Project-URL: Issues, https://github.com/phil65/schemez/issues
56
- Project-URL: Source, https://github.com/phil65/schemez
57
- Provides-Extra: ai
58
- Provides-Extra: yaml
59
- Description-Content-Type: text/markdown
60
-
61
- # Schemez
62
-
63
- [![PyPI License](https://img.shields.io/pypi/l/schemez.svg)](https://pypi.org/project/schemez/)
64
- [![Package status](https://img.shields.io/pypi/status/schemez.svg)](https://pypi.org/project/schemez/)
65
- [![Monthly downloads](https://img.shields.io/pypi/dm/schemez.svg)](https://pypi.org/project/schemez/)
66
- [![Distribution format](https://img.shields.io/pypi/format/schemez.svg)](https://pypi.org/project/schemez/)
67
- [![Wheel availability](https://img.shields.io/pypi/wheel/schemez.svg)](https://pypi.org/project/schemez/)
68
- [![Python version](https://img.shields.io/pypi/pyversions/schemez.svg)](https://pypi.org/project/schemez/)
69
- [![Implementation](https://img.shields.io/pypi/implementation/schemez.svg)](https://pypi.org/project/schemez/)
70
- [![Releases](https://img.shields.io/github/downloads/phil65/schemez/total.svg)](https://github.com/phil65/schemez/releases)
71
- [![Github Contributors](https://img.shields.io/github/contributors/phil65/schemez)](https://github.com/phil65/schemez/graphs/contributors)
72
- [![Github Discussions](https://img.shields.io/github/discussions/phil65/schemez)](https://github.com/phil65/schemez/discussions)
73
- [![Github Forks](https://img.shields.io/github/forks/phil65/schemez)](https://github.com/phil65/schemez/forks)
74
- [![Github Issues](https://img.shields.io/github/issues/phil65/schemez)](https://github.com/phil65/schemez/issues)
75
- [![Github Issues](https://img.shields.io/github/issues-pr/phil65/schemez)](https://github.com/phil65/schemez/pulls)
76
- [![Github Watchers](https://img.shields.io/github/watchers/phil65/schemez)](https://github.com/phil65/schemez/watchers)
77
- [![Github Stars](https://img.shields.io/github/stars/phil65/schemez)](https://github.com/phil65/schemez/stars)
78
- [![Github Repository size](https://img.shields.io/github/repo-size/phil65/schemez)](https://github.com/phil65/schemez)
79
- [![Github last commit](https://img.shields.io/github/last-commit/phil65/schemez)](https://github.com/phil65/schemez/commits)
80
- [![Github release date](https://img.shields.io/github/release-date/phil65/schemez)](https://github.com/phil65/schemez/releases)
81
- [![Github language count](https://img.shields.io/github/languages/count/phil65/schemez)](https://github.com/phil65/schemez)
82
- [![Github commits this month](https://img.shields.io/github/commit-activity/m/phil65/schemez)](https://github.com/phil65/schemez)
83
- [![Package status](https://codecov.io/gh/phil65/schemez/branch/main/graph/badge.svg)](https://codecov.io/gh/phil65/schemez/)
84
- [![PyUp](https://pyup.io/repos/github/phil65/schemez/shield.svg)](https://pyup.io/repos/github/phil65/schemez/)
85
-
86
- [Read the documentation!](https://phil65.github.io/schemez/)
@@ -1,19 +0,0 @@
1
- schemez/__init__.py,sha256=KkwJF8pfbzw_4cBxwXuUyXIExqdxS8iumvyAL-JUED4,1669
2
- schemez/bind_kwargs.py,sha256=ChyArgNa5R8VdwSJmmrQItMH9Ld6hStWBISw-T1wyws,6228
3
- schemez/code.py,sha256=usZLov9i5KpK1W2VJxngUzeetgrINtodiooG_AxN-y4,2072
4
- schemez/convert.py,sha256=b6Sz11lq0HvpXfMREOqnnw8rcVg2XzTKhjjPNc4YIoE,4403
5
- schemez/create_type.py,sha256=wrdqdzXtfxZfsgp9IroldoYGTJs_Rdli8TiscqhV2bI,11647
6
- schemez/docstrings.py,sha256=kmd660wcomXzKac0SSNYxPRNbVCUovrpmE9jwnVRS6c,4115
7
- schemez/executable.py,sha256=YM4WcmRyJ9CpzKpNgS0A-Ri0Jd7mAzfHmoaXONI-mIs,7134
8
- schemez/functionschema.py,sha256=Z8kL1ckU6pEoRhOsFY6NziLhVudELHyylsFVXrCUcwU,26127
9
- schemez/helpers.py,sha256=IVuoFbzPJs3eqJBrr0BA6SIHncoU6BFJGP41zTijzXM,8716
10
- schemez/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- schemez/pydantic_types.py,sha256=8vgSl8i2z9n0fB-8AJj-D3TBByEWE5IxItBxQ0XwXFI,1640
12
- schemez/schema.py,sha256=u6SDhYDtfCjgy2Aa-_MDLLNcUfEXbeye4T-W6s3AED8,9558
13
- schemez/schema_generators.py,sha256=Gze7S7dQkTsl_1ckeHLXPxx4jQo7RB6hHQM-5fpAsrA,6973
14
- schemez/schemadef/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- schemez/schemadef/schemadef.py,sha256=FtD7TOnYxiuYOIfadRHKkkbZn98mWFb0_lKfPsPR-hI,14393
16
- schemez/typedefs.py,sha256=kxh-Vc5eKvCShdznJrg5N4_Fr8g1kYlkadEAdzgQ43w,6091
17
- schemez-1.2.0.dist-info/WHEEL,sha256=I8-bO5cg2sb8TH6ZM6EgCP87Y1cV_f9UGgWnfAhVOZI,78
18
- schemez-1.2.0.dist-info/METADATA,sha256=Nao7AGsQ0CGUiHDXT2nSE0AMOnwqlMybd8KTQSAaIB0,5399
19
- schemez-1.2.0.dist-info/RECORD,,