schemez 1.1.0__py3-none-any.whl → 1.1.1__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/helpers.py CHANGED
@@ -4,19 +4,19 @@ from __future__ import annotations
4
4
 
5
5
  import asyncio
6
6
  import importlib
7
- import json
8
7
  import os
9
8
  from pathlib import Path
10
9
  import subprocess
11
10
  import sys
12
11
  import tempfile
13
- from typing import TYPE_CHECKING, Any
12
+ from typing import TYPE_CHECKING, Any, Literal
14
13
 
15
14
  from pydantic import BaseModel
15
+ from pydantic_core import to_json
16
16
 
17
17
 
18
18
  StrPath = str | os.PathLike[str]
19
-
19
+ PythonVersion = Literal["3.13", "3.14", "3.15"]
20
20
 
21
21
  if TYPE_CHECKING:
22
22
  from collections.abc import Callable
@@ -177,11 +177,48 @@ def resolve_type_string(type_string: str, safe: bool = True) -> type:
177
177
  raise ValueError(msg) from e
178
178
 
179
179
 
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
+
180
216
  async def model_to_python_code(
181
217
  model: type[BaseModel],
182
218
  *,
183
219
  class_name: str | None = None,
184
- target_python_version: str | None = None,
220
+ target_python_version: PythonVersion | None = None,
221
+ model_type: str = "pydantic.BaseModel",
185
222
  ) -> str:
186
223
  """Convert a BaseModel to Python code asynchronously.
187
224
 
@@ -190,6 +227,7 @@ async def model_to_python_code(
190
227
  class_name: Optional custom class name for the generated code
191
228
  target_python_version: Target Python version for code generation.
192
229
  Defaults to current system Python version.
230
+ model_type: Type of the generated model. Defaults to "pydantic.BaseModel".
193
231
 
194
232
  Returns:
195
233
  Generated Python code as string
@@ -198,67 +236,50 @@ async def model_to_python_code(
198
236
  RuntimeError: If datamodel-codegen is not available
199
237
  subprocess.CalledProcessError: If code generation fails
200
238
  """
201
- try:
202
- # Check if datamodel-codegen is available
203
- proc = await asyncio.create_subprocess_exec(
204
- "datamodel-codegen",
205
- "--version",
206
- stdout=asyncio.subprocess.PIPE,
207
- stderr=asyncio.subprocess.PIPE,
208
- )
209
- await proc.communicate()
210
- if proc.returncode != 0:
211
- raise subprocess.CalledProcessError(
212
- proc.returncode or -1, "datamodel-codegen"
213
- )
214
- except FileNotFoundError as e:
215
- msg = "datamodel-codegen not available"
216
- raise RuntimeError(msg) from e
239
+ working_prefix = await _detect_command("datamodel-codegen")
217
240
 
218
- # Get model schema
219
241
  schema = model.model_json_schema()
220
242
  name = class_name or model.__name__
221
- python_version = (
222
- target_python_version or f"{sys.version_info.major}.{sys.version_info.minor}"
223
- )
224
-
225
- # Create temporary file with schema
243
+ py = target_python_version or f"{sys.version_info.major}.{sys.version_info.minor}"
226
244
  with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
227
- json.dump(schema, f)
245
+ # Use pydantic_core.to_json for proper schema serialization
246
+ schema_json = to_json(schema, indent=2).decode()
247
+ f.write(schema_json)
228
248
  schema_file = Path(f.name)
229
249
 
230
- try:
231
- # Generate model using datamodel-codegen
250
+ args = [
251
+ "--input",
252
+ str(schema_file),
253
+ "--input-file-type",
254
+ "jsonschema",
255
+ "--output-model-type",
256
+ model_type,
257
+ "--class-name",
258
+ name,
259
+ "--disable-timestamp",
260
+ "--use-union-operator",
261
+ "--use-schema-description",
262
+ "--enum-field-as-literal",
263
+ "all",
264
+ "--target-python-version",
265
+ py,
266
+ ]
267
+
268
+ try: # Generate model using datamodel-codegen
232
269
  proc = await asyncio.create_subprocess_exec(
270
+ *working_prefix,
233
271
  "datamodel-codegen",
234
- "--input",
235
- str(schema_file),
236
- "--input-file-type",
237
- "jsonschema",
238
- "--output-model-type",
239
- "pydantic.BaseModel",
240
- "--class-name",
241
- name,
242
- "--disable-timestamp",
243
- "--use-union-operator",
244
- "--use-schema-description",
245
- "--enum-field-as-literal",
246
- "all",
247
- "--target-python-version",
248
- python_version,
272
+ *args,
249
273
  stdout=asyncio.subprocess.PIPE,
250
274
  stderr=asyncio.subprocess.PIPE,
251
275
  )
