Skip to content

Commit bc718d3

Browse files
committed
Merge pull request #1489 from dhermes/bigtable-happybase-table
Adding Bigtable HappyBase Table class.
2 parents 14263ee + beb897d commit bc718d3

File tree

5 files changed

+289
-0
lines changed

5 files changed

+289
-0
lines changed

gcloud/bigtable/happybase/connection.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import six
2121

2222
from gcloud.bigtable.client import Client
23+
from gcloud.bigtable.happybase.table import Table
2324

2425

2526
# Constants reproduced here for HappyBase compatibility, though values
@@ -202,3 +203,34 @@ def close(self):
202203
def __del__(self):
203204
if self._cluster is not None:
204205
self.close()
206+
207+
def _table_name(self, name):
208+
"""Construct a table name by optionally adding a table name prefix.
209+
210+
:type name: str
211+
:param name: The name to have a prefix added to it.
212+
213+
:rtype: str
214+
:returns: The prefixed name, if the current connection has a table
215+
prefix set.
216+
"""
217+
if self.table_prefix is None:
218+
return name
219+
220+
return self.table_prefix + self.table_prefix_separator + name
221+
222+
def table(self, name, use_prefix=True):
223+
"""Table factory.
224+
225+
:type name: str
226+
:param name: The name of the table to be created.
227+
228+
:type use_prefix: bool
229+
:param use_prefix: Whether to use the table prefix (if any).
230+
231+
:rtype: `Table <gcloud.bigtable.happybase.table.Table>`
232+
:returns: Table instance owned by this connection.
233+
"""
234+
if use_prefix:
235+
name = self._table_name(name)
236+
return Table(name, self)

