8000 fix: handle 0 item response in querysets · tableau/server-client-python@c073f53 · GitHub
[go: up one dir, main page]

Skip to content

Commit c073f53

Browse files
committed
fix: handle 0 item response in querysets
A flaw in the __iter__ logic introduced to handle scenarios where a pagination element is not included in the response xml resulted in an infinite loop. This PR introduces a few changes to protect against this: 1. After running QuerySet._fetch_all(), if the result_cache is empty, return instead of performing other comparisons. 2. Ensure that any non-None total_available is returned from the PaginationItem's object. 3. In _fetch_all, check if there is a PaginationItem that has been populated so as to not call the server side endpoint muliple times before returning.
1 parent 9b1b940 commit c073f53

File tree

2 files changed

+16
-2
lines changed

2 files changed

+16
-2
lines changed

tableauserverclient/server/query.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ def __iter__(self: Self) -> Iterator[T]:
8585
# up overrunning the total number of pages. Catch the
8686
# error and break out of the loop.
8787
raise StopIteration
88+
if len(self._result_cache) == 0:
89+
return
8890
yield from self._result_cache
8991
# If the length of the QuerySet is unknown, continue fetching until
9092
# the result cache is empty.
@@ -150,7 +152,7 @@ def _fetch_all(self: Self) -> None:
150152
"""
151153
Retrieve the data and store result and pagination item in cache
152154
"""
153-
if not self._result_cache:
155+
if not self._result_cache and self._pagination_item._page_number is None:
154156
response = self.model.get(self.request_options)
155157
if isinstance(response, tuple):
156158
self._result_cache, self._pagination_item = response
@@ -159,7 +161,7 @@ def _fetch_all(self: Self) -> None:
159161
self._pagination_item = PaginationItem()
160162

161163
def __len__(self: Self) -> int:
162-
return self.total_available or sys.maxsize
164+
return sys.maxsize if self.total_available is None else self.total_available
163165

164166
@property
165167
def total_available(self: Self) -> int:

test/test_pager.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import contextlib
22
import os
33
import unittest
4+
import xml.etree.ElementTree as ET
45

56
import requests_mock
67

@@ -122,3 +123,14 @@ def test_pager_view(self) -> None:
122123
m.get(self.server.views.baseurl, text=view_xml)
123124
for view in TSC.Pager(self.server.views):
124125
assert view.name is not None
126+
127+
def test_queryset_no_matches(self) -> None:
128+
elem = ET.Element("tsResponse", xmlns="http://tableau.com/api")
129+
ET.SubElement(elem, "pagination", totalAvailable="0")
130+
ET.SubElement(elem, "groups")
131+
xml = ET.tostring(elem).decode("utf-8")
132+
with requests_mock.mock() as m:
133+
m.get(self.server.groups.baseurl, text=xml)
134+
all_groups = self.server.groups.all()
135+
groups = list(all_groups)
136+
assert len(groups) == 0

0 commit comments

Comments
 (0)
0