44enabling integration with modern IDEs and validation tools that support JSON Schema.
55"""
66
7- import inspect
8- import typing
97from collections .abc import Mapping , Sequence
10- from typing import Any , Dict , List , Optional , Union
8+ from typing import Any , Callable , Dict , Union
119
12- from voluptuous import validators as v
1310from voluptuous .schema_builder import (
1411 Extra ,
1512 Marker ,
@@ -49,7 +46,7 @@ def convert(self) -> Dict[str, Any]:
4946 Returns:
5047 A dictionary representing the JSON Schema
5148 """
52- json_schema = {
49+ json_schema : Dict [ str , Any ] = {
5350 "$schema" : "https://json-schema.org/draft/2020-12/schema" ,
5451 "type" : "object" ,
5552 }
@@ -62,9 +59,10 @@ def convert(self) -> Dict[str, Any]:
6259 json_schema .update (converted )
6360 else :
6461 # If it's not a dict, wrap it appropriately
62+ wrapped_schema = self ._wrap_non_object_schema (converted )
6563 json_schema = {
6664 "$schema" : "https://json-schema.org/draft/2020-12/schema" ,
67- ** self . _wrap_non_object_schema ( converted ) ,
65+ ** wrapped_schema ,
6866 }
6967
7068 # Add definitions if any were created
@@ -149,8 +147,8 @@ def _convert_primitive_type(self, type_class: type) -> Dict[str, str]:
149147 return {"type" : type_mapping .get (type_class , "string" )}
150148
151149 def _convert_mapping (self , mapping : Mapping ) -> Dict [str , Any ]:
152- """Convert a mapping (dictionary) schema to JSON Schema object."""
153- json_schema = {
150+ """Convert a mapping (dictionary) schema to a JSON Schema object."""
151+ json_schema : Dict [ str , Any ] = {
154152 "type" : "object" ,
155153 "properties" : {},
156154 "additionalProperties" : False ,
@@ -186,8 +184,8 @@ def _convert_mapping(self, mapping: Mapping) -> Dict[str, Any]:
186184 json_schema ["properties" ][prop_name ][
187185 "default"
188186 ] = default_value
189- except :
190- # If default factory fails, skip adding default
187+ except ( TypeError , ValueError , AttributeError ) :
188+ # If the default factory fails, skip adding default
191189 pass
192190 else :
193191 # Only add if the default value is JSON serializable
@@ -236,7 +234,7 @@ def _convert_sequence(self, sequence: Sequence) -> Dict[str, Any]:
236234 }
237235
238236 def _convert_set (self , set_schema : Union [set , frozenset ]) -> Dict [str , Any ]:
239- """Convert a set schema to JSON Schema array with unique items."""
237+ """Convert a set schema to a JSON Schema array with unique items."""
240238 if not set_schema :
241239 return {"type" : "array" , "uniqueItems" : True , "maxItems" : 0 }
242240
@@ -248,9 +246,9 @@ def _convert_marker(self, marker: Marker) -> Any:
248246 """Convert a Marker instance to its underlying schema."""
249247 return self ._convert_schema_element (marker .schema )
250248
251- def _convert_callable (self , func : callable ) -> Dict [str , Any ]:
249+ def _convert_callable (self , func : Callable [..., Any ] ) -> Dict [str , Any ]:
252250 """Convert a callable validator to JSON Schema."""
253- # For generic callables, we can't determine much about the expected type
251+ # For generic callables, we can determine little about the expected type
254252 # This is a limitation of the conversion process
255253 return {
256254 "description" : f"Custom validator: { getattr (func , '__name__' , 'anonymous' )} "
@@ -278,9 +276,9 @@ def _is_json_serializable(self, value: Any) -> bool:
278276
279277 # Validator-specific conversion methods
280278
281- def _convert_range (self , range_validator : v . Range ) -> Dict [str , Any ]:
279+ def _convert_range (self , range_validator : Any ) -> Dict [str , Any ]:
282280 """Convert Range validator to JSON Schema numeric constraints."""
283- schema = {"type" : "number" }
281+ schema : Dict [ str , Any ] = {"type" : "number" }
284282
285283 if hasattr (range_validator , 'min' ) and range_validator .min is not None :
286284 if getattr (range_validator , 'min_included' , True ):
@@ -296,9 +294,9 @@ def _convert_range(self, range_validator: v.Range) -> Dict[str, Any]:
296294
297295 return schema
298296
299- def _convert_length (self , length_validator : v . Length ) -> Dict [str , Any ]:
297+ def _convert_length (self , length_validator : Any ) -> Dict [str , Any ]:
300298 """Convert Length validator to JSON Schema string/array length constraints."""
301- schema = {}
299+ schema : Dict [ str , Any ] = {}
302300
303301 if hasattr (length_validator , 'min' ) and length_validator .min is not None :
304302 schema ["minLength" ] = length_validator .min
@@ -308,7 +306,7 @@ def _convert_length(self, length_validator: v.Length) -> Dict[str, Any]:
308306
309307 return schema
310308
311- def _convert_all (self , all_validator : v . All ) -> Dict [str , Any ]:
309+ def _convert_all (self , all_validator : Any ) -> Dict [str , Any ]:
312310 """Convert All validator to JSON Schema allOf constraint."""
313311 if not hasattr (all_validator , 'validators' ):
314312 return {}
@@ -326,7 +324,7 @@ def _convert_all(self, all_validator: v.All) -> Dict[str, Any]:
326324 else :
327325 return {}
328326
329- def _convert_any (self , any_validator : v . Any ) -> Dict [str , Any ]:
327+ def _convert_any (self , any_validator : Any ) -> Dict [str , Any ]:
330328 """Convert Any validator to JSON Schema anyOf constraint."""
331329 if not hasattr (any_validator , 'validators' ):
332330 return {}
@@ -344,46 +342,51 @@ def _convert_any(self, any_validator: v.Any) -> Dict[str, Any]:
344342 else :
345343 return {}
346344
347- def _convert_in (self , in_validator : v . In ) -> Dict [str , Any ]:
345+ def _convert_in (self , in_validator : Any ) -> Dict [str , Any ]:
348346 """Convert In validator to JSON Schema enum constraint."""
349347 if hasattr (in_validator , 'container' ):
350- return {"enum" : list (in_validator .container )}
348+ # Handle both Container and Iterable types
349+ container = in_validator .container
350+ if hasattr (container , '__iter__' ):
351+ return {"enum" : list (container )}
351352 return {}
352353
353- def _convert_match (self , match_validator : v . Match ) -> Dict [str , Any ]:
354+ def _convert_match (self , match_validator : Any ) -> Dict [str , Any ]:
354355 """Convert Match validator to JSON Schema pattern constraint."""
355356 if hasattr (match_validator , 'pattern' ):
356357 pattern = match_validator .pattern
357358 if hasattr (pattern , 'pattern' ): # compiled regex
358359 pattern = pattern .pattern
359- return {"type" : "string" , "pattern" : pattern }
360+ return {"type" : "string" , "pattern" : str ( pattern ) }
360361 return {"type" : "string" }
361362
362- def _convert_email (self , email_validator : v . Email ) -> Dict [str , Any ]:
363+ def _convert_email (self , email_validator : Any ) -> Dict [str , Any ]:
363364 """Convert Email validator to JSON Schema email format."""
364365 return {"type" : "string" , "format" : "email" }
365366
366- def _convert_url (self , url_validator : v . Url ) -> Dict [str , Any ]:
367+ def _convert_url (self , url_validator : Any ) -> Dict [str , Any ]:
367368 """Convert Url validator to JSON Schema uri format."""
368369 return {"type" : "string" , "format" : "uri" }
369370
370- def _convert_date (self , date_validator : v . Date ) -> Dict [str , Any ]:
371+ def _convert_date (self , date_validator : Any ) -> Dict [str , Any ]:
371372 """Convert Date validator to JSON Schema date format."""
372373 return {"type" : "string" , "format" : "date" }
373374
374- def _convert_datetime (self , datetime_validator : v . Datetime ) -> Dict [str , Any ]:
375+ def _convert_datetime (self , datetime_validator : Any ) -> Dict [str , Any ]:
375376 """Convert Datetime validator to JSON Schema date-time format."""
376377 return {"type" : "string" , "format" : "date-time" }
377378
378- def _convert_coerce (self , coerce_validator : v . Coerce ) -> Dict [str , Any ]:
379- """Convert Coerce validator based on target type."""
379+ def _convert_coerce (self , coerce_validator : Any ) -> Dict [str , Any ]:
380+ """Convert Coerce validator based on a target type."""
380381 if hasattr (coerce_validator , 'type' ):
381- return self ._convert_primitive_type (coerce_validator .type )
382+ coerce_type = coerce_validator .type
383+ if isinstance (coerce_type , type ):
384+ return self ._convert_primitive_type (coerce_type )
382385 return {}
383386
384- def _convert_clamp (self , clamp_validator : v . Clamp ) -> Dict [str , Any ]:
387+ def _convert_clamp (self , clamp_validator : Any ) -> Dict [str , Any ]:
385388 """Convert Clamp validator to Range-like constraints."""
386- schema = {"type" : "number" }
389+ schema : Dict [ str , Any ] = {"type" : "number" }
387390
388391 if hasattr (clamp_validator , 'min' ) and clamp_validator .min is not None :
389392 schema ["minimum" ] = clamp_validator .min
@@ -393,9 +396,7 @@ def _convert_clamp(self, clamp_validator: v.Clamp) -> Dict[str, Any]:
393396
394397 return schema
395398
396- def _convert_exactsequence (
397- self , exact_validator : v .ExactSequence
398- ) -> Dict [str , Any ]:
399+ def _convert_exactsequence (self , exact_validator : Any ) -> Dict [str , Any ]:
399400 """Convert ExactSequence validator to JSON Schema with exact items."""
400401 if hasattr (exact_validator , 'validators' ):
401402 items_schemas = [
0 commit comments