Skip to content

Commit cb824a4

Browse files
fix: handle situation where GitLab does not return values
If a query returns more than 10,000 records than the following values are NOT returned: x.total_pages x.total Modify the code to allow no value to be set for these values. If there is not a value returned the functions will now return None. Update unit test so no longer `xfail` https://docs.gitlab.com/ee/user/gitlab_com/index.html#pagination-response-headers Closes python-gitlab#1686
1 parent 501f9a1 commit cb824a4

File tree

5 files changed

+35
-23
lines changed

5 files changed

+35
-23
lines changed

docs/api-usage.rst

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,17 @@ The generator exposes extra listing information as received from the server:
265265
* ``prev_page``: if ``None`` the current page is the first one
266266
* ``next_page``: if ``None`` the current page is the last one
267267
* ``per_page``: number of items per page
268-
* ``total_pages``: total number of pages available
269-
* ``total``: total number of items in the list
268+
* ``total_pages``: total number of pages available. This may be a ``None`` value.
269+
* ``total``: total number of items in the list. This may be a ``None`` value.
270+
271+
.. note::
272+
273+
For performance reasons, if a query returns more than 10,000 records, GitLab
274+
does not return the ``total_pages`` or ``total`` headers. In this case,
275+
``total_pages`` and ``total`` will have a value of ``None``.
276+
277+
For more information see:
278+
https://docs.gitlab.com/ee/user/gitlab_com/index.html#pagination-response-headers
270279

271280
Sudo
272281
====

gitlab/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,12 +288,12 @@ def per_page(self) -> int:
288288
return self._list.per_page
289289

290290
@property
291-
def total_pages(self) -> int:
291+
def total_pages(self) -> Optional[int]:
292292
"""The total number of pages."""
293293
return self._list.total_pages
294294

295295
@property
296-
def total(self) -> int:
296+
def total(self) -> Optional[int]:
297297
"""The total number of items."""
298298
return self._list.total
299299

gitlab/client.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -917,14 +917,12 @@ def _query(
917917
self._next_url = next_url
918918
except KeyError:
919919
self._next_url = None
920-
self._current_page: Optional[Union[str, int]] = result.headers.get("X-Page")
921-
self._prev_page: Optional[Union[str, int]] = result.headers.get("X-Prev-Page")
922-
self._next_page: Optional[Union[str, int]] = result.headers.get("X-Next-Page")
923-
self._per_page: Optional[Union[str, int]] = result.headers.get("X-Per-Page")
924-
self._total_pages: Optional[Union[str, int]] = result.headers.get(
925-
"X-Total-Pages"
926-
)
927-
self._total: Optional[Union[str, int]] = result.headers.get("X-Total")
920+
self._current_page: Optional[str] = result.headers.get("X-Page")
921+
self._prev_page: Optional[str] = result.headers.get("X-Prev-Page")
922+
self._next_page: Optional[str] = result.headers.get("X-Next-Page")
923+
self._per_page: Optional[str] = result.headers.get("X-Per-Page")
924+
self._total_pages: Optional[str] = result.headers.get("X-Total-Pages")
925+
self._total: Optional[str] = result.headers.get("X-Total")
928926

929927
try:
930928
self._data: List[Dict[str, Any]] = result.json()
@@ -965,19 +963,22 @@ def per_page(self) -> int:
965963
assert self._per_page is not None
966964
return int(self._per_page)
967965

966+
# NOTE(jlvillal): When a query returns more than 10,000 items, GitLab doesn't return
967+
# the headers 'x-total-pages' and 'x-total'. In those cases we return None.
968+
# https://docs.gitlab.com/ee/user/gitlab_com/index.html#pagination-response-headers
968969
@property
969-
def total_pages(self) -> int:
970+
def total_pages(self) -> Optional[int]:
970971
"""The total number of pages."""
971-
if TYPE_CHECKING:
972-
assert self._total_pages is not None
973-
return int(self._total_pages)
972+
if self._total_pages is not None:
973+
return int(self._total_pages)
974+
return None
974975

975976
@property
976-
def total(self) -> int:
977+
def total(self) -> Optional[int]:
977978
"""The total number of items."""
978-
if TYPE_CHECKING:
979-
assert self._total is not None
980-
return int(self._total)
979+
if self._total is not None:
980+
return int(self._total)
981+
return None
981982

982983
def __iter__(self) -> "GitlabList":
983984
return self

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,6 @@ disable = [
8787
"useless-object-inheritance",
8888

8989
]
90+
91+
[tool.pytest.ini_options]
92+
xfail_strict = true

tests/unit/test_gitlab.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
# You should have received a copy of the GNU Lesser General Public License
1717
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1818

19+
import copy
1920
import pickle
2021
import warnings
21-
from copy import deepcopy
2222

2323
import pytest
2424
import responses
@@ -109,15 +109,14 @@ def _strip_pagination_headers(response):
109109
"""
110110
https://docs.gitlab.com/ee/user/gitlab_com/index.html#pagination-response-headers
111111
"""
112-
stripped = deepcopy(response)
112+
stripped = copy.deepcopy(response)
113113

114114
del stripped["headers"]["X-Total-Pages"]
115115
del stripped["headers"]["X-Total"]
116116

117117
return stripped
118118

119119

120-
@pytest.mark.xfail(reason="See #1686")
121120
@responses.activate
122121
def test_gitlab_build_list_missing_headers(gl, resp_page_1, resp_page_2):
123122
stripped_page_1 = _strip_pagination_headers(resp_page_1)

0 commit comments

Comments
 (0)