From c9e26e16de9e63b6f7d2bc1fc7397df171dd27f7 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Thu, 12 Feb 2015 15:46:38 -0500 Subject: [PATCH 1/3] Move the 'thread-local stack' implementation up from datastore. Preparing to tackle #15, which needs similar support in storage. --- gcloud/_localstack.py | 57 ++++++++++++++++++++++++++++++++++ gcloud/datastore/batch.py | 55 ++------------------------------ gcloud/datastore/test_batch.py | 28 ----------------- gcloud/test__localstack.py | 43 +++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 81 deletions(-) create mode 100644 gcloud/_localstack.py create mode 100644 gcloud/test__localstack.py diff --git a/gcloud/_localstack.py b/gcloud/_localstack.py new file mode 100644 index 000000000000..a44f33d7e492 --- /dev/null +++ b/gcloud/_localstack.py @@ -0,0 +1,57 @@ +# Copyright 2014 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +try: + from threading import local as Local +except ImportError: # pragma: NO COVER (who doesn't have it?) + class Local(object): + """Placeholder for non-threaded applications.""" + + +class _LocalStack(Local): + """Manage a thread-local LIFO stack of resources. + + Intended for use in :class:`gcloud.datastore.batch.Batch.__enter__`, + :class:`gcloud.storage.batch.Batch.__enter__`, etc. + """ + def __init__(self): + super(_LocalStack, self).__init__() + self._stack = [] + + def __iter__(self): + """Iterate the stack in LIFO order. + """ + return iter(reversed(self._stack)) + + def push(self, resource): + """Push a resource onto our stack. + """ + self._stack.append(resource) + + def pop(self): + """Pop a resource from our stack. + + :raises: IndexError if the stack is empty. + :returns: the top-most resource, after removing it. + """ + return self._stack.pop() + + @property + def top(self): + """Get the top-most resource + + :returns: the top-most item, or None if the stack is empty. + """ + if len(self._stack) > 0: + return self._stack[-1] diff --git a/gcloud/datastore/batch.py b/gcloud/datastore/batch.py index c489934087ba..2b61a96ab820 100644 --- a/gcloud/datastore/batch.py +++ b/gcloud/datastore/batch.py @@ -13,66 +13,15 @@ # limitations under the License. """Create / interact with a batch of updates / deletes.""" -try: - from threading import local as Local -except ImportError: # pragma: NO COVER (who doesn't have it?) - class Local(object): - """Placeholder for non-threaded applications.""" +from gcloud._localstack import _LocalStack from gcloud.datastore import _implicit_environ from gcloud.datastore import helpers from gcloud.datastore.key import _dataset_ids_equal from gcloud.datastore import _datastore_v1_pb2 as datastore_pb -class _Batches(Local): - """Manage a thread-local LIFO stack of active batches / transactions. - - Intended for use only in :class:`gcloud.datastore.batch.Batch.__enter__` - """ - def __init__(self): - super(_Batches, self).__init__() - self._stack = [] - - def __iter__(self): - """Iterate the stack in LIFO order. - """ - return iter(reversed(self._stack)) - - def push(self, batch): - """Push a batch / transaction onto our stack. - - Intended for use only in :meth:`gcloud.datastore.batch.Batch.__enter__` - - :type batch: :class:`gcloud.datastore.batch.Batch` or - :class:`gcloud.datastore.transaction.Transaction` - """ - self._stack.append(batch) - - def pop(self): - """Pop a batch / transaction from our stack. - - Intended for use only in :meth:`gcloud.datastore.batch.Batch.__enter__` - - :rtype: :class:`gcloud.datastore.batch.Batch` or - :class:`gcloud.datastore.transaction.Transaction` - :raises: IndexError if the stack is empty. - """ - return self._stack.pop() - - @property - def top(self): - """Get the top-most batch / transaction - - :rtype: :class:`gcloud.datastore.batch.Batch` or - :class:`gcloud.datastore.transaction.Transaction` or None - :returns: the top-most item, or None if the stack is empty. - """ - if len(self._stack) > 0: - return self._stack[-1] - - -_BATCHES = _Batches() +_BATCHES = _LocalStack() class Batch(object): diff --git a/gcloud/datastore/test_batch.py b/gcloud/datastore/test_batch.py index 579fee92a612..9f3a1b70dfb8 100644 --- a/gcloud/datastore/test_batch.py +++ b/gcloud/datastore/test_batch.py @@ -15,34 +15,6 @@ import unittest2 -class Test_Batches(unittest2.TestCase): - - def _getTargetClass(self): - from gcloud.datastore.batch import _Batches - - return _Batches - - def _makeOne(self): - return self._getTargetClass()() - - def test_it(self): - batch1, batch2 = object(), object() - batches = self._makeOne() - self.assertEqual(list(batches), []) - self.assertTrue(batches.top is None) - batches.push(batch1) - self.assertTrue(batches.top is batch1) - batches.push(batch2) - self.assertTrue(batches.top is batch2) - popped = batches.pop() - self.assertTrue(popped is batch2) - self.assertTrue(batches.top is batch1) - self.assertEqual(list(batches), [batch1]) - popped = batches.pop() - self.assertTrue(batches.top is None) - self.assertEqual(list(batches), []) - - class TestBatch(unittest2.TestCase): def _getTargetClass(self): diff --git a/gcloud/test__localstack.py b/gcloud/test__localstack.py new file mode 100644 index 000000000000..f615b8caa790 --- /dev/null +++ b/gcloud/test__localstack.py @@ -0,0 +1,43 @@ +# Copyright 2014 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest2 + + +class Test__LocalStack(unittest2.TestCase): + + def _getTargetClass(self): + from gcloud._localstack import _LocalStack + + return _LocalStack + + def _makeOne(self): + return self._getTargetClass()() + + def test_it(self): + batch1, batch2 = object(), object() + batches = self._makeOne() + self.assertEqual(list(batches), []) + self.assertTrue(batches.top is None) + batches.push(batch1) + self.assertTrue(batches.top is batch1) + batches.push(batch2) + self.assertTrue(batches.top is batch2) + popped = batches.pop() + self.assertTrue(popped is batch2) + self.assertTrue(batches.top is batch1) + self.assertEqual(list(batches), [batch1]) + popped = batches.pop() + self.assertTrue(batches.top is None) + self.assertEqual(list(batches), []) From 83ed43763efb0f4af5234e6aac01af8c58f17911 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Thu, 12 Feb 2015 16:27:36 -0500 Subject: [PATCH 2/3] Add module docstring. --- gcloud/_localstack.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gcloud/_localstack.py b/gcloud/_localstack.py index a44f33d7e492..144ba76874dc 100644 --- a/gcloud/_localstack.py +++ b/gcloud/_localstack.py @@ -11,6 +11,10 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +"""Thread-local resource stack. + +Not an API. +""" try: from threading import local as Local From c6821d66880a7626d46039ef11ce3427df687c08 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Thu, 12 Feb 2015 16:42:43 -0500 Subject: [PATCH 3/3] Clarify 'not an API' in docstring. See: https://github.com/GoogleCloudPlatform/gcloud-python/pull/622#discussion_r24622366. [ci skip]. --- gcloud/_localstack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gcloud/_localstack.py b/gcloud/_localstack.py index 144ba76874dc..2026acbd4fc1 100644 --- a/gcloud/_localstack.py +++ b/gcloud/_localstack.py @@ -13,7 +13,7 @@ # limitations under the License. """Thread-local resource stack. -Not an API. +This module is not part of the public API surface of `gcloud`. """ try: