Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move the 'thread-local stack' implementation up from datastore. #622

Merged
merged 3 commits into from
Feb 12, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions gcloud/_localstack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# 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.
"""Thread-local resource stack.

This module is not part of the public API surface of `gcloud`.
"""

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]
55 changes: 2 additions & 53 deletions gcloud/datastore/batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
28 changes: 0 additions & 28 deletions gcloud/datastore/test_batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
43 changes: 43 additions & 0 deletions gcloud/test__localstack.py
Original file line number Diff line number Diff line change
@@ -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), [])