def read_row(self, row_key, filter_=None, timeout_seconds=None): """Read a single row from this table. :type row_key: bytes :param row_key: The key of the row to read from. :type filter_: :class:`.row.RowFilter`, :class:`.row.RowFilterChain`, :class:`.row.RowFilterUnion` or :class:`.row.ConditionalRowFilter` :param filter_: (Optional) The filter to apply to the contents of the row. If unset, returns the entire row. :type timeout_seconds: int :param timeout_seconds: Number of seconds for request time-out. If not passed, defaults to value set on table. :rtype: :class:`.PartialRowData`, :data:`NoneType <types.NoneType>` :returns: The contents of the row if any chunks were returned in the response, otherwise :data:`None`. :raises: :class:`ValueError <exceptions.ValueError>` if a commit row chunk is never encountered. """ request_pb = _create_row_request(self.name, row_key=row_key, filter_=filter_) timeout_seconds = timeout_seconds or self.timeout_seconds response_iterator = self.client.data_stub.ReadRows(request_pb, timeout_seconds) # We expect an iterator of `data_messages_pb2.ReadRowsResponse` result = PartialRowData(row_key) for read_rows_response in response_iterator: result.update_from_read_rows(read_rows_response) # Make sure the result actually contains data. if not result._chunks_encountered: return None # Make sure the result was committed by the back-end. if not result.committed: raise ValueError('The row remains partial / is not committed.') return result