Skip to content
Open
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
5 changes: 3 additions & 2 deletions tableauserverclient/server/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@ def __iter__(self: Self) -> Iterator[T]:
if e.code == "400006":
# If the endpoint does not support pagination, it will end
# up overrunning the total number of pages. Catch the
# error and break out of the loop.
raise StopIteration
# error and return to cleanly end the generator.
# (raise StopIteration would cause RuntimeError per PEP 479)
return
if len(self._result_cache) == 0:
return
yield from self._result_cache
Expand Down
33 changes: 33 additions & 0 deletions test/test_pager.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,36 @@ def test_queryset_no_matches(server: TSC.Server) -> None:
all_groups = server.groups.all()
groups = list(all_groups)
assert len(groups) == 0


def test_queryset_400006_returns_cleanly() -> None:
"""Regression test for PEP 479: 400006 must not raise RuntimeError.

Before the fix, QuerySet.__iter__ raised StopIteration which PEP 479
converts to RuntimeError inside a generator. This test directly exercises
the __iter__ method with a mock that raises 400006 on the second call.
"""
from tableauserverclient.server.endpoint.exceptions import ServerResponseError
from tableauserverclient.server.query import QuerySet

calls = [0]

class MockEndpoint:
def get(self, req_options=None):
calls[0] += 1
assert calls[0] <= 10, f"get() called {calls[0]} times — infinite loop detected"
if calls[0] >= 2:
raise ServerResponseError("400006", "Bad Request", "Invalid page number", "http://test")
item = TSC.ProjectItem(name="Test")
item._id = "abc"
pagination = TSC.PaginationItem()
pagination._total_available = None # unknown size — triggers loop
return [item], pagination

qs: QuerySet[TSC.ProjectItem] = QuerySet(MockEndpoint()) # type: ignore[arg-type]

# Use a generator expression to bypass list()'s __len__ optimization,
# which would consume the first page before __iter__ starts.
# Before the fix this raised: RuntimeError: generator raised StopIteration
results = [x for x in qs]
assert len(results) == 1
Loading