devrev-Python-SDK 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- devrev/__init__.py +47 -0
- devrev/client.py +343 -0
- devrev/config.py +180 -0
- devrev/exceptions.py +205 -0
- devrev/models/__init__.py +499 -0
- devrev/models/accounts.py +187 -0
- devrev/models/articles.py +109 -0
- devrev/models/base.py +147 -0
- devrev/models/code_changes.py +103 -0
- devrev/models/conversations.py +115 -0
- devrev/models/dev_users.py +258 -0
- devrev/models/groups.py +140 -0
- devrev/models/links.py +107 -0
- devrev/models/parts.py +110 -0
- devrev/models/rev_users.py +177 -0
- devrev/models/slas.py +112 -0
- devrev/models/tags.py +90 -0
- devrev/models/timeline_entries.py +100 -0
- devrev/models/webhooks.py +109 -0
- devrev/models/works.py +280 -0
- devrev/py.typed +1 -0
- devrev/services/__init__.py +74 -0
- devrev/services/accounts.py +325 -0
- devrev/services/articles.py +80 -0
- devrev/services/base.py +234 -0
- devrev/services/code_changes.py +80 -0
- devrev/services/conversations.py +98 -0
- devrev/services/dev_users.py +401 -0
- devrev/services/groups.py +103 -0
- devrev/services/links.py +68 -0
- devrev/services/parts.py +100 -0
- devrev/services/rev_users.py +235 -0
- devrev/services/slas.py +82 -0
- devrev/services/tags.py +80 -0
- devrev/services/timeline_entries.py +80 -0
- devrev/services/webhooks.py +80 -0
- devrev/services/works.py +363 -0
- devrev/utils/__init__.py +14 -0
- devrev/utils/deprecation.py +49 -0
- devrev/utils/http.py +521 -0
- devrev/utils/logging.py +139 -0
- devrev/utils/pagination.py +155 -0
- devrev_python_sdk-1.0.0.dist-info/METADATA +774 -0
- devrev_python_sdk-1.0.0.dist-info/RECORD +45 -0
- devrev_python_sdk-1.0.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""Pagination utilities for DevRev SDK.
|
|
2
|
+
|
|
3
|
+
This module provides iterator-based pagination for list endpoints.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from collections.abc import AsyncIterator, Callable, Iterator
|
|
9
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from devrev.models.base import PaginatedResponse
|
|
13
|
+
|
|
14
|
+
T = TypeVar("T")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PaginatedIterator(Iterator[T]):
|
|
18
|
+
"""Iterator for synchronous paginated responses.
|
|
19
|
+
|
|
20
|
+
Automatically fetches next pages as needed.
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
```python
|
|
24
|
+
from devrev import DevRevClient
|
|
25
|
+
from devrev.utils.pagination import paginate
|
|
26
|
+
|
|
27
|
+
client = DevRevClient()
|
|
28
|
+
for account in paginate(client.accounts.list, "accounts"):
|
|
29
|
+
print(account.display_name)
|
|
30
|
+
```
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
fetch_page: Callable[[str | None], PaginatedResponse],
|
|
36
|
+
items_attr: str,
|
|
37
|
+
limit: int | None = None,
|
|
38
|
+
) -> None:
|
|
39
|
+
"""Initialize the paginated iterator.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
fetch_page: Function that fetches a page given a cursor
|
|
43
|
+
items_attr: Attribute name for items in the response
|
|
44
|
+
limit: Maximum total items to return (None = unlimited)
|
|
45
|
+
"""
|
|
46
|
+
self._fetch_page = fetch_page
|
|
47
|
+
self._items_attr = items_attr
|
|
48
|
+
self._limit = limit
|
|
49
|
+
self._cursor: str | None = None
|
|
50
|
+
self._items: list[T] = []
|
|
51
|
+
self._item_index = 0
|
|
52
|
+
self._total_returned = 0
|
|
53
|
+
self._exhausted = False
|
|
54
|
+
|
|
55
|
+
def __iter__(self) -> PaginatedIterator[T]:
|
|
56
|
+
"""Return self as iterator."""
|
|
57
|
+
return self
|
|
58
|
+
|
|
59
|
+
def __next__(self) -> T:
|
|
60
|
+
"""Get next item, fetching more pages as needed."""
|
|
61
|
+
# Check if we've hit our limit
|
|
62
|
+
if self._limit is not None and self._total_returned >= self._limit:
|
|
63
|
+
raise StopIteration
|
|
64
|
+
|
|
65
|
+
# If we've consumed all items in current page, fetch next
|
|
66
|
+
while self._item_index >= len(self._items):
|
|
67
|
+
if self._exhausted:
|
|
68
|
+
raise StopIteration
|
|
69
|
+
|
|
70
|
+
response = self._fetch_page(self._cursor)
|
|
71
|
+
self._items = getattr(response, self._items_attr, [])
|
|
72
|
+
self._item_index = 0
|
|
73
|
+
self._cursor = response.next_cursor
|
|
74
|
+
|
|
75
|
+
if not self._cursor:
|
|
76
|
+
self._exhausted = True
|
|
77
|
+
|
|
78
|
+
# If no items in this page, check if we're done
|
|
79
|
+
if not self._items and self._exhausted:
|
|
80
|
+
raise StopIteration
|
|
81
|
+
|
|
82
|
+
item = self._items[self._item_index]
|
|
83
|
+
self._item_index += 1
|
|
84
|
+
self._total_returned += 1
|
|
85
|
+
return item
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class AsyncPaginatedIterator(AsyncIterator[T]):
|
|
89
|
+
"""Async iterator for paginated responses.
|
|
90
|
+
|
|
91
|
+
Automatically fetches next pages as needed.
|
|
92
|
+
|
|
93
|
+
Example:
|
|
94
|
+
```python
|
|
95
|
+
from devrev import AsyncDevRevClient
|
|
96
|
+
from devrev.utils.pagination import async_paginate
|
|
97
|
+
|
|
98
|
+
async with AsyncDevRevClient() as client:
|
|
99
|
+
async for account in async_paginate(client.accounts.list, "accounts"):
|
|
100
|
+
print(account.display_name)
|
|
101
|
+
```
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
def __init__(
|
|
105
|
+
self,
|
|
106
|
+
fetch_page: Callable[[str | None], Any], # Returns Awaitable
|
|
107
|
+
items_attr: str,
|
|
108
|
+
limit: int | None = None,
|
|
109
|
+
) -> None:
|
|
110
|
+
"""Initialize the async paginated iterator.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
fetch_page: Async function that fetches a page given a cursor
|
|
114
|
+
items_attr: Attribute name for items in the response
|
|
115
|
+
limit: Maximum total items to return (None = unlimited)
|
|
116
|
+
"""
|
|
117
|
+
self._fetch_page = fetch_page
|
|
118
|
+
self._items_attr = items_attr
|
|
119
|
+
self._limit = limit
|
|
120
|
+
self._cursor: str | None = None
|
|
121
|
+
self._items: list[T] = []
|
|
122
|
+
self._item_index = 0
|
|
123
|
+
self._total_returned = 0
|
|
124
|
+
self._exhausted = False
|
|
125
|
+
|
|
126
|
+
def __aiter__(self) -> AsyncPaginatedIterator[T]:
|
|
127
|
+
"""Return self as async iterator."""
|
|
128
|
+
return self
|
|
129
|
+
|
|
130
|
+
async def __anext__(self) -> T:
|
|
131
|
+
"""Get next item, fetching more pages as needed."""
|
|
132
|
+
# Check if we've hit our limit
|
|
133
|
+
if self._limit is not None and self._total_returned >= self._limit:
|
|
134
|
+
raise StopAsyncIteration
|
|
135
|
+
|
|
136
|
+
# If we've consumed all items in current page, fetch next
|
|
137
|
+
while self._item_index >= len(self._items):
|
|
138
|
+
if self._exhausted:
|
|
139
|
+
raise StopAsyncIteration
|
|
140
|
+
|
|
141
|
+
response = await self._fetch_page(self._cursor)
|
|
142
|
+
self._items = getattr(response, self._items_attr, [])
|
|
143
|
+
self._item_index = 0
|
|
144
|
+
self._cursor = response.next_cursor
|
|
145
|
+
|
|
146
|
+
if not self._cursor:
|
|
147
|
+
self._exhausted = True
|
|
148
|
+
|
|
149
|
+
if not self._items and self._exhausted:
|
|
150
|
+
raise StopAsyncIteration
|
|
151
|
+
|
|
152
|
+
item = self._items[self._item_index]
|
|
153
|
+
self._item_index += 1
|
|
154
|
+
self._total_returned += 1
|
|
155
|
+
return item
|