gcloud/bigtable/happybase/table.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Copyright 2016 Google Inc. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Google Cloud Bigtable HappyBase table module."""
16+
17+
18+
from gcloud.bigtable.table import Table as _LowLevelTable
19+
20+
21+
def make_row(cell_map, include_timestamp):
22+
"""Make a row dict for a Thrift cell mapping.
23+
24+
.. note::
25+
26+
This method is only provided for HappyBase compatibility, but does not
27+
actually work.
28+
29+
:type cell_map: dict
30+
:param cell_map: Dictionary with ``fam:col`` strings as keys and ``TCell``
31+
instances as values.
32+
33+
:type include_timestamp: bool
34+
:param include_timestamp: Flag to indicate if cell timestamps should be
35+
included with the output.
36+
37+
:raises: :class:`NotImplementedError <exceptions.NotImplementedError>`
38+
always
39+
"""
40+
raise NotImplementedError('The Cloud Bigtable API output is not the same '
41+
'as the output from the Thrift server, so this '
42+
'helper can not be implemented.', 'Called with',
43+
cell_map, include_timestamp)
44+
45+
46+
def make_ordered_row(sorted_columns, include_timestamp):
47+
"""Make a row dict for sorted Thrift column results from scans.
48+
49+
.. note::
50+
51+
This method is only provided for HappyBase compatibility, but does not
52+
actually work.
53+
54+
:type sorted_columns: list
55+
:param sorted_columns: List of ``TColumn`` instances from Thrift.
56+
57+
:type include_timestamp: bool
58+
:param include_timestamp: Flag to indicate if cell timestamps should be
59+
included with the output.
60+
61+
:raises: :class:`NotImplementedError <exceptions.NotImplementedError>`
62+
always
63+
"""
64+
raise NotImplementedError('The Cloud Bigtable API output is not the same '
65+
'as the output from the Thrift server, so this '
66+
'helper can not be implemented.', 'Called with',
67+
sorted_columns, include_timestamp)
68+
69+
70+
class Table(object):
71+
"""Representation of Cloud Bigtable table.
72+
73+
Used for adding data and
74+
75+
:type name: str
76+
:param name: The name of the table.
77+
78+
:type connection: :class:`.Connection`
79+
:param connection: The connection which has access to the table.
80+
"""
81+
82+
def __init__(self, name, connection):
83+
self.name = name
84+
# This remains as legacy for HappyBase, but only the cluster
85+
# from the connection is needed.
86+
self.connection = connection
87+
self._low_level_table = None
88+
if self.connection is not None:
89+
self._low_level_table = _LowLevelTable(self.name,
90+
self.connection._cluster)
91+
92+
def __repr__(self):
93+
return '<table.Table name=%r>' % (self.name,)

gcloud/bigtable/happybase/test_connection.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,72 @@ def test___del__no_cluster(self):
212212
connection.__del__()
213213
self.assertEqual(cluster._client.stop_calls, 0)
214214

215+
def test__table_name_with_prefix_set(self):
216+
table_prefix = 'table-prefix'
217+
table_prefix_separator = '<>'
218+
cluster = _Cluster()
219+
220+
connection = self._makeOne(
221+
autoconnect=False,
222+
table_prefix=table_prefix,
223+
table_prefix_separator=table_prefix_separator,
224+
cluster=cluster)
225+
226+
name = 'some-name'
227+
prefixed = connection._table_name(name)
228+
self.assertEqual(prefixed,
229+
table_prefix + table_prefix_separator + name)
230+
231+
def test__table_name_with_no_prefix_set(self):
232+
cluster = _Cluster()
233+
connection = self._makeOne(autoconnect=False,
234+
cluster=cluster)
235+
236+
name = 'some-name'
237+
prefixed = connection._table_name(name)
238+
self.assertEqual(prefixed, name)
239+
240+
def test_table_factory(self):
241+
from gcloud.bigtable.happybase.table import Table
242+
243+
cluster = _Cluster() # Avoid implicit environ check.
244+
connection = self._makeOne(autoconnect=False, cluster=cluster)
245+
246+
name = 'table-name'
247+
table = connection.table(name)
248+
249+
self.assertTrue(isinstance(table, Table))
250+
self.assertEqual(table.name, name)
251+
self.assertEqual(table.connection, connection)
252+
253+
def _table_factory_prefix_helper(self, use_prefix=True):
254+
from gcloud.bigtable.happybase.table import Table
255+
256+
cluster = _Cluster() # Avoid implicit environ check.
257+
table_prefix = 'table-prefix'
258+
table_prefix_separator = '<>'
259+
connection = self._makeOne(
260+
autoconnect=False, table_prefix=table_prefix,
261+
table_prefix_separator=table_prefix_separator,
262+
cluster=cluster)
263+
264+
name = 'table-name'
265+
table = connection.table(name, use_prefix=use_prefix)
266+
267+
self.assertTrue(isinstance(table, Table))
268+
prefixed_name = table_prefix + table_prefix_separator + name
269+
if use_prefix:
270+
self.assertEqual(table.name, prefixed_name)
271+
else:
272+
self.assertEqual(table.name, name)
273+
self.assertEqual(table.connection, connection)
274+
275+
def test_table_factory_with_prefix(self):
276+
self._table_factory_prefix_helper(use_prefix=True)
277+
278+
def test_table_factory_with_ignored_prefix(self):
279+
self._table_factory_prefix_helper(use_prefix=False)
280+
215281

216282
class _Client(object):
217283

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Copyright 2016 Google Inc. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import unittest2
16+
17+
18+
class Test_make_row(unittest2.TestCase):
19+
20+
def _callFUT(self, *args, **kwargs):
21+
from gcloud.bigtable.happybase.table import make_row
22+
return make_row(*args, **kwargs)
23+
24+
def test_it(self):
25+
with self.assertRaises(NotImplementedError):
26+
self._callFUT({}, False)
27+
28+
29+
class Test_make_ordered_row(unittest2.TestCase):
30+
31+
def _callFUT(self, *args, **kwargs):
32+
from gcloud.bigtable.happybase.table import make_ordered_row
33+
return make_ordered_row(*args, **kwargs)
34+
35+
def test_it(self):
36+
with self.assertRaises(NotImplementedError):
37+
self._callFUT([], False)
38+
39+
40+
class TestTable(unittest2.TestCase):
41+
42+
def _getTargetClass(self):
43+
from gcloud.bigtable.happybase.table import Table
44+
return Table
45+
46+
def _makeOne(self, *args, **kwargs):
47+
return self._getTargetClass()(*args, **kwargs)
48+
49+
def test_constructor(self):
50+
from gcloud._testing import _Monkey
51+
from gcloud.bigtable.happybase import table as MUT
52+
53+
name = 'table-name'
54+
cluster = object()
55+
connection = _Connection(cluster)
56+
tables_constructed = []
57+
58+
def make_low_level_table(*args, **kwargs):
59+
result = _MockLowLevelTable(*args, **kwargs)
60+
tables_constructed.append(result)
61+
return result
62+
63+
with _Monkey(MUT, _LowLevelTable=make_low_level_table):
64+
table = self._makeOne(name, connection)
65+
self.assertEqual(table.name, name)
66+
self.assertEqual(table.connection, connection)
67+
68+
table_instance, = tables_constructed
69+
self.assertEqual(table._low_level_table, table_instance)
70+
self.assertEqual(table_instance.args, (name, cluster))
71+
self.assertEqual(table_instance.kwargs, {})
72+
73+
def test_constructor_null_connection(self):
74+
name = 'table-name'
75+
connection = None
76+
table = self._makeOne(name, connection)
77+
self.assertEqual(table.name, name)
78+
self.assertEqual(table.connection, connection)
79+
self.assertEqual(table._low_level_table, None)
80+
81+
def test___repr__(self):
82+
name = 'table-name'
83+
table = self._makeOne(name, None)
84+
self.assertEqual(repr(table), '<table.Table name=\'table-name\'>')
85+
86+
87+
class _Connection(object):
88+
89+
def __init__(self, cluster):
90+
self._cluster = cluster
91+
92+
93+
class _MockLowLevelTable(object):
94+
95+
def __init__(self, *args, **kwargs):
96+
self.args = args
97+
self.kwargs = kwargs

scripts/verify_included_modules.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
'gcloud.bigtable.cluster',
3535
'gcloud.bigtable.column_family',
3636
'gcloud.bigtable.happybase.connection',
37+
'gcloud.bigtable.happybase.table',
3738
'gcloud.bigtable.row',
3839
'gcloud.bigtable.row_data',
3940
'gcloud.bigtable.table',

0 commit comments

Comments
 (0)