string-schema 0.1.0__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.
- simple_schema/__init__.py +108 -0
- simple_schema/core/__init__.py +23 -0
- simple_schema/core/builders.py +244 -0
- simple_schema/core/fields.py +138 -0
- simple_schema/core/validators.py +242 -0
- simple_schema/examples/__init__.py +36 -0
- simple_schema/examples/presets.py +345 -0
- simple_schema/examples/recipes.py +380 -0
- simple_schema/integrations/__init__.py +15 -0
- simple_schema/integrations/json_schema.py +385 -0
- simple_schema/integrations/openapi.py +484 -0
- simple_schema/integrations/pydantic.py +662 -0
- simple_schema/integrations/reverse.py +275 -0
- simple_schema/parsing/__init__.py +16 -0
- simple_schema/parsing/optimizer.py +246 -0
- simple_schema/parsing/string_parser.py +703 -0
- simple_schema/parsing/syntax.py +250 -0
- simple_schema/utilities.py +501 -0
- string_schema-0.1.0.dist-info/METADATA +545 -0
- string_schema-0.1.0.dist-info/RECORD +23 -0
- string_schema-0.1.0.dist-info/WHEEL +5 -0
- string_schema-0.1.0.dist-info/licenses/LICENSE +21 -0
- string_schema-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pydantic Utility Functions for Simple Schema
|
|
3
|
+
|
|
4
|
+
This module provides the core utility functions that transform Simple Schema
|
|
5
|
+
into a comprehensive Pydantic utility, enabling string-based schema usage
|
|
6
|
+
throughout the Python ecosystem.
|
|
7
|
+
|
|
8
|
+
Key Functions:
|
|
9
|
+
- create_model(): String schema → Pydantic model
|
|
10
|
+
- validate_to_dict(): Validate data → dict
|
|
11
|
+
- validate_to_model(): Validate data → Pydantic model
|
|
12
|
+
- returns_dict(): Decorator for dict validation
|
|
13
|
+
- returns_model(): Decorator for model validation
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import functools
|
|
17
|
+
import uuid
|
|
18
|
+
from typing import Any, Dict, Type, Union, Callable, Optional, List
|
|
19
|
+
import logging
|
|
20
|
+
|
|
21
|
+
# Optional pydantic import
|
|
22
|
+
try:
|
|
23
|
+
from pydantic import BaseModel, ValidationError
|
|
24
|
+
HAS_PYDANTIC = True
|
|
25
|
+
except ImportError:
|
|
26
|
+
HAS_PYDANTIC = False
|
|
27
|
+
BaseModel = None
|
|
28
|
+
ValidationError = None
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def string_to_model(schema_str: str, name: Optional[str] = None) -> Type[BaseModel]:
|
|
34
|
+
"""
|
|
35
|
+
Create Pydantic model from string schema.
|
|
36
|
+
|
|
37
|
+
This is the main utility function that converts string schemas directly
|
|
38
|
+
to Pydantic model classes, eliminating verbose model definitions.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
schema_str: String schema definition (e.g., "name:string, email:email, age:int?")
|
|
42
|
+
name: Optional name for the model class. If not provided, generates one.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Dynamically created Pydantic model class
|
|
46
|
+
|
|
47
|
+
Examples:
|
|
48
|
+
# Basic usage
|
|
49
|
+
UserModel = string_to_model("name:string(min=1,max=100), email:email, age:int(0,120)?")
|
|
50
|
+
user = UserModel(name="John", email="john@example.com", age=30)
|
|
51
|
+
|
|
52
|
+
# Array schemas
|
|
53
|
+
ProductModel = string_to_model("[{name:string, price:number(min=0), category:enum(electronics,clothing,books)}]")
|
|
54
|
+
products = ProductModel([{"name": "iPhone", "price": 999, "category": "electronics"}])
|
|
55
|
+
|
|
56
|
+
# Complex nested schemas
|
|
57
|
+
ProfileModel = string_to_model("name:string, email:email, profile:{bio:text?, avatar:url?}?")
|
|
58
|
+
"""
|
|
59
|
+
if not HAS_PYDANTIC:
|
|
60
|
+
raise ImportError("Pydantic is required for string_to_model. Install with: pip install pydantic")
|
|
61
|
+
|
|
62
|
+
# Import here to avoid circular imports
|
|
63
|
+
from .parsing.string_parser import parse_string_schema
|
|
64
|
+
from pydantic import create_model as pydantic_create_model, Field
|
|
65
|
+
from typing import List
|
|
66
|
+
|
|
67
|
+
# Generate model name if not provided
|
|
68
|
+
if name is None:
|
|
69
|
+
name = f"GeneratedModel_{str(uuid.uuid4()).replace('-', '')[:8]}"
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
# Validate the schema string first
|
|
73
|
+
from .parsing.string_parser import validate_string_schema
|
|
74
|
+
validation_result = validate_string_schema(schema_str)
|
|
75
|
+
if not validation_result['valid']:
|
|
76
|
+
error_msg = "Invalid schema syntax"
|
|
77
|
+
if validation_result['errors']:
|
|
78
|
+
error_msg += f": {', '.join(validation_result['errors'])}"
|
|
79
|
+
raise ValueError(error_msg)
|
|
80
|
+
|
|
81
|
+
# Convert string to JSON Schema
|
|
82
|
+
json_schema = parse_string_schema(schema_str)
|
|
83
|
+
|
|
84
|
+
# Handle array schemas specially
|
|
85
|
+
if json_schema.get('type') == 'array':
|
|
86
|
+
# For array schemas, use RootModel for Pydantic v2 compatibility
|
|
87
|
+
items_schema = json_schema.get('items', {})
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
# Try Pydantic v2 RootModel first
|
|
91
|
+
from pydantic import RootModel
|
|
92
|
+
|
|
93
|
+
if items_schema.get('type') == 'object':
|
|
94
|
+
# Array of objects: create nested model for items
|
|
95
|
+
from .integrations.pydantic import create_pydantic_from_json_schema
|
|
96
|
+
ItemModel = create_pydantic_from_json_schema(f"{name}Item", items_schema)
|
|
97
|
+
|
|
98
|
+
# Create the array model using RootModel
|
|
99
|
+
class ArrayModel(RootModel[List[ItemModel]]):
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
# Set the name
|
|
103
|
+
ArrayModel.__name__ = name
|
|
104
|
+
return ArrayModel
|
|
105
|
+
else:
|
|
106
|
+
# Array of simple types
|
|
107
|
+
type_mapping = {
|
|
108
|
+
'string': str,
|
|
109
|
+
'integer': int,
|
|
110
|
+
'number': float,
|
|
111
|
+
'boolean': bool
|
|
112
|
+
}
|
|
113
|
+
item_type = type_mapping.get(items_schema.get('type', 'string'), str)
|
|
114
|
+
|
|
115
|
+
# Create the array model using RootModel
|
|
116
|
+
class ArrayModel(RootModel[List[item_type]]):
|
|
117
|
+
pass
|
|
118
|
+
|
|
119
|
+
# Set the name
|
|
120
|
+
ArrayModel.__name__ = name
|
|
121
|
+
return ArrayModel
|
|
122
|
+
|
|
123
|
+
except ImportError:
|
|
124
|
+
# Fallback to Pydantic v1 style with __root__
|
|
125
|
+
if items_schema.get('type') == 'object':
|
|
126
|
+
# Array of objects: create nested model for items
|
|
127
|
+
from .integrations.pydantic import create_pydantic_from_json_schema
|
|
128
|
+
ItemModel = create_pydantic_from_json_schema(f"{name}Item", items_schema)
|
|
129
|
+
|
|
130
|
+
# Create constraints for the array
|
|
131
|
+
constraints = {}
|
|
132
|
+
if 'minItems' in json_schema:
|
|
133
|
+
constraints['min_length'] = json_schema['minItems']
|
|
134
|
+
if 'maxItems' in json_schema:
|
|
135
|
+
constraints['max_length'] = json_schema['maxItems']
|
|
136
|
+
|
|
137
|
+
# Create the array model
|
|
138
|
+
field_info = Field(**constraints) if constraints else Field()
|
|
139
|
+
return pydantic_create_model(name, __root__=(List[ItemModel], field_info))
|
|
140
|
+
else:
|
|
141
|
+
# Array of simple types
|
|
142
|
+
type_mapping = {
|
|
143
|
+
'string': str,
|
|
144
|
+
'integer': int,
|
|
145
|
+
'number': float,
|
|
146
|
+
'boolean': bool
|
|
147
|
+
}
|
|
148
|
+
item_type = type_mapping.get(items_schema.get('type', 'string'), str)
|
|
149
|
+
|
|
150
|
+
# Create constraints for the array
|
|
151
|
+
constraints = {}
|
|
152
|
+
if 'minItems' in json_schema:
|
|
153
|
+
constraints['min_length'] = json_schema['minItems']
|
|
154
|
+
if 'maxItems' in json_schema:
|
|
155
|
+
constraints['max_length'] = json_schema['maxItems']
|
|
156
|
+
|
|
157
|
+
field_info = Field(**constraints) if constraints else Field()
|
|
158
|
+
return pydantic_create_model(name, __root__=(List[item_type], field_info))
|
|
159
|
+
else:
|
|
160
|
+
# Regular object schema
|
|
161
|
+
from .integrations.pydantic import create_pydantic_from_json_schema
|
|
162
|
+
return create_pydantic_from_json_schema(name, json_schema)
|
|
163
|
+
|
|
164
|
+
except Exception as e:
|
|
165
|
+
raise ValueError(f"Failed to create model from schema '{schema_str}': {str(e)}") from e
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# Legacy alias for backward compatibility
|
|
169
|
+
def create_model(schema_str: str, name: Optional[str] = None) -> Type[BaseModel]:
|
|
170
|
+
"""
|
|
171
|
+
Create Pydantic model from string schema.
|
|
172
|
+
|
|
173
|
+
DEPRECATED: Use string_to_model() instead for consistent naming.
|
|
174
|
+
"""
|
|
175
|
+
return string_to_model(schema_str, name)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def validate_to_dict(data: Union[Dict[str, Any], Any], schema_str: str) -> Union[Dict[str, Any], List[Any]]:
|
|
179
|
+
"""
|
|
180
|
+
Validate data against string schema and return validated dict or list.
|
|
181
|
+
|
|
182
|
+
Perfect for API endpoints and JSON responses where you need validated
|
|
183
|
+
dictionaries rather than model instances.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
data: Data to validate (dict, list, model instance, or any object)
|
|
187
|
+
schema_str: String schema definition for validation
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
Validated dictionary or list
|
|
191
|
+
|
|
192
|
+
Raises:
|
|
193
|
+
ValidationError: If data doesn't match schema
|
|
194
|
+
ValueError: If schema is invalid
|
|
195
|
+
|
|
196
|
+
Examples:
|
|
197
|
+
# API endpoint usage
|
|
198
|
+
user_dict = validate_to_dict(raw_data, "name:string, email:email, age:int?")
|
|
199
|
+
# Returns: {"name": "John", "email": "john@example.com", "age": 30}
|
|
200
|
+
|
|
201
|
+
# Array validation
|
|
202
|
+
events = validate_to_dict(raw_events, "[{user_id:uuid, event:enum(login,logout,purchase), timestamp:datetime}]")
|
|
203
|
+
"""
|
|
204
|
+
if not HAS_PYDANTIC:
|
|
205
|
+
raise ImportError("Pydantic is required for validate_to_dict. Install with: pip install pydantic")
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
# Create temporary model for validation
|
|
209
|
+
TempModel = string_to_model(schema_str, "TempValidationModel")
|
|
210
|
+
|
|
211
|
+
# Check if this is an array schema
|
|
212
|
+
from .parsing.string_parser import parse_string_schema
|
|
213
|
+
json_schema = parse_string_schema(schema_str)
|
|
214
|
+
is_array_schema = json_schema.get('type') == 'array'
|
|
215
|
+
|
|
216
|
+
if is_array_schema:
|
|
217
|
+
# For array schemas, validate the data directly
|
|
218
|
+
try:
|
|
219
|
+
# Try Pydantic v2 RootModel style
|
|
220
|
+
validated_instance = TempModel(data)
|
|
221
|
+
# Return the validated array data
|
|
222
|
+
return validated_instance.model_dump() if hasattr(validated_instance, 'model_dump') else validated_instance.dict()
|
|
223
|
+
except:
|
|
224
|
+
# Fallback to Pydantic v1 style
|
|
225
|
+
validated_instance = TempModel(__root__=data)
|
|
226
|
+
# Return the validated array data
|
|
227
|
+
return validated_instance.model_dump()['__root__'] if hasattr(validated_instance, 'model_dump') else validated_instance.dict()['__root__']
|
|
228
|
+
else:
|
|
229
|
+
# Handle different input types for object schemas
|
|
230
|
+
if isinstance(data, dict):
|
|
231
|
+
validated_instance = TempModel(**data)
|
|
232
|
+
elif hasattr(data, '__dict__'):
|
|
233
|
+
# Handle objects with attributes
|
|
234
|
+
validated_instance = TempModel(**data.__dict__)
|
|
235
|
+
else:
|
|
236
|
+
# Try direct validation
|
|
237
|
+
validated_instance = TempModel(data)
|
|
238
|
+
|
|
239
|
+
# Return as dictionary (use model_dump for Pydantic v2, fallback to dict for v1)
|
|
240
|
+
if hasattr(validated_instance, 'model_dump'):
|
|
241
|
+
return validated_instance.model_dump()
|
|
242
|
+
else:
|
|
243
|
+
return validated_instance.dict()
|
|
244
|
+
|
|
245
|
+
except ValidationError as e:
|
|
246
|
+
# Re-raise the original validation error
|
|
247
|
+
raise e
|
|
248
|
+
except Exception as e:
|
|
249
|
+
raise ValueError(f"Failed to validate data against schema '{schema_str}': {str(e)}") from e
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def validate_to_model(data: Union[Dict[str, Any], Any], schema_str: str):
|
|
253
|
+
"""
|
|
254
|
+
Validate data against string schema and return Pydantic model instance.
|
|
255
|
+
|
|
256
|
+
Perfect for business logic where you need full Pydantic model features
|
|
257
|
+
like methods, validators, and type safety.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
data: Data to validate (dict, list, model instance, or any object)
|
|
261
|
+
schema_str: String schema definition for validation
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Validated Pydantic model instance
|
|
265
|
+
|
|
266
|
+
Raises:
|
|
267
|
+
ValidationError: If data doesn't match schema
|
|
268
|
+
ValueError: If schema is invalid
|
|
269
|
+
|
|
270
|
+
Examples:
|
|
271
|
+
# Business logic usage
|
|
272
|
+
user_model = validate_to_model(raw_data, "name:string, email:email, age:int?")
|
|
273
|
+
print(user_model.name) # Access with full type safety
|
|
274
|
+
|
|
275
|
+
# Complex validation
|
|
276
|
+
profile = validate_to_model(data, "name:string, email:email, profile:{bio:text?, avatar:url?}?")
|
|
277
|
+
if profile.profile:
|
|
278
|
+
print(profile.profile.bio)
|
|
279
|
+
"""
|
|
280
|
+
if not HAS_PYDANTIC:
|
|
281
|
+
raise ImportError("Pydantic is required for validate_to_model. Install with: pip install pydantic")
|
|
282
|
+
|
|
283
|
+
try:
|
|
284
|
+
# Create temporary model for validation
|
|
285
|
+
TempModel = string_to_model(schema_str, "TempValidationModel")
|
|
286
|
+
|
|
287
|
+
# Check if this is an array schema
|
|
288
|
+
from .parsing.string_parser import parse_string_schema
|
|
289
|
+
json_schema = parse_string_schema(schema_str)
|
|
290
|
+
is_array_schema = json_schema.get('type') == 'array'
|
|
291
|
+
|
|
292
|
+
if is_array_schema:
|
|
293
|
+
# For array schemas, validate the data directly
|
|
294
|
+
try:
|
|
295
|
+
# Try Pydantic v2 RootModel style
|
|
296
|
+
return TempModel(data)
|
|
297
|
+
except:
|
|
298
|
+
# Fallback to Pydantic v1 style
|
|
299
|
+
return TempModel(__root__=data)
|
|
300
|
+
else:
|
|
301
|
+
# Handle different input types for object schemas
|
|
302
|
+
if isinstance(data, dict):
|
|
303
|
+
return TempModel(**data)
|
|
304
|
+
elif hasattr(data, '__dict__'):
|
|
305
|
+
# Handle objects with attributes
|
|
306
|
+
return TempModel(**data.__dict__)
|
|
307
|
+
else:
|
|
308
|
+
# Try direct validation
|
|
309
|
+
return TempModel(data)
|
|
310
|
+
|
|
311
|
+
except ValidationError as e:
|
|
312
|
+
# Re-raise the original validation error
|
|
313
|
+
raise e
|
|
314
|
+
except Exception as e:
|
|
315
|
+
raise ValueError(f"Failed to validate data against schema '{schema_str}': {str(e)}") from e
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def returns_dict(schema_str: str) -> Callable:
|
|
319
|
+
"""
|
|
320
|
+
Decorator that validates function return values to dict format.
|
|
321
|
+
|
|
322
|
+
Perfect for API endpoints where you want to ensure consistent,
|
|
323
|
+
validated dictionary responses.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
schema_str: String schema definition for return value validation
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
Decorator function
|
|
330
|
+
|
|
331
|
+
Examples:
|
|
332
|
+
# API endpoint decorator
|
|
333
|
+
@returns_dict("id:uuid, name:string, status:enum(created,updated)")
|
|
334
|
+
def create_user(user_data):
|
|
335
|
+
# Business logic here
|
|
336
|
+
return {"id": generate_uuid(), "name": user_data["name"], "status": "created"}
|
|
337
|
+
|
|
338
|
+
# Data pipeline decorator
|
|
339
|
+
@returns_dict("[{user_id:uuid, event:enum(login,logout,purchase), timestamp:datetime}]")
|
|
340
|
+
def process_event_stream(raw_events):
|
|
341
|
+
# Transform and validate events
|
|
342
|
+
return transformed_events # Returns list of validated dicts
|
|
343
|
+
"""
|
|
344
|
+
def decorator(func: Callable) -> Callable:
|
|
345
|
+
@functools.wraps(func)
|
|
346
|
+
def wrapper(*args, **kwargs):
|
|
347
|
+
result = func(*args, **kwargs)
|
|
348
|
+
try:
|
|
349
|
+
return validate_to_dict(result, schema_str)
|
|
350
|
+
except Exception as e:
|
|
351
|
+
raise ValueError(f"Function '{func.__name__}' returned invalid data for schema '{schema_str}': {str(e)}") from e
|
|
352
|
+
return wrapper
|
|
353
|
+
return decorator
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def returns_model(schema_str: str) -> Callable:
|
|
357
|
+
"""
|
|
358
|
+
Decorator that validates function return values to Pydantic model instances.
|
|
359
|
+
|
|
360
|
+
Perfect for business logic functions where you want to ensure type-safe
|
|
361
|
+
model instances with full Pydantic features.
|
|
362
|
+
|
|
363
|
+
Args:
|
|
364
|
+
schema_str: String schema definition for return value validation
|
|
365
|
+
|
|
366
|
+
Returns:
|
|
367
|
+
Decorator function
|
|
368
|
+
|
|
369
|
+
Examples:
|
|
370
|
+
# Business logic decorator
|
|
371
|
+
@returns_model("name:string, email:email, profile:{bio:text?, avatar:url?}?")
|
|
372
|
+
def enrich_user_data(basic_data):
|
|
373
|
+
# Enhancement logic here
|
|
374
|
+
return enhanced_user_data # Returns validated Pydantic model
|
|
375
|
+
|
|
376
|
+
# Data processing decorator
|
|
377
|
+
@returns_model("id:uuid, processed_at:datetime, result:{score:float(0,1), confidence:float(0,1)}")
|
|
378
|
+
def process_ml_result(raw_result):
|
|
379
|
+
# ML processing logic
|
|
380
|
+
return processed_result # Returns validated model with type safety
|
|
381
|
+
"""
|
|
382
|
+
def decorator(func: Callable) -> Callable:
|
|
383
|
+
@functools.wraps(func)
|
|
384
|
+
def wrapper(*args, **kwargs):
|
|
385
|
+
result = func(*args, **kwargs)
|
|
386
|
+
try:
|
|
387
|
+
return validate_to_model(result, schema_str)
|
|
388
|
+
except Exception as e:
|
|
389
|
+
raise ValueError(f"Function '{func.__name__}' returned invalid data for schema '{schema_str}': {str(e)}") from e
|
|
390
|
+
return wrapper
|
|
391
|
+
return decorator
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
# Utility functions for enhanced error handling and debugging
|
|
395
|
+
|
|
396
|
+
def get_model_info(model_class) -> Dict[str, Any]:
|
|
397
|
+
"""
|
|
398
|
+
Get detailed information about a generated Pydantic model.
|
|
399
|
+
|
|
400
|
+
Args:
|
|
401
|
+
model_class: Pydantic model class
|
|
402
|
+
|
|
403
|
+
Returns:
|
|
404
|
+
Dictionary with model information including fields, types, and constraints
|
|
405
|
+
"""
|
|
406
|
+
if not HAS_PYDANTIC or not issubclass(model_class, BaseModel):
|
|
407
|
+
raise ValueError("Input must be a Pydantic model class")
|
|
408
|
+
|
|
409
|
+
info = {
|
|
410
|
+
"model_name": model_class.__name__,
|
|
411
|
+
"fields": {},
|
|
412
|
+
"required_fields": [],
|
|
413
|
+
"optional_fields": []
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
# Handle both Pydantic v1 and v2
|
|
417
|
+
if hasattr(model_class, 'model_fields'):
|
|
418
|
+
# Pydantic v2
|
|
419
|
+
fields_dict = model_class.model_fields
|
|
420
|
+
for field_name, field_info in fields_dict.items():
|
|
421
|
+
field_data = {
|
|
422
|
+
"type": str(field_info.annotation) if hasattr(field_info, 'annotation') else "unknown",
|
|
423
|
+
"required": field_info.is_required() if hasattr(field_info, 'is_required') else True,
|
|
424
|
+
"default": field_info.default if hasattr(field_info, 'default') and field_info.default is not ... else None,
|
|
425
|
+
"constraints": {}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
# Extract constraints from field_info
|
|
429
|
+
if hasattr(field_info, 'constraints'):
|
|
430
|
+
constraints = field_info.constraints
|
|
431
|
+
for constraint in constraints:
|
|
432
|
+
if hasattr(constraint, 'ge') and constraint.ge is not None:
|
|
433
|
+
field_data["constraints"]["min_value"] = constraint.ge
|
|
434
|
+
if hasattr(constraint, 'le') and constraint.le is not None:
|
|
435
|
+
field_data["constraints"]["max_value"] = constraint.le
|
|
436
|
+
if hasattr(constraint, 'min_length') and constraint.min_length is not None:
|
|
437
|
+
field_data["constraints"]["min_length"] = constraint.min_length
|
|
438
|
+
if hasattr(constraint, 'max_length') and constraint.max_length is not None:
|
|
439
|
+
field_data["constraints"]["max_length"] = constraint.max_length
|
|
440
|
+
|
|
441
|
+
info["fields"][field_name] = field_data
|
|
442
|
+
|
|
443
|
+
if field_data["required"]:
|
|
444
|
+
info["required_fields"].append(field_name)
|
|
445
|
+
else:
|
|
446
|
+
info["optional_fields"].append(field_name)
|
|
447
|
+
else:
|
|
448
|
+
# Pydantic v1 fallback
|
|
449
|
+
fields_dict = getattr(model_class, '__fields__', {})
|
|
450
|
+
for field_name, field_info in fields_dict.items():
|
|
451
|
+
field_data = {
|
|
452
|
+
"type": str(getattr(field_info, 'type_', 'unknown')),
|
|
453
|
+
"required": getattr(field_info, 'required', True),
|
|
454
|
+
"default": getattr(field_info, 'default', None) if getattr(field_info, 'default', ...) is not ... else None,
|
|
455
|
+
"constraints": {}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
info["fields"][field_name] = field_data
|
|
459
|
+
|
|
460
|
+
if field_data["required"]:
|
|
461
|
+
info["required_fields"].append(field_name)
|
|
462
|
+
else:
|
|
463
|
+
info["optional_fields"].append(field_name)
|
|
464
|
+
|
|
465
|
+
return info
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def validate_schema_compatibility(schema_str: str) -> Dict[str, Any]:
|
|
469
|
+
"""
|
|
470
|
+
Validate that a string schema is compatible with Pydantic model generation.
|
|
471
|
+
|
|
472
|
+
Args:
|
|
473
|
+
schema_str: String schema definition to validate
|
|
474
|
+
|
|
475
|
+
Returns:
|
|
476
|
+
Dictionary with compatibility information and any warnings
|
|
477
|
+
"""
|
|
478
|
+
from .parsing.string_parser import validate_string_schema
|
|
479
|
+
|
|
480
|
+
result = validate_string_schema(schema_str)
|
|
481
|
+
|
|
482
|
+
# Add Pydantic-specific compatibility checks
|
|
483
|
+
compatibility = {
|
|
484
|
+
"pydantic_compatible": result["valid"],
|
|
485
|
+
"warnings": result.get("warnings", []),
|
|
486
|
+
"errors": result.get("errors", []),
|
|
487
|
+
"features_used": result.get("features_used", []),
|
|
488
|
+
"recommendations": []
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
# Add recommendations for better Pydantic usage
|
|
492
|
+
if "arrays" in compatibility["features_used"]:
|
|
493
|
+
compatibility["recommendations"].append("Consider using List[Model] type hints for better IDE support")
|
|
494
|
+
|
|
495
|
+
if "union_types" in compatibility["features_used"]:
|
|
496
|
+
compatibility["recommendations"].append("Union types work well with Pydantic's automatic type coercion")
|
|
497
|
+
|
|
498
|
+
if "special_types" in compatibility["features_used"]:
|
|
499
|
+
compatibility["recommendations"].append("Special types (email, url, etc.) provide automatic validation")
|
|
500
|
+
|
|
501
|
+
return compatibility
|