Skip to content

Commit 001847a

Browse files
committed
Redfeine take to leave the receiver in a useful state.
1 parent 2d9e952 commit 001847a

File tree

3 files changed

+45
-30
lines changed

3 files changed

+45
-30
lines changed

docs/query.md

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ print(values[1])
4545

4646
The following `Query` methods all return `self` (the same `Query` instance), so method calls can be chained to further manipulate the underlying iterator.
4747

48-
| Method | Aliases | Description |
49-
| --------------- | ----------------------- | -------------------------------------------------- |
50-
| `skip(n: int)` | `drop` | Drop up to _n_ matches from the iterator. |
51-
| `limit(n: int)` | `head`, `take`, `first` | Yield at most _n_ matches from the iterator. |
52-
| `tail(n: int)` | `last` | Drop matches from the iterator up to the last _n_. |
48+
| Method | Aliases | Description |
49+
| --------------- | --------------- | -------------------------------------------------- |
50+
| `skip(n: int)` | `drop` | Drop up to _n_ matches from the iterator. |
51+
| `limit(n: int)` | `head`, `first` | Yield at most _n_ matches from the iterator. |
52+
| `tail(n: int)` | `last` | Drop matches from the iterator up to the last _n_. |
5353

5454
## Terminal methods
5555

@@ -64,6 +64,22 @@ These are terminal methods of the `Query` class. They can not be chained.
6464
| `first_one()` | `one` | Return the first `JSONPathMatch`, or `None` if there were no matches. |
6565
| `last_one()` | | Return the last `JSONPathMatch`, or `None` if there were no matches. |
6666

67+
## Take
68+
69+
[`Query.take(self, n: int)`](api.md#jsonpath.Query.take) returns a new `Query` instance, iterating over the next _n_ matches. It leaves the existing query in a safe state, ready to resume iteration of remaining matches.
70+
71+
```python
72+
from jsonpath import query
73+
74+
it = query("$.some.*", {"some": [0, 1, 2, 3]})
75+
76+
for match in it.take(2):
77+
print(match.value) # 0, 1
78+
79+
for value in it.values():
80+
print(value) # 2, 3
81+
```
82+
6783
## Tee
6884

6985
And finally there's `tee()`, which creates multiple independent queries from one query iterator. It is not safe to use the initial `Query` instance after calling `tee()`.

jsonpath/fluent_api.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from typing import TYPE_CHECKING
77
from typing import Iterable
88
from typing import Iterator
9+
from typing import List
910
from typing import Optional
1011
from typing import Tuple
1112

@@ -33,47 +34,37 @@ def __init__(self, it: Iterable[JSONPathMatch]) -> None:
3334
def __iter__(self) -> Iterator[JSONPathMatch]:
3435
return self._it
3536

36-
def take(self, n: int) -> Query:
37+
def limit(self, n: int) -> Query:
3738
"""Limit the query iterator to at most _n_ matches.
3839
3940
Raises:
4041
ValueError: If _n_ < 0.
4142
"""
4243
if n < 0:
43-
raise ValueError("can't take a negative number of matches")
44+
raise ValueError("can't limit by a negative number of matches")
4445

4546
self._it = itertools.islice(self._it, n)
4647
return self
4748

48-
def limit(self, n: int) -> Query:
49-
"""Limit the query iterator to at most _n_ matches.
50-
51-
`limit()` is an alias of `take()`.
52-
53-
Raises:
54-
ValueError: If _n_ < 0.
55-
"""
56-
return self.take(n)
57-
5849
def head(self, n: int) -> Query:
5950
"""Limit the query iterator to at most the first _n_ matches.
6051
61-
`head()` is an alias for `take()`.
52+
`head()` is an alias for `limit()`.
6253
6354
Raises:
6455
ValueError: If _n_ < 0.
6556
"""
66-
return self.take(n)
57+
return self.limit(n)
6758

6859
def first(self, n: int) -> Query:
6960
"""Limit the query iterator to at most the first _n_ matches.
7061
71-
`first()` is an alias for `take()`.
62+
`first()` is an alias for `limit()`.
7263
7364
Raises:
7465
ValueError: If _n_ < 0.
7566
"""
76-
return self.take(n)
67+
return self.limit(n)
7768

7869
def drop(self, n: int) -> Query:
7970
"""Skip up to _n_ matches from the query iterator.
@@ -162,3 +153,10 @@ def tee(self, n: int = 2) -> Tuple[Query, ...]:
162153
It is not safe to use a `Query` instance after calling `tee()`.
163154
"""
164155
return tuple(Query(it) for it in itertools.tee(self._it, n))
156+
157+
def take(self, n: int) -> Query:
158+
"""Return a new query iterating over the next _n_ matches.
159+
160+
It is safe to continue using this query after calling take.
161+
"""
162+
return Query(list(itertools.islice(self._it, n)))

tests/test_fluent_api.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -123,18 +123,10 @@ def test_query_limit_all() -> None:
123123

124124
def test_query_limit_negative() -> None:
125125
"""Test that we get an exception if limit is negative."""
126-
with pytest.raises(ValueError, match="can't take a negative number of matches"):
126+
with pytest.raises(ValueError, match="can't limit by a negative number of matches"):
127127
query("$.some.*", {"some": [0, 1, 2, 3]}).limit(-1)
128128

129129

130-
def test_query_take() -> None:
131-
"""Test that we can limit the number of matches with `take`."""
132-
it = query("$.some.*", {"some": [0, 1, 2, 3]}).take(2)
133-
matches = list(it)
134-
assert len(matches) == 2 # noqa: PLR2004
135-
assert [m.obj for m in matches] == [0, 1]
136-
137-
138130
def test_query_head() -> None:
139131
"""Test that we can limit the number of matches with `head`."""
140132
it = query("$.some.*", {"some": [0, 1, 2, 3]}).head(2)
@@ -248,3 +240,12 @@ def test_query_pointers() -> None:
248240
pointers = list(query("$.some.*", {"some": [0, 1, 2, 3]}).pointers())
249241
assert len(pointers) == 4 # noqa: PLR2004
250242
assert pointers[0] == JSONPointer("/some/0")
243+
244+
245+
def test_query_take() -> None:
246+
"""Test that we can take matches from a query iterable."""
247+
it = query("$.some.*", {"some": [0, 1, 2, 3]})
248+
head = list(it.take(2).values())
249+
assert len(head) == 2 # noqa: PLR2004
250+
assert head == [0, 1]
251+
assert list(it.values()) == [2, 3]

0 commit comments

Comments
 (0)