def test_time(): validator = Time() value, error = validator.validate_or_error("12:00:01") assert value == datetime.time(12, 0, 1) validator = Time() value, error = validator.validate_or_error("12:00:01.001") assert value == datetime.time(12, 0, 1, 1000) validator = Time() value, error = validator.validate_or_error("12:00:01.000001") assert value == datetime.time(12, 0, 1, 1) validator = Time() value, error = validator.validate_or_error(datetime.time(12, 0, 1)) assert value == datetime.time(12, 0, 1) validator = Time() value, error = validator.validate_or_error("12.00.01") assert error == ValidationError(text="Must be a valid time format.", code="format") validator = Time() value, error = validator.validate_or_error("12:00:60") assert error == ValidationError(text="Must be a real time.", code="invalid")
def test_datetime(): validator = DateTime() value, error = validator.validate_or_error("2049-1-1 12:00:00") assert value == datetime.datetime(2049, 1, 1, 12, 0, 0) validator = DateTime() value, error = validator.validate_or_error("2049-1-1 12:00:00.001") assert value == datetime.datetime(2049, 1, 1, 12, 0, 0, 1000) tzinfo = datetime.timezone.utc validator = DateTime() value, error = validator.validate_or_error("2049-1-1 12:00:00Z") assert value == datetime.datetime(2049, 1, 1, 12, 0, 0, tzinfo=tzinfo) tzinfo = datetime.timezone(-datetime.timedelta(hours=2, minutes=30)) validator = DateTime() value, error = validator.validate_or_error("2049-1-1 12:00:00-0230") assert value == datetime.datetime(2049, 1, 1, 12, 0, 0, tzinfo=tzinfo) validator = DateTime() value, error = validator.validate_or_error( datetime.datetime(2049, 1, 1, 12, 0, 0)) assert value == datetime.datetime(2049, 1, 1, 12, 0, 0) validator = DateTime() value, error = validator.validate_or_error("2049:01:01 12:00:00") assert error == ValidationError(text="Must be a valid datetime format.", code="format") validator = DateTime() value, error = validator.validate_or_error("2049-01-01 12:00:60") assert error == ValidationError(text="Must be a real datetime.", code="invalid")
def test_all_of(): validator = AllOf([Integer(multiple_of=5), Integer(multiple_of=3)]) value, error = validator.validate_or_error(3) assert value is None assert error == ValidationError(text="Must be a multiple of 5.", code="multiple_of") validator = AllOf([Integer(multiple_of=5), Integer(multiple_of=3)]) value, error = validator.validate_or_error(5) assert value is None assert error == ValidationError(text="Must be a multiple of 3.", code="multiple_of") validator = AllOf([Integer(multiple_of=5), Integer(multiple_of=3)]) value, error = validator.validate_or_error(15) assert value == 15 assert error is None
def validate(self, value: typing.Any, *, strict: bool = False) -> typing.Any: if value is None and self.allow_null: return None elif value is None: raise self.validation_error("null") elif not isinstance(value, list): raise self.validation_error("type") if (self.min_items is not None and self.min_items == self.max_items and len(value) != self.min_items): raise self.validation_error("exact_items") if self.min_items is not None and len(value) < self.min_items: if self.min_items == 1: raise self.validation_error("empty") raise self.validation_error("min_items") elif self.max_items is not None and len(value) > self.max_items: raise self.validation_error("max_items") # Ensure all items are of the right type. validated = [] error_messages = [] # type: typing.List[Message] if self.unique_items: seen_items = set() # type: typing.Set[typing.Any] for pos, item in enumerate(value): validator = None if isinstance(self.items, list): if pos < len(self.items): validator = self.items[pos] elif isinstance(self.additional_items, Field): validator = self.additional_items elif self.items is not None: validator = self.items if validator is None: validated.append(item) else: item, error = validator.validate_or_error(item, strict=strict) if error: error_messages += error.messages(add_prefix=pos) else: validated.append(item) if self.unique_items: if item in seen_items: text = self.get_error_text("unique_items") message = Message(text=text, code="unique_items", index=[pos]) error_messages.append(message) else: seen_items.add(item) if error_messages: raise ValidationError(error_messages) return validated
def test_url(): validator = String(format="url") for item in [ "http://www.python.org", "https://www.python.org", "http://www.python007.org", "http://42.com", "http://python.org", "http://python.org/intro", "https://python.org/intro/firstlesson/", "http://www.google.com" ]: value, error = validator.validate_or_error(item) assert value == urllib.parse.urlparse(item) validator = String(format="url") for item in [ "www.python.org", "www.python42.org", "42.com", "python.org" "inside.python.org", "python.org/intro", "python.org/intro/firstlesson/" ]: value, error = validator.validate_or_error(item) assert value == urllib.parse.urlparse("http://" + item) validator = String(format="url") for item in ["python@sas", "pythonv7", ".com"]: value, error = validator.validate_or_error(item) assert error == ValidationError(text="Must be valid URL format.", code="format")
def validate_with_positions( *, token: Token, validator: typing.Union[Field, typing.Type[Schema]]) -> typing.Any: try: return validator.validate(token.value) except ValidationError as error: messages = [] for message in error.messages(): if message.code == "required": field = message.index[-1] token = token.lookup(message.index[:-1]) text = f"The field {field!r} is required." else: token = token.lookup(message.index) text = message.text positional_message = Message( text=text, code=message.code, index=message.index, start_position=token.start, end_position=token.end, ) messages.append(positional_message) messages = sorted( messages, key=lambda m: m.start_position.char_index # type: ignore ) raise ValidationError(messages=messages)
def test_choice(): validator = Choice(choices=[("R", "red"), ("B", "blue"), ("G", "green")]) value, error = validator.validate_or_error(None) assert error == ValidationError(text="May not be null.", code="null") validator = Choice(choices=[("R", "red"), ("B", "blue"), ("G", "green")], allow_null=True) value, error = validator.validate_or_error(None) assert value is None assert error is None validator = Choice(choices=[("R", "red"), ("B", "blue"), ("G", "green")]) value, error = validator.validate_or_error("") assert error == ValidationError(text="This field is required.", code="required") validator = Choice(choices=[("R", "red"), ("B", "blue"), ("G", "green")], allow_null=True) value, error = validator.validate_or_error("") assert value is None assert error is None validator = Choice(choices=[("R", "red"), ("B", "blue"), ("G", "green")]) value, error = validator.validate_or_error("Z") assert error == ValidationError(text="Not a valid choice.", code="choice") validator = Choice(choices=[("R", "red"), ("B", "blue"), ("G", "green")]) value, error = validator.validate_or_error("R") assert value == "R" validator = Choice( choices=[("", "empty"), ("R", "red"), ("B", "blue"), ("G", "green")], allow_null=True, ) value, error = validator.validate_or_error("") assert value == "" validator = Choice( choices=[("", "empty"), ("R", "red"), ("B", "blue"), ("G", "green")], allow_null=True, ) value, error = validator.validate_or_error(None) assert value is None validator = Choice(choices=["red", "green", "blue"]) value, error = validator.validate_or_error("red") assert value is "red"
def test_union(): validator = Union(any_of=[Integer(), String()]) value, error = validator.validate_or_error("abc") assert value == "abc" assert error is None validator = Union(any_of=[Integer(), String()]) value, error = validator.validate_or_error(123) assert value == 123 assert error is None validator = Union(any_of=[Integer(), String()]) value, error = validator.validate_or_error(None) assert value is None assert error == ValidationError(text="May not be null.", code="null") validator = Union(any_of=[Integer(), String()]) value, error = validator.validate_or_error(True) assert value is None assert error == ValidationError(text="Did not match any valid type.", code="union") validator = Union(any_of=[Integer(allow_null=True), String()]) value, error = validator.validate_or_error(None) assert error is None assert value is None validator = Union(any_of=[Integer(), String()], allow_null=True) value, error = validator.validate_or_error(None) assert error is None assert value is None validator = Union(any_of=[Integer(maximum=1000), String()]) value, error = validator.validate_or_error(9999) assert value is None assert error == ValidationError(text="Must be less than or equal to 1000.", code="maximum") validator = Integer() | String() | Boolean() value, error = validator.validate_or_error(123) assert value == 123 validator = Integer() | (String() | Boolean()) value, error = validator.validate_or_error(123) assert value == 123
def test_date(): validator = Date() value, error = validator.validate_or_error("2049-01-01") assert value == datetime.date(2049, 1, 1) validator = Date() value, error = validator.validate_or_error(datetime.date(2049, 1, 1)) assert value == datetime.date(2049, 1, 1) validator = Date() value, error = validator.validate_or_error("20490101") assert error == ValidationError(text="Must be a valid date format.", code="format") validator = Date() value, error = validator.validate_or_error("2049-01-32") assert error == ValidationError(text="Must be a real date.", code="invalid")
def test_not(): validator = Not(Integer()) value, error = validator.validate_or_error("abc") assert value == "abc" assert error is None validator = Not(Integer()) value, error = validator.validate_or_error(5) assert value is None assert error == ValidationError(text="Must not match.", code="negated")
def test_boolean(): validator = Boolean() value, error = validator.validate_or_error(True) assert value is True validator = Boolean() value, error = validator.validate_or_error(False) assert value is False validator = Boolean() value, error = validator.validate_or_error("True") assert value is True validator = Boolean() value, error = validator.validate_or_error(1) assert value is True validator = Boolean() value, error = validator.validate_or_error(None) assert error == ValidationError(text="May not be null.", code="null") validator = Boolean() value, error = validator.validate_or_error(2) assert error == ValidationError(text="Must be a boolean.", code="type") validator = Boolean() value, error = validator.validate_or_error([]) assert error == ValidationError(text="Must be a boolean.", code="type") validator = Boolean(allow_null=True) value, error = validator.validate_or_error(None) assert value is None assert error is None validator = Boolean(allow_null=True) value, error = validator.validate_or_error("") assert value is None assert error is None validator = Boolean() value, error = validator.validate_or_error("True", strict=True) assert error == ValidationError(text="Must be a boolean.", code="type")
def test_uuid(): validator = String(format="uuid") value, error = validator.validate_or_error( "93e19019-c7a6-45fe-8936-f6f4d550f35f") assert value == uuid.UUID("93e19019-c7a6-45fe-8936-f6f4d550f35f") validator = String(format="uuid") value, error = validator.validate_or_error( "1245a678-1234-1234-1234-123412341234") assert error == ValidationError(text="Must be valid UUID format.", code="format")
def test_const(): validator = Const(const=None) value, error = validator.validate_or_error(None) assert value is None assert error is None validator = Const(const=None) value, error = validator.validate_or_error(123) assert value is None assert error == ValidationError(text="Must be null.", code="only_null") validator = Const(const="abc") value, error = validator.validate_or_error("def") assert value is None assert error == ValidationError(text="Must be the value 'abc'.", code="const") validator = Const(const="abc") value, error = validator.validate_or_error("abc") assert value == "abc" assert error is None
def test_one_of(): validator = OneOf([String(), Integer()]) value, error = validator.validate_or_error(123) assert value is 123 assert error is None validator = OneOf([String(allow_null=True), Integer()]) value, error = validator.validate_or_error(None) assert value is None assert error is None validator = OneOf([String(allow_null=True), Integer(allow_null=True)]) value, error = validator.validate_or_error(None) assert value is None assert error == ValidationError( text="Matched more than one type.", code="multiple_matches" ) validator = OneOf([String(), Integer()]) value, error = validator.validate_or_error(None) assert value is None assert error == ValidationError( text="Did not match any valid type.", code="no_match" )
def test_email(): validator = String(format="email") for item in [ "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**", "[email protected]", "*****@*****.**", "*****@*****.**", "*****@*****.**" ]: value, error = validator.validate_or_error(item) assert value == item validator = String(format="email") for item in [ "info@python", "python@python", "python@[email protected]", "@python.org" ]: value, error = validator.validate_or_error(item) assert error == ValidationError(text="Must be valid Email format.", code="format")
def validation_error(self, code: str) -> ValidationError: text = self.errors[code].format(**self.__dict__) return ValidationError(text=text, code=code)
def validate(self, value: typing.Any, *, strict: bool = False) -> typing.Any: if value is None and self.allow_null: return None elif value is None: raise self.validation_error("null") elif not isinstance(value, (dict, typing.Mapping)): raise self.validation_error("type") validated = {} error_messages = [] # Ensure all property keys are strings. for key in value.keys(): if not isinstance(key, str): text = self.get_error_text("invalid_key") message = Message(text=text, code="invalid_key", index=[key]) error_messages.append(message) elif self.property_names is not None: _, error = self.property_names.validate_or_error(key) if error is not None: text = self.get_error_text("invalid_property") message = Message(text=text, code="invalid_property", index=[key]) error_messages.append(message) # Min/Max properties if self.min_properties is not None: if len(value) < self.min_properties: if self.min_properties == 1: raise self.validation_error("empty") else: raise self.validation_error("min_properties") if self.max_properties is not None: if len(value) > self.max_properties: raise self.validation_error("max_properties") # Required properties for key in self.required: if key not in value: text = self.get_error_text("required") message = Message(text=text, code="required", index=[key]) error_messages.append(message) # Properties for key, child_schema in self.properties.items(): if key not in value: if child_schema.has_default(): validated[key] = child_schema.get_default_value() continue item = value[key] child_value, error = child_schema.validate_or_error(item, strict=strict) if not error: validated[key] = child_value else: error_messages += error.messages(add_prefix=key) # Pattern properties if self.pattern_properties: for key in list(value.keys()): for pattern, child_schema in self.pattern_properties.items(): if isinstance(key, str) and re.search(pattern, key): item = value[key] child_value, error = child_schema.validate_or_error( item, strict=strict) if not error: validated[key] = child_value else: error_messages += error.messages(add_prefix=key) # Additional properties validated_keys = set(validated.keys()) error_keys = set( [message.index[0] for message in error_messages if message.index]) remaining = [ key for key in value.keys() if key not in validated_keys | error_keys ] if self.additional_properties is True: for key in remaining: validated[key] = value[key] elif self.additional_properties is False: for key in remaining: text = self.get_error_text("invalid_property") message = Message(text=text, code="invalid_property", key=key) error_messages.append(message) elif self.additional_properties is not None: assert isinstance(self.additional_properties, Field) child_schema = self.additional_properties for key in remaining: item = value[key] child_value, error = child_schema.validate_or_error( item, strict=strict) if not error: validated[key] = child_value else: error_messages += error.messages(add_prefix=key) if error_messages: raise ValidationError(messages=error_messages) return validated
def test_never_match(): validator = NeverMatch() value, error = validator.validate_or_error(123) assert value is None assert error == ValidationError(text="This never validates.", code="never")
def test_array(): validator = Array() value, error = validator.validate_or_error([]) assert value == [] validator = Array() value, error = validator.validate_or_error(None) assert error == ValidationError(text="May not be null.", code="null") validator = Array() value, error = validator.validate_or_error(123) assert error == ValidationError(text="Must be an array.", code="type") validator = Array(allow_null=True) value, error = validator.validate_or_error(None) assert value is None assert error is None validator = Array(min_items=1) value, error = validator.validate_or_error([]) assert error == ValidationError(text="Must not be empty.", code="empty") validator = Array(min_items=1) value, error = validator.validate_or_error([1]) assert value == [1] validator = Array(min_items=2) value, error = validator.validate_or_error([]) assert error == ValidationError(text="Must have at least 2 items.", code="min_items") validator = Array(min_items=2) value, error = validator.validate_or_error([1, 2]) assert value == [1, 2] validator = Array(max_items=2) value, error = validator.validate_or_error([]) assert value == [] validator = Array(max_items=2) value, error = validator.validate_or_error([1, 2, 3]) assert error == ValidationError(text="Must have no more than 2 items.", code="max_items") validator = Array(exact_items=2) value, error = validator.validate_or_error([1, 2]) assert value == [1, 2] validator = Array(exact_items=2) value, error = validator.validate_or_error([1, 2, 3]) assert error == ValidationError(text="Must have 2 items.", code="exact_items") validator = Array(items=Integer()) value, error = validator.validate_or_error(["1", 2, "3"]) assert value == [1, 2, 3] validator = Array(items=Integer()) value, error = validator.validate_or_error(["a", 2, "c"]) assert dict(error) == {0: "Must be a number.", 2: "Must be a number."} validator = Array(items=[String(), Integer()]) value, error = validator.validate_or_error(["a", "b", "c"]) assert error == ValidationError(text="Must have 2 items.", code="exact_items") validator = Array(items=[String(), Integer()]) value, error = validator.validate_or_error(["a", "123"]) assert value == ["a", 123] validator = Array(items=[String(), Integer()], additional_items=Integer()) value, error = validator.validate_or_error(["a", "123", "456"]) assert value == ["a", 123, 456] validator = Array(items=String(), unique_items=True) value, error = validator.validate_or_error(["a", "b"]) assert value == ["a", "b"] validator = Array(items=String(), unique_items=True) value, error = validator.validate_or_error(["a", "a"]) assert dict(error) == {1: "Items must be unique."} validator = Array(items=[String(), Integer(), Boolean()], min_items=1) value, error = validator.validate_or_error(["a"]) assert value == ["a"] validator = Array(items=[String(), Integer(), Boolean()], min_items=1) value, error = validator.validate_or_error(["a", "123"]) assert value == ["a", 123] validator = Array(items=[String(), Integer(), Boolean()], min_items=1) value, error = validator.validate_or_error([]) assert error == ValidationError(text="Must not be empty.", code="empty") validator = Array(items=[String(), Integer(), Boolean()], min_items=2) value, error = validator.validate_or_error([]) assert error == ValidationError(text="Must have at least 2 items.", code="min_items")
def test_string(): validator = String() validated = validator.validate_or_error("abc") assert validated assert validated.value == "abc" assert validated.error is None validator = String() value, error = validator.validate_or_error("") assert error == ValidationError(text="Must not be blank.", code="blank") validator = String() value, error = validator.validate_or_error(None) assert error == ValidationError(text="May not be null.", code="null") validator = String() value, error = validator.validate_or_error(123) assert error == ValidationError(text="Must be a string.", code="type") validator = String(max_length=10) value, error = validator.validate_or_error("abc" * 10) assert error == ValidationError( text="Must have no more than 10 characters.", code="max_length") validator = String(min_length=3) value, error = validator.validate_or_error("a") assert error == ValidationError(text="Must have at least 3 characters.", code="min_length") validator = String(allow_blank=False) value, error = validator.validate_or_error("") assert error == ValidationError(text="Must not be blank.", code="blank") validator = String(allow_blank=True) value, error = validator.validate_or_error("") assert value == "" validator = String(allow_blank=True) value, error = validator.validate_or_error(None) assert value == "" validator = String(allow_null=True) value, error = validator.validate_or_error(None) assert value is None assert error is None validator = String(allow_null=True) value, error = validator.validate_or_error("") assert value is None assert error is None validator = String(allow_null=True) value, error = validator.validate_or_error(" ") assert value is None assert error is None validator = String(pattern="^[abc]*$") value, error = validator.validate_or_error("cba") assert value == "cba" validator = String(pattern="^[abc]*$") value, error = validator.validate_or_error("cbxa") assert error == ValidationError(text="Must match the pattern /^[abc]*$/.", code="pattern") validator = String() value, error = validator.validate_or_error(" ") assert error == ValidationError(text="Must not be blank.", code="blank") validator = String() value, error = validator.validate_or_error(" test ") assert value == "test" validator = String(trim_whitespace=False) value, error = validator.validate_or_error(" ") assert value == " " validator = String(trim_whitespace=False) value, error = validator.validate_or_error(" test ") assert value == " test "
def test_float(): validator = Float() value, error = validator.validate_or_error(123.1) assert value == 123.1 validator = Float() value, error = validator.validate_or_error(123) assert value == 123.0 validator = Float() value, error = validator.validate_or_error("123.1") assert value == 123.1 validator = Float() value, error = validator.validate_or_error(None) assert error == ValidationError(text="May not be null.", code="null") validator = Float() value, error = validator.validate_or_error("abc") assert error == ValidationError(text="Must be a number.", code="type") validator = Float() value, error = validator.validate_or_error(True) assert error == ValidationError(text="Must be a number.", code="type") validator = Float() value, error = validator.validate_or_error(float("inf")) assert error == ValidationError(text="Must be finite.", code="finite") validator = Float() value, error = validator.validate_or_error(float("nan")) assert error == ValidationError(text="Must be finite.", code="finite") validator = Float() value, error = validator.validate_or_error("123", strict=True) assert error == ValidationError(text="Must be a number.", code="type") validator = Float(allow_null=True) value, error = validator.validate_or_error(None) assert value is None assert error is None validator = Float(maximum=10.0) value, error = validator.validate_or_error(100.0) assert error == ValidationError(text="Must be less than or equal to 10.0.", code="maximum") validator = Float(maximum=10.0) value, error = validator.validate_or_error(10.0) assert value == 10.0 validator = Float(minimum=3.0) value, error = validator.validate_or_error(1.0) assert error == ValidationError( text="Must be greater than or equal to 3.0.", code="minimum") validator = Float(minimum=3.0) value, error = validator.validate_or_error(3.0) assert value == 3.0 validator = Float(exclusive_maximum=10.0) value, error = validator.validate_or_error(10.0) assert error == ValidationError(text="Must be less than 10.0.", code="exclusive_maximum") validator = Float(exclusive_minimum=3.0) value, error = validator.validate_or_error(3.0) assert error == ValidationError(text="Must be greater than 3.0.", code="exclusive_minimum") validator = Float(precision="0.01") value, error = validator.validate_or_error("123.456") assert value == 123.46 validator = Float(multiple_of=0.05, precision="0.01") value, error = validator.validate_or_error("123.05") assert value == 123.05 validator = Float(multiple_of=0.05, precision="0.01") value, error = validator.validate_or_error("123.06") assert error == ValidationError(text="Must be a multiple of 0.05.", code="multiple_of")
def test_integer(): validator = Integer() value, error = validator.validate_or_error(123) assert value == 123 validator = Integer() value, error = validator.validate_or_error("123") assert value == 123 validator = Integer() value, error = validator.validate_or_error(123.0) assert value == 123 validator = Integer() value, error = validator.validate_or_error("123.0") assert value == 123 validator = Integer() value, error = validator.validate_or_error(None) assert error == ValidationError(text="May not be null.", code="null") validator = Integer() value, error = validator.validate_or_error("abc") assert error == ValidationError(text="Must be a number.", code="type") validator = Integer() value, error = validator.validate_or_error(True) assert error == ValidationError(text="Must be a number.", code="type") validator = Integer() value, error = validator.validate_or_error(123.1) assert error == ValidationError(text="Must be an integer.", code="integer") validator = Integer() value, error = validator.validate_or_error(float("inf")) assert error == ValidationError(text="Must be an integer.", code="integer") validator = Integer() value, error = validator.validate_or_error(float("nan")) assert error == ValidationError(text="Must be an integer.", code="integer") validator = Integer() value, error = validator.validate_or_error("123", strict=True) assert error == ValidationError(text="Must be a number.", code="type") validator = Integer(allow_null=True) value, error = validator.validate_or_error(None) assert value is None assert error is None validator = Integer(allow_null=True) value, error = validator.validate_or_error("") assert value is None assert error is None validator = Integer(allow_null=True) value, error = validator.validate_or_error("", strict=True) assert error == ValidationError(text="Must be a number.", code="type") validator = Integer(maximum=10) value, error = validator.validate_or_error(100) assert error == ValidationError(text="Must be less than or equal to 10.", code="maximum") validator = Integer(maximum=10) value, error = validator.validate_or_error(10) assert value == 10 validator = Integer(minimum=3) value, error = validator.validate_or_error(1) assert error == ValidationError(text="Must be greater than or equal to 3.", code="minimum") validator = Integer(minimum=3) value, error = validator.validate_or_error(3) assert value == 3 validator = Integer(exclusive_maximum=10) value, error = validator.validate_or_error(10) assert error == ValidationError(text="Must be less than 10.", code="exclusive_maximum") validator = Integer(exclusive_minimum=3) value, error = validator.validate_or_error(3) assert error == ValidationError(text="Must be greater than 3.", code="exclusive_minimum") validator = Integer(multiple_of=10) value, error = validator.validate_or_error(5) assert error == ValidationError(text="Must be a multiple of 10.", code="multiple_of")
def validation_error(self, code: str) -> ValidationError: text = self.get_error_text(code) return ValidationError(text=text, code=code)