Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c330cde

Browse files
committed
BigTable: PartialRowsData iterator
1 parent 83e1703 commit c330cde

File tree

3 files changed

+128
-59
lines changed

3 files changed

+128
-59
lines changed

bigtable/google/cloud/bigtable/row_data.py

Lines changed: 71 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,69 @@ def __eq__(self, other):
221221
def __ne__(self, other):
222222
return not self == other
223223

224+
def __iter__(self):
225+
return self._consume_next(True)
226+
227+
def _consume_next(self, yield_=False):
228+
""" Helper for consume_next.
229+
230+
:type yield_: bool
231+
:param yield_: if True, yields rows as they complete,
232+
else finish iteration of the response_iterator
233+
"""
234+
while True:
235+
response = six.next(self._response_iterator)
236+
self._counter += 1
237+
238+
if self._last_scanned_row_key is None: # first response
239+
if response.last_scanned_row_key:
240+
raise InvalidReadRowsResponse()
241+
242+
self._last_scanned_row_key = response.last_scanned_row_key
243+
244+
row = self._row
245+
cell = self._cell
246+
247+
for chunk in response.chunks:
248+
249+
self._validate_chunk(chunk)
250+
251+
if chunk.reset_row:
252+
row = self._row = None
253+
cell = self._cell = self._previous_cell = None
254+
continue
255+
256+
if row is None:
257+
row = self._row = PartialRowData(chunk.row_key)
258+
259+
if cell is None:
260+
qualifier = None
261+
if chunk.HasField('qualifier'):
262+
qualifier = chunk.qualifier.value
263+
264+
cell = self._cell = PartialCellData(
265+
chunk.row_key,
266+
chunk.family_name.value,
267+
qualifier,
268+
chunk.timestamp_micros,
269+
chunk.labels,
270+
chunk.value)
271+
self._copy_from_previous(cell)
272+
else:
273+
cell.append_value(chunk.value)
274+
275+
if chunk.commit_row:
276+
self._save_current_row()
277+
if yield_:
278+
yield self._previous_row
279+
row = cell = None
280+
continue
281+
282+
if chunk.value_size == 0:
283+
self._save_current_cell()
284+
cell = None
285+
break
286+
224287
@property
225288
def state(self):
226289
"""State machine state.
@@ -262,54 +325,10 @@ def consume_next(self):
262325
Parse the response and its chunks into a new/existing row in
263326
:attr:`_rows`. Rows are returned in order by row key.
264327
"""
265-
response = six.next(self._response_iterator)
266-
self._counter += 1
267-
268-
if self._last_scanned_row_key is None: # first response
269-
if response.last_scanned_row_key:
270-
raise InvalidReadRowsResponse()
271-
272-
self._last_scanned_row_key = response.last_scanned_row_key
273-
274-
row = self._row
275-
cell = self._cell
276-
277-
for chunk in response.chunks:
278-
279-
self._validate_chunk(chunk)
280-
281-
if chunk.reset_row:
282-
row = self._row = None
283-
cell = self._cell = self._previous_cell = None
284-
continue
285-
286-
if row is None:
287-
row = self._row = PartialRowData(chunk.row_key)
288-
289-
if cell is None:
290-
qualifier = None
291-
if chunk.HasField('qualifier'):
292-
qualifier = chunk.qualifier.value
293-
294-
cell = self._cell = PartialCellData(
295-
chunk.row_key,
296-
chunk.family_name.value,
297-
qualifier,
298-
chunk.timestamp_micros,
299-
chunk.labels,
300-
chunk.value)
301-
self._copy_from_previous(cell)
302-
else:
303-
cell.append_value(chunk.value)
304-
305-
if chunk.commit_row:
306-
self._save_current_row()
307-
row = cell = None
308-
continue
309-
310-
if chunk.value_size == 0:
311-
self._save_current_cell()
312-
cell = None
328+
try:
329+
next(self._consume_next(False))
330+
except StopIteration:
331+
return False
313332

