Skip to content

Commit 5a4aafb

Browse files
author
Gauvain Pocentek
committed
Restore the prvious listing behavior
Return lists by default : this makes the explicit use of pagination work again. Use generators only when `as_list` is explicitly set to `False`.
1 parent d7c7911 commit 5a4aafb

File tree

5 files changed

+56
-48
lines changed

5 files changed

+56
-48
lines changed

docs/switching-to-v4.rst

+4-19
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,15 @@ following important changes in the python API:
6363
gl = gitlab.Gitlab(...)
6464
p = gl.projects.get(project_id)
6565
66-
* Listing methods (``manager.list()`` for instance) now return generators
66+
* Listing methods (``manager.list()`` for instance) can now return generators
6767
(:class:`~gitlab.base.RESTObjectList`). They handle the calls to the API when
68-
needed.
68+
needed to fetch new items.
6969

70-
If you need to get all the items at once, use the ``all=True`` parameter:
70+
By default you will still get lists. To get generators use ``as_list=False``:
7171

7272
.. code-block:: python
7373
74-
all_projects = gl.projects.list(all=True)
74+
all_projects_g = gl.projects.list(as_list=False)
7575
7676
* The "nested" managers (for instance ``gl.project_issues`` or
7777
``gl.group_members``) are not available anymore. Their goal was to provide a
@@ -114,18 +114,3 @@ following important changes in the python API:
114114
+ :attr:`~gitlab.Gitlab.http_post`
115115
+ :attr:`~gitlab.Gitlab.http_put`
116116
+ :attr:`~gitlab.Gitlab.http_delete`
117-
118-
* The users ``get_by_username`` method has been removed. It doesn't exist in
119-
the GitLab API. You can use the ``username`` filter attribute when listing to
120-
get a similar behavior:
121-
122-
.. code-block:: python
123-
124-
user = list(gl.users.list(username='jdoe'))[0]
125-
126-
127-
Undergoing work
128-
===============
129-
130-
* The ``page`` and ``per_page`` arguments for listing don't behave as they used
131-
to. Their behavior will be restored.

gitlab/__init__.py

+25-10
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,7 @@ def http_get(self, path, query_data={}, streamed=False, **kwargs):
712712
else:
713713
return result
714714

715-
def http_list(self, path, query_data={}, **kwargs):
715+
def http_list(self, path, query_data={}, as_list=None, **kwargs):
716716
"""Make a GET request to the Gitlab server for list-oriented queries.
717717
718718
Args:
@@ -723,19 +723,33 @@ def http_list(self, path, query_data={}, **kwargs):
723723
all)
724724
725725
Returns:
726-
GitlabList: A generator giving access to the objects. If an ``all``
727-
kwarg is defined and True, returns a list of all the objects (will
728-
possibly make numerous calls to the Gtilab server and eat a lot of
729-
memory)
726+
list: A list of the objects returned by the server. If `as_list` is
727+
False and no pagination-related arguments (`page`, `per_page`,
728+
`all`) are defined then a GitlabList object (generator) is returned
729+
instead. This object will make API calls when needed to fetch the
730+
next items from the server.
730731
731732
Raises:
732733
GitlabHttpError: When the return code is not 2xx
733734
GitlabParsingError: If the json data could not be parsed
734735
"""
736+
737+
# In case we want to change the default behavior at some point
738+
as_list = True if as_list is None else as_list
739+
740+
get_all = kwargs.get('all', False)
735741
url = self._build_url(path)
736-
get_all = kwargs.pop('all', False)
737-
obj_gen = GitlabList(self, url, query_data, **kwargs)
738-
return list(obj_gen) if get_all else obj_gen
742+
743+
if get_all is True:
744+
return list(GitlabList(self, url, query_data, **kwargs))
745+
746+
if 'page' in kwargs or 'per_page' in kwargs or as_list is True:
747+
# pagination requested, we return a list
748+
return list(GitlabList(self, url, query_data, get_next=False,
749+
**kwargs))
750+
751+
# No pagination, generator requested
752+
return GitlabList(self, url, query_data, **kwargs)
739753

740754
def http_post(self, path, query_data={}, post_data={}, **kwargs):
741755
"""Make a POST request to the Gitlab server.
@@ -816,9 +830,10 @@ class GitlabList(object):
816830
the API again when needed.
817831
"""
818832

819-
def __init__(self, gl, url, query_data, **kwargs):
833+
def __init__(self, gl, url, query_data, get_next=True, **kwargs):
820834
self._gl = gl
821835
self._query(url, query_data, **kwargs)
836+
self._get_next = get_next
822837

