Mercurial > hgrepos > Python > libs > data-schema
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: |
