comparison env/lib/python3.9/site-packages/boto/dynamodb2/results.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 class ResultSet(object):
2 """
3 A class used to lazily handle page-to-page navigation through a set of
4 results.
5
6 It presents a transparent iterator interface, so that all the user has
7 to do is use it in a typical ``for`` loop (or list comprehension, etc.)
8 to fetch results, even if they weren't present in the current page of
9 results.
10
11 This is used by the ``Table.query`` & ``Table.scan`` methods.
12
13 Example::
14
15 >>> users = Table('users')
16 >>> results = ResultSet()
17 >>> results.to_call(users.query, username__gte='johndoe')
18 # Now iterate. When it runs out of results, it'll fetch the next page.
19 >>> for res in results:
20 ... print res['username']
21
22 """
23 def __init__(self, max_page_size=None):
24 super(ResultSet, self).__init__()
25 self.the_callable = None
26 self.call_args = []
27 self.call_kwargs = {}
28 self._results = []
29 self._offset = -1
30 self._results_left = True
31 self._last_key_seen = None
32 self._fetches = 0
33 self._max_page_size = max_page_size
34 self._limit = None
35
36 @property
37 def first_key(self):
38 return 'exclusive_start_key'
39
40 def _reset(self):
41 """
42 Resets the internal state of the ``ResultSet``.
43
44 This prevents results from being cached long-term & consuming
45 excess memory.
46
47 Largely internal.
48 """
49 self._results = []
50 self._offset = 0
51
52 def __iter__(self):
53 return self
54
55 def __next__(self):
56 self._offset += 1
57
58 if self._offset >= len(self._results):
59 if self._results_left is False:
60 raise StopIteration()
61
62 self.fetch_more()
63
64 # It's possible that previous call to ``fetch_more`` may not return
65 # anything useful but there may be more results. Loop until we get
66 # something back, making sure we guard for no results left.
67 while not len(self._results) and self._results_left:
68 self.fetch_more()
69
70 if self._offset < len(self._results):
71 if self._limit is not None:
72 self._limit -= 1
73
74 if self._limit < 0:
75 raise StopIteration()
76
77 return self._results[self._offset]
78 else:
79 raise StopIteration()
80
81 next = __next__
82
83 def to_call(self, the_callable, *args, **kwargs):
84 """
85 Sets up the callable & any arguments to run it with.
86
87 This is stored for subsequent calls so that those queries can be
88 run without requiring user intervention.
89
90 Example::
91
92 # Just an example callable.
93 >>> def squares_to(y):
94 ... for x in range(1, y):
95 ... yield x**2
96 >>> rs = ResultSet()
97 # Set up what to call & arguments.
98 >>> rs.to_call(squares_to, y=3)
99
100 """
101 if not callable(the_callable):
102 raise ValueError(
103 'You must supply an object or function to be called.'
104 )
105
106 # We pop the ``limit``, if present, to track how many we should return
107 # to the user. This isn't the same as the ``limit`` that the low-level
108 # DDB api calls use (which limit page size, not the overall result set).
109 self._limit = kwargs.pop('limit', None)
110
111 if self._limit is not None and self._limit < 0:
112 self._limit = None
113
114 self.the_callable = the_callable
115 self.call_args = args
116 self.call_kwargs = kwargs
117
118 def fetch_more(self):
119 """
120 When the iterator runs out of results, this method is run to re-execute
121 the callable (& arguments) to fetch the next page.
122
123 Largely internal.
124 """
125 self._reset()
126
127 args = self.call_args[:]
128 kwargs = self.call_kwargs.copy()
129
130 if self._last_key_seen is not None:
131 kwargs[self.first_key] = self._last_key_seen
132
133 # If the page size is greater than limit set them
134 # to the same value
135 if self._limit and self._max_page_size and self._max_page_size > self._limit:
136 self._max_page_size = self._limit
137
138 # Put in the max page size.
139 if self._max_page_size is not None:
140 kwargs['limit'] = self._max_page_size
141 elif self._limit is not None:
142 # If max_page_size is not set and limit is available
143 # use it as the page size
144 kwargs['limit'] = self._limit
145
146 results = self.the_callable(*args, **kwargs)
147 self._fetches += 1
148 new_results = results.get('results', [])
149 self._last_key_seen = results.get('last_key', None)
150
151 if len(new_results):
152 self._results.extend(results['results'])
153
154 # Check the limit, if it's present.
155 if self._limit is not None and self._limit >= 0:
156 limit = self._limit
157 limit -= len(results['results'])
158 # If we've exceeded the limit, we don't have any more
159 # results to look for.
160 if limit <= 0:
161 self._results_left = False
162
163 if self._last_key_seen is None:
164 self._results_left = False
165
166
167 class BatchGetResultSet(ResultSet):
168 def __init__(self, *args, **kwargs):
169 self._keys_left = kwargs.pop('keys', [])
170 self._max_batch_get = kwargs.pop('max_batch_get', 100)
171 super(BatchGetResultSet, self).__init__(*args, **kwargs)
172
173 def fetch_more(self):
174 self._reset()
175
176 args = self.call_args[:]
177 kwargs = self.call_kwargs.copy()
178
179 # Slice off the max we can fetch.
180 kwargs['keys'] = self._keys_left[:self._max_batch_get]
181 self._keys_left = self._keys_left[self._max_batch_get:]
182
183 if len(self._keys_left) <= 0:
184 self._results_left = False
185
186 results = self.the_callable(*args, **kwargs)
187
188 if not len(results.get('results', [])):
189 return
190
191 self._results.extend(results['results'])
192
193 for offset, key_data in enumerate(results.get('unprocessed_keys', [])):
194 # We've got an unprocessed key. Reinsert it into the list.
195 # DynamoDB only returns valid keys, so there should be no risk of
196 # missing keys ever making it here.
197 self._keys_left.insert(offset, key_data)
198
199 if len(self._keys_left) > 0:
200 self._results_left = True
201
202 # Decrease the limit, if it's present.
203 if self.call_kwargs.get('limit'):
204 self.call_kwargs['limit'] -= len(results['results'])