11import sys
22import warnings
3+ from functools import partial
34from typing import Any
5+ from typing import Callable
46from typing import Dict
57from typing import Iterable
68from typing import Optional
1113 from functools import cached_property
1214else :
1315 from backports .cached_property import cached_property
16+ from jsonschema ._format import FormatChecker
1417from jsonschema .protocols import Validator
1518from openapi_schema_validator import OAS30Validator
1619
1720from openapi_core .spec import Spec
1821from openapi_core .unmarshalling .schemas .datatypes import CustomFormattersDict
1922from openapi_core .unmarshalling .schemas .datatypes import FormattersDict
23+ from openapi_core .unmarshalling .schemas .datatypes import UnmarshallersDict
2024from openapi_core .unmarshalling .schemas .enums import ValidationContext
21- from openapi_core .unmarshalling .schemas .exceptions import (
22- FormatterNotFoundError ,
23- )
2425from openapi_core .unmarshalling .schemas .formatters import Formatter
2526from openapi_core .unmarshalling .schemas .unmarshallers import AnyUnmarshaller
2627from openapi_core .unmarshalling .schemas .unmarshallers import ArrayUnmarshaller
4748
4849
4950class SchemaValidatorsFactory :
50-
51- CONTEXTS = {
52- ValidationContext .REQUEST : "write" ,
53- ValidationContext .RESPONSE : "read" ,
54- }
55-
5651 def __init__ (
5752 self ,
5853 schema_validator_class : Type [Validator ],
54+ base_format_checker : Optional [FormatChecker ] = None ,
55+ formatters : Optional [CustomFormattersDict ] = None ,
56+ format_unmarshallers : Optional [UnmarshallersDict ] = None ,
5957 custom_formatters : Optional [CustomFormattersDict ] = None ,
60- context : Optional [ValidationContext ] = None ,
6158 ):
6259 self .schema_validator_class = schema_validator_class
60+ if base_format_checker is None :
61+ base_format_checker = self .schema_validator_class .FORMAT_CHECKER
62+ self .base_format_checker = base_format_checker
63+ if formatters is None :
64+ formatters = {}
65+ self .formatters = formatters
66+ if format_unmarshallers is None :
67+ format_unmarshallers = {}
68+ self .format_unmarshallers = format_unmarshallers
6369 if custom_formatters is None :
6470 custom_formatters = {}
6571 self .custom_formatters = custom_formatters
66- self .context = context
6772
68- def create ( self , schema : Spec ) -> Validator :
69- resolver = schema . accessor . resolver # type: ignore
70- custom_format_checks = {
73+ @ cached_property
74+ def format_checker ( self ) -> FormatChecker :
75+ format_checks : Dict [ str , Callable [[ Any ], bool ]] = {
7176 name : formatter .validate
72- for name , formatter in self .custom_formatters .items ()
77+ for formatters_list in [self .formatters , self .custom_formatters ]
78+ for name , formatter in formatters_list .items ()
7379 }
74- format_checker = build_format_checker (** custom_format_checks )
75- kwargs = {
76- "resolver" : resolver ,
77- "format_checker" : format_checker ,
78- }
79- if self .context is not None :
80- kwargs [self .CONTEXTS [self .context ]] = True
80+ format_checks .update (
81+ {
82+ name : self ._create_checker (name )
83+ for name , _ in self .format_unmarshallers .items ()
84+ }
85+ )
86+ return build_format_checker (self .base_format_checker , ** format_checks )
87+
88+ def _create_checker (self , name : str ) -> Callable [[Any ], bool ]:
89+ if name in self .base_format_checker .checkers :
90+ return partial (self .base_format_checker .check , format = name )
91+
92+ return lambda x : True
93+
94+ def get_checker (self , name : str ) -> Callable [[Any ], bool ]:
95+ if name in self .format_checker .checkers :
96+ return partial (self .format_checker .check , format = name )
97+
98+ return lambda x : True
99+
100+ def create (self , schema : Spec ) -> Validator :
101+ resolver = schema .accessor .resolver # type: ignore
81102 with schema .open () as schema_dict :
82- return self .schema_validator_class (schema_dict , ** kwargs )
103+ return self .schema_validator_class (
104+ schema_dict ,
105+ resolver = resolver ,
106+ format_checker = self .format_checker ,
107+ )
108+
109+
110+ class SchemaFormattersFactory :
111+ def __init__ (
112+ self ,
113+ validators_factory : SchemaValidatorsFactory ,
114+ formatters : Optional [CustomFormattersDict ] = None ,
115+ format_unmarshallers : Optional [UnmarshallersDict ] = None ,
116+ custom_formatters : Optional [CustomFormattersDict ] = None ,
117+ ):
118+ self .validators_factory = validators_factory
119+ if formatters is None :
120+ formatters = {}
121+ self .formatters = formatters
122+ if format_unmarshallers is None :
123+ format_unmarshallers = {}
124+ self .format_unmarshallers = format_unmarshallers
125+ if custom_formatters is None :
126+ custom_formatters = {}
127+ self .custom_formatters = custom_formatters
128+
129+ def create (self , schema_format : str ) -> Optional [Formatter ]:
130+ if schema_format in self .custom_formatters :
131+ return self .custom_formatters [schema_format ]
132+ if schema_format in self .formatters :
133+ return self .formatters [schema_format ]
134+ if schema_format in self .format_unmarshallers :
135+ validate_callable = self .validators_factory .get_checker (
136+ schema_format
137+ )
138+ format_callable = self .format_unmarshallers [schema_format ]
139+ return Formatter .from_callables (validate_callable , format_callable )
140+
141+ return None
83142
84143
85144class SchemaUnmarshallersFactory :
@@ -104,21 +163,40 @@ class SchemaUnmarshallersFactory:
104163 def __init__ (
105164 self ,
106165 schema_validator_class : Type [Validator ],
166+ base_format_checker : Optional [FormatChecker ] = None ,
167+ formatters : Optional [CustomFormattersDict ] = None ,
168+ format_unmarshallers : Optional [UnmarshallersDict ] = None ,
107169 custom_formatters : Optional [CustomFormattersDict ] = None ,
108170 context : Optional [ValidationContext ] = None ,
109171 ):
110172 self .schema_validator_class = schema_validator_class
173+ self .base_format_checker = base_format_checker
111174 if custom_formatters is None :
112175 custom_formatters = {}
176+ self .formatters = formatters
177+ if format_unmarshallers is None :
178+ format_unmarshallers = {}
179+ self .format_unmarshallers = format_unmarshallers
113180 self .custom_formatters = custom_formatters
114181 self .context = context
115182
116183 @cached_property
117184 def validators_factory (self ) -> SchemaValidatorsFactory :
118185 return SchemaValidatorsFactory (
119186 self .schema_validator_class ,
187+ self .base_format_checker ,
188+ self .formatters ,
189+ self .format_unmarshallers ,
190+ self .custom_formatters ,
191+ )
192+
193+ @cached_property
194+ def formatters_factory (self ) -> SchemaFormattersFactory :
195+ return SchemaFormattersFactory (
196+ self .validators_factory ,
197+ self .formatters ,
198+ self .format_unmarshallers ,
120199 self .custom_formatters ,
121- self .context ,
122200 )
123201
124202 def create (
@@ -134,7 +212,7 @@ def create(
134212 validator = self .validators_factory .create (schema )
135213
136214 schema_format = schema .getkey ("format" )
137- formatter = self .custom_formatters . get (schema_format )
215+ formatter = self .formatters_factory . create (schema_format )
138216
139217 schema_type = type_override or schema .getkey ("type" , "any" )
140218 if isinstance (schema_type , Iterable ) and not isinstance (
0 commit comments