252
- stdout, stderr = await proc.communicate()
276
+ stdout, _stderr = await proc.communicate()
253
277
 
254
278
  if proc.returncode != 0:
255
- msg = f"datamodel-codegen failed: {stderr.decode()}"
256
- raise subprocess.CalledProcessError(
257
- proc.returncode or -1, "datamodel-codegen"
258
- )
279
+ code = proc.returncode or -1
280
+ raise subprocess.CalledProcessError(code, "datamodel-codegen")
259
281
 
260
282
  return stdout.decode().strip()
261
283
 
262
- finally:
263
- # Cleanup temp file
284
+ finally: # Cleanup temp file
264
285
  schema_file.unlink(missing_ok=True)
schemez/schema.py CHANGED
@@ -19,6 +19,7 @@ if TYPE_CHECKING:
19
19
 
20
20
  StrPath = str | os.PathLike[str]
21
21
  SourceType = Literal["pdf", "image"]
22
+ PythonVersion = Literal["3.13", "3.14", "3.15"]
22
23
 
23
24
  DEFAULT_SYSTEM_PROMPT = "You are a schema extractor for {name} BaseModels."
24
25
  DEFAULT_USER_PROMPT = "Extract information from this document:"
@@ -260,7 +261,7 @@ class Schema(BaseModel):
260
261
  cls,
261
262
  *,
262
263
  class_name: str | None = None,
263
- target_python_version: str | None = None,
264
+ target_python_version: PythonVersion | None = None,
264
265
  ) -> str:
265
266
  """Convert this model to Python code asynchronously.
266
267
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: schemez
3
- Version: 1.1.0
3
+ Version: 1.1.1
4
4
  Summary: Pydantic shim for config stuff
5
5
  Keywords:
6
6
  Author: Philipp Temminghoff
@@ -2,12 +2,12 @@ schemez/__init__.py,sha256=iDo1ZpV07BUOmRmISV6QDA8s3viJR5V1NnrBsdw6eVM,985
2
2
  schemez/code.py,sha256=usZLov9i5KpK1W2VJxngUzeetgrINtodiooG_AxN-y4,2072
3
3
  schemez/convert.py,sha256=b6Sz11lq0HvpXfMREOqnnw8rcVg2XzTKhjjPNc4YIoE,4403
4
4
  schemez/docstrings.py,sha256=kmd660wcomXzKac0SSNYxPRNbVCUovrpmE9jwnVRS6c,4115
5
- schemez/helpers.py,sha256=TeYYb0_SghARkDwfXzlnrSErE4bhoGqzmDSCW63eOms,8016
5
+ schemez/helpers.py,sha256=IVuoFbzPJs3eqJBrr0BA6SIHncoU6BFJGP41zTijzXM,8716
6
6
  schemez/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  schemez/pydantic_types.py,sha256=8vgSl8i2z9n0fB-8AJj-D3TBByEWE5IxItBxQ0XwXFI,1640
8
- schemez/schema.py,sha256=tBrEuYuq_vqgjNZMebhdkbcFWXhVqRPTZCuCqL0boVk,9500
8
+ schemez/schema.py,sha256=u6SDhYDtfCjgy2Aa-_MDLLNcUfEXbeye4T-W6s3AED8,9558
9
9
  schemez/schemadef/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  schemez/schemadef/schemadef.py,sha256=FtD7TOnYxiuYOIfadRHKkkbZn98mWFb0_lKfPsPR-hI,14393
11
- schemez-1.1.0.dist-info/WHEEL,sha256=I8-bO5cg2sb8TH6ZM6EgCP87Y1cV_f9UGgWnfAhVOZI,78
12
- schemez-1.1.0.dist-info/METADATA,sha256=BdB7YfOeV3zs62Z1CjDfLSL0qhDZXg-4-s9cXdItdqs,5359
13
- schemez-1.1.0.dist-info/RECORD,,
11
+ schemez-1.1.1.dist-info/WHEEL,sha256=I8-bO5cg2sb8TH6ZM6EgCP87Y1cV_f9UGgWnfAhVOZI,78
12
+ schemez-1.1.1.dist-info/METADATA,sha256=JwgerPl41Wss2TGr4FLUr3tic62ma4lUAN8rTqgJA9I,5359
13
+ schemez-1.1.1.dist-info/RECORD,,