8000 feat(async): move GitlabList to types · python-gitlab/python-gitlab@0585d6a · GitHub
[go: up one dir, main page]

Skip to content
8000

Commit 0585d6a

Browse files
committed
feat(async): move GitlabList to types
Also provide async and sync interface to interact with GitlabList
1 parent 55f8439 commit 0585d6a

File tree

1 file changed

+152
-1
lines changed

1 file changed

+152
-1
lines changed

gitlab/types.py

Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717

1818

19-
class GitlabAttribute(object):
19+
class GitlabAttribute:
2020
def __init__(self, value=None):
2121
self._value = value
2222

@@ -54,3 +54,154 @@ def get_file_name(self, attr_name=None):
5454
class ImageAttribute(FileAttribute):
5555
def get_file_name(self, attr_name=None):
5656
return "%s.png" % attr_name if attr_name else "image.png"
57+
58+
59+
class GitlabList:
60+
"""Generator representing a list of remote objects.
61+
62+
The object handles the links returned by a query to the API, and will call
63+
the API again when needed.
64+
"""
65+
66+
@property
67+
def current_page(self):
68+
"""The current page number."""
69+
return int(self._current_page)
70+
71+
@property
72+
def prev_page(self):
73+
"""The next page number.
74+
75+
If None, the current page is the last.
76+
"""
77+
return int(self._prev_page) if self._prev_page else None
78+
79+
@property
80+
def next_page(self):
81+
"""The next page number.
82+
83+
If None, the current page is the last.
84+
"""
85+
return int(self._next_page) if self._next_page else None
86+
87+
@property
88+
def per_page(self):
89+
"""The number of items per page."""
90+
return int(self._per_page)
91+
92+
@property
93+
def total_pages(self):
94+
"""The total number of pages."""
95+
return int(self._total_pages)
96+
97+
@property
98+
def total(self):
99+
"""The total number of items."""
100+
return int(self._total)
101+
102+
def __len__(self):
103+
return int(self._total)
104+
105+
106+
class GitlabList:
107+
"""Generator representing a list of remote objects.
108+
109+
The object handles the links returned by a query to the API, and will call
110+
the API again when needed.
111+
"""
112+
113+
@classmethod
114+
def create(cls, gl, url, query_data, get_next=True, **kwargs):
115+
self = GitlabList()
116+
self._gl = gl
117+
self._query(url, query_data, **kwargs)
118+
self._get_next = get_next
119+
return self
120+
121+
@classmethod
122+
async def acreate(cls, gl, url, query_data, get_next=True, **kwargs):
123+
"""Create GitlabList with data
124+
125+
Create is made in factory way since it's cleaner to use such way
126+
instead of make async __init__
127+
"""
128+
self = GitlabList()
129+
self._gl = gl
130+
await self._aquery(url, query_data, **kwargs)
131+
self._get_next = get_next
132+
return self
133+
134+
def _process_query_result(self, result):
135+
try:
136+
self._next_url = result.links["next"]["url"]
137+
except KeyError:
138+
self._next_url = None
139+
self._current_page = result.headers.get("X-Page")
140+
self._prev_page = result.headers.get("X-Prev-Page")
141+
self._next_page = result.headers.get("X-Next-Page")
142+
self._per_page = result.headers.get("X-Per-Page")
143+
self._total_pages = result.headers.get("X-Total-Pages")
144+
self._total = result.headers.get("X-Total")
145+
146+
try:
147+
self._data = result.json()
148+
except Exception:
149+
raise GitlabParsingError(error_message="Failed to parse the server message")
150+
151+
self._current = 0
152+
153+
async def _aquery(self, url, query_data=None, **kwargs):
154+
query_data = query_data or {}
155+
result = await self._gl.http_request(
156+
"get", url, query_data=query_data, **kwargs
157+
)
158+
self._process_query_result(result)
159+
160+
def _query(self, url, query_data=None, **kwargs):
161+
query_data = query_data or {}
162+
result = self._gl.http_request("get", url, query_data=query_data, **kwargs)
163+
return self._process_query_result(result)
164+
165+
def __iter__(self):
166+
return self
167+
168+
def __next__(self):
169+
return self.next()
170+
171+
def next(self):
172+
try:
173+
item = self._data[self._current]
174+
self._current += 1
175+
return item
176+
except IndexError:
177+
pass
178+
179+
if self._next_url and self._get_next is True:
180+
self._query(self._next_url)
181+
return self.next()
182+
183+
raise StopIteration
184+
185+
def __aiter__(self):
186+
return self
187+
188+
async def __anext__(self):
189+
return await self.anext()
190+
191+
async def anext(self):
192+
try:
193+
item = self._data[self._current]
194+
self._current += 1
195+
return item
196+
except IndexError:
197+
pass
198+
199+
if self._next_url and self._get_next is True:
200+
await self._aquery(self._next_url)
201+
return await self.anext()
202+
203+
raise StopAsyncIteration
204+
205+
async def as_list(self):
206+
# since list() does not support async way
207+
return [o async for o in self]

0 commit comments

Comments
 (0)
0