schemez 1.2.0__py3-none-any.whl → 1.2.3__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"
@@ -119,6 +119,9 @@ class FunctionSchema(pydantic.BaseModel):
119
119
  required = self.parameters.get("required", self.required)
120
120
 
121
121
  for name, details in properties.items():
122
+ if name.startswith("_"): # TODO: kwarg for renaming instead perhaps?
123
+ logger.debug("Skipping parameter %s due to leading underscore", name)
124
+ continue
122
125
  # Get base type
123
126
  if "enum" in details:
124
127
  values = tuple(details["enum"]) # type: ignore
@@ -246,19 +249,14 @@ class FunctionSchema(pydantic.BaseModel):
246
249
  RuntimeError: If datamodel-codegen is not available
247
250
  subprocess.CalledProcessError: If code generation fails
248
251
  """
252
+ import shutil
249
253
  import subprocess
250
254
  import tempfile
251
255
 
252
- try:
253
- # Check if datamodel-codegen is available
254
- subprocess.run(
255
- ["datamodel-codegen", "--version"],
256
- check=True,
257
- capture_output=True,
258
- )
259
- except (subprocess.CalledProcessError, FileNotFoundError) as e:
256
+ # Check if datamodel-codegen is available
257
+ if not shutil.which("datamodel-codegen"):
260
258
  msg = "datamodel-codegen not available"
261
- raise RuntimeError(msg) from e
259
+ raise RuntimeError(msg)
262
260
 
263
261
  name = class_name or f"{self.name.title()}Response"
264
262
 
@@ -393,7 +391,7 @@ def _is_optional_type(typ: type) -> TypeGuard[type]:
393
391
  True if the type is Optional, False otherwise
394
392
  """
395
393
  origin = typing.get_origin(typ)
396
- if origin not in (typing.Union, types.UnionType): # pyright: ignore
394
+ if origin not in {typing.Union, types.UnionType}: # pyright: ignore
397
395
  return False
398
396
  args = typing.get_args(typ)
399
397
  # Check if any of the union members is None or NoneType
