1import numbers 2 3from pyrsistent import pmap 4import attr 5 6from jsonschema.compat import int_types, str_types 7from jsonschema.exceptions import UndefinedTypeCheck 8 9 10def is_array(checker, instance): 11 return isinstance(instance, list) 12 13 14def is_bool(checker, instance): 15 return isinstance(instance, bool) 16 17 18def is_integer(checker, instance): 19 # bool inherits from int, so ensure bools aren't reported as ints 20 if isinstance(instance, bool): 21 return False 22 return isinstance(instance, int_types) 23 24 25def is_null(checker, instance): 26 return instance is None 27 28 29def is_number(checker, instance): 30 # bool inherits from int, so ensure bools aren't reported as ints 31 if isinstance(instance, bool): 32 return False 33 return isinstance(instance, numbers.Number) 34 35 36def is_object(checker, instance): 37 return isinstance(instance, dict) 38 39 40def is_string(checker, instance): 41 return isinstance(instance, str_types) 42 43 44def is_any(checker, instance): 45 return True 46 47 48@attr.s(frozen=True) 49class TypeChecker(object): 50 """ 51 A ``type`` property checker. 52 53 A `TypeChecker` performs type checking for an `IValidator`. Type 54 checks to perform are updated using `TypeChecker.redefine` or 55 `TypeChecker.redefine_many` and removed via `TypeChecker.remove`. 56 Each of these return a new `TypeChecker` object. 57 58 Arguments: 59 60 type_checkers (dict): 61 62 The initial mapping of types to their checking functions. 63 """ 64 _type_checkers = attr.ib(default=pmap(), converter=pmap) 65 66 def is_type(self, instance, type): 67 """ 68 Check if the instance is of the appropriate type. 69 70 Arguments: 71 72 instance (object): 73 74 The instance to check 75 76 type (str): 77 78 The name of the type that is expected. 79 80 Returns: 81 82 bool: Whether it conformed. 83 84 85 Raises: 86 87 `jsonschema.exceptions.UndefinedTypeCheck`: 88 if type is unknown to this object. 89 """ 90 try: 91 fn = self._type_checkers[type] 92 except KeyError: 93 raise UndefinedTypeCheck(type) 94 95 return fn(self, instance) 96 97 def redefine(self, type, fn): 98 """ 99 Produce a new checker with the given type redefined. 100 101 Arguments: 102 103 type (str): 104 105 The name of the type to check. 106 107 fn (collections.Callable): 108 109 A function taking exactly two parameters - the type 110 checker calling the function and the instance to check. 111 The function should return true if instance is of this 112 type and false otherwise. 113 114 Returns: 115 116 A new `TypeChecker` instance. 117 """ 118 return self.redefine_many({type: fn}) 119 120 def redefine_many(self, definitions=()): 121 """ 122 Produce a new checker with the given types redefined. 123 124 Arguments: 125 126 definitions (dict): 127 128 A dictionary mapping types to their checking functions. 129 130 Returns: 131 132 A new `TypeChecker` instance. 133 """ 134 return attr.evolve( 135 self, type_checkers=self._type_checkers.update(definitions), 136 ) 137 138 def remove(self, *types): 139 """ 140 Produce a new checker with the given types forgotten. 141 142 Arguments: 143 144 types (~collections.Iterable): 145 146 the names of the types to remove. 147 148 Returns: 149 150 A new `TypeChecker` instance 151 152 Raises: 153 154 `jsonschema.exceptions.UndefinedTypeCheck`: 155 156 if any given type is unknown to this object 157 """ 158 159 checkers = self._type_checkers 160 for each in types: 161 try: 162 checkers = checkers.remove(each) 163 except KeyError: 164 raise UndefinedTypeCheck(each) 165 return attr.evolve(self, type_checkers=checkers) 166 167 168draft3_type_checker = TypeChecker( 169 { 170 u"any": is_any, 171 u"array": is_array, 172 u"boolean": is_bool, 173 u"integer": is_integer, 174 u"object": is_object, 175 u"null": is_null, 176 u"number": is_number, 177 u"string": is_string, 178 }, 179) 180draft4_type_checker = draft3_type_checker.remove(u"any") 181draft6_type_checker = draft4_type_checker.redefine( 182 u"integer", 183 lambda checker, instance: ( 184 is_integer(checker, instance) or 185 isinstance(instance, float) and instance.is_integer() 186 ), 187) 188draft7_type_checker = draft6_type_checker 189