jsonschema-path 0.4.2__tar.gz → 0.4.3__tar.gz
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.
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/PKG-INFO +18 -2
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/README.rst +17 -1
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/jsonschema_path/__init__.py +1 -1
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/jsonschema_path/accessors.py +59 -6
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/jsonschema_path/paths.py +21 -3
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/pyproject.toml +2 -2
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/LICENSE +0 -0
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/jsonschema_path/handlers/__init__.py +0 -0
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/jsonschema_path/handlers/file.py +0 -0
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/jsonschema_path/handlers/protocols.py +0 -0
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/jsonschema_path/handlers/requests.py +0 -0
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/jsonschema_path/handlers/urllib.py +0 -0
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/jsonschema_path/handlers/utils.py +0 -0
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/jsonschema_path/loaders.py +0 -0
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/jsonschema_path/py.typed +0 -0
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/jsonschema_path/readers.py +0 -0
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/jsonschema_path/retrievers.py +0 -0
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/jsonschema_path/typing.py +0 -0
- {jsonschema_path-0.4.2 → jsonschema_path-0.4.3}/jsonschema_path/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jsonschema-path
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: JSONSchema Spec with object-oriented paths
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -120,6 +120,21 @@ Usage
|
|
|
120
120
|
{'type': 'string', 'default': '1.0'}
|
|
121
121
|
|
|
122
122
|
|
|
123
|
+
Resolved cache
|
|
124
|
+
##############
|
|
125
|
+
|
|
126
|
+
The resolved-path cache is intended for repeated path lookups and may significantly improve
|
|
127
|
+
``read_value``/membership hot paths. Cache entries are invalidated when the
|
|
128
|
+
resolver registry evolves during reference resolution.
|
|
129
|
+
|
|
130
|
+
This cache is optional and disabled by default
|
|
131
|
+
(``resolved_cache_maxsize=0``). You can enable it when creating paths or
|
|
132
|
+
accessors, for example:
|
|
133
|
+
|
|
134
|
+
.. code-block:: python
|
|
135
|
+
|
|
136
|
+
>>> path = SchemaPath.from_dict(d, resolved_cache_maxsize=64)
|
|
137
|
+
|
|
123
138
|
Benchmarks
|
|
124
139
|
##########
|
|
125
140
|
|
|
@@ -140,12 +155,13 @@ For a quick smoke run:
|
|
|
140
155
|
poetry run python -m tests.benchmarks.bench_parse --output reports/bench-parse.quick.json --quick
|
|
141
156
|
poetry run python -m tests.benchmarks.bench_lookup --output reports/bench-lookup.quick.json --quick
|
|
142
157
|
|
|
143
|
-
You can also control repeats/warmup via env vars:
|
|
158
|
+
You can also control repeats/warmup and resolved cache maxsize via env vars:
|
|
144
159
|
|
|
145
160
|
.. code-block:: console
|
|
146
161
|
|
|
147
162
|
export JSONSCHEMA_PATH_BENCH_REPEATS=5
|
|
148
163
|
export JSONSCHEMA_PATH_BENCH_WARMUP=1
|
|
164
|
+
export JSONSCHEMA_PATH_BENCH_RESOLVED_CACHE_MAXSIZE=64
|
|
149
165
|
|
|
150
166
|
Compare two results:
|
|
151
167
|
|
|
@@ -90,6 +90,21 @@ Usage
|
|
|
90
90
|
{'type': 'string', 'default': '1.0'}
|
|
91
91
|
|
|
92
92
|
|
|
93
|
+
Resolved cache
|
|
94
|
+
##############
|
|
95
|
+
|
|
96
|
+
The resolved-path cache is intended for repeated path lookups and may significantly improve
|
|
97
|
+
``read_value``/membership hot paths. Cache entries are invalidated when the
|
|
98
|
+
resolver registry evolves during reference resolution.
|
|
99
|
+
|
|
100
|
+
This cache is optional and disabled by default
|
|
101
|
+
(``resolved_cache_maxsize=0``). You can enable it when creating paths or
|
|
102
|
+
accessors, for example:
|
|
103
|
+
|
|
104
|
+
.. code-block:: python
|
|
105
|
+
|
|
106
|
+
>>> path = SchemaPath.from_dict(d, resolved_cache_maxsize=64)
|
|
107
|
+
|
|
93
108
|
Benchmarks
|
|
94
109
|
##########
|
|
95
110
|
|
|
@@ -110,12 +125,13 @@ For a quick smoke run:
|
|
|
110
125
|
poetry run python -m tests.benchmarks.bench_parse --output reports/bench-parse.quick.json --quick
|
|
111
126
|
poetry run python -m tests.benchmarks.bench_lookup --output reports/bench-lookup.quick.json --quick
|
|
112
127
|
|
|
113
|
-
You can also control repeats/warmup via env vars:
|
|
128
|
+
You can also control repeats/warmup and resolved cache maxsize via env vars:
|
|
114
129
|
|
|
115
130
|
.. code-block:: console
|
|
116
131
|
|
|
117
132
|
export JSONSCHEMA_PATH_BENCH_REPEATS=5
|
|
118
133
|
export JSONSCHEMA_PATH_BENCH_WARMUP=1
|
|
134
|
+
export JSONSCHEMA_PATH_BENCH_RESOLVED_CACHE_MAXSIZE=64
|
|
119
135
|
|
|
120
136
|
Compare two results:
|
|
121
137
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""JSONSchema spec accessors module."""
|
|
2
2
|
|
|
3
|
+
from collections import OrderedDict
|
|
3
4
|
from collections.abc import Hashable
|
|
4
5
|
from collections.abc import Iterator
|
|
5
6
|
from collections.abc import Sequence
|
|
@@ -27,9 +28,23 @@ from jsonschema_path.utils import is_ref
|
|
|
27
28
|
class SchemaAccessor(LookupAccessor):
|
|
28
29
|
_resolver_refs: dict[int, Resolver[Schema] | None] = {}
|
|
29
30
|
|
|
30
|
-
def __init__(
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
schema: Schema,
|
|
34
|
+
resolver: Resolver[Schema],
|
|
35
|
+
resolved_cache_maxsize: int = 0,
|
|
36
|
+
):
|
|
37
|
+
if resolved_cache_maxsize < 0:
|
|
38
|
+
raise ValueError("resolved_cache_maxsize must be >= 0")
|
|
39
|
+
|
|
31
40
|
super().__init__(cast(LookupNode, schema))
|
|
32
41
|
self.resolver = resolver
|
|
42
|
+
self._resolved_cache_maxsize = resolved_cache_maxsize
|
|
43
|
+
self._resolved_cache: OrderedDict[
|
|
44
|
+
tuple[tuple[LookupKey, ...], int],
|
|
45
|
+
Resolved[LookupNode],
|
|
46
|
+
] = OrderedDict()
|
|
47
|
+
self._resolver_version = 0
|
|
33
48
|
|
|
34
49
|
self._resolver_refs[id(schema)] = resolver
|
|
35
50
|
|
|
@@ -40,6 +55,7 @@ class SchemaAccessor(LookupAccessor):
|
|
|
40
55
|
specification: Specification[Schema] = DRAFT202012,
|
|
41
56
|
base_uri: str = "",
|
|
42
57
|
handlers: ResolverHandlers | None = None,
|
|
58
|
+
resolved_cache_maxsize: int = 0,
|
|
43
59
|
) -> "SchemaAccessor":
|
|
44
60
|
if handlers is None:
|
|
45
61
|
handlers = default_handlers
|
|
@@ -50,7 +66,11 @@ class SchemaAccessor(LookupAccessor):
|
|
|
50
66
|
)
|
|
51
67
|
registry = registry.with_resource(base_uri, base_resource)
|
|
52
68
|
resolver = registry.resolver(base_uri=base_uri)
|
|
53
|
-
return cls(
|
|
69
|
+
return cls(
|
|
70
|
+
schema,
|
|
71
|
+
resolver,
|
|
72
|
+
resolved_cache_maxsize=resolved_cache_maxsize,
|
|
73
|
+
)
|
|
54
74
|
|
|
55
75
|
def __getitem__(self, parts: Sequence[LookupKey]) -> LookupNode:
|
|
56
76
|
resolved = self.get_resolved(parts)
|
|
@@ -143,13 +163,46 @@ class SchemaAccessor(LookupAccessor):
|
|
|
143
163
|
pass
|
|
144
164
|
|
|
145
165
|
def get_resolved(self, parts: Sequence[LookupKey]) -> Resolved[LookupNode]:
|
|
166
|
+
cache_key = self._resolved_cache_key(parts)
|
|
167
|
+
if cache_key is not None:
|
|
168
|
+
cached_resolved = self._resolved_cache.get(cache_key)
|
|
169
|
+
if cached_resolved is not None:
|
|
170
|
+
self._resolved_cache.move_to_end(cache_key)
|
|
171
|
+
return cached_resolved
|
|
172
|
+
|
|
146
173
|
resolved = self._get_resolved(self.node, parts, resolver=self.resolver)
|
|
147
|
-
|
|
148
|
-
self.resolver.
|
|
149
|
-
|
|
150
|
-
|
|
174
|
+
if resolved.resolver._registry is not self.resolver._registry:
|
|
175
|
+
self.resolver = self.resolver._evolve(
|
|
176
|
+
self.resolver._base_uri,
|
|
177
|
+
registry=resolved.resolver._registry,
|
|
178
|
+
)
|
|
179
|
+
self._resolver_version += 1
|
|
180
|
+
self._resolved_cache.clear()
|
|
181
|
+
|
|
182
|
+
cache_key = self._resolved_cache_key(parts)
|
|
183
|
+
if cache_key is not None:
|
|
184
|
+
self._resolved_cache[cache_key] = resolved
|
|
185
|
+
self._resolved_cache.move_to_end(cache_key)
|
|
186
|
+
if len(self._resolved_cache) > self._resolved_cache_maxsize:
|
|
187
|
+
self._resolved_cache.popitem(last=False)
|
|
188
|
+
|
|
151
189
|
return resolved
|
|
152
190
|
|
|
191
|
+
def _resolved_cache_key(
|
|
192
|
+
self,
|
|
193
|
+
parts: Sequence[LookupKey],
|
|
194
|
+
) -> tuple[tuple[LookupKey, ...], int] | None:
|
|
195
|
+
if self._resolved_cache_maxsize <= 0:
|
|
196
|
+
return None
|
|
197
|
+
|
|
198
|
+
parts_tuple = tuple(parts)
|
|
199
|
+
try:
|
|
200
|
+
hash(parts_tuple)
|
|
201
|
+
except TypeError:
|
|
202
|
+
return None
|
|
203
|
+
|
|
204
|
+
return (parts_tuple, self._resolver_version)
|
|
205
|
+
|
|
153
206
|
@classmethod
|
|
154
207
|
def _get_resolved(
|
|
155
208
|
cls,
|
|
@@ -105,6 +105,7 @@ class SchemaPath(AccessorPath[SchemaNode, SchemaKey, SchemaValue]):
|
|
|
105
105
|
specification: Specification[Schema] = DRAFT202012,
|
|
106
106
|
base_uri: str = "",
|
|
107
107
|
handlers: ResolverHandlers = default_handlers,
|
|
108
|
+
resolved_cache_maxsize: int = 0,
|
|
108
109
|
spec_url: str | None = None,
|
|
109
110
|
ref_resolver_handlers: ResolverHandlers | None = None,
|
|
110
111
|
) -> TSchemaPath:
|
|
@@ -127,6 +128,7 @@ class SchemaPath(AccessorPath[SchemaNode, SchemaKey, SchemaValue]):
|
|
|
127
128
|
specification=specification,
|
|
128
129
|
base_uri=base_uri,
|
|
129
130
|
handlers=handlers,
|
|
131
|
+
resolved_cache_maxsize=resolved_cache_maxsize,
|
|
130
132
|
)
|
|
131
133
|
|
|
132
134
|
return cls(accessor, *args, separator=separator)
|
|
@@ -135,19 +137,29 @@ class SchemaPath(AccessorPath[SchemaNode, SchemaKey, SchemaValue]):
|
|
|
135
137
|
def from_path(
|
|
136
138
|
cls: type[TSchemaPath],
|
|
137
139
|
path: Path,
|
|
140
|
+
resolved_cache_maxsize: int = 0,
|
|
138
141
|
) -> TSchemaPath:
|
|
139
142
|
reader = PathReader(path)
|
|
140
143
|
data, base_uri = reader.read()
|
|
141
|
-
return cls.from_dict(
|
|
144
|
+
return cls.from_dict(
|
|
145
|
+
data,
|
|
146
|
+
base_uri=base_uri,
|
|
147
|
+
resolved_cache_maxsize=resolved_cache_maxsize,
|
|
148
|
+
)
|
|
142
149
|
|
|
143
150
|
@classmethod
|
|
144
151
|
def from_file_path(
|
|
145
152
|
cls: type[TSchemaPath],
|
|
146
153
|
file_path: str,
|
|
154
|
+
resolved_cache_maxsize: int = 0,
|
|
147
155
|
) -> TSchemaPath:
|
|
148
156
|
reader = FilePathReader(file_path)
|
|
149
157
|
data, base_uri = reader.read()
|
|
150
|
-
return cls.from_dict(
|
|
158
|
+
return cls.from_dict(
|
|
159
|
+
data,
|
|
160
|
+
base_uri=base_uri,
|
|
161
|
+
resolved_cache_maxsize=resolved_cache_maxsize,
|
|
162
|
+
)
|
|
151
163
|
|
|
152
164
|
@classmethod
|
|
153
165
|
def from_file(
|
|
@@ -155,10 +167,16 @@ class SchemaPath(AccessorPath[SchemaNode, SchemaKey, SchemaValue]):
|
|
|
155
167
|
fileobj: SupportsRead,
|
|
156
168
|
base_uri: str = "",
|
|
157
169
|
spec_url: str | None = None,
|
|
170
|
+
resolved_cache_maxsize: int = 0,
|
|
158
171
|
) -> TSchemaPath:
|
|
159
172
|
reader = FileReader(fileobj)
|
|
160
173
|
data, _ = reader.read()
|
|
161
|
-
return cls.from_dict(
|
|
174
|
+
return cls.from_dict(
|
|
175
|
+
data,
|
|
176
|
+
base_uri=base_uri,
|
|
177
|
+
spec_url=spec_url,
|
|
178
|
+
resolved_cache_maxsize=resolved_cache_maxsize,
|
|
179
|
+
)
|
|
162
180
|
|
|
163
181
|
def str_keys(self) -> Sequence[str]:
|
|
164
182
|
keys = list(self.keys())
|
|
@@ -19,7 +19,7 @@ ignore_missing_imports = true
|
|
|
19
19
|
|
|
20
20
|
[tool.poetry]
|
|
21
21
|
name = "jsonschema-path"
|
|
22
|
-
version = "0.4.
|
|
22
|
+
version = "0.4.3"
|
|
23
23
|
description = "JSONSchema Spec with object-oriented paths"
|
|
24
24
|
authors = ["Artur Maciag <maciag.artur@gmail.com>"]
|
|
25
25
|
license = "Apache-2.0"
|
|
@@ -93,7 +93,7 @@ message_template = "Version {new_version}"
|
|
|
93
93
|
tag_template = "{new_version}"
|
|
94
94
|
|
|
95
95
|
[tool.tbump.version]
|
|
96
|
-
current = "0.4.
|
|
96
|
+
current = "0.4.3"
|
|
97
97
|
regex = '''
|
|
98
98
|
(?P<major>\d+)
|
|
99
99
|
\.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|