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
14 changes: 0 additions & 14 deletions test/test_workbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,6 @@ def test_get_by_id_missing_id(server: TSC.Server) -> None:

def test_refresh_id(server: TSC.Server) -> None:
server.version = "2.8"
server.workbooks.baseurl
response_xml = REFRESH_XML.read_text()
with requests_mock.mock() as m:
m.post(
Expand All @@ -182,7 +181,6 @@ def test_refresh_id(server: TSC.Server) -> None:

def test_refresh_already_running(server: TSC.Server) -> None:
server.version = "2.8"
server.workbooks.baseurl
response_xml = WORKBOOK_REFRESH_DUPLICATE_XML.read_text()
with requests_mock.mock() as m:
m.post(
Expand All @@ -196,7 +194,6 @@ def test_refresh_already_running(server: TSC.Server) -> None:

def test_refresh_object(server: TSC.Server) -> None:
server.version = "2.8"
server.workbooks.baseurl
workbook = TSC.WorkbookItem("")
workbook._id = "3cc6cd06-89ce-4fdc-b935-5294135d6d42"
response_xml = REFRESH_XML.read_text()
Expand Down Expand Up @@ -326,7 +323,6 @@ def test_download_sanitizes_name(server: TSC.Server) -> None:
def test_download_extract_only(server: TSC.Server) -> None:
# Pretend we're 2.5 for 'extract_only'
server.version = "2.5"
server.workbooks.baseurl

with requests_mock.mock() as m:
m.get(
Expand Down Expand Up @@ -467,7 +463,6 @@ def test_populate_connections_missing_id(server: TSC.Server) -> None:

def test_populate_pdf(server: TSC.Server) -> None:
server.version = "3.4"
server.workbooks.baseurl
response = POPULATE_PDF.read_bytes()
with requests_mock.mock() as m:
m.get(
Expand All @@ -487,7 +482,6 @@ def test_populate_pdf(server: TSC.Server) -> None:

def test_populate_pdf_unsupported(server: TSC.Server) -> None:
server.version = "3.4"
server.workbooks.baseurl
with requests_mock.mock() as m:
m.get(
server.workbooks.baseurl + "/1f951daf-4061-451a-9df1-69a8062664f2/pdf?type=a5&orientation=landscape",
Expand All @@ -507,7 +501,6 @@ def test_populate_pdf_unsupported(server: TSC.Server) -> None:

def test_populate_pdf_vf_dims(server: TSC.Server) -> None:
server.version = "3.23"
server.workbooks.baseurl
response = POPULATE_PDF.read_bytes()
with requests_mock.mock() as m:
m.get(
Expand All @@ -531,7 +524,6 @@ def test_populate_pdf_vf_dims(server: TSC.Server) -> None:

def test_populate_powerpoint(server: TSC.Server) -> None:
server.version = "3.8"
server.workbooks.baseurl
response = POPULATE_POWERPOINT.read_bytes()
with requests_mock.mock() as m:
m.get(server.workbooks.baseurl + "/1f951daf-4061-451a-9df1-69a8062664f2/powerpoint?maxAge=1", content=response)
Expand Down Expand Up @@ -881,7 +873,6 @@ def test_synchronous_publish_timeout_error(server: TSC.Server) -> None:

def test_delete_extracts_all(server: TSC.Server) -> None:
server.version = "3.10"
server.workbooks.baseurl

response_xml = PUBLISH_ASYNC_XML.read_text()
with requests_mock.mock() as m:
Expand All @@ -895,7 +886,6 @@ def test_delete_extracts_all(server: TSC.Server) -> None:

def test_create_extracts_all(server: TSC.Server) -> None:
server.version = "3.10"
server.workbooks.baseurl

response_xml = PUBLISH_ASYNC_XML.read_text()
with requests_mock.mock() as m:
Expand All @@ -909,7 +899,6 @@ def test_create_extracts_all(server: TSC.Server) -> None:

def test_create_extracts_one(server: TSC.Server) -> None:
server.version = "3.10"
server.workbooks.baseurl

datasource = TSC.DatasourceItem("test")
datasource._id = "1f951daf-4061-451a-9df1-69a8062664f2"
Expand All @@ -925,7 +914,6 @@ def test_create_extracts_one(server: TSC.Server) -> None:


def test_revisions(server: TSC.Server) -> None:
server.workbooks.baseurl
workbook = TSC.WorkbookItem("project", "test")
workbook._id = "06b944d2-959d-4604-9305-12323c95e70e"

Expand Down Expand Up @@ -956,7 +944,6 @@ def test_revisions(server: TSC.Server) -> None:


def test_delete_revision(server: TSC.Server) -> None:
server.workbooks.baseurl
workbook = TSC.WorkbookItem("project", "test")
workbook._id = "06b944d2-959d-4604-9305-12323c95e70e"

Expand Down Expand Up @@ -986,7 +973,6 @@ def test_bad_download_response(server: TSC.Server) -> None:


def test_odata_connection(server: TSC.Server) -> None:
server.workbooks.baseurl
workbook = TSC.WorkbookItem("project", "test")
workbook._id = "06b944d2-959d-4604-9305-12323c95e70e"
connection = TSC.ConnectionItem()
Expand Down
Loading