Skip to content

Commit

Permalink
Implementing HappyBase Connection.__del__.
Browse files Browse the repository at this point in the history
This makes sure to close a connection when a connection object is
destroyed.

This required updating our test cluster stubs to have a client
which could be stop()-ed.
  • Loading branch information
dhermes committed Sep 5, 2015
1 parent 85b016b commit 56c6094
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 21 deletions.
11 changes: 11 additions & 0 deletions gcloud_bigtable/happybase/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ def __init__(self, host=DEFAULT_HOST, port=DEFAULT_PORT, timeout=None,
if autoconnect:
self.open()

self._initialized = True

def open(self):
"""Open the underlying transport to Cloud Bigtable.
Expand All @@ -191,6 +193,15 @@ def close(self):
"""
self._cluster.client.stop()

def __del__(self):
try:
self._initialized
except AttributeError:
# Failure from constructor
return
else:
self.close()

def table(self, name, use_prefix=True):
"""Table factory.
Expand Down
46 changes: 30 additions & 16 deletions gcloud_bigtable/happybase/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,10 @@ def _makeOne(self, *args, **kwargs):

def test_constructor_defaults(self):
cluster = _Cluster() # Avoid implicit environ check.
# autoconnect=True is default, so we need a valid client
cluster.client = client = _Client()
self.assertEqual(client.start_calls, 0)
self.assertEqual(cluster.client.start_calls, 0)
connection = self._makeOne(cluster=cluster)
self.assertEqual(client.start_calls, 1)
self.assertEqual(client.stop_calls, 0)
self.assertEqual(cluster.client.start_calls, 1)
self.assertEqual(cluster.client.stop_calls, 0)

self.assertEqual(connection._cluster, cluster)
self.assertEqual(connection.table_prefix, None)
Expand All @@ -103,7 +101,7 @@ def test_constructor_missing_cluster(self):
from gcloud_bigtable._testing import _Monkey
from gcloud_bigtable.happybase import connection as MUT

cluster = object()
cluster = _Cluster()
timeout = object()
mock_get_cluster = _MockCalled(cluster)
with _Monkey(MUT, _get_cluster=mock_get_cluster):
Expand All @@ -119,7 +117,7 @@ def test_constructor_explicit(self):
timeout = object()
table_prefix = 'table-prefix'
table_prefix_separator = 'sep'
cluster_copy = object()
cluster_copy = _Cluster()
cluster = _Cluster(cluster_copy)

connection = self._makeOne(
Expand Down Expand Up @@ -168,21 +166,36 @@ def test_constructor_with_protocol(self):

def test_open(self):
cluster = _Cluster() # Avoid implicit environ check.
cluster.client = client = _Client()
connection = self._makeOne(autoconnect=False, cluster=cluster)
self.assertEqual(client.start_calls, 0)
self.assertEqual(cluster.client.start_calls, 0)
connection.open()
self.assertEqual(client.start_calls, 1)
self.assertEqual(client.stop_calls, 0)
self.assertEqual(cluster.client.start_calls, 1)
self.assertEqual(cluster.client.stop_calls, 0)

def test_close(self):
cluster = _Cluster() # Avoid implicit environ check.
cluster.client = client = _Client()
connection = self._makeOne(autoconnect=False, cluster=cluster)
self.assertEqual(client.stop_calls, 0)
self.assertEqual(cluster.client.stop_calls, 0)
connection.close()
self.assertEqual(client.stop_calls, 1)
self.assertEqual(client.start_calls, 0)
self.assertEqual(cluster.client.stop_calls, 1)
self.assertEqual(cluster.client.start_calls, 0)

def test___del__good_initialization(self):
cluster = _Cluster() # Avoid implicit environ check.
connection = self._makeOne(autoconnect=False, cluster=cluster)
self.assertEqual(cluster.client.stop_calls, 0)
connection.__del__()
self.assertEqual(cluster.client.stop_calls, 1)

def test___del__bad_initialization(self):
cluster = _Cluster() # Avoid implicit environ check.
connection = self._makeOne(autoconnect=False, cluster=cluster)
# Fake that initialization failed.
del connection._initialized

self.assertEqual(cluster.client.stop_calls, 0)
connection.__del__()
self.assertEqual(cluster.client.stop_calls, 0)

def test_table(self):
cluster = _Cluster() # Avoid implicit environ check.
Expand Down Expand Up @@ -277,7 +290,8 @@ class _Cluster(object):

def __init__(self, *copies):
self.copies = list(copies)
self.client = None
# Included to support Connection.__del__
self.client = _Client()

def copy(self):
if self.copies:
Expand Down
21 changes: 16 additions & 5 deletions gcloud_bigtable/happybase/test_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def test_constructor_defaults(self):
from gcloud_bigtable.happybase.connection import Connection

size = 11
cluster_copy = object()
cluster_copy = _Cluster()
all_copies = [cluster_copy] * size
cluster = _Cluster(*all_copies) # Avoid implicit environ check.
pool = self._makeOne(size, cluster=cluster)
Expand Down Expand Up @@ -77,9 +77,9 @@ def open(self):
self._open_called = True

# First make sure the custom Connection class does as expected.
cluster_copy1 = object()
cluster_copy2 = object()
cluster_copy3 = object()
cluster_copy1 = _Cluster()
cluster_copy2 = _Cluster()
cluster_copy3 = _Cluster()
cluster = _Cluster(cluster_copy1, cluster_copy2, cluster_copy3)
connection = ConnectionWithOpen(autoconnect=False, cluster=cluster)
self.assertFalse(connection._open_called)
Expand All @@ -105,7 +105,7 @@ def test_constructor_infers_cluster(self):
from gcloud_bigtable.happybase import pool as MUT

size = 1
cluster_copy = object()
cluster_copy = _Cluster()
all_copies = [cluster_copy] * size
cluster = _Cluster(*all_copies)

Expand Down Expand Up @@ -144,10 +144,21 @@ def test_connection(self):
pool.connection(timeout=timeout)


class _Client(object):

def __init__(self):
self.stop_calls = 0

def stop(self):
self.stop_calls += 1


class _Cluster(object):

def __init__(self, *copies):
self.copies = list(copies)
# Included to support Connection.__del__
self.client = _Client()

def copy(self):
if self.copies:
Expand Down

0 comments on commit 56c6094

Please sign in to comment.