Skip to content

Commit 297c442

Browse files
committed
Refactor
1 parent c28ff63 commit 297c442

File tree

1 file changed

+54
-34
lines changed

1 file changed

+54
-34
lines changed

jsonpath/fluent_api.py

Lines changed: 54 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,16 @@
2828

2929

3030
class Projection(Enum):
31-
"""Projection style."""
31+
"""Projection style used by `Query.select()`."""
3232

3333
RELATIVE = auto()
3434
ROOT = auto()
3535
FLAT = auto()
3636

3737

38+
EMPTY = object()
39+
40+
3841
class Query:
3942
"""A fluent API for managing `JSONPathMatch` iterators.
4043
@@ -157,40 +160,57 @@ def select(
157160
Returns an iterable of objects built from selecting _expressions_ relative to
158161
each match from the current query.
159162
"""
160-
for m in self._it:
161-
if isinstance(m.obj, Sequence) or projection == Projection.FLAT:
162-
obj: Union[List[Any], Dict[str, Any]] = []
163-
elif isinstance(m.obj, Mapping):
164-
obj = {}
163+
return filter(
164+
bool,
165+
(self._select(m, expressions, projection) for m in self._it),
166+
)
167+
168+
def _select(
169+
self,
170+
match: JSONPathMatch,
171+
expressions: Tuple[str, ...],
172+
projection: Projection,
173+
) -> object:
174+
if isinstance(match.obj, Sequence) or projection == Projection.FLAT:
175+
obj: Union[List[Any], Dict[str, Any]] = []
176+
elif isinstance(match.obj, Mapping):
177+
obj = {}
178+
else:
179+
return None
180+
181+
patch = JSONPatch()
182+
183+
for expr in expressions:
184+
self._patch(match, expr, patch, projection)
185+
186+
return patch.apply(obj)
187+
188+
def _patch(
189+
self,
190+
match: JSONPathMatch,
191+
expr: str,
192+
patch: JSONPatch,
193+
projection: Projection,
194+
) -> None:
195+
root_pointer = match.pointer()
196+
197+
for rel_match in self._env.finditer(expr, match.obj): # type: ignore
198+
if projection == Projection.FLAT:
199+
patch.addap("/-", rel_match.obj)
200+
elif projection == Projection.ROOT:
201+
# Pointer string without a leading slash
202+
rel_pointer = "/".join(
203+
str(p).replace("~", "~0").replace("/", "~1")
204+
for p in rel_match.parts
205+
)
206+
pointer = root_pointer / rel_pointer
207+
_patch_parents(pointer.parent(), patch, match.obj) # type: ignore
208+
patch.addap(pointer, rel_match.obj)
165209
else:
166-
return
167-
168-
root_pointer = m.pointer()
169-
patch = JSONPatch()
170-
171-
for expr in expressions:
172-
for match in self._env.finditer(expr, m.obj): # type: ignore
173-
if projection == Projection.FLAT:
174-
patch.addap("/-", match.obj)
175-
elif projection == Projection.ROOT:
176-
# Pointer string without a leading slash
177-
rel_pointer = "/".join(
178-
str(p).replace("~", "~0").replace("/", "~1")
179-
for p in match.parts
180-
)
181-
_pointer = root_pointer / rel_pointer
182-
_patch_parents(_pointer.parent(), patch, m.obj) # type: ignore
183-
patch.addap(_pointer, match.obj)
184-
else:
185-
# Natural projection
186-
_pointer = match.pointer()
187-
_patch_parents(_pointer.parent(), patch, m.obj) # type: ignore
188-
patch.addap(_pointer, match.obj)
189-
190-
patch.apply(obj)
191-
192-
if obj:
193-
yield obj
210+
# Natural projection
211+
pointer = rel_match.pointer()
212+
_patch_parents(pointer.parent(), patch, match.obj) # type: ignore
213+
patch.addap(pointer, rel_match.obj)
194214

195215
def first_one(self) -> Optional[JSONPathMatch]:
196216
"""Return the first `JSONPathMatch` or `None` if there were no matches."""

0 commit comments

Comments
 (0)