Skip to content

Commit 8db9143

Browse files
committed
More fluent API test cases.
1 parent b2c114a commit 8db9143

File tree

2 files changed

+98
-8
lines changed

2 files changed

+98
-8
lines changed

jsonpath/fluent_api.py

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def __iter__(self) -> Iterator[JSONPathMatch]:
3333
return self._it
3434

3535
def take(self, n: int) -> Query:
36-
"""Limit the result set to at most _n_ matches.
36+
"""Limit the query iterator to at most _n_ matches.
3737
3838
Raises:
3939
ValueError: If _n_ < 0.
@@ -45,7 +45,7 @@ def take(self, n: int) -> Query:
4545
return self
4646

4747
def limit(self, n: int) -> Query:
48-
"""Limit the result set to at most _n_ matches.
48+
"""Limit the query iterator to at most _n_ matches.
4949
5050
`limit()` is an alias of `take()`.
5151
@@ -55,7 +55,7 @@ def limit(self, n: int) -> Query:
5555
return self.take(n)
5656

5757
def head(self, n: int) -> Query:
58-
"""Take the first _n_ matches.
58+
"""Limit the query iterator to at most the first _n_ matches.
5959
6060
`head()` is an alias for `take()`.
6161
@@ -64,8 +64,18 @@ def head(self, n: int) -> Query:
6464
"""
6565
return self.take(n)
6666

67+
def first(self, n: int) -> Query:
68+
"""Limit the query iterator to at most the first _n_ matches.
69+
70+
`first()` is an alias for `take()`.
71+
72+
Raises:
73+
ValueError: If _n_ < 0.
74+
"""
75+
return self.take(n)
76+
6777
def drop(self, n: int) -> Query:
68-
"""Skip up to _n_ matches from the result set.
78+
"""Skip up to _n_ matches from the query iterator.
6979
7080
Raises:
7181
ValueError: If _n_ < 0.
@@ -79,15 +89,15 @@ def drop(self, n: int) -> Query:
7989
return self
8090

8191
def skip(self, n: int) -> Query:
82-
"""Skip up to _n_ matches from the result set.
92+
"""Skip up to _n_ matches from the query iterator.
8393
8494
Raises:
8595
ValueError: If _n_ < 0.
8696
"""
8797
return self.drop(n)
8898

8999
def tail(self, n: int) -> Query:
90-
"""Drop matches up to the last _n_ matches.
100+
"""Drop matches up to the last _n_ matches from the iterator.
91101
92102
Raises:
93103
ValueError: If _n_ < 0.
@@ -98,6 +108,16 @@ def tail(self, n: int) -> Query:
98108
self._it = iter(collections.deque(self._it, maxlen=n))
99109
return self
100110

111+
def last(self, n: int) -> Query:
112+
"""Drop up to the last _n_ matches from the iterator.
113+
114+
`last()` is an alias for `tail()`.
115+
116+
Raises:
117+
ValueError: If _n_ < 0.
118+
"""
119+
return self.tail(n)
120+
101121
def values(self) -> Iterable[object]:
102122
"""Return an iterable of objects associated with each match."""
103123
return (m.obj for m in self._it)
@@ -110,14 +130,21 @@ def items(self) -> Iterable[Tuple[str, object]]:
110130
"""Return an iterable of (object, normalized path) tuples for each match."""
111131
return ((m.path, m.obj) for m in self._it)
112132

113-
def first(self) -> Optional[JSONPathMatch]:
133+
def first_one(self) -> Optional[JSONPathMatch]:
114134
"""Return the first `JSONPathMatch` or `None` if there were no matches."""
115135
try:
116136
return next(self._it)
117137
except StopIteration:
118138
return None
119139

