Skip to content

Commit b1367d4

Browse files
committed
Node locations as persistent linked lists
1 parent c36c0af commit b1367d4

File tree

5 files changed

+53
-16
lines changed

5 files changed

+53
-16
lines changed

jsonpath_rfc9535/location.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""JSONPath node locations as persistent linked lists."""
2+
3+
from __future__ import annotations
4+
5+
from typing import Iterator
6+
from typing import Optional
7+
from typing import Union
8+
9+
10+
class Location:
11+
"""JSONPath node location."""
12+
13+
__slots__ = ("value", "next")
14+
15+
def __init__(
16+
self, value: Union[int, str, None], _next: Optional[Location] = None
17+
) -> None:
18+
self.value = value
19+
self.next = _next
20+
21+
def __iter__(self) -> Iterator[Union[int, str]]:
22+
if self.value is not None:
23+
yield self.value
24+
25+
_next = self.next
26+
while _next is not None:
27+
if _next.value is not None:
28+
yield _next.value
29+
_next = _next.next
30+
31+
def prepend(self, value: Union[int, str]) -> Location:
32+
"""Return a copy of this location with _value_ appended to the front."""
33+
return Location(value, self)

jsonpath_rfc9535/node.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
from typing import TYPE_CHECKING
66
from typing import List
77
from typing import Tuple
8-
from typing import Union
98

109
if TYPE_CHECKING:
1110
from .environment import JSONValue
11+
from .location import Location
1212

1313

1414
class JSONPathNode:
@@ -29,17 +29,20 @@ def __init__(
2929
self,
3030
*,
3131
value: object,
32-
location: Tuple[Union[int, str], ...],
32+
location: Location,
3333
root: JSONValue,
3434
) -> None:
3535
self.value: object = value
36-
self.location: Tuple[Union[int, str], ...] = location
36+
self.location: Location = location
3737
self.root = root
3838

3939
def path(self) -> str:
4040
"""Return the normalized path to this node."""
4141
return "$" + "".join(
42-
(f"['{p}']" if isinstance(p, str) else f"[{p}]" for p in self.location)
42+
(
43+
f"['{p}']" if isinstance(p, str) else f"[{p}]"
44+
for p in reversed(list(self.location))
45+
)
4346
)
4447

4548
def __str__(self) -> str:

jsonpath_rfc9535/query.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from typing import Optional
88
from typing import Tuple
99

10+
from .location import Location
1011
from .node import JSONPathNode
1112
from .node import JSONPathNodeList
1213
from .segments import JSONPathRecursiveDescentSegment
@@ -69,7 +70,7 @@ def finditer(
6970
nodes: Iterable[JSONPathNode] = [
7071
JSONPathNode(
7172
value=value,
72-
location=(),
73+
location=Location(None),
7374
root=value,
7475
)
7576
]

jsonpath_rfc9535/segments.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def _visit(self, node: JSONPathNode, depth: int = 1) -> Iterable[JSONPathNode]:
9090
if isinstance(val, (dict, list)):
9191
_node = JSONPathNode(
9292
value=val,
93-
location=node.location + (name,),
93+
location=node.location.prepend(name),
9494
root=node.root,
9595
)
9696
yield from self._visit(_node, depth + 1)
@@ -99,7 +99,7 @@ def _visit(self, node: JSONPathNode, depth: int = 1) -> Iterable[JSONPathNode]:
9999
if isinstance(element, (dict, list)):
100100
_node = JSONPathNode(
101101
value=element,
102-
location=node.location + (i,),
102+
location=node.location.prepend(i),
103103
root=node.root,
104104
)
105105
yield from self._visit(_node, depth + 1)
@@ -177,13 +177,13 @@ def _nondeterministic_children(node: JSONPathNode) -> Iterable[JSONPathNode]:
177177
for name, val in items:
178178
yield JSONPathNode(
179179
value=val,
180-
location=node.location + (name,),
180+
location=node.location.prepend(name),
181181
root=node.root,
182182
)
183183
elif isinstance(node.value, list):
184184
for i, element in enumerate(node.value):
185185
yield JSONPathNode(
186186
value=element,
187-
location=node.location + (i,),
187+
location=node.location.prepend(i),
188188
root=node.root,
189189
)

jsonpath_rfc9535/selectors.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def resolve(self, node: JSONPathNode) -> Iterable[JSONPathNode]:
7878
with suppress(KeyError):
7979
yield JSONPathNode(
8080
value=node.value[self.name],
81-
location=node.location + (self.name,),
81+
location=node.location.prepend(self.name),
8282
root=node.root,
8383
)
8484

@@ -127,7 +127,7 @@ def resolve(self, node: JSONPathNode) -> Iterable[JSONPathNode]:
127127
with suppress(IndexError):
128128
_node = JSONPathNode(
129129
value=node.value[self.index],
130-
location=node.location + (norm_index,),
130+
location=node.location.prepend(norm_index),
131131
root=node.root,
132132
)
133133
yield _node
@@ -188,7 +188,7 @@ def resolve(self, node: JSONPathNode) -> Iterable[JSONPathNode]:
188188
norm_index = self._normalized_index(node.value, idx)
189189
_node = JSONPathNode(
190190
value=element,
191-
location=node.location + (norm_index,),
191+
location=node.location.prepend(norm_index),
192192
root=node.root,
193193
)
194194
yield _node
@@ -223,7 +223,7 @@ def resolve(self, node: JSONPathNode) -> Iterable[JSONPathNode]:
223223
for name, val in members:
224224
_node = JSONPathNode(
225225
value=val,
226-
location=node.location + (name,),
226+
location=node.location.prepend(name),
227227
root=node.root,
228228
)
229229
yield _node
@@ -232,7 +232,7 @@ def resolve(self, node: JSONPathNode) -> Iterable[JSONPathNode]:
232232
for i, element in enumerate(node.value):
233233
_node = JSONPathNode(
234234
value=element,
235-
location=node.location + (i,),
235+
location=node.location.prepend(i),
236236
root=node.root,
237237
)
238238
yield _node
@@ -286,7 +286,7 @@ def resolve(self, node: JSONPathNode) -> Iterable[JSONPathNode]: # noqa: PLR091
286286
if self.expression.evaluate(context):
287287
yield JSONPathNode(
288288
value=val,
289-
location=node.location + (name,),
289+
location=node.location.prepend(name),
290290
root=node.root,
291291
)
292292
except JSONPathTypeError as err:
@@ -305,7 +305,7 @@ def resolve(self, node: JSONPathNode) -> Iterable[JSONPathNode]: # noqa: PLR091
305305
if self.expression.evaluate(context):
306306
yield JSONPathNode(
307307
value=element,
308-
location=node.location + (i,),
308+
location=node.location.prepend(i),
309309
root=node.root,
310310
)
311311
except JSONPathTypeError as err:

0 commit comments

Comments
 (0)