Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ coverage.xml
.pytest_cache/
cov.xml

# Profiling data
mprofile*

# Translations
*.mo
*.pot
Expand Down Expand Up @@ -81,6 +84,7 @@ ENV/
# Dev utils
dev.py
profile_.py
tests/test_dev.py

# Test fixtures
comparison_regression_suite.yaml
Expand Down
10 changes: 9 additions & 1 deletion jsonpath_rfc9535/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,15 @@ def __init__(
def path(self) -> str:
"""Return the normalized path to this node."""
return "$" + "".join(
(f"['{p}']" if isinstance(p, str) else f"[{p}]" for p in self.location)
f"['{p}']" if isinstance(p, str) else f"[{p}]" for p in self.location
)

def new_child(self, value: object, key: Union[int, str]) -> JSONPathNode:
"""Return a new node using this node's location."""
return JSONPathNode(
value=value,
location=self.location + (key,),
root=self.root,
)

def __str__(self) -> str:
Expand Down
26 changes: 5 additions & 21 deletions jsonpath_rfc9535/segments.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
from typing import Tuple

from .exceptions import JSONPathRecursionError
from .node import JSONPathNode

if TYPE_CHECKING:
from .environment import JSONPathEnvironment
from .node import JSONPathNode
from .selectors import JSONPathSelector
from .tokens import Token

Expand Down Expand Up @@ -88,20 +88,12 @@ def _visit(self, node: JSONPathNode, depth: int = 1) -> Iterable[JSONPathNode]:
if isinstance(node.value, dict):
for name, val in node.value.items():
if isinstance(val, (dict, list)):
_node = JSONPathNode(
value=val,
location=node.location + (name,),
root=node.root,
)
_node = node.new_child(val, name)
yield from self._visit(_node, depth + 1)
elif isinstance(node.value, list):
for i, element in enumerate(node.value):
if isinstance(element, (dict, list)):
_node = JSONPathNode(
value=element,
location=node.location + (i,),
root=node.root,
)
_node = node.new_child(element, i)
yield from self._visit(_node, depth + 1)

def _nondeterministic_visit(
Expand Down Expand Up @@ -175,15 +167,7 @@ def _nondeterministic_children(node: JSONPathNode) -> Iterable[JSONPathNode]:
items = list(node.value.items())
random.shuffle(items)
for name, val in items:
yield JSONPathNode(
value=val,
location=node.location + (name,),
root=node.root,
)
yield node.new_child(val, name)
elif isinstance(node.value, list):
for i, element in enumerate(node.value):
yield JSONPathNode(
value=element,
location=node.location + (i,),
root=node.root,
)
yield node.new_child(element, i)
49 changes: 8 additions & 41 deletions jsonpath_rfc9535/selectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
from .exceptions import JSONPathIndexError
from .exceptions import JSONPathTypeError
from .filter_expressions import FilterContext
from .node import JSONPathNode

if TYPE_CHECKING:
from .environment import JSONPathEnvironment
from .filter_expressions import FilterExpression
from .node import JSONPathNode
from .tokens import Token


Expand Down Expand Up @@ -76,11 +76,7 @@ def resolve(self, node: JSONPathNode) -> Iterable[JSONPathNode]:
"""Select a value from a dict/object by its property/key."""
if isinstance(node.value, dict):
with suppress(KeyError):
yield JSONPathNode(
value=node.value[self.name],
location=node.location + (self.name,),
root=node.root,
)
yield node.new_child(node.value[self.name], self.name)


class IndexSelector(JSONPathSelector):
Expand Down Expand Up @@ -125,12 +121,7 @@ def resolve(self, node: JSONPathNode) -> Iterable[JSONPathNode]:
if isinstance(node.value, list):
norm_index = self._normalized_index(node.value)
with suppress(IndexError):
_node = JSONPathNode(
value=node.value[self.index],
location=node.location + (norm_index,),
root=node.root,
)
yield _node
yield node.new_child(node.value[self.index], norm_index)


class SliceSelector(JSONPathSelector):
Expand Down Expand Up @@ -185,13 +176,7 @@ def resolve(self, node: JSONPathNode) -> Iterable[JSONPathNode]:
idx = self.slice.start or 0
step = self.slice.step or 1
for element in node.value[self.slice]:
norm_index = self._normalized_index(node.value, idx)
_node = JSONPathNode(
value=element,
location=node.location + (norm_index,),
root=node.root,
)
yield _node
yield node.new_child(element, self._normalized_index(node.value, idx))
idx += step


Expand Down Expand Up @@ -221,21 +206,11 @@ def resolve(self, node: JSONPathNode) -> Iterable[JSONPathNode]:
members = node.value.items()

for name, val in members:
_node = JSONPathNode(
value=val,
location=node.location + (name,),
root=node.root,
)
yield _node
yield node.new_child(val, name)

elif isinstance(node.value, list):
for i, element in enumerate(node.value):
_node = JSONPathNode(
value=element,
location=node.location + (i,),
root=node.root,
)
yield _node
yield node.new_child(element, i)


class Filter(JSONPathSelector):
Expand Down Expand Up @@ -284,11 +259,7 @@ def resolve(self, node: JSONPathNode) -> Iterable[JSONPathNode]: # noqa: PLR091
)
try:
if self.expression.evaluate(context):
yield JSONPathNode(
value=val,
location=node.location + (name,),
root=node.root,
)
yield node.new_child(val, name)
except JSONPathTypeError as err:
if not err.token:
err.token = self.token
Expand All @@ -303,11 +274,7 @@ def resolve(self, node: JSONPathNode) -> Iterable[JSONPathNode]: # noqa: PLR091
)
try:
if self.expression.evaluate(context):
yield JSONPathNode(
value=element,
location=node.location + (i,),
root=node.root,
)
yield node.new_child(element, i)
except JSONPathTypeError as err:
if not err.token:
err.token = self.token
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cts_nondeterminism.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def _result_repr(rv: List[object]) -> Tuple[str, ...]:

env = MockEnv()

# Repeat enough times to has high probability that we've covered all
# Repeat enough times so as to have high probability that we've covered all
# valid permutations.
results = {
_result_repr(env.find(case.selector, case.document).values())
Expand Down