823838
def _query(self, url, query_data={}, **kwargs):
824839
result = self._gl.http_request('get', url, query_data=query_data,
@@ -856,7 +871,7 @@ def next(self):
856871
self._current += 1
857872
return item
858873
except IndexError:
859-
if self._next_url:
874+
if self._next_url and self._get_next is True:
860875
self._query(self._next_url)
861876
return self.next()
862877

gitlab/mixins.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,15 @@ def list(self, **kwargs):
7171
"""Retrieve a list of objects.
7272
7373
Args:
74-
**kwargs: Extra options to send to the Gitlab server (e.g. sudo).
75-
If ``all`` is passed and set to True, the entire list of
76-
objects will be returned.
74+
all (bool): If True, return all the items, without pagination
75+
per_page (int): Number of items to retrieve per request
76+
page (int): ID of the page to return (starts with page 1)
77+
as_list (bool): If set to False and no pagination option is
78+
defined, return a generator instead of a list
79+
**kwargs: Extra options to send to the Gitlab server (e.g. sudo)
7780
7881
Returns:
79-
RESTObjectList: Generator going through the list of objects, making
80-
queries to the server when required.
81-
If ``all=True`` is passed as argument, returns
82-
list(RESTObjectList).
82+
list: The list of objects, or a generator if `as_list` is False
8383
8484
Raises:
8585
GitlabAuthenticationError: If authentication is not correct

gitlab/v4/objects.py

+13-4
Original file line numberDiff line numberDiff line change
@@ -1087,7 +1087,8 @@ def closes_issues(self, **kwargs):
10871087
RESTObjectList: List of issues
10881088
"""
10891089
path = '%s/%s/closes_issues' % (self.manager.path, self.get_id())
1090-
data_list = self.manager.gitlab.http_list(path, **kwargs)
1090+
data_list = self.manager.gitlab.http_list(path, as_list=False,
1091+
**kwargs)
10911092
manager = ProjectIssueManager(self.manager.gitlab,
10921093
parent=self.manager._parent)
10931094
return RESTObjectList(manager, ProjectIssue, data_list)
@@ -1108,7 +1109,8 @@ def commits(self, **kwargs):
11081109
"""
11091110

11101111
path = '%s/%s/commits' % (self.manager.path, self.get_id())
1111-
data_list = self.manager.gitlab.http_list(path, **kwargs)
1112+
data_list = self.manager.gitlab.http_list(path, as_list=False,
1113+
**kwargs)
11121114
manager = ProjectCommitManager(self.manager.gitlab,
11131115
parent=self.manager._parent)
11141116
return RESTObjectList(manager, ProjectCommit, data_list)
@@ -1197,7 +1199,8 @@ def issues(self, **kwargs):
11971199
"""
11981200

11991201
path = '%s/%s/issues' % (self.manager.path, self.get_id())
1200-
data_list = self.manager.gitlab.http_list(path, **kwargs)
1202+
data_list = self.manager.gitlab.http_list(path, as_list=False,
1203+
**kwargs)
12011204
manager = ProjectCommitManager(self.manager.gitlab,
12021205
parent=self.manager._parent)
12031206
# FIXME(gpocentek): the computed manager path is not correct
@@ -1218,7 +1221,8 @@ def merge_requests(self, **kwargs):
12181221
RESTObjectList: The list of merge requests
12191222
"""
12201223
path = '%s/%s/merge_requests' % (self.manager.path, self.get_id())
1221-
data_list = self.manager.gitlab.http_list(path, **kwargs)
1224+
data_list = self.manager.gitlab.http_list(path, as_list=False,
1225+
**kwargs)
12221226
manager = ProjectCommitManager(self.manager.gitlab,
12231227
parent=self.manager._parent)
12241228
# FIXME(gpocentek): the computed manager path is not correct
@@ -2009,6 +2013,11 @@ def all(self, scope=None, **kwargs):
20092013
Args:
20102014
scope (str): The scope of runners to show, one of: specific,
20112015
shared, active, paused, online
2016+
all (bool): If True, return all the items, without pagination
2017+
per_page (int): Number of items to retrieve per request
2018+
page (int): ID of the page to return (starts with page 1)
2019+
as_list (bool): If set to False and no pagination option is
2020+
defined, return a generator instead of a list
20122021
**kwargs: Extra options to send to the server (e.g. sudo)
20132022
20142023
Raises:

tools/python_test_v4.py

+7-8
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
{'email': 'foobar@example.com', 'username': 'foobar',
5656
'name': 'Foo Bar', 'password': 'foobar_password'})
5757

58-
assert gl.users.list(search='foobar').next().id == foobar_user.id
58+
assert gl.users.list(search='foobar')[0].id == foobar_user.id
5959
usercmp = lambda x,y: cmp(x.id, y.id)
6060
expected = sorted([new_user, foobar_user], cmp=usercmp)
6161
actual = sorted(list(gl.users.list(search='foo')), cmp=usercmp)
@@ -92,7 +92,7 @@
9292
group1 = gl.groups.create({'name': 'group1', 'path': 'group1'})
9393
group2 = gl.groups.create({'name': 'group2', 'path': 'group2'})
9494

95-
p_id = gl.groups.list(search='group2').next().id
95+
p_id = gl.groups.list(search='group2')[0].id
9696
group3 = gl.groups.create({'name': 'group3', 'path': 'group3', 'parent_id': p_id})
9797

9898
assert(len(gl.groups.list()) == 3)
@@ -139,12 +139,11 @@
139139
assert(len(gl.projects.list(search="admin")) == 1)
140140

141141
# test pagination
142-
# FIXME => we should return lists, not RESTObjectList
143-
#l1 = gl.projects.list(per_page=1, page=1)
144-
#l2 = gl.projects.list(per_page=1, page=2)
145-
#assert(len(l1) == 1)
146-
#assert(len(l2) == 1)
147-
#assert(l1[0].id != l2[0].id)
142+
l1 = gl.projects.list(per_page=1, page=1)
143+
l2 = gl.projects.list(per_page=1, page=2)
144+
assert(len(l1) == 1)
145+
assert(len(l2) == 1)
146+
assert(l1[0].id != l2[0].id)
148147

149148
# project content (files)
150149
admin_project.files.create({'file_path': 'README',

0 commit comments

Comments
 (0)