Skip to content

Commit 0caf855

Browse files
authored
fix: paginate backwards if starting_after == None (#1563)
This fixes the case where `starting_after` is present in `_retrieve_params` but is `None` so that the `auto_paging_iter` correctly paginates backwards. closes #1562
1 parent 40a8d7e commit 0caf855

File tree

2 files changed

+69
-4
lines changed

2 files changed

+69
-4
lines changed

stripe/_list_object.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ def _auto_paging_iter(self) -> Iterator[T]:
132132

133133
while True:
134134
if (
135-
"ending_before" in self._retrieve_params
136-
and "starting_after" not in self._retrieve_params
135+
self._retrieve_params.get("ending_before") is not None
136+
and self._retrieve_params.get("starting_after") is None
137137
):
138138
for item in reversed(page):
139139
yield item
@@ -151,8 +151,8 @@ async def _auto_paging_iter_async(self) -> AsyncIterator[T]:
151151

152152
while True:
153153
if (
154-
"ending_before" in self._retrieve_params
155-
and "starting_after" not in self._retrieve_params
154+
self._retrieve_params.get("ending_before") is not None
155+
and self._retrieve_params.get("starting_after") is None
156156
):
157157
for item in reversed(page):
158158
yield item

tests/api_resources/test_list_object.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,38 @@ def test_iter_reverse(self, http_client_mock):
323323

324324
assert seen == ["pm_126", "pm_125", "pm_124", "pm_123"]
325325

326+
def test_iter_reverse_none_starting_after(self, http_client_mock):
327+
"""Test that if `starting_after` is present in the retrieve params, but is
328+
`None`, that reverse pagination still occurs as expected.
329+
"""
330+
lo = stripe.ListObject.construct_from(
331+
self.pageable_model_response(["pm_125", "pm_126"], True), "mykey"
332+
)
333+
lo._retrieve_params = {
334+
"foo": "bar",
335+
"ending_before": "pm_127",
336+
"starting_after": None,
337+
}
338+
339+
http_client_mock.stub_request(
340+
"get",
341+
path="/v1/pageablemodels",
342+
query_string="ending_before=pm_125&foo=bar",
343+
rbody=json.dumps(
344+
self.pageable_model_response(["pm_123", "pm_124"], False)
345+
),
346+
)
347+
348+
seen = [item["id"] for item in lo.auto_paging_iter()]
349+
350+
http_client_mock.assert_requested(
351+
"get",
352+
path="/v1/pageablemodels",
353+
query_string="ending_before=pm_125&foo=bar",
354+
)
355+
356+
assert seen == ["pm_126", "pm_125", "pm_124", "pm_123"]
357+
326358
def test_class_method_two_pages(self, http_client_mock):
327359
http_client_mock.stub_request(
328360
"get",
@@ -566,3 +598,36 @@ async def test_iter_reverse(self, http_client_mock):
566598
)
567599

568600
assert seen == ["pm_126", "pm_125", "pm_124", "pm_123"]
601+
602+
@pytest.mark.anyio
603+
async def test_iter_reverse_none_starting_after(self, http_client_mock):
604+
"""Test that if `starting_after` is present in the retrieve params, but is
605+
`None`, that reverse pagination still occurs as expected.
606+
"""
607+
lo = stripe.ListObject.construct_from(
608+
self.pageable_model_response(["pm_125", "pm_126"], True), "mykey"
609+
)
610+
lo._retrieve_params = {
611+
"foo": "bar",
612+
"ending_before": "pm_127",
613+
"starting_after": None,
614+
}
615+
616+
http_client_mock.stub_request(
617+
"get",
618+
path="/v1/pageablemodels",
619+
query_string="ending_before=pm_125&foo=bar",
620+
rbody=json.dumps(
621+
self.pageable_model_response(["pm_123", "pm_124"], False)
622+
),
623+
)
624+
625+
seen = [item["id"] async for item in lo.auto_paging_iter()]
626+
627+
http_client_mock.assert_requested(
628+
"get",
629+
path="/v1/pageablemodels",
630+
query_string="ending_before=pm_125&foo=bar",
631+
)
632+
633+
assert seen == ["pm_126", "pm_125", "pm_124", "pm_123"]

0 commit comments

Comments
 (0)