120-
def last(self) -> Optional[JSONPathMatch]:
140+
def one(self) -> Optional[JSONPathMatch]:
141+
"""Return the first `JSONPathMatch` or `None` if there were no matches.
142+
143+
`one()` is an alias for `first_one()`.
144+
"""
145+
return self.first_one()
146+
147+
def last_one(self) -> Optional[JSONPathMatch]:
121148
"""Return the last `JSONPathMatch` or `None` if there were no matches."""
122149
try:
123150
return next(iter(self.tail(1)))

tests/test_fluent_api.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Test cases for the fluent API."""
22
import pytest
33

4+
from jsonpath import JSONPathMatch
45
from jsonpath import query
56

67

@@ -141,6 +142,14 @@ def test_query_head() -> None:
141142
assert [m.obj for m in matches] == [0, 1]
142143

143144

145+
def test_query_first() -> None:
146+
"""Test that we can limit the number of matches with `first`."""
147+
it = query("$.some.*", {"some": [0, 1, 2, 3]}).first(2)
148+
matches = list(it)
149+
assert len(matches) == 2 # noqa: PLR2004
150+
assert [m.obj for m in matches] == [0, 1]
151+
152+
144153
def test_query_tail() -> None:
145154
"""Test that we can get the last _n_ matches."""
146155
it = query("$.some.*", {"some": [0, 1, 2, 3]}).tail(2)
@@ -177,3 +186,57 @@ def test_query_tail_negative() -> None:
177186
"""Test that we get an exception if tail is given a negative integer."""
178187
with pytest.raises(ValueError, match="can't select a negative number of matches"):
179188
query("$.some.*", {"some": [0, 1, 2, 3]}).tail(-1)
189+
190+
191+
def test_query_last() -> None:
192+
"""Test that we can get the last _n_ matches with `last`."""
193+
it = query("$.some.*", {"some": [0, 1, 2, 3]}).last(2)
194+
matches = list(it)
195+
assert len(matches) == 2 # noqa: PLR2004
196+
assert [m.obj for m in matches] == [2, 3]
197+
198+
199+
def test_query_first_one() -> None:
200+
"""Test that we can get the first match from a query iterator."""
201+
maybe_match = query("$.some.*", {"some": [0, 1, 2, 3]}).first_one()
202+
assert isinstance(maybe_match, JSONPathMatch)
203+
assert maybe_match.value == 0
204+
205+
206+
def test_query_first_one_of_empty_iterator() -> None:
207+
"""Test that `first_one` returns `None` if the iterator is empty."""
208+
maybe_match = query("$.nosuchthing.*", {"some": [0, 1, 2, 3]}).first_one()
209+
assert maybe_match is None
210+
211+
212+
def test_query_one() -> None:
213+
"""Test that we can get the first match from a query iterator with `one`."""
214+
maybe_match = query("$.some.*", {"some": [0, 1, 2, 3]}).one()
215+
assert isinstance(maybe_match, JSONPathMatch)
216+
assert maybe_match.value == 0
217+
218+
219+
def test_query_last_one() -> None:
220+
"""Test that we can get the last match from a query iterator."""
221+
maybe_match = query("$.some.*", {"some": [0, 1, 2, 3]}).last_one()
222+
assert isinstance(maybe_match, JSONPathMatch)
223+
assert maybe_match.value == 3 # noqa: PLR2004
224+
225+
226+
def test_query_last_of_empty_iterator() -> None:
227+
"""Test that `last_one` returns `None` if the iterator is empty."""
228+
maybe_match = query("$.nosuchthing.*", {"some": [0, 1, 2, 3]}).last_one()
229+
assert maybe_match is None
230+
231+
232+
def test_query_tee() -> None:
233+
"""Test that we can tee a query iterator."""
234+
it1, it2 = query("$.some.*", {"some": [0, 1, 2, 3]}).tee()
235+
236+
rv1 = it1.skip(1).one()
237+
assert rv1 is not None
238+
assert rv1.value == 1
239+
240+
rv2 = it2.skip(2).one()
241+
assert rv2 is not None
242+
assert rv2.value == 2 # noqa: PLR2004

0 commit comments

Comments
 (0)