Skip to content

Commit

Permalink
Completing interface for Batch.
Browse files Browse the repository at this point in the history
Actually implemented and tested the context manager, but did
not implement the three public instance methods.
  • Loading branch information
dhermes committed Sep 4, 2015
1 parent 1ac713d commit b3d47c9
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 1 deletion.
85 changes: 84 additions & 1 deletion gcloud_bigtable/happybase/batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class Batch(object):
can't be transactional.
:type wal: object
:param wal: Unused parameter (Boolean for using the HBase Write Ahead Log.)
:param wal: Unused parameter (Boolean for using the HBase Write Ahead Log).
Provided for compatibility with HappyBase, but irrelevant for
Cloud Bigtable since it does not have a Write Ahead Log.
Expand All @@ -68,3 +68,86 @@ def __init__(self, table, timestamp=None, batch_size=None,
self._batch_size = batch_size
self._timestamp = timestamp
self._transaction = transaction

def send(self):
"""Send / commit the batch of mutations to the server.
:raises: :class:`NotImplementedError <exceptions.NotImplementedError>`
temporarily until the method is implemented.
"""
raise NotImplementedError('Temporarily not implemented.')

def put(self, row, data, wal=None):
"""Insert data into a row in the table owned by this batch.
:type row: str
:param row: The row key where the mutation will be "put".
:type data: dict
:param data: Dictionary containing the data to be inserted. The keys
are columns names (of the form ``fam:col``) and the values
are strings (bytes) to be stored in those columns.
:type wal: :data:`NoneType <types.NoneType>`
:param wal: Unused parameter (to over-ride the default on the
instance). Provided for compatibility with HappyBase, but
irrelevant for Cloud Bigtable since it does not have a
Write Ahead Log.
:raises: :class:`NotImplementedError <exceptions.NotImplementedError>`
temporarily until the method is implemented.
"""
raise NotImplementedError('Temporarily not implemented.')

def delete(self, row, columns=None, wal=None):
"""Delete data from a row in the table owned by this batch.
:type row: str
:param row: The row key where the mutation will be "put".
:type columns: list
:param columns: (Optional) Iterable containing column names (as
strings). Each column name can be either
* an entire column family: ``fam`` or ``fam:``
* an single column: ``fam:col``
:type wal: :data:`NoneType <types.NoneType>`
:param wal: Unused parameter (to over-ride the default on the
instance). Provided for compatibility with HappyBase, but
irrelevant for Cloud Bigtable since it does not have a
Write Ahead Log.
:raises: :class:`NotImplementedError <exceptions.NotImplementedError>`
temporarily until the method is implemented.
"""
raise NotImplementedError('Temporarily not implemented.')

def __enter__(self):
"""Enter context manager, no set-up required."""
return self

def __exit__(self, exc_type, exc_value, traceback):
"""Exit context manager, no set-up required.
:type exc_type: type
:param exc_type: The type of the exception if one occurred while the
context manager was active. Otherwise, :data:`None`.
:type exc_value: :class:`Exception <exceptions.Exception>`
:param exc_value: An instance of ``exc_type`` if an exception occurred
while the context was active.
Otherwise, :data:`None`.
:type traceback: ``traceback`` type
:param traceback: The traceback where the exception occurred (if one
did occur). Otherwise, :data:`None`.
"""
# If the context manager encountered an exception and the batch is
# transactional, we don't commit the mutations.
if self._transaction and exc_type is not None:
return

# NOTE: For non-transactional batches, this will even commit mutations
# if an error occurred during the context manager.
self.send()
89 changes: 89 additions & 0 deletions gcloud_bigtable/happybase/test_batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,92 @@ def test_constructor_with_batch_size_and_transactional(self):
with self.assertRaises(TypeError):
self._makeOne(table, batch_size=batch_size,
transaction=transaction)

def test_send(self):
table = object()
batch = self._makeOne(table)
with self.assertRaises(NotImplementedError):
batch.send()

def test_put(self):
table = object()
batch = self._makeOne(table)

row = 'row-key'
data = {}
wal = None
with self.assertRaises(NotImplementedError):
batch.put(row, data, wal=wal)

def test_delete(self):
table = object()
batch = self._makeOne(table)

row = 'row-key'
columns = []
wal = None
with self.assertRaises(NotImplementedError):
batch.delete(row, columns=columns, wal=wal)

def test_context_manager(self):
klass = self._getTargetClass()

class BatchWithSend(klass):

_send_called = False

def send(self):
self._send_called = True

table = object()
batch = BatchWithSend(table)
self.assertFalse(batch._send_called)

with batch:
pass

self.assertTrue(batch._send_called)

def test_context_manager_with_exception_non_transactional(self):
klass = self._getTargetClass()

class BatchWithSend(klass):

_send_called = False

def send(self):
self._send_called = True

table = object()
batch = BatchWithSend(table)
self.assertFalse(batch._send_called)

with self.assertRaises(ValueError):
with batch:
raise ValueError('Something bad happened')

self.assertTrue(batch._send_called)

def test_context_manager_with_exception_transactional(self):
klass = self._getTargetClass()

class BatchWithSend(klass):

_send_called = False

def send(self):
self._send_called = True

table = object()
batch = BatchWithSend(table, transaction=True)
self.assertFalse(batch._send_called)

with self.assertRaises(ValueError):
with batch:
raise ValueError('Something bad happened')

self.assertFalse(batch._send_called)

# Just to make sure send() actually works (and to make cover happy).
batch.send()
self.assertTrue(batch._send_called)

0 comments on commit b3d47c9

Please sign in to comment.