comparison data_schema/__init__.py @ 13:940676a0de84

ERRORS and WARNINGS are now enums
author Franz Glasner <fzglas.hg@dom66.de>
date Fri, 07 Jul 2023 02:10:32 +0200
parents 62823085e582
children cfb97c7c9e5b
comparison
equal deleted inserted replaced
12:8e0b60f868dd 13:940676a0de84
29 29
30 import ast 30 import ast
31 import collections 31 import collections
32 import copy 32 import copy
33 import datetime 33 import datetime
34 import enum
34 import re 35 import re
35 import urllib.parse 36 import urllib.parse
36 37
37 import rfc3986 38 import rfc3986
38 39
55 WARNING: "WARNING", 56 WARNING: "WARNING",
56 INFO: "INFO", 57 INFO: "INFO",
57 } 58 }
58 _name_to_level = {name: level for (level, name) in _level_to_name.items()} 59 _name_to_level = {name: level for (level, name) in _level_to_name.items()}
59 60
60 ERRORS = { 61
61 10000: NC_("schema-msg", "dict expected"), 62 @enum.unique
62 10001: NC_("schema-msg", "list expected"), 63 class ERRORS(enum.Enum):
63 10002: NC_("schema-msg", "string expected"), 64 E10000 = NC_("schema-msg", "dict expected")
64 10003: NC_("schema-msg", "dict key must be a string"), 65 E10001 = NC_("schema-msg", "list expected")
65 10004: NC_("schema-msg", "additional key encountered"), 66 E10002 = NC_("schema-msg", "string expected")
66 10005: NC_("schema-msg", "required key(s) missing"), 67 E10003 = NC_("schema-msg", "dict key must be a string")
67 10006: NC_("schema-msg", "min string length encountered"), 68 E10004 = NC_("schema-msg", "additional key encountered")
68 10007: NC_("schema-msg", "max string length exceeded"), 69 E10005 = NC_("schema-msg", "required key(s) missing")
69 10008: NC_("schema-msg", "string value does not match the required RE pattern"), 70 E10006 = NC_("schema-msg", "min string length encountered")
70 10009: NC_("schema-msg", "string value does not validate"), 71 E10007 = NC_("schema-msg", "max string length exceeded")
71 10010: NC_("schema-msg", "validation error"), 72 E10008 = NC_("schema-msg", "string value does not match the required RE pattern")
72 10011: NC_("schema-msg", "None/Null object expected"), 73 E10009 = NC_("schema-msg", "string value does not validate")
73 10012: NC_("schema-msg", "min list length encountered"), 74 E10010 = NC_("schema-msg", "validation error")
74 10013: NC_("schema-msg", "max list length exceeded"), 75 E10011 = NC_("schema-msg", "None/Null object expected")
75 10014: NC_("schema-msg", "tuple expected"), 76 E10012 = NC_("schema-msg", "min list length encountered")
76 10015: NC_("schema-msg", "min tuple length encountered"), 77 E10013 = NC_("schema-msg", "max list length exceeded")
77 10016: NC_("schema-msg", "max tuple length exceeded"), 78 E10014 = NC_("schema-msg", "tuple expected")
78 10017: NC_("schema-msg", "additional items in tuple not allowed"), 79 E10015 = NC_("schema-msg", "min tuple length encountered")
79 10018: NC_("schema-msg", "object is not empty"), 80 E10016 = NC_("schema-msg", "max tuple length exceeded")
80 10019: NC_("schema-msg", "more than one match in `one-of' detected"), 81 E10017 = NC_("schema-msg", "additional items in tuple not allowed")
81 10020: NC_("schema-msg", "int expected"), 82 E10018 = NC_("schema-msg", "object is not empty")
82 10021: NC_("schema-msg", "int value lower than minValue"), 83 E10019 = NC_("schema-msg", "more than one match in `one-of' detected")
83 10022: NC_("schema-msg", "int value greater than maxValue"), 84 E10020 = NC_("schema-msg", "int expected")
84 10023: NC_("schema-msg", "float expected"), 85 E10021 = NC_("schema-msg", "int value lower than minValue")
85 10024: NC_("schema-msg", "float value lower than minValue"), 86 E10022 = NC_("schema-msg", "int value greater than maxValue")
86 10025: NC_("schema-msg", "float value greater than maxValue"), 87 E10023 = NC_("schema-msg", "float expected")
87 10026: NC_("schema-msg", "boolean value expected"), 88 E10024 = NC_("schema-msg", "float value lower than minValue")
88 10027: NC_("schema-msg", "boolean true expected"), 89 E10025 = NC_("schema-msg", "float value greater than maxValue")
89 10028: NC_("schema-msg", "boolean false expected"), 90 E10026 = NC_("schema-msg", "boolean value expected")
90 10029: NC_("schema-msg", "`not' expected problems but got none"), 91 E10027 = NC_("schema-msg", "boolean true expected")
91 10030: NC_("schema-msg", "numeric type (int or float) expected"), 92 E10028 = NC_("schema-msg", "boolean false expected")
92 10031: NC_("schema-msg", "numeric value lower than minValue"), 93 E10029 = NC_("schema-msg", "`not' expected problems but got none")
93 10032: NC_("schema-msg", "numeric value greater than maxValue"), 94 E10030 = NC_("schema-msg", "numeric type (int or float) expected")
94 10033: NC_("schema-msg", "a plain scalar value expected"), 95 E10031 = NC_("schema-msg", "numeric value lower than minValue")
95 10034: NC_("schema-msg", "dict key does not match required schema"), 96 E10032 = NC_("schema-msg", "numeric value greater than maxValue")
96 10035: NC_("schema-msg", "binary data expected"), 97 E10033 = NC_("schema-msg", "a plain scalar value expected")
97 10036: NC_("schema-msg", "length of binary data lower than minValue"), 98 E10034 = NC_("schema-msg", "dict key does not match required schema")
98 10037: NC_("schema-msg", "length of binary data exceeds maxValue"), 99 E10035 = NC_("schema-msg", "binary data expected")
99 10038: NC_("schema-msg", "a set is expected"), 100 E10036 = NC_("schema-msg", "length of binary data lower than minValue")
100 10039: NC_("schema-msg", "length of set lower than minLength"), 101 E10037 = NC_("schema-msg", "length of binary data exceeds maxValue")
101 10040: NC_("schema-msg", "length of set greater than maxLength"), 102 E10038 = NC_("schema-msg", "a set is expected")
102 10041: NC_("schema-msg", "timestamp expected"), 103 E10039 = NC_("schema-msg", "length of set lower than minLength")
103 10042: NC_("schema-msg", "value of timestamp does not validate"), 104 E10040 = NC_("schema-msg", "length of set greater than maxLength")
104 10043: NC_("schema-msg", "enumerated string value expected but not found"), 105 E10041 = NC_("schema-msg", "timestamp expected")
105 10044: NC_("schema-msg", "referenced object doest not exist"), 106 E10042 = NC_("schema-msg", "value of timestamp does not validate")
106 10045: NC_("schema-msg", "key is not contained in referenced object"), 107 E10043 = NC_("schema-msg", "enumerated string value expected but not found")
107 10046: NC_("schema-msg", "referenced object is not a container"), 108 E10044 = NC_("schema-msg", "referenced object doest not exist")
108 10047: NC_("schema-msg", "binary data does not match the required RE pattern"), 109 E10045 = NC_("schema-msg", "key is not contained in referenced object")
109 10048: NC_("schema-msg", "enumerated integer value expected but not found"), 110 E10046 = NC_("schema-msg", "referenced object is not a container")
110 10049: NC_("schema-msg", "enumerated number value expected but not found"), 111 E10047 = NC_("schema-msg", "binary data does not match the required RE pattern")
111 10050: NC_("schema-msg", "min dict length encountered"), 112 E10048 = NC_("schema-msg", "enumerated integer value expected but not found")
112 10051: NC_("schema-msg", "max dict length exceeded"), 113 E10049 = NC_("schema-msg", "enumerated number value expected but not found")
113 10052: NC_("schema-msg", "index constraint violated"), 114 E10050 = NC_("schema-msg", "min dict length encountered")
114 10053: NC_("schema-msg", "`one-of' failed"), 115 E10051 = NC_("schema-msg", "max dict length exceeded")
115 10054: NC_("schema-msg", "failing `one-of' item"), 116 E10052 = NC_("schema-msg", "index constraint violated")
116 10055: NC_("schema-msg", "`any-of' failed"), 117 E10053 = NC_("schema-msg", "`one-of' failed")
117 10056: NC_("schema-msg", "failing `any-of' item"), 118 E10054 = NC_("schema-msg", "failing `one-of' item")
118 10057: NC_("schema-msg", "`all-of' failed"), 119 E10055 = NC_("schema-msg", "`any-of' failed")
119 10058: NC_("schema-msg", "failing `all-of' item"), 120 E10056 = NC_("schema-msg", "failing `any-of' item")
120 } 121 E10057 = NC_("schema-msg", "`all-of' failed")
121 122 E10058 = NC_("schema-msg", "failing `all-of' item")
122 WARNINGS = { 123
123 80000: NC_("schema-msg", "duplicate dict key"), 124
124 } 125 @enum.unique
125 126 class WARNINGS(enum.Enum):
126 if not set(ERRORS.keys()).isdisjoint(set(WARNINGS.keys())): 127 W80000 = NC_("schema-msg", "duplicate dict key")
127 raise ValueError("ERRORS and WARNINGS must be disjoint") 128
129
130 # Check some invariants at import time
131 for e in ERRORS.__members__:
132 assert e.startswith('E'), "ERROR code `{}' shall start with letter `E'".format(e)
133 assert 10000 <= int(e[1:], 10) < 80000, "Invalid ERROR code number in `{}'".format(e)
134 for w in WARNINGS.__members__:
135 assert w.startswith('W'), "WARNING code `{}' must start with letter `W'".format(w)
136 assert 80000 <= int(w[1:], 10), "Invalid WARNING code number in `{}'".format(w)
128 137
129 138
130 TYPE_RE = type(re.compile(r"\A.+\Z")) 139 TYPE_RE = type(re.compile(r"\A.+\Z"))
131 140
132 _SENTINEL = object() 141 _SENTINEL = object()
149 158
150 159
151 def problem_message(pr): 160 def problem_message(pr):
152 if isinstance(pr, ValidationProblem): 161 if isinstance(pr, ValidationProblem):
153 code = getattr(pr, "code", None) 162 code = getattr(pr, "code", None)
154 else: 163 elif isinstance(pr, (ERRORS, WARNINGS)):
155 code = pr 164 code = pr
156 msg = ERRORS.get(code, None) 165 else:
157 if msg is None: 166 if pr >= 80000:
158 msg = WARNINGS[code] 167 code = WARNINGS["W" + str(pr)]
159 return msg 168 else:
169 code = ERRORS["E" + str(pr)]
170 return code.value
160 171
161 172
162 class ValidationProblem(object): 173 class ValidationProblem(object):
163 174
164 __slots__ = ("code", "severity", "hint", "context", "cause", "index") 175 __slots__ = ("code", "severity", "hint", "context", "cause", "index")
643 if idx < 0: 654 if idx < 0:
644 idx = len(parent.current_object) + idx 655 idx = len(parent.current_object) + idx
645 if idx == effective_index: 656 if idx == effective_index:
646 break 657 break
647 else: 658 else:
648 yield ValidationProblem(code=10052, context=context) 659 yield ValidationProblem(code=ERRORS.E10052, context=context)
649 660
650 661
651 def validate_dict(obj, schema, context): 662 def validate_dict(obj, schema, context):
652 if _is_null_allowed_for_object(obj, schema, context): 663 if _is_null_allowed_for_object(obj, schema, context):
653 return 664 return
654 if not isinstance(obj, dict): 665 if not isinstance(obj, dict):
655 yield ValidationProblem(code=10000, hint="got: {}".format(type(obj).__name__), context=context) 666 yield ValidationProblem(code=ERRORS.E10000, hint="got: {}".format(type(obj).__name__), context=context)
656 return 667 return
657 yield from _validate_index_constraint(obj, schema, context) 668 yield from _validate_index_constraint(obj, schema, context)
658 minlen = schema.get("minLength", None) 669 minlen = schema.get("minLength", None)
659 if minlen: 670 if minlen:
660 if len(obj) < minlen: 671 if len(obj) < minlen:
661 yield ValidationProblem(code=10050, hint=obj, context=context) 672 yield ValidationProblem(code=ERRORS.E10050, hint=obj, context=context)
662 maxlen = schema.get("maxLength", None) 673 maxlen = schema.get("maxLength", None)
663 if maxlen is not None: 674 if maxlen is not None:
664 if len(obj) > maxlen: 675 if len(obj) > maxlen:
665 yield ValidationProblem(code=10051, hint=obj, context=context) 676 yield ValidationProblem(code=ERRORS.E10051, hint=obj, context=context)
666 schema_keys = schema.get("keys", {}) if schema else {} 677 schema_keys = schema.get("keys", {}) if schema else {}
667 seen_keys = set() 678 seen_keys = set()
668 schema_keynames = schema.get_child("keyNames", None) 679 schema_keynames = schema.get_child("keyNames", None)
669 idx = -1 680 idx = -1
670 for key, item in obj.items(): 681 for key, item in obj.items():
671 idx += 1 682 idx += 1
672 if schema_keynames is None: 683 if schema_keynames is None:
673 if not isinstance(key, str): 684 if not isinstance(key, str):
674 yield ValidationProblem(code=10003, hint=repr(key), context=context) 685 yield ValidationProblem(code=ERRORS.E10003, hint=repr(key), context=context)
675 else: 686 else:
676 # validate the key against given schema 687 # validate the key against given schema
677 new_context = Context(context, key=key, key_index=idx, current_object=key) 688 new_context = Context(context, key=key, key_index=idx, current_object=key)
678 key_probs = list(_validate(key, schema_keynames, new_context)) 689 key_probs = list(_validate(key, schema_keynames, new_context))
679 if key_probs: 690 if key_probs:
680 yield ValidationProblem( 691 yield ValidationProblem(
681 code=10034, hint=key, context=context, cause=key_probs) 692 code=ERRORS.E10034, hint=key, context=context, cause=key_probs)
682 if context.settings.break_on_keynames_problems: 693 if context.settings.break_on_keynames_problems:
683 return 694 return
684 if key in seen_keys: 695 if key in seen_keys:
685 yield ValidationProblem(code=80000, hint=key, context=context) 696 yield ValidationProblem(code=WARNINGS.W80000, hint=key, context=context)
686 else: 697 else:
687 seen_keys.add(key) 698 seen_keys.add(key)
688 # XXX FIXME: context: new leaf context with new key for recursion 699 # XXX FIXME: context: new leaf context with new key for recursion
689 if key in schema_keys: 700 if key in schema_keys:
690 new_context = Context(context, key=key, key_index=idx, current_object=item) 701 new_context = Context(context, key=key, key_index=idx, current_object=item)
693 # check whether additional keys are allowed 704 # check whether additional keys are allowed
694 additional_keys = schema.get_child("additionalKeys", False) 705 additional_keys = schema.get_child("additionalKeys", False)
695 if isinstance(additional_keys, bool): 706 if isinstance(additional_keys, bool):
696 if not additional_keys: 707 if not additional_keys:
697 if not _is_in_skip_keys(key, context.settings.skip_keys): 708 if not _is_in_skip_keys(key, context.settings.skip_keys):
698 yield ValidationProblem(code=10004, hint=str(key), context=context) 709 yield ValidationProblem(code=ERRORS.E10004, hint=str(key), context=context)
699 else: 710 else:
700 if not _is_in_skip_keys(key, context.settings.skip_keys): 711 if not _is_in_skip_keys(key, context.settings.skip_keys):
701 # try this as the common schema for all the additional keys 712 # try this as the common schema for all the additional keys
702 new_context = Context(context, key=key, key_index=idx, current_object=item) 713 new_context = Context(context, key=key, key_index=idx, current_object=item)
703 yield from _validate(item, additional_keys, new_context) 714 yield from _validate(item, additional_keys, new_context)
706 required_keys = set(schema.get("required", set())) 717 required_keys = set(schema.get("required", set()))
707 except (TypeError, ValueError): 718 except (TypeError, ValueError):
708 raise SchemaError("`required` must be an iterable") 719 raise SchemaError("`required` must be an iterable")
709 if not required_keys <= seen_keys: 720 if not required_keys <= seen_keys:
710 hs = [str(i) for i in required_keys - seen_keys] 721 hs = [str(i) for i in required_keys - seen_keys]
711 yield ValidationProblem(code=10005, hint=sorted(hs), context=context) 722 yield ValidationProblem(code=ERRORS.E10005, hint=sorted(hs), context=context)
712 723
713 724
714 def validate_list(obj, schema, context): 725 def validate_list(obj, schema, context):
715 if _is_null_allowed_for_object(obj, schema, context): 726 if _is_null_allowed_for_object(obj, schema, context):
716 return 727 return
717 if not isinstance(obj, (list, tuple)): 728 if not isinstance(obj, (list, tuple)):
718 yield ValidationProblem(code=10001, hint="got: {}".format(type(obj).__name__), context=context) 729 yield ValidationProblem(code=ERRORS.E10001, hint="got: {}".format(type(obj).__name__), context=context)
719 return 730 return
720 yield from _validate_index_constraint(obj, schema, context) 731 yield from _validate_index_constraint(obj, schema, context)
721 minlen = schema.get("minLength", None) 732 minlen = schema.get("minLength", None)
722 if minlen: 733 if minlen:
723 if len(obj) < minlen: 734 if len(obj) < minlen:
724 yield ValidationProblem(code=10012, hint=obj, context=context) 735 yield ValidationProblem(code=ERRORS.E10012, hint=obj, context=context)
725 maxlen = schema.get("maxLength", None) 736 maxlen = schema.get("maxLength", None)
726 if maxlen is not None: 737 if maxlen is not None:
727 if len(obj) > maxlen: 738 if len(obj) > maxlen:
728 yield ValidationProblem(code=10013, hint=obj, context=context) 739 yield ValidationProblem(code=ERRORS.E10013, hint=obj, context=context)
729 try: 740 try:
730 schema_items = schema.ensure_child_schema(schema["items"]) 741 schema_items = schema.ensure_child_schema(schema["items"])
731 except KeyError: 742 except KeyError:
732 schema_items = _Schema(schema, False, {"type": validate_deny}) 743 schema_items = _Schema(schema, False, {"type": validate_deny})
733 for idx, o in enumerate(obj): 744 for idx, o in enumerate(obj):
737 748
738 def validate_set(obj, schema, context): 749 def validate_set(obj, schema, context):
739 if _is_null_allowed_for_object(obj, schema, context): 750 if _is_null_allowed_for_object(obj, schema, context):
740 return 751 return
741 if not isinstance(obj, (set, frozenset)): 752 if not isinstance(obj, (set, frozenset)):
742 yield ValidationProblem(code=10038, hint="got: {}".format(type(obj).__name__), context=context) 753 yield ValidationProblem(code=ERRORS.E10038, hint="got: {}".format(type(obj).__name__), context=context)
743 return 754 return
744 yield from _validate_index_constraint(obj, schema, context) 755 yield from _validate_index_constraint(obj, schema, context)
745 minlen = schema.get("minLength", None) 756 minlen = schema.get("minLength", None)
746 if minlen: 757 if minlen:
747 if len(obj) < minlen: 758 if len(obj) < minlen:
748 yield ValidationProblem(code=10039, hint=obj, context=context) 759 yield ValidationProblem(code=ERRORS.E10039, hint=obj, context=context)
749 maxlen = schema.get("maxLength", None) 760 maxlen = schema.get("maxLength", None)
750 if maxlen is not None: 761 if maxlen is not None:
751 if len(obj) > maxlen: 762 if len(obj) > maxlen:
752 yield ValidationProblem(code=10040, hint=obj, context=context) 763 yield ValidationProblem(code=ERRORS.E10040, hint=obj, context=context)
753 try: 764 try:
754 schema_items = schema.ensure_child_schema(schema["items"]) 765 schema_items = schema.ensure_child_schema(schema["items"])
755 except KeyError: 766 except KeyError:
756 schema_items = _Schema(schema, False, {"type": validate_deny}) 767 schema_items = _Schema(schema, False, {"type": validate_deny})
757 for o in obj: 768 for o in obj:
761 772
762 def validate_tuple(obj, schema, context): 773 def validate_tuple(obj, schema, context):
763 if _is_null_allowed_for_object(obj, schema, context): 774 if _is_null_allowed_for_object(obj, schema, context):
764 return 775 return
765 if not isinstance(obj, (list, tuple)): 776 if not isinstance(obj, (list, tuple)):
766 yield ValidationProblem(code=10014, hint="got: {}".format(type(obj).__name__), context=context) 777 yield ValidationProblem(code=ERRORS.E10014, hint="got: {}".format(type(obj).__name__), context=context)
767 return 778 return
768 yield from _validate_index_constraint(obj, schema, context) 779 yield from _validate_index_constraint(obj, schema, context)
769 minlen = schema.get("minLength", None) 780 minlen = schema.get("minLength", None)
770 if minlen: 781 if minlen:
771 if len(obj) < minlen: 782 if len(obj) < minlen:
772 yield ValidationProblem(code=10015, hint=obj, context=context) 783 yield ValidationProblem(code=ERRORS.E10015, hint=obj, context=context)
773 maxlen = schema.get("maxLength", None) 784 maxlen = schema.get("maxLength", None)
774 if maxlen is not None: 785 if maxlen is not None:
775 if len(obj) > maxlen: 786 if len(obj) > maxlen:
776 yield ValidationProblem(code=10016, hint=obj, context=context) 787 yield ValidationProblem(code=ERRORS.E10016, hint=obj, context=context)
777 schema_items = schema.get("items", []) 788 schema_items = schema.get("items", [])
778 if not isinstance(schema_items, (list, tuple)): 789 if not isinstance(schema_items, (list, tuple)):
779 raise SchemaError("tuple items require a list of schemata in items") 790 raise SchemaError("tuple items require a list of schemata in items")
780 for idx, o in enumerate(obj): 791 for idx, o in enumerate(obj):
781 # early exit at maxlen 792 # early exit at maxlen
786 schema_index = schema.ensure_child_schema(schema_items[idx]) 797 schema_index = schema.ensure_child_schema(schema_items[idx])
787 except IndexError: 798 except IndexError:
788 additional_items = schema.get_child("additionalItems", False) 799 additional_items = schema.get_child("additionalItems", False)
789 if isinstance(additional_items, bool): 800 if isinstance(additional_items, bool):
790 if not additional_items: 801 if not additional_items:
791 yield ValidationProblem(code=10017, context=new_context) 802 yield ValidationProblem(code=ERRORS.E10017, context=new_context)
792 else: 803 else:
793 yield from _validate(o, additional_items, new_context) 804 yield from _validate(o, additional_items, new_context)
794 else: 805 else:
795 yield from _validate(o, schema_index, new_context) 806 yield from _validate(o, schema_index, new_context)
796 807
797 808
798 def validate_str(obj, schema, context): 809 def validate_str(obj, schema, context):
799 if _is_null_allowed_for_object(obj, schema, context): 810 if _is_null_allowed_for_object(obj, schema, context):
800 return 811 return
801 if not isinstance(obj, str): 812 if not isinstance(obj, str):
802 yield ValidationProblem(code=10002, hint=obj, context=context) 813 yield ValidationProblem(code=ERRORS.E10002, hint=obj, context=context)
803 else: 814 else:
804 yield from _validate_index_constraint(obj, schema, context) 815 yield from _validate_index_constraint(obj, schema, context)
805 enumvalues = schema.get("enum", None) 816 enumvalues = schema.get("enum", None)
806 if enumvalues is not None: 817 if enumvalues is not None:
807 for ev in enumvalues: 818 for ev in enumvalues:
808 if ev == obj: 819 if ev == obj:
809 break 820 break
810 else: 821 else:
811 yield ValidationProblem(code=10043, hint=obj, context=context) 822 yield ValidationProblem(code=ERRORS.E10043, hint=obj, context=context)
812 minlen = schema.get("minLength", None) 823 minlen = schema.get("minLength", None)
813 if minlen: 824 if minlen:
814 if len(obj) < minlen: 825 if len(obj) < minlen:
815 yield ValidationProblem(code=10006, hint=obj, context=context) 826 yield ValidationProblem(code=ERRORS.E10006, hint=obj, context=context)
816 maxlen = schema.get("maxLength", None) 827 maxlen = schema.get("maxLength", None)
817 if maxlen is not None: 828 if maxlen is not None:
818 if len(obj) > maxlen: 829 if len(obj) > maxlen:
819 yield ValidationProblem(code=10007, hint=obj, context=context) 830 yield ValidationProblem(code=ERRORS.E10007, hint=obj, context=context)
820 pattern = schema.get("pattern", None) 831 pattern = schema.get("pattern", None)
821 if pattern is not None: 832 if pattern is not None:
822 if isinstance(pattern, str): 833 if isinstance(pattern, str):
823 mo = re.search(pattern, obj) 834 mo = re.search(pattern, obj)
824 if not mo: 835 if not mo:
825 yield ValidationProblem(code=10008, context=context) 836 yield ValidationProblem(code=ERRORS.E10008, context=context)
826 elif isinstance(pattern, TYPE_RE): 837 elif isinstance(pattern, TYPE_RE):
827 mo = pattern.search(obj) 838 mo = pattern.search(obj)
828 if not mo: 839 if not mo:
829 yield ValidationProblem(code=10008, context=context) 840 yield ValidationProblem(code=ERRORS.E10008, context=context)
830 elif callable(pattern): 841 elif callable(pattern):
831 yield from pattern(obj, schema, context) 842 yield from pattern(obj, schema, context)
832 else: 843 else:
833 raise SchemaError("unknown pattern type") 844 raise SchemaError("unknown pattern type")
834 is_contained = schema.get("is-contained-in-ref", None) 845 is_contained = schema.get("is-contained-in-ref", None)
836 refobj = try_get_reference(is_contained, 847 refobj = try_get_reference(is_contained,
837 context, 848 context,
838 schema, 849 schema,
839 default=_SENTINEL) 850 default=_SENTINEL)
840 if refobj is _SENTINEL: 851 if refobj is _SENTINEL:
841 yield ValidationProblem(code=10044, context=context) 852 yield ValidationProblem(code=ERRORS.E10044, context=context)
842 else: 853 else:
843 try: 854 try:
844 if obj not in refobj: 855 if obj not in refobj:
845 yield ValidationProblem(code=10045, context=context) 856 yield ValidationProblem(code=ERRORS.E10045, context=context)
846 except TypeError: 857 except TypeError:
847 yield ValidationProblem(code=10046, context=context) 858 yield ValidationProblem(code=ERRORS.E10046, context=context)
848 859
849 860
850 def validate_binary(obj, schema, context): 861 def validate_binary(obj, schema, context):
851 if not isinstance(obj, (bytes, bytearray)): 862 if not isinstance(obj, (bytes, bytearray)):
852 yield ValidationProblem(code=10035, hint=obj, context=context) 863 yield ValidationProblem(code=ERRORS.E10035, hint=obj, context=context)
853 else: 864 else:
854 yield from _validate_index_constraint(obj, schema, context) 865 yield from _validate_index_constraint(obj, schema, context)
855 minlen = schema.get("minLength", None) 866 minlen = schema.get("minLength", None)
856 if minlen: 867 if minlen:
857 if len(obj) < minlen: 868 if len(obj) < minlen:
858 yield ValidationProblem(code=10036, hint=obj, context=context) 869 yield ValidationProblem(code=ERRORS.E10036, hint=obj, context=context)
859 maxlen = schema.get("maxLength", None) 870 maxlen = schema.get("maxLength", None)
860 if maxlen is not None: 871 if maxlen is not None:
861 if len(obj) > maxlen: 872 if len(obj) > maxlen:
862 yield ValidationProblem(code=10037, hint=obj, context=context) 873 yield ValidationProblem(code=ERRORS.E10037, hint=obj, context=context)
863 pattern = schema.get("pattern", None) 874 pattern = schema.get("pattern", None)
864 if pattern is not None: 875 if pattern is not None:
865 if isinstance(pattern, (str, bytes, bytearray)): 876 if isinstance(pattern, (str, bytes, bytearray)):
866 if isinstance(pattern, str): 877 if isinstance(pattern, str):
867 if "'''" not in pattern: 878 if "'''" not in pattern:
874 raise SchemaError("incompatible bytes pattern") 885 raise SchemaError("incompatible bytes pattern")
875 else: 886 else:
876 bytes_pattern = pattern 887 bytes_pattern = pattern
877 mo = re.search(bytes_pattern, obj) 888 mo = re.search(bytes_pattern, obj)
878 if not mo: 889 if not mo:
879 yield ValidationProblem(code=10047, context=context) 890 yield ValidationProblem(code=ERRORS.E10047, context=context)
880 elif isinstance(pattern, TYPE_RE): 891 elif isinstance(pattern, TYPE_RE):
881 mo = pattern.search(obj) 892 mo = pattern.search(obj)
882 if not mo: 893 if not mo:
883 yield ValidationProblem(code=10047, context=context) 894 yield ValidationProblem(code=ERRORS.E10047, context=context)
884 elif callable(pattern): 895 elif callable(pattern):
885 yield from pattern(obj, schema, context) 896 yield from pattern(obj, schema, context)
886 else: 897 else:
887 raise SchemaError("unknown pattern type") 898 raise SchemaError("unknown pattern type")
888 899
889 900
890 def validate_timestamp(obj, schema, context): 901 def validate_timestamp(obj, schema, context):
891 if not isinstance(obj, datetime.datetime): 902 if not isinstance(obj, datetime.datetime):
892 yield ValidationProblem(code=10041, hint=obj, context=context) 903 yield ValidationProblem(code=ERRORS.E10041, hint=obj, context=context)
893 else: 904 else:
894 yield from _validate_index_constraint(obj, schema, context) 905 yield from _validate_index_constraint(obj, schema, context)
895 value = schema.get("value", None) 906 value = schema.get("value", None)
896 if value is not None: 907 if value is not None:
897 if callable(value): 908 if callable(value):
902 913
903 def validate_integer(obj, schema, context): 914 def validate_integer(obj, schema, context):
904 if _is_null_allowed_for_object(obj, schema, context): 915 if _is_null_allowed_for_object(obj, schema, context):
905 return 916 return
906 if not isinstance(obj, int): 917 if not isinstance(obj, int):
907 yield ValidationProblem(code=10020, hint=obj, context=context) 918 yield ValidationProblem(code=ERRORS.E10020, hint=obj, context=context)
908 else: 919 else:
909 yield from _validate_index_constraint(obj, schema, context) 920 yield from _validate_index_constraint(obj, schema, context)
910 minValue = schema.get("minValue", None) 921 minValue = schema.get("minValue", None)
911 if minValue is not None and obj < minValue: 922 if minValue is not None and obj < minValue:
912 yield ValidationProblem(code=10021, hint=obj, context=context) 923 yield ValidationProblem(code=ERRORS.E10021, hint=obj, context=context)
913 maxValue = schema.get("maxValue", None) 924 maxValue = schema.get("maxValue", None)
914 if maxValue is not None and obj > maxValue: 925 if maxValue is not None and obj > maxValue:
915 yield ValidationProblem(code=10022, hint=obj, context=context) 926 yield ValidationProblem(code=ERRORS.E10022, hint=obj, context=context)
916 enumvalues = schema.get("enum", None) 927 enumvalues = schema.get("enum", None)
917 if enumvalues is not None: 928 if enumvalues is not None:
918 for ev in enumvalues: 929 for ev in enumvalues:
919 if ev == obj: 930 if ev == obj:
920 break 931 break
921 else: 932 else:
922 yield ValidationProblem(code=10048, hint=obj, context=context) 933 yield ValidationProblem(code=ERRORS.E10048, hint=obj, context=context)
923 value = schema.get("value", None) 934 value = schema.get("value", None)
924 if value is not None: 935 if value is not None:
925 if callable(value): 936 if callable(value):
926 yield from value(obj, schema, context) 937 yield from value(obj, schema, context)
927 else: 938 else:
930 941
931 def validate_float(obj, schema, context): 942 def validate_float(obj, schema, context):
932 if _is_null_allowed_for_object(obj, schema, context): 943 if _is_null_allowed_for_object(obj, schema, context):
933 return 944 return
934 if not isinstance(obj, float): 945 if not isinstance(obj, float):
935 yield ValidationProblem(code=10023, hint=obj, context=context) 946 yield ValidationProblem(code=ERRORS.E10023, hint=obj, context=context)
936 else: 947 else:
937 yield from _validate_index_constraint(obj, schema, context) 948 yield from _validate_index_constraint(obj, schema, context)
938 minValue = schema.get("minValue", None) 949 minValue = schema.get("minValue", None)
939 if minValue is not None and obj < minValue: 950 if minValue is not None and obj < minValue:
940 yield ValidationProblem(code=10024, hint=obj, context=context) 951 yield ValidationProblem(code=ERRORS.E10024, hint=obj, context=context)
941 maxValue = schema.get("maxValue", None) 952 maxValue = schema.get("maxValue", None)
942 if maxValue is not None and obj > maxValue: 953 if maxValue is not None and obj > maxValue:
943 yield ValidationProblem(code=10025, hint=obj, context=context) 954 yield ValidationProblem(code=ERRORS.E10025, hint=obj, context=context)
944 value = schema.get("value", None) 955 value = schema.get("value", None)
945 if value is not None: 956 if value is not None:
946 if callable(value): 957 if callable(value):
947 yield from value(obj, schema, context) 958 yield from value(obj, schema, context)
948 else: 959 else:
951 962
952 def validate_number(obj, schema, context): 963 def validate_number(obj, schema, context):
953 if _is_null_allowed_for_object(obj, schema, context): 964 if _is_null_allowed_for_object(obj, schema, context):
954 return 965 return
955 if not isinstance(obj, (int, float)): 966 if not isinstance(obj, (int, float)):
956 yield ValidationProblem(code=10030, hint=obj, context=context) 967 yield ValidationProblem(code=ERRORS.E10030, hint=obj, context=context)
957 else: 968 else:
958 yield from _validate_index_constraint(obj, schema, context) 969 yield from _validate_index_constraint(obj, schema, context)
959 minValue = schema.get("minValue", None) 970 minValue = schema.get("minValue", None)
960 if minValue is not None and isinstance(obj, float): 971 if minValue is not None and isinstance(obj, float):
961 minValue *= 1.0 972 minValue *= 1.0
962 if minValue is not None and obj < minValue: 973 if minValue is not None and obj < minValue:
963 yield ValidationProblem(code=10031, hint=obj, context=context) 974 yield ValidationProblem(code=ERRORS.E10031, hint=obj, context=context)
964 maxValue = schema.get("maxValue", None) 975 maxValue = schema.get("maxValue", None)
965 if maxValue is not None and isinstance(obj, float): 976 if maxValue is not None and isinstance(obj, float):
966 maxValue *= 1.0 977 maxValue *= 1.0
967 if maxValue is not None and obj > maxValue: 978 if maxValue is not None and obj > maxValue:
968 yield ValidationProblem(code=10032, hint=obj, context=context) 979 yield ValidationProblem(code=ERRORS.E10032, hint=obj, context=context)
969 enumvalues = schema.get("enum", None) 980 enumvalues = schema.get("enum", None)
970 if enumvalues is not None: 981 if enumvalues is not None:
971 for ev in enumvalues: 982 for ev in enumvalues:
972 if ev == obj: 983 if ev == obj:
973 break 984 break
974 else: 985 else:
975 yield ValidationProblem(code=10049, hint=obj, context=context) 986 yield ValidationProblem(code=ERRORS.E10049, hint=obj, context=context)
976 value = schema.get("value", None) 987 value = schema.get("value", None)
977 if value is not None: 988 if value is not None:
978 if callable(value): 989 if callable(value):
979 yield from value(obj, schema, context) 990 yield from value(obj, schema, context)
980 else: 991 else:
984 def validate_scalar(obj, schema, context): 995 def validate_scalar(obj, schema, context):
985 if _is_null_allowed_for_object(obj, schema, context): 996 if _is_null_allowed_for_object(obj, schema, context):
986 return 997 return
987 yield from _validate_index_constraint(obj, schema, context) 998 yield from _validate_index_constraint(obj, schema, context)
988 if obj is None: 999 if obj is None:
989 yield ValidationProblem(code=10033, hint=obj, context=context) 1000 yield ValidationProblem(code=ERRORS.E10033, hint=obj, context=context)
990 if isinstance(obj, (dict, list, tuple, set, frozenset)): 1001 if isinstance(obj, (dict, list, tuple, set, frozenset)):
991 yield ValidationProblem(code=10033, hint=obj, context=context) 1002 yield ValidationProblem(code=ERRORS.E10033, hint=obj, context=context)
992 1003
993 1004
994 def validate_deny(obj, schema, context): 1005 def validate_deny(obj, schema, context):
995 yield from _validate_index_constraint(obj, schema, context) 1006 yield from _validate_index_constraint(obj, schema, context)
996 yield ValidationProblem(code=10010, context=context) 1007 yield ValidationProblem(code=ERRORS.E10010, context=context)
997 1008
998 1009
999 def validate_accept(obj, schema, context): 1010 def validate_accept(obj, schema, context):
1000 yield from _validate_index_constraint(obj, schema, context) 1011 yield from _validate_index_constraint(obj, schema, context)
1001 1012
1002 1013
1003 def validate_null(obj, schema, context): 1014 def validate_null(obj, schema, context):
1004 yield from _validate_index_constraint(obj, schema, context) 1015 yield from _validate_index_constraint(obj, schema, context)
1005 if obj is not None: 1016 if obj is not None:
1006 yield ValidationProblem(code=10011, context=context) 1017 yield ValidationProblem(code=ERRORS.E10011, context=context)
1007 1018
1008 1019
1009 def validate_empty(obj, schema, context): 1020 def validate_empty(obj, schema, context):
1010 yield from _validate_index_constraint(obj, schema, context) 1021 yield from _validate_index_constraint(obj, schema, context)
1011 if obj is None: 1022 if obj is None:
1012 return 1023 return
1013 if isinstance(obj, (dict, list, tuple, set, frozenset)) and not obj: 1024 if isinstance(obj, (dict, list, tuple, set, frozenset)) and not obj:
1014 return 1025 return
1015 yield ValidationProblem(10018, context=context) 1026 yield ValidationProblem(ERRORS.E10018, context=context)
1016 1027
1017 1028
1018 def validate_bool(obj, schema, context): 1029 def validate_bool(obj, schema, context):
1019 if _is_null_allowed_for_object(obj, schema, context): 1030 if _is_null_allowed_for_object(obj, schema, context):
1020 return 1031 return
1021 if not isinstance(obj, bool): 1032 if not isinstance(obj, bool):
1022 yield ValidationProblem(code=10026, hint=obj, context=context) 1033 yield ValidationProblem(code=ERRORS.E10026, hint=obj, context=context)
1023 else: 1034 else:
1024 yield from _validate_index_constraint(obj, schema, context) 1035 yield from _validate_index_constraint(obj, schema, context)
1025 value = schema.get("value", None) 1036 value = schema.get("value", None)
1026 if value is not None: 1037 if value is not None:
1027 if callable(value): 1038 if callable(value):
1028 yield from value(obj, schema, context) 1039 yield from value(obj, schema, context)
1029 elif value and not obj: 1040 elif value and not obj:
1030 yield ValidationProblem(code=10027, hint=obj, context=context) 1041 yield ValidationProblem(code=ERRORS.E10027, hint=obj, context=context)
1031 elif not value and obj: 1042 elif not value and obj:
1032 yield ValidationProblem(code=10028, hint=obj, context=context) 1043 yield ValidationProblem(code=ERRORS.E10028, hint=obj, context=context)
1033 1044
1034 1045
1035 def validate_allOf(obj, schema, context): 1046 def validate_allOf(obj, schema, context):
1036 if not isinstance(schema, (list, tuple)): 1047 if not isinstance(schema, (list, tuple)):
1037 raise SchemaError("require a list of schematas for `all-of'") 1048 raise SchemaError("require a list of schematas for `all-of'")
1041 tr = list(_validate(obj, s, context)) 1052 tr = list(_validate(obj, s, context))
1042 if tr: 1053 if tr:
1043 res.append((idx, tr, )) 1054 res.append((idx, tr, ))
1044 if res: 1055 if res:
1045 yield ValidationProblem( 1056 yield ValidationProblem(
1046 code=10057, 1057 code=ERRORS.E10057,
1047 context=context, 1058 context=context,
1048 cause=[ 1059 cause=[
1049 ValidationProblem( 1060 ValidationProblem(
1050 code=10058, 1061 code=ERRORS.E10058,
1051 context=context, 1062 context=context,
1052 cause=tr, 1063 cause=tr,
1053 index=idx) for (idx, tr) in res]) 1064 index=idx) for (idx, tr) in res])
1054 1065
1055 1066
1066 # Erfolg: gleich positiv zurueck ohne Meldungen 1077 # Erfolg: gleich positiv zurueck ohne Meldungen
1067 return 1078 return
1068 # Ansonsten: alle Fehlschlaege protokollieren 1079 # Ansonsten: alle Fehlschlaege protokollieren
1069 if res: 1080 if res:
1070 yield ValidationProblem( 1081 yield ValidationProblem(
1071 code=10055, 1082 code=ERRORS.E10055,
1072 context=context, 1083 context=context,
1073 cause=[ 1084 cause=[
1074 ValidationProblem( 1085 ValidationProblem(
1075 code=10056, 1086 code=ERRORS.E10056,
1076 context=context, 1087 context=context,
1077 cause=tr) for tr in res]) 1088 cause=tr) for tr in res])
1078 1089
1079 1090
1080 def validate_oneOf(obj, schema, context): 1091 def validate_oneOf(obj, schema, context):
1093 return 1104 return
1094 elif len(success_res) == 0: 1105 elif len(success_res) == 0:
1095 # Ansonsten: alle Fehlschlaege protokollieren 1106 # Ansonsten: alle Fehlschlaege protokollieren
1096 if failed_res: 1107 if failed_res:
1097 yield ValidationProblem( 1108 yield ValidationProblem(
1098 code=10053, 1109 code=ERRORS.E10053,
1099 context=context, 1110 context=context,
1100 cause=[ 1111 cause=[
1101 ValidationProblem( 1112 ValidationProblem(
1102 code=10054, 1113 code=ERRORS.E10054,
1103 context=context, 1114 context=context,
1104 cause=tr, 1115 cause=tr,
1105 index=idx) for (idx, tr) in failed_res]) 1116 index=idx) for (idx, tr) in failed_res])
1106 else: 1117 else:
1107 # Die Indizes der "zuvielen" in "hint" anzeigen 1118 # Die Indizes der "zuvielen" in "hint" anzeigen
1108 yield ValidationProblem(code=10019, hint=",".join([str(k) for k in success_res])) 1119 yield ValidationProblem(code=ERRORS.E10019, hint=",".join([str(k) for k in success_res]))
1109 1120
1110 1121
1111 def validate_not(obj, schema, context): 1122 def validate_not(obj, schema, context):
1112 assert isinstance(schema, _Schema) 1123 assert isinstance(schema, _Schema)
1113 res = list(_validate(obj, schema, context)) 1124 res = list(_validate(obj, schema, context))
1114 if not res: 1125 if not res:
1115 yield ValidationProblem(code=10029, hint=obj, context=context, 1126 yield ValidationProblem(code=ERRORS.E10029, hint=obj, context=context,
1116 cause=res) 1127 cause=res)
1117 1128
1118 1129
1119 def process_schema_references(schema, context, check_single_ref_key=True): 1130 def process_schema_references(schema, context, check_single_ref_key=True):
1120 try: 1131 try: