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.
@@ -0,0 +1,108 @@
1
+ """
2
+ Simple Schema - A simple, LLM-friendly schema definition library
3
+
4
+ This library provides an intuitive way to define data schemas using simple syntax
5
+ that works well with Large Language Models for data extraction and validation.
6
+
7
+ Key Features:
8
+ - Clear, descriptive function names that tell you exactly what they do
9
+ - Direct conversion paths: string → JSON Schema, Pydantic, OpenAPI
10
+ - String-based schema syntax parsing
11
+ - JSON Schema generation
12
+ - Pydantic model integration
13
+ - Built-in validation and optimization
14
+ """
15
+
16
+ # Main conversion functions (clear names)
17
+ from .parsing.string_parser import (
18
+ string_to_json_schema,
19
+ validate_string_syntax,
20
+ # Legacy names for backward compatibility
21
+ parse_string_schema,
22
+ validate_string_schema
23
+ )
24
+
25
+ # 🎯 Core Pydantic Utility Functions
26
+ from .utilities import (
27
+ string_to_model, # String → Pydantic model (main utility)
28
+ validate_to_dict, # Validate data → dict
29
+ validate_to_model, # Validate data → Pydantic model
30
+ returns_dict, # Decorator for dict validation
31
+ returns_model, # Decorator for model validation
32
+ get_model_info, # Model introspection utility
33
+ validate_schema_compatibility, # Schema compatibility checker
34
+ # Legacy alias for backward compatibility
35
+ create_model # Use string_to_model instead
36
+ )
37
+
38
+ # Integration functions (consistent naming)
39
+ from .integrations.pydantic import (
40
+ string_to_model_code,
41
+ json_schema_to_model,
42
+ model_to_string,
43
+ model_to_json_schema,
44
+ # Legacy names for backward compatibility
45
+ string_to_pydantic,
46
+ string_to_pydantic_code,
47
+ json_schema_to_pydantic,
48
+ create_pydantic_from_json_schema
49
+ )
50
+
51
+ from .integrations.openapi import (
52
+ string_to_openapi,
53
+ openapi_to_string,
54
+ openapi_to_json_schema
55
+ )
56
+
57
+ from .integrations.json_schema import (
58
+ json_schema_to_openapi,
59
+ json_schema_to_string,
60
+ # Legacy names for backward compatibility
61
+ convert_to_openapi_schema
62
+ )
63
+
64
+ # Note: Built-in presets moved to examples/ directory
65
+ # Import them from simple_schema.examples.presets if needed
66
+
67
+ __version__ = "0.1.0"
68
+ __author__ = "importal"
69
+
70
+ __all__ = [
71
+ # 🎯 Forward conversion functions (source → target)
72
+ "string_to_json_schema", # String → JSON Schema
73
+ "string_to_model", # String → Pydantic model (main utility)
74
+ "string_to_model_code", # String → Pydantic code
75
+ "string_to_openapi", # String → OpenAPI schema
76
+ "json_schema_to_model", # JSON Schema → Pydantic model
77
+ "json_schema_to_openapi", # JSON Schema → OpenAPI schema
78
+ "validate_string_syntax", # Validate string syntax
79
+
80
+ # 🔄 Reverse conversion functions (target → source)
81
+ "model_to_string", # Pydantic model → String
82
+ "model_to_json_schema", # Pydantic model → JSON Schema
83
+ "json_schema_to_string", # JSON Schema → String
84
+ "openapi_to_string", # OpenAPI schema → String
85
+ "openapi_to_json_schema", # OpenAPI schema → JSON Schema
86
+
87
+ # 🔍 Data validation functions
88
+ "validate_to_dict", # Validate data → dict
89
+ "validate_to_model", # Validate data → Pydantic model
90
+
91
+ # 🎨 Function decorators
92
+ "returns_dict", # Decorator for dict validation
93
+ "returns_model", # Decorator for model validation
94
+
95
+ # 🔧 Utility functions
96
+ "get_model_info", # Model introspection utility
97
+ "validate_schema_compatibility", # Schema compatibility checker
98
+
99
+ # 🔙 Legacy names (for backward compatibility)
100
+ "create_model", # Use string_to_model instead
101
+ "string_to_pydantic", # Use string_to_model instead
102
+ "string_to_pydantic_code", # Use string_to_model_code instead
103
+ "json_schema_to_pydantic", # Use json_schema_to_model instead
104
+ "parse_string_schema",
105
+ "validate_string_schema",
106
+ "create_pydantic_from_json_schema",
107
+ "convert_to_openapi_schema",
108
+ ]
@@ -0,0 +1,23 @@
1
+ """
2
+ Core module for Simple Schema
3
+
4
+ Contains the fundamental classes and functions for schema definition and building.
5
+ """
6
+
7
+ from .fields import SimpleField
8
+ from .builders import (
9
+ simple_schema,
10
+ list_of_objects_schema,
11
+ simple_array_schema,
12
+ quick_pydantic_model
13
+ )
14
+ from .validators import validate_schema
15
+
16
+ __all__ = [
17
+ "SimpleField",
18
+ "simple_schema",
19
+ "list_of_objects_schema",
20
+ "simple_array_schema",
21
+ "quick_pydantic_model",
22
+ "validate_schema"
23
+ ]
@@ -0,0 +1,244 @@
1
+ """
2
+ Schema builders for Simple Schema
3
+
4
+ Contains functions for building schemas from SimpleField definitions.
5
+ """
6
+
7
+ from typing import Any, Dict, List, Optional, Union, Type
8
+ import logging
9
+
10
+ from .fields import SimpleField
11
+
12
+ # Optional pydantic import
13
+ try:
14
+ from pydantic import BaseModel, Field, create_model
15
+ HAS_PYDANTIC = True
16
+ except ImportError:
17
+ HAS_PYDANTIC = False
18
+ BaseModel = None
19
+ Field = None
20
+ create_model = None
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ def simple_schema(fields: Dict[str, Union[str, SimpleField]]) -> Dict[str, Any]:
26
+ """Generate JSON Schema from simple field definitions with enhanced support"""
27
+ properties = {}
28
+ required = []
29
+
30
+ for field_name, field_def in fields.items():
31
+ if isinstance(field_def, str):
32
+ field_def = SimpleField(field_def)
33
+
34
+ prop_schema = _simple_field_to_json_schema(field_def)
35
+ properties[field_name] = prop_schema
36
+
37
+ if field_def.required:
38
+ required.append(field_name)
39
+
40
+ schema = {
41
+ "type": "object",
42
+ "properties": properties
43
+ }
44
+ if required:
45
+ schema["required"] = required
46
+
47
+ return schema
48
+
49
+
50
+ def _simple_field_to_json_schema(field: SimpleField) -> Dict[str, Any]:
51
+ """Convert SimpleField to JSON Schema property with enhanced features"""
52
+ # Handle union types first
53
+ if field.union_types and len(field.union_types) > 1:
54
+ union_schemas = []
55
+ for union_type in field.union_types:
56
+ if union_type == "null":
57
+ union_schemas.append({"type": "null"})
58
+ else:
59
+ union_schemas.append({"type": union_type})
60
+ prop = {"anyOf": union_schemas}
61
+
62
+ # Add description to the union
63
+ if field.description:
64
+ prop["description"] = field.description
65
+
66
+ return prop
67
+
68
+ # Regular single type
69
+ prop = {"type": field.field_type}
70
+
71
+ if field.description:
72
+ prop["description"] = field.description
73
+ if field.default is not None:
74
+ prop["default"] = field.default
75
+
76
+ # Handle enum/choices
77
+ if field.choices:
78
+ prop["enum"] = field.choices
79
+
80
+ # Add format hints for special types
81
+ if field.format_hint:
82
+ if field.format_hint == "email":
83
+ prop["format"] = "email"
84
+ elif field.format_hint in ["url", "uri"]:
85
+ prop["format"] = "uri"
86
+ elif field.format_hint == "datetime":
87
+ prop["format"] = "date-time"
88
+ elif field.format_hint == "date":
89
+ prop["format"] = "date"
90
+ elif field.format_hint == "uuid":
91
+ prop["format"] = "uuid"
92
+ # Note: phone doesn't have a standard JSON Schema format
93
+
94
+ # Numeric constraints
95
+ if field.field_type in ["integer", "number"]:
96
+ if field.min_val is not None:
97
+ prop["minimum"] = field.min_val
98
+ if field.max_val is not None:
99
+ prop["maximum"] = field.max_val
100
+
101
+ # String constraints
102
+ if field.field_type == "string":
103
+ if field.min_length is not None:
104
+ prop["minLength"] = field.min_length
105
+ if field.max_length is not None:
106
+ prop["maxLength"] = field.max_length
107
+
108
+ # Array constraints (for when this field itself is an array)
109
+ if field.min_items is not None:
110
+ prop["minItems"] = field.min_items
111
+ if field.max_items is not None:
112
+ prop["maxItems"] = field.max_items
113
+
114
+ return prop
115
+
116
+
117
+ def list_of_objects_schema(item_fields: Dict[str, Union[str, SimpleField]],
118
+ description: str = "List of objects",
119
+ min_items: Optional[int] = None,
120
+ max_items: Optional[int] = None) -> Dict[str, Any]:
121
+ """Generate schema for array of objects with enhanced constraints"""
122
+ item_schema = simple_schema(item_fields)
123
+
124
+ array_schema = {
125
+ "type": "array",
126
+ "description": description,
127
+ "items": item_schema
128
+ }
129
+
130
+ # Add array size constraints
131
+ if min_items is not None:
132
+ array_schema["minItems"] = min_items
133
+ if max_items is not None:
134
+ array_schema["maxItems"] = max_items
135
+
136
+ return array_schema
137
+
138
+
139
+ def simple_array_schema(item_type: str = 'string', description: str = 'Array of items',
140
+ min_items: Optional[int] = None, max_items: Optional[int] = None,
141
+ format_hint: Optional[str] = None) -> Dict[str, Any]:
142
+ """Generate schema for simple arrays like [string], [int], [email]"""
143
+ items_schema = {"type": item_type}
144
+
145
+ # Add format for special types
146
+ if format_hint:
147
+ if format_hint == "email":
148
+ items_schema["format"] = "email"
149
+ elif format_hint in ["url", "uri"]:
150
+ items_schema["format"] = "uri"
151
+ elif format_hint == "datetime":
152
+ items_schema["format"] = "date-time"
153
+ elif format_hint == "date":
154
+ items_schema["format"] = "date"
155
+ elif format_hint == "uuid":
156
+ items_schema["format"] = "uuid"
157
+
158
+ array_schema = {
159
+ "type": "array",
160
+ "description": description,
161
+ "items": items_schema
162
+ }
163
+
164
+ # Add array size constraints
165
+ if min_items is not None:
166
+ array_schema["minItems"] = min_items
167
+ if max_items is not None:
168
+ array_schema["maxItems"] = max_items
169
+
170
+ return array_schema
171
+
172
+
173
+ def quick_pydantic_model(name: str, fields: Dict[str, Union[str, SimpleField]]) -> Type:
174
+ """Create Pydantic model from simple field definitions"""
175
+ if not HAS_PYDANTIC:
176
+ raise ImportError("Pydantic is required for quick_pydantic_model. Install with: pip install pydantic")
177
+
178
+ pydantic_fields = {}
179
+
180
+ for field_name, field_def in fields.items():
181
+ if isinstance(field_def, str):
182
+ field_def = SimpleField(field_def)
183
+
184
+ from ..integrations.pydantic import _simple_field_to_pydantic
185
+ python_type, field_info = _simple_field_to_pydantic(field_def)
186
+ pydantic_fields[field_name] = (python_type, field_info)
187
+
188
+ return create_model(name, **pydantic_fields)
189
+
190
+
191
+ def _simple_field_to_pydantic(field: SimpleField) -> tuple:
192
+ """Convert SimpleField to Pydantic field specification"""
193
+ if not HAS_PYDANTIC:
194
+ raise ImportError("Pydantic is required for this function")
195
+
196
+ # Type mapping
197
+ type_mapping = {
198
+ 'string': str,
199
+ 'integer': int,
200
+ 'number': float,
201
+ 'boolean': bool
202
+ }
203
+
204
+ python_type = type_mapping.get(field.field_type, str)
205
+
206
+ # Handle union types
207
+ if field.union_types and len(field.union_types) > 1:
208
+ from typing import Union as TypingUnion
209
+ union_python_types = []
210
+ for union_type in field.union_types:
211
+ if union_type == "null":
212
+ union_python_types.append(type(None))
213
+ else:
214
+ union_python_types.append(type_mapping.get(union_type, str))
215
+ python_type = TypingUnion[tuple(union_python_types)]
216
+
217
+ # Handle optional fields
218
+ if not field.required:
219
+ python_type = Optional[python_type]
220
+
221
+ # Build Field arguments
222
+ field_kwargs = {}
223
+
224
+ if field.description:
225
+ field_kwargs['description'] = field.description
226
+
227
+ if field.default is not None:
228
+ field_kwargs['default'] = field.default
229
+ elif not field.required:
230
+ field_kwargs['default'] = None
231
+
232
+ # Numeric constraints
233
+ if field.min_val is not None:
234
+ field_kwargs['ge'] = field.min_val
235
+ if field.max_val is not None:
236
+ field_kwargs['le'] = field.max_val
237
+
238
+ # String constraints
239
+ if field.min_length is not None:
240
+ field_kwargs['min_length'] = field.min_length
241
+ if field.max_length is not None:
242
+ field_kwargs['max_length'] = field.max_length
243
+
244
+ return python_type, Field(**field_kwargs) if field_kwargs else Field()
@@ -0,0 +1,138 @@
1
+ """
2
+ Core field definitions for Simple Schema
3
+
4
+ Contains the SimpleField class and related field utilities.
5
+ """
6
+
7
+ from typing import Any, List, Optional, Union
8
+ import logging
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class SimpleField:
14
+ """Enhanced SimpleField with support for all new features"""
15
+
16
+ def __init__(self, field_type: str, description: str = "", required: bool = True,
17
+ default: Any = None, min_val: Optional[Union[int, float]] = None,
18
+ max_val: Optional[Union[int, float]] = None, min_length: Optional[int] = None,
19
+ max_length: Optional[int] = None, choices: Optional[List[Any]] = None,
20
+ min_items: Optional[int] = None, max_items: Optional[int] = None,
21
+ format_hint: Optional[str] = None, union_types: Optional[List[str]] = None):
22
+ """
23
+ Initialize a SimpleField with comprehensive validation options.
24
+
25
+ Args:
26
+ field_type: The base type (string, integer, number, boolean)
27
+ description: Human-readable description of the field
28
+ required: Whether the field is required (default: True)
29
+ default: Default value if field is not provided
30
+ min_val: Minimum value for numeric types
31
+ max_val: Maximum value for numeric types
32
+ min_length: Minimum length for string types
33
+ max_length: Maximum length for string types
34
+ choices: List of allowed values (enum)
35
+ min_items: Minimum items for array types
36
+ max_items: Maximum items for array types
37
+ format_hint: Special format hint (email, url, datetime, etc.)
38
+ union_types: List of types for union fields
39
+ """
40
+ self.field_type = field_type
41
+ self.description = description
42
+ self.required = required
43
+ self.default = default
44
+ self.min_val = min_val
45
+ self.max_val = max_val
46
+ self.min_length = min_length
47
+ self.max_length = max_length
48
+ self.choices = choices
49
+ self.min_items = min_items
50
+ self.max_items = max_items
51
+ self.format_hint = format_hint
52
+ self.union_types = union_types or []
53
+
54
+ def __repr__(self):
55
+ """String representation of the field"""
56
+ parts = [f"type={self.field_type}"]
57
+ if self.description:
58
+ parts.append(f"desc='{self.description}'")
59
+ if not self.required:
60
+ parts.append("optional")
61
+ if self.choices:
62
+ parts.append(f"choices={self.choices}")
63
+ if self.union_types:
64
+ parts.append(f"union={self.union_types}")
65
+ return f"SimpleField({', '.join(parts)})"
66
+
67
+ def to_dict(self) -> dict:
68
+ """Convert field to dictionary representation"""
69
+ result = {
70
+ 'type': self.field_type,
71
+ 'required': self.required
72
+ }
73
+
74
+ if self.description:
75
+ result['description'] = self.description
76
+ if self.default is not None:
77
+ result['default'] = self.default
78
+ if self.min_val is not None:
79
+ result['min_val'] = self.min_val
80
+ if self.max_val is not None:
81
+ result['max_val'] = self.max_val
82
+ if self.min_length is not None:
83
+ result['min_length'] = self.min_length
84
+ if self.max_length is not None:
85
+ result['max_length'] = self.max_length
86
+ if self.choices:
87
+ result['choices'] = self.choices
88
+ if self.min_items is not None:
89
+ result['min_items'] = self.min_items
90
+ if self.max_items is not None:
91
+ result['max_items'] = self.max_items
92
+ if self.format_hint:
93
+ result['format_hint'] = self.format_hint
94
+ if self.union_types:
95
+ result['union_types'] = self.union_types
96
+
97
+ return result
98
+
99
+ @classmethod
100
+ def from_dict(cls, data: dict) -> 'SimpleField':
101
+ """Create SimpleField from dictionary representation"""
102
+ return cls(
103
+ field_type=data['type'],
104
+ description=data.get('description', ''),
105
+ required=data.get('required', True),
106
+ default=data.get('default'),
107
+ min_val=data.get('min_val'),
108
+ max_val=data.get('max_val'),
109
+ min_length=data.get('min_length'),
110
+ max_length=data.get('max_length'),
111
+ choices=data.get('choices'),
112
+ min_items=data.get('min_items'),
113
+ max_items=data.get('max_items'),
114
+ format_hint=data.get('format_hint'),
115
+ union_types=data.get('union_types')
116
+ )
117
+
118
+
119
+ # Utility functions for creating common field types
120
+ def create_enhanced_field(field_type: str, **kwargs) -> SimpleField:
121
+ """Create enhanced SimpleField with all features"""
122
+ return SimpleField(field_type, **kwargs)
123
+
124
+
125
+ def create_special_type_field(special_type: str, required: bool = True, **kwargs) -> SimpleField:
126
+ """Create field with special type hints"""
127
+ return SimpleField('string', format_hint=special_type, required=required, **kwargs)
128
+
129
+
130
+ def create_enum_field(values: List[str], required: bool = True, **kwargs) -> SimpleField:
131
+ """Create enum field with specific values"""
132
+ return SimpleField('string', choices=values, required=required, **kwargs)
133
+
134
+
135
+ def create_union_field(types: List[str], required: bool = True, **kwargs) -> SimpleField:
136
+ """Create union field with multiple types"""
137
+ primary_type = types[0] if types else 'string'
138
+ return SimpleField(primary_type, union_types=types, required=required, **kwargs)