@@ -448,7 +446,7 @@ def _resolve_type_annotation(
448
446
  args = typing.get_args(typ)
449
447
 
450
448
  # Handle Union types (including Optional)
451
- if origin in (typing.Union, types.UnionType): # pyright: ignore
449
+ if origin in {typing.Union, types.UnionType}: # pyright: ignore
452
450
  # For Optional (union with None), filter out None type
453
451
  non_none_types = [t for t in args if t is not type(None)]
454
452
  if non_none_types:
@@ -495,7 +493,7 @@ def _resolve_type_annotation(
495
493
  schema["required"] = required
496
494
  # Handle mappings - updated check
497
495
  elif (
498
- origin in (dict, typing.Dict) # noqa: UP006
496
+ origin in {dict, typing.Dict} # noqa: UP006
499
497
  or (origin is not None and isinstance(origin, type) and issubclass(origin, dict))
500
498
  ):
501
499
  schema["type"] = "object"
@@ -503,14 +501,14 @@ def _resolve_type_annotation(
503
501
  schema["additionalProperties"] = True
504
502
 
505
503
  # Handle sequences
506
- elif origin in (
504
+ elif origin in {
507
505
  list,
508
506
  set,
509
507
  tuple,
510
508
  frozenset,
511
509
  typing.List, # noqa: UP006 # pyright: ignore
512
510
  typing.Set, # noqa: UP006 # pyright: ignore
513
- ) or (
511
+ } or (
514
512
  origin is not None
515
513
  and origin.__module__ == "collections.abc"
516
514
  and origin.__name__ in {"Sequence", "MutableSequence", "Collection"}
@@ -534,11 +532,11 @@ def _resolve_type_annotation(
534
532
  schema["enum"] = [e.value for e in typ]
535
533
 
536
534
  # Basic types
537
- elif typ in (str, Path, UUID, re.Pattern):
535
+ elif typ in {str, Path, UUID, re.Pattern}:
538
536
  schema["type"] = "string"
539
537
  elif typ is int:
540
538
  schema["type"] = "integer"
541
- elif typ in (float, decimal.Decimal):
539
+ elif typ in {float, decimal.Decimal}:
542
540
  schema["type"] = "number"
543
541
  elif typ is bool:
544
542
  schema["type"] = "boolean"
@@ -696,10 +694,10 @@ def create_schema(
696
694
  # Skip the first parameter for bound methods
697
695
  if skip_first and i == 0:
698
696
  continue
699
- if param.kind in (
697
+ if param.kind in {
700
698
  inspect.Parameter.VAR_POSITIONAL,
701
699
  inspect.Parameter.VAR_KEYWORD,
702
- ):
700
+ }:
703
701
  continue
704
702
 
705
703
  param_doc = next(
@@ -726,7 +724,7 @@ def create_schema(
726
724
  function_type = _determine_function_type(func)
727
725
  return_hint = hints.get("return", Any)
728
726
 
729
- if function_type in (FunctionType.SYNC_GENERATOR, FunctionType.ASYNC_GENERATOR):
727
+ if function_type in {FunctionType.SYNC_GENERATOR, FunctionType.ASYNC_GENERATOR}:
730
728
  element_type = next(
731
729
  (t for t in typing.get_args(return_hint) if t is not type(None)),
732
730
  Any,
schemez/helpers.py CHANGED
@@ -6,6 +6,7 @@ import asyncio
6
6
  import importlib
7
7
  import os
8
8
  from pathlib import Path
9
+ import shutil
9
10
  import subprocess
10
11
  import sys
11
12
  import tempfile
@@ -177,53 +178,17 @@ def resolve_type_string(type_string: str, safe: bool = True) -> type:
177
178
  raise ValueError(msg) from e
178
179
 
179
180
 
180
- async def _detect_command(command: str, *, test_flag: str = "--version") -> list[str]:
181
- """Detect the correct command prefix for running a command.
182
-
183
- Tries 'uv run' first, then falls back to direct execution.
184
-
185
- Args:
186
- command: The command to detect
187
- test_flag: Flag to test command availability with
188
-
189
- Returns:
190
- Command prefix list (empty for direct execution)
191
-
192
- Raises:
193
- RuntimeError: If command is not available
194
- """
195
- cmd_prefixes = [["uv", "run"], []]
196
-
197
- for prefix in cmd_prefixes:
198
- try:
199
- proc = await asyncio.create_subprocess_exec(
200
- *prefix,
201
- command,
202
- test_flag,
203
- stdout=asyncio.subprocess.PIPE,
204
- stderr=asyncio.subprocess.PIPE,
205
- )
206
- await proc.communicate()
207
- if proc.returncode == 0:
208
- return prefix
209
- except FileNotFoundError:
210
- continue
211
-
212
- msg = f"{command} not available (tried both 'uv run' and direct execution)"
213
- raise RuntimeError(msg)
214
-
215
-
216
181
  async def model_to_python_code(
217
- model: type[BaseModel],
182
+ model: type[BaseModel] | dict[str, Any],
218
183
  *,
219
184
  class_name: str | None = None,
220
185
  target_python_version: PythonVersion | None = None,
221
186
  model_type: str = "pydantic.BaseModel",
222
187
  ) -> str:
223
- """Convert a BaseModel to Python code asynchronously.
188
+ """Convert a BaseModel or schema dict to Python code asynchronously.
224
189
 
225
190
  Args:
226
- model: The BaseModel class to convert
191
+ model: The BaseModel class or schema dictionary to convert
227
192
  class_name: Optional custom class name for the generated code
228
193
  target_python_version: Target Python version for code generation.
229
194
  Defaults to current system Python version.
@@ -236,10 +201,17 @@ async def model_to_python_code(
236
201
  RuntimeError: If datamodel-codegen is not available
237
202
  subprocess.CalledProcessError: If code generation fails
238
203
  """
239
- working_prefix = await _detect_command("datamodel-codegen")
240
-
241
- schema = model.model_json_schema()
242
- name = class_name or model.__name__
204
+ # Check if datamodel-codegen is available
205
+ if not shutil.which("datamodel-codegen"):
206
+ msg = "datamodel-codegen not available"
207
+ raise RuntimeError(msg)
208
+
209
+ if isinstance(model, dict):
210
+ schema = model
211
+ name = class_name or "GeneratedModel"
212
+ else:
213
+ schema = model.model_json_schema()
214
+ name = class_name or model.__name__
243
215
  py = target_python_version or f"{sys.version_info.major}.{sys.version_info.minor}"
244
216
  with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
245
217
  # Use pydantic_core.to_json for proper schema serialization
@@ -267,7 +239,6 @@ async def model_to_python_code(
267
239
 
268
240
  try: # Generate model using datamodel-codegen
269
241
  proc = await asyncio.create_subprocess_exec(
270
- *working_prefix,
271
242
  "datamodel-codegen",
272
243
  *args,
273
244
  stdout=asyncio.subprocess.PIPE,
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.3
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=NXQsZKeo7GRr7gnhEMHJvnjZhQktn9Kjf1W8xWyWh5Y,26135
9
+ schemez/helpers.py,sha256=_4-S8n7m3yajVYdYckSyrG1GsfMqCOfm5O-bGkYI1qc,7917
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.3.dist-info/licenses/LICENSE,sha256=AteGCH9r177TxxrOFEiOARrastASsf7yW6MQxlAHdwA,1078
23
+ schemez-1.2.3.dist-info/WHEEL,sha256=X16MKk8bp2DRsAuyteHJ-9qOjzmnY0x1aj0P1ftqqWA,78
24
+ schemez-1.2.3.dist-info/METADATA,sha256=VPcDp5yYQYgz_YPcUsmDFhVh3-oeL7JTOdS4cAw0Aio,11616
25
+ schemez-1.2.3.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.9.0
2
+ Generator: uv 0.9.2
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,,