subschema 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. subschema/__init__.py +19 -0
  2. subschema/api.py +172 -0
  3. subschema/cli.py +75 -0
  4. subschema/dialects.py +484 -0
  5. subschema/exceptions.py +100 -0
  6. subschema/kernel/__init__.py +33 -0
  7. subschema/kernel/applicators.py +1644 -0
  8. subschema/kernel/certificates.py +14 -0
  9. subschema/kernel/composition.py +188 -0
  10. subschema/kernel/constraints.py +93 -0
  11. subschema/kernel/context.py +131 -0
  12. subschema/kernel/contracts.py +243 -0
  13. subschema/kernel/difference.py +6252 -0
  14. subschema/kernel/disjointness.py +133 -0
  15. subschema/kernel/domains/__init__.py +3 -0
  16. subschema/kernel/domains/arrays.py +913 -0
  17. subschema/kernel/domains/numbers.py +731 -0
  18. subschema/kernel/domains/objects.py +2219 -0
  19. subschema/kernel/domains/strings.py +744 -0
  20. subschema/kernel/domains/types.py +543 -0
  21. subschema/kernel/driver.py +104 -0
  22. subschema/kernel/engine.py +63 -0
  23. subschema/kernel/evaluation.py +924 -0
  24. subschema/kernel/finite.py +715 -0
  25. subschema/kernel/formulas.py +668 -0
  26. subschema/kernel/ir.py +735 -0
  27. subschema/kernel/json_data.py +88 -0
  28. subschema/kernel/normalization.py +65 -0
  29. subschema/kernel/overlaps.py +152 -0
  30. subschema/kernel/projection.py +99 -0
  31. subschema/kernel/references.py +1047 -0
  32. subschema/kernel/regex.py +1006 -0
  33. subschema/kernel/sat.py +3153 -0
  34. subschema/kernel/scalars.py +651 -0
  35. subschema/kernel/schemas.py +206 -0
  36. subschema/kernel/semantic.py +833 -0
  37. subschema/kernel/symbolic.py +201 -0
  38. subschema/kernel/validation.py +422 -0
  39. subschema/kernel/values.py +56 -0
  40. subschema/kernel/witnesses.py +824 -0
  41. subschema-0.0.1.dist-info/METADATA +102 -0
  42. subschema-0.0.1.dist-info/RECORD +45 -0
  43. subschema-0.0.1.dist-info/WHEEL +4 -0
  44. subschema-0.0.1.dist-info/entry_points.txt +2 -0
  45. subschema-0.0.1.dist-info/licenses/LICENSE +177 -0
subschema/__init__.py ADDED
@@ -0,0 +1,19 @@
1
+ from subschema import api, exceptions
2
+ from subschema.dialects import Dialect
3
+
4
+ is_subschema = api.is_subschema
5
+ meet_schemas = api.meet_schemas
6
+ join_schemas = api.join_schemas
7
+ is_equivalent = api.is_equivalent
8
+
9
+ canonicalize_schema = api.canonicalize_schema
10
+
11
+ __all__ = [
12
+ "Dialect",
13
+ "canonicalize_schema",
14
+ "exceptions",
15
+ "is_equivalent",
16
+ "is_subschema",
17
+ "join_schemas",
18
+ "meet_schemas",
19
+ ]
subschema/api.py ADDED
@@ -0,0 +1,172 @@
1
+ """
2
+ Public API entrypoints.
3
+ """
4
+
5
+ from copy import deepcopy
6
+
7
+ from subschema.dialects import (
8
+ resolve_dialect,
9
+ strip_inactive_keywords_for_dialect,
10
+ validate_supported_keywords,
11
+ )
12
+ from subschema.kernel.contracts import ProofBudgets, ProofOptions
13
+ from subschema.kernel.engine import ProofEngine
14
+ from subschema.kernel.json_data import ensure_json_value
15
+ from subschema.kernel.normalization import normalize_boolean_schemas
16
+ from subschema.kernel.validation import validate_raw_schema_for_dialect
17
+
18
+
19
+ def canonicalize_schema(s, *, dialect=None):
20
+ """Return a modern normalized schema without embedding removed checker objects."""
21
+ ensure_json_value(s, label="schema")
22
+ resolved_dialect = resolve_dialect(s, dialect=dialect)
23
+ _validate_public_schema(s, resolved_dialect)
24
+ schema = strip_inactive_keywords_for_dialect(
25
+ normalize_boolean_schemas(deepcopy(s)), resolved_dialect
26
+ )
27
+ validate_supported_keywords(schema, resolved_dialect)
28
+ return schema
29
+
30
+
31
+ def _validate_public_schema(schema, dialect):
32
+ stripped = strip_inactive_keywords_for_dialect(schema, dialect)
33
+ validate_raw_schema_for_dialect(stripped, dialect)
34
+
35
+
36
+ def _resolve_proof_options(
37
+ proof_options=None,
38
+ *,
39
+ endeavor=False,
40
+ max_work=None,
41
+ timeout_ms=None,
42
+ ):
43
+ if not isinstance(endeavor, bool):
44
+ raise TypeError("endeavor must be a boolean")
45
+ if proof_options is not None:
46
+ if not isinstance(proof_options, ProofOptions):
47
+ raise TypeError("proof_options must be a ProofOptions instance")
48
+ if endeavor or max_work is not None or timeout_ms is not None:
49
+ raise ValueError(
50
+ "proof_options cannot be combined with endeavor, max_work, or "
51
+ "timeout_ms"
52
+ )
53
+ return proof_options
54
+ if (max_work is not None or timeout_ms is not None) and not endeavor:
55
+ raise ValueError("max_work and timeout_ms require endeavor=True")
56
+ if endeavor:
57
+ return ProofOptions(
58
+ endeavor=endeavor,
59
+ budgets=ProofBudgets(
60
+ max_work=4096 if max_work is None else max_work,
61
+ timeout_ms=1000 if timeout_ms is None else timeout_ms,
62
+ ),
63
+ )
64
+ return None
65
+
66
+
67
+ def is_subschema(
68
+ s1,
69
+ s2,
70
+ *,
71
+ dialect=None,
72
+ proof_options=None,
73
+ endeavor=False,
74
+ max_work=None,
75
+ timeout_ms=None,
76
+ ):
77
+ """Entry point for schema subtype checking."""
78
+ ensure_json_value(s1, label="lhs schema")
79
+ ensure_json_value(s2, label="rhs schema")
80
+ resolved_dialect = resolve_dialect(s1, s2, dialect=dialect)
81
+ _validate_public_schema(s1, resolved_dialect)
82
+ _validate_public_schema(s2, resolved_dialect)
83
+ options = _resolve_proof_options(
84
+ proof_options,
85
+ endeavor=endeavor,
86
+ max_work=max_work,
87
+ timeout_ms=timeout_ms,
88
+ )
89
+ return ProofEngine.for_schemas(
90
+ s1, s2, dialect=resolved_dialect, options=options
91
+ ).is_subschema_bool(s1, s2)
92
+
93
+
94
+ def meet_schemas(
95
+ s1,
96
+ s2,
97
+ *,
98
+ dialect=None,
99
+ proof_options=None,
100
+ endeavor=False,
101
+ max_work=None,
102
+ timeout_ms=None,
103
+ ):
104
+ """Entry point for schema meet operation."""
105
+ ensure_json_value(s1, label="lhs schema")
106
+ ensure_json_value(s2, label="rhs schema")
107
+ resolved_dialect = resolve_dialect(s1, s2, dialect=dialect)
108
+ _validate_public_schema(s1, resolved_dialect)
109
+ _validate_public_schema(s2, resolved_dialect)
110
+ options = _resolve_proof_options(
111
+ proof_options,
112
+ endeavor=endeavor,
113
+ max_work=max_work,
114
+ timeout_ms=timeout_ms,
115
+ )
116
+ return ProofEngine.for_schemas(
117
+ s1, s2, dialect=resolved_dialect, options=options
118
+ ).meet(s1, s2)
119
+
120
+
121
+ def join_schemas(
122
+ s1,
123
+ s2,
124
+ *,
125
+ dialect=None,
126
+ proof_options=None,
127
+ endeavor=False,
128
+ max_work=None,
129
+ timeout_ms=None,
130
+ ):
131
+ """Entry point for schema join operation."""
132
+ ensure_json_value(s1, label="lhs schema")
133
+ ensure_json_value(s2, label="rhs schema")
134
+ resolved_dialect = resolve_dialect(s1, s2, dialect=dialect)
135
+ _validate_public_schema(s1, resolved_dialect)
136
+ _validate_public_schema(s2, resolved_dialect)
137
+ options = _resolve_proof_options(
138
+ proof_options,
139
+ endeavor=endeavor,
140
+ max_work=max_work,
141
+ timeout_ms=timeout_ms,
142
+ )
143
+ return ProofEngine.for_schemas(
144
+ s1, s2, dialect=resolved_dialect, options=options
145
+ ).join(s1, s2)
146
+
147
+
148
+ def is_equivalent(
149
+ s1,
150
+ s2,
151
+ *,
152
+ dialect=None,
153
+ proof_options=None,
154
+ endeavor=False,
155
+ max_work=None,
156
+ timeout_ms=None,
157
+ ):
158
+ """Entry point for schema equivalence check operation."""
159
+ options = _resolve_proof_options(
160
+ proof_options,
161
+ endeavor=endeavor,
162
+ max_work=max_work,
163
+ timeout_ms=timeout_ms,
164
+ )
165
+ return is_subschema(
166
+ s1, s2, dialect=dialect, proof_options=options
167
+ ) and is_subschema(
168
+ s2,
169
+ s1,
170
+ dialect=dialect,
171
+ proof_options=options,
172
+ )
subschema/cli.py ADDED
@@ -0,0 +1,75 @@
1
+ import argparse
2
+
3
+ from subschema.api import is_subschema
4
+ from subschema.kernel import ProofBudgets, ProofOptions
5
+ from subschema.kernel.json_data import strict_json_load
6
+
7
+
8
+ def int_at_least_minus_one(value):
9
+ try:
10
+ parsed = int(value)
11
+ except ValueError as err:
12
+ raise argparse.ArgumentTypeError(f"{value!r} is not an integer") from err
13
+ if parsed < -1:
14
+ raise argparse.ArgumentTypeError("value must be -1 or greater")
15
+ return parsed
16
+
17
+
18
+ def load_json_file(path, label):
19
+ with open(path) as fh:
20
+ try:
21
+ return strict_json_load(fh)
22
+ except Exception as err:
23
+ raise SystemExit(f"{label} {err}") from err
24
+
25
+
26
+ def main():
27
+ """CLI entry point for subschema"""
28
+
29
+ parser = argparse.ArgumentParser(
30
+ description=(
31
+ "CLI for subschema tool which checks whether a LHS JSON "
32
+ "schema is a subschema (<:) of another RHS JSON schema."
33
+ )
34
+ )
35
+ parser.add_argument(
36
+ "--endeavor", action="store_true", help="enable finite expensive proof products"
37
+ )
38
+ parser.add_argument("--max-work", type=int_at_least_minus_one, default=None)
39
+ parser.add_argument("--timeout-ms", type=int_at_least_minus_one, default=None)
40
+ parser.add_argument(
41
+ "LHS",
42
+ metavar="lhs",
43
+ type=str,
44
+ help="Path to the JSON file which has the LHS JSON schema",
45
+ )
46
+ parser.add_argument(
47
+ "RHS",
48
+ metavar="rhs",
49
+ type=str,
50
+ help="Path to the JSON file which has the RHS JSON schema",
51
+ )
52
+
53
+ args = parser.parse_args()
54
+ if (args.max_work is not None or args.timeout_ms is not None) and not args.endeavor:
55
+ parser.error("--max-work and --timeout-ms require --endeavor")
56
+ s1_file_path = args.LHS
57
+ s2_file_path = args.RHS
58
+
59
+ s1 = load_json_file(s1_file_path, "LHS file:")
60
+ s2 = load_json_file(s2_file_path, "RHS file:")
61
+ proof_options = None
62
+ if args.endeavor:
63
+ proof_options = ProofOptions(
64
+ endeavor=args.endeavor,
65
+ budgets=ProofBudgets(
66
+ max_work=4096 if args.max_work is None else args.max_work,
67
+ timeout_ms=1000 if args.timeout_ms is None else args.timeout_ms,
68
+ ),
69
+ )
70
+
71
+ print("LHS <: RHS", is_subschema(s1, s2, proof_options=proof_options))
72
+
73
+
74
+ if __name__ == "__main__":
75
+ main()