314333
def consume_all(self, max_loops=None):
315334
"""Consume the streamed responses until there are no more.
@@ -324,13 +343,12 @@ def consume_all(self, max_loops=None):
324343
"""
325344
curr_loop = 0
326345
if max_loops is None:
327-
max_loops = float('inf')
346+
while True:
347+
if self.consume_next() is False: # guard against None
348+
return
328349
while curr_loop < max_loops:
329350
curr_loop += 1
330-
try:
331-
self.consume_next()
332-
except StopIteration:
333-
break
351+
self.consume_next()
334352

335353
@staticmethod
336354
def _validate_chunk_status(chunk):

bigtable/tests/system.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,23 @@ def test_read_row(self):
405405
}
406406
self.assertEqual(partial_row_data.cells, expected_row_contents)
407407

408+
def test_read_rows_iter(self):
409+
row = self._table.row(ROW_KEY)
410+
row_alt = self._table.row(ROW_KEY_ALT)
411+
self.rows_to_delete.extend([row, row_alt])
412+
413+
cell1, cell2, cell3, cell4 = self._write_to_row(row, row_alt,
414+
row, row_alt)
415+
row.commit()
416+
row_alt.commit()
417+
keys = [ROW_KEY, ROW_KEY_ALT]
418+
rows_data = self._table.read_rows()
419+
self.assertEqual(rows_data.rows, {})
420+
for data, key in zip(rows_data, keys):
421+
self.assertEqual(data.row_key, key)
422+
self.assertEqual(data, self._table.read_row(key))
423+
self.assertEqual(data.cells, self._table.read_row(key).cells)
424+
408425
def test_read_rows(self):
409426
row = self._table.row(ROW_KEY)
410427
row_alt = self._table.row(ROW_KEY_ALT)

bigtable/tests/unit/test_row_data.py

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,12 @@ def __init__(self, *args, **kwargs):
203203
self._consumed = []
204204

205205
def consume_next(self):
206-
value = self._response_iterator.next()
207-
self._consumed.append(value)
208-
return value
206+
try:
207+
value = self._response_iterator.next()
208+
self._consumed.append(value)
209+
return value
210+
except StopIteration:
211+
return False
209212

210213
return FakePartialRowsData
211214

@@ -522,6 +525,21 @@ def test_invalid_last_row_missing_commit(self):
522525

523526
# Non-error cases
524527

528+
def test_iter(self):
529+
values = [mock.Mock()] * 3
530+
chunks, results = self._load_json_test('two rows')
531+
532+
for value in values:
533+
value.chunks = chunks
534+
response_iterator = _MockCancellableIterator(*values)
535+
536+
partial_rows = self._make_one(response_iterator)
537+
partial_rows._last_scanned_row_key = 'BEFORE'
538+
539+
for data, value in zip(partial_rows, results):
540+
flattened = self._sort_flattend_cells(_flatten_cells(data))
541+
self.assertEqual(flattened[0], value)
542+
525543
_marker = object()
526544

527545
def _match_results(self, testcase_name, expected_result=_marker):
@@ -641,12 +659,28 @@ def _flatten_cells(prd):
641659
from google.cloud._helpers import _bytes_to_unicode
642660
from google.cloud._helpers import _microseconds_from_datetime
643661

644-
for row_key, row in prd.rows.items():
645-
for family_name, family in row.cells.items():
662+
try:
663+
# Flatten PartialRowsData
664+
for row_key, row in prd.rows.items():
665+
for family_name, family in row.cells.items():
666+
for qualifier, column in family.items():
667+
for cell in column:
668+
yield {
669+
u'rk': _bytes_to_unicode(row_key),
670+
u'fm': family_name,
671+
u'qual': _bytes_to_unicode(qualifier),
672+
u'ts': _microseconds_from_datetime(cell.timestamp),
673+
u'value': _bytes_to_unicode(cell.value),
674+
u'label': u' '.join(cell.labels),
675+
u'error': False,
676+
}
677+
except AttributeError:
678+
# Flatten PartialRowData
679+
for family_name, family in prd.cells.items():
646680
for qualifier, column in family.items():
647681
for cell in column:
648682
yield {
649-
u'rk': _bytes_to_unicode(row_key),
683+
u'rk': _bytes_to_unicode(prd.row_key),
650684
u'fm': family_name,
651685
u'qual': _bytes_to_unicode(qualifier),
652686
u'ts': _microseconds_from_datetime(cell.timestamp),

0 commit comments

Comments
 (0)