11import python_jsonschema_objects .util as util
22import python_jsonschema_objects .validators as validators
3+ import python_jsonschema_objects .pattern_properties as pattern_properties
34
45import collections
56import itertools
910import logging
1011logger = logging .getLogger (__name__ )
1112
13+ logger .addHandler (logging .NullHandler ())
14+
1215
1316
1417# Long is no longer a thing in python3.x
@@ -33,16 +36,7 @@ class ProtocolBase(collections.MutableMapping):
3336 """
3437 __propinfo__ = {}
3538 __required__ = set ()
36-
37- __SCHEMA_TYPES__ = {
38- 'array' : list ,
39- 'boolean' : bool ,
40- 'integer' : int ,
41- 'number' : (float , int , long ),
42- 'null' : type (None ),
43- 'string' : six .string_types ,
44- 'object' : dict
45- }
39+ __object_attr_list__ = set (["_properties" , "_extended_properties" ])
4640
4741 def as_dict (self ):
4842 """ Return a dictionary containing the current values
@@ -55,8 +49,10 @@ def as_dict(self):
5549 for prop in self :
5650 propval = getattr (self , prop )
5751
58- if isinstance (propval , list ):
59- out [prop ] = [getattr (x , 'as_dict' , lambda :x )() for x in propval ]
52+ if hasattr (propval , 'for_json' ):
53+ out [prop ] = propval .for_json ()
54+ elif isinstance (propval , list ):
55+ out [prop ] = [getattr (x , 'for_json' , lambda :x )() for x in propval ]
6056 elif isinstance (propval , (ProtocolBase , LiteralValue )):
6157 out [prop ] = propval .as_dict ()
6258 elif propval is not None :
@@ -145,7 +141,7 @@ def __new__(cls, **props):
145141 else : # We got nothing
146142 raise validators .ValidationError (
147143 "Unable to instantiate any valid types: \n "
148- "\n " .join ("{0}: {1}" .format (k , e ) for k , e in validation_errors )
144+ "" .join ("{0}: {1}\n " .format (k , e ) for k , e in validation_errors )
149145 )
150146
151147 return obj
@@ -165,38 +161,29 @@ def __init__(self, **props):
165161 import sys
166162 raise six .reraise (type (e ), type (e )(str (e ) + " \n while setting '{0}' in {1}" .format (
167163 prop , self .__class__ .__name__ )), sys .exc_info ()[2 ])
168-
164+ if getattr (self , '__strict__' , None ):
165+ self .validate ()
169166 #if len(props) > 0:
170167 # self.validate()
171168
172169 def __setattr__ (self , name , val ):
173- if name . startswith ( "_" ) :
170+ if name in self . __object_attr_list__ :
174171 object .__setattr__ (self , name , val )
175172 elif name in self .__propinfo__ :
176173 # If its in __propinfo__, then it actually has a property defined.
177174 # The property does special validation, so we actually need to
178175 # run its setter. We get it from the class definition and call
179176 # it directly. XXX Heinous.
180- prop = self .__class__ . __dict__ [ self .__prop_names__ [name ]]
177+ prop = getattr ( self .__class__ , self .__prop_names__ [name ])
181178 prop .fset (self , val )
182179 else :
183180 # This is an additional property of some kind
184- typ = getattr (self , '__extensible__' , None )
185- if typ is False :
181+ try :
182+ val = self .__extensible__ .instantiate (name , val )
183+ except Exception as e :
186184 raise validators .ValidationError (
187- "Attempted to set unknown property '{0}', "
188- "but 'additionalProperties' is false." .format (name ))
189- if typ is True :
190- # There is no type defined, so just make it a basic literal
191- # Pick the type based on the type of the values
192- valtype = [k for k , t in six .iteritems (self .__SCHEMA_TYPES__ )
193- if t is not None and isinstance (val , t )]
194- valtype = valtype [0 ]
195- val = MakeLiteral (name , valtype , val )
196- elif isinstance (typ , type ) and getattr (typ , 'isLiteralClass' , None ) is True :
197- val = typ (val )
198- elif isinstance (typ , type ) and util .safe_issubclass (typ , ProtocolBase ):
199- val = typ (** util .coerce_for_expansion (val ))
185+ "Attempted to set unknown property '{0}': {1} "
186+ .format (name , e ))
200187
201188 self ._extended_properties [name ] = val
202189
@@ -245,17 +232,17 @@ def serialize(self):
245232
246233 def validate (self ):
247234 """ Applies all defined validation to the current
248- state of the object, and raises an error if
235+ state of the object, and raises an error if
249236 they are not all met.
250-
237+
251238 Raises:
252239 ValidationError: if validations do not pass
253240 """
254241
255242 propname = lambda x : self .__prop_names__ [x ]
256243 missing = [x for x in self .__required__
257- if propname (x ) not in self ._properties
258- or self ._properties [propname (x )] is None ]
244+ if propname (x ) not in self ._properties or
245+ self ._properties [propname (x )] is None ]
259246
260247 if len (missing ) > 0 :
261248 raise validators .ValidationError (
@@ -281,13 +268,14 @@ def validate(self):
281268
282269 return True
283270
271+
284272def MakeLiteral (name , typ , value , ** properties ):
285- properties .update ({'type' : typ })
286- klass = type (str (name ), tuple ((LiteralValue ,)), {
287- '__propinfo__' : { '__literal__' : properties }
288- })
273+ properties .update ({'type' : typ })
274+ klass = type (str (name ), tuple ((LiteralValue ,)), {
275+ '__propinfo__' : {'__literal__' : properties }
276+ })
289277
290- return klass (value )
278+ return klass (value )
291279
292280
293281class TypeProxy (object ):
@@ -304,6 +292,7 @@ def __call__(self, *a, **kw):
304292 self .__class__ , klass ))
305293 try :
306294 obj = klass (* a , ** kw )
295+ obj .validate ()
307296 except TypeError as e :
308297 validation_errors .append ((klass , e ))
309298 except validators .ValidationError as e :
@@ -314,7 +303,7 @@ def __call__(self, *a, **kw):
314303 else : # We got nothing
315304 raise validators .ValidationError (
316305 "Unable to instantiate any valid types: \n "
317- "\n " .join ("{0}: {1}" .format (k , e ) for k , e in validation_errors )
306+ "" .join ("{0}: {1}\n " .format (k , e ) for k , e in validation_errors )
318307 )
319308
320309
@@ -355,6 +344,9 @@ def __repr__(self):
355344 str (self ._value )
356345 )
357346
347+ def __str__ (self ):
348+ return str (self ._value )
349+
358350 def validate (self ):
359351 info = self .propinfo ('__literal__' )
360352
@@ -364,16 +356,14 @@ def validate(self):
364356 if validator is not None :
365357 validator (paramval , self ._value , info )
366358
359+ def __eq__ (self , other ):
360+ return self ._value == other
361+
362+ def __hash__ (self ):
363+ return hash (self ._value )
367364
368- def __cmp__ (self , other ):
369- if isinstance (other , six .integer_types ):
370- return cmp (int (self ), other )
371- elif isinstance (other , six .string_types ):
372- return cmp (str (self ), other )
373- elif isinstance (other , float ):
374- return cmp (float (self ), other )
375- else :
376- return cmp (id (self ), id (other ))
365+ def __lt__ (self , other ):
366+ return self ._value < other
377367
378368 def __int__ (self ):
379369 return int (self ._value )
@@ -418,7 +408,7 @@ def construct(self, uri, *args, **kw):
418408 logger .debug (util .lazy_format ("Constructed {0}" , ret ))
419409 return ret
420410
421- def _construct (self , uri , clsdata , parent = (ProtocolBase ,)):
411+ def _construct (self , uri , clsdata , parent = (ProtocolBase ,), ** kw ):
422412
423413 if 'anyOf' in clsdata :
424414 raise NotImplementedError (
@@ -437,7 +427,7 @@ def _construct(self, uri, clsdata, parent=(ProtocolBase,)):
437427 self .resolved [uri ] = self ._build_object (
438428 uri ,
439429 clsdata ,
440- parents )
430+ parents , ** kw )
441431 return self .resolved [uri ]
442432
443433 elif '$ref' in clsdata :
@@ -473,13 +463,25 @@ def _construct(self, uri, clsdata, parent=(ProtocolBase,)):
473463 ** clsdata_copy )
474464 return self .resolved [uri ]
475465
466+ elif isinstance (clsdata .get ('type' ), list ):
467+ types = []
468+ for i , item_detail in enumerate (clsdata ['type' ]):
469+ subdata = {k : v for k , v in six .iteritems (clsdata ) if k != 'type' }
470+ subdata ['type' ] = item_detail
471+ types .append (self ._build_literal (
472+ uri + "_%s" % i ,
473+ subdata ))
474+
475+ self .resolved [uri ] = TypeProxy (types )
476+ return self .resolved [uri ]
477+
476478 elif (clsdata .get ('type' , None ) == 'object' or
477479 clsdata .get ('properties' , None ) is not None or
478480 clsdata .get ('additionalProperties' , False )):
479481 self .resolved [uri ] = self ._build_object (
480482 uri ,
481483 clsdata ,
482- parent )
484+ parent , ** kw )
483485 return self .resolved [uri ]
484486 elif clsdata .get ('type' ) in ('integer' , 'number' , 'string' , 'boolean' , 'null' ):
485487 self .resolved [uri ] = self ._build_literal (
@@ -513,7 +515,7 @@ def _build_literal(self, nm, clsdata):
513515
514516 return cls
515517
516- def _build_object (self , nm , clsdata , parents ):
518+ def _build_object (self , nm , clsdata , parents , ** kw ):
517519 logger .debug (util .lazy_format ("Building object {0}" , nm ))
518520
519521 props = {}
@@ -640,28 +642,10 @@ def _build_object(self, nm, clsdata, parents):
640642 # Need a validation to check that it meets one of them
641643 props ['__validation__' ] = {'type' : klasses }
642644
643- props ['__extensible__' ] = True
644- if 'additionalProperties' in clsdata :
645- addlProp = clsdata ['additionalProperties' ]
646-
647- if addlProp is False :
648- props ['__extensible__' ] = False
649- elif addlProp is True :
650- props ['__extensible__' ] = True
651- else :
652- if '$ref' in addlProp :
653- refs = self .resolve_classes ([addlProp ])
654- else :
655- uri = "{0}/{1}_{2}" .format (nm ,
656- "<additionalProperties>" , "<anonymous>" )
657- self .resolved [uri ] = self .construct (
658- uri ,
659- addlProp ,
660- (ProtocolBase ,))
661- refs = [self .resolved [uri ]]
662-
663- props ['__extensible__' ] = refs [0 ]
664-
645+ props ['__extensible__' ] = pattern_properties .ExtensibleValidator (
646+ nm ,
647+ clsdata ,
648+ self )
665649
666650 props ['__prop_names__' ] = name_translation
667651
@@ -679,7 +663,8 @@ def _build_object(self, nm, clsdata, parents):
679663 .format (nm , invalid_requires ))
680664
681665 props ['__required__' ] = required
682-
666+ if required and kw .get ("strict" ):
667+ props ['__strict__' ] = True
683668 cls = type (str (nm .split ('/' )[- 1 ]), tuple (parents ), props )
684669
685670 return cls
@@ -703,7 +688,9 @@ def setprop(self, val):
703688 if not isinstance (typ , dict ):
704689 type_checks .append (typ )
705690 continue
706- typ = ProtocolBase .__SCHEMA_TYPES__ [typ ['type' ]]
691+ typ = next (t
692+ for n , t in validators .SCHEMA_TYPE_MAPPING
693+ if typ ['type' ] == t )
707694 if typ is None :
708695 typ = type (None )
709696 if isinstance (typ , (list , tuple )):
@@ -759,6 +746,11 @@ def setprop(self, val):
759746 instance = info ['validator' ](val )
760747 val = instance .validate ()
761748
749+ elif util .safe_issubclass (info ['type' ], validators .ArrayValidator ):
750+ # An array type may have already been converted into an ArrayValidator
751+ instance = info ['type' ](val )
752+ val = instance .validate ()
753+
762754 elif getattr (info ['type' ], 'isLiteralClass' , False ) is True :
763755 if not isinstance (val , info ['type' ]):
764756 validator = info ['type' ](val )
@@ -769,12 +761,16 @@ def setprop(self, val):
769761 val = info ['type' ](** util .coerce_for_expansion (val ))
770762
771763 val .validate ()
764+
765+ elif isinstance (info ['type' ], TypeProxy ):
766+ val = info ['type' ](val )
767+
772768 elif info ['type' ] is None :
773769 # This is the null value
774770 if val is not None :
775771 raise validators .ValidationError (
776772 "None is only valid value for null" )
777-
773+
778774 else :
779775 raise TypeError ("Unknown object type: '{0}'" .format (info ['type' ]))
780776
0 commit comments