Skip to content

Commit f73ad7e

Browse files
committed
Merge pull request #622 from tseaver/move-thread_local_stack_up
Move the 'thread-local stack' implementation up from datastore.
2 parents 32e50eb + c6821d6 commit f73ad7e

File tree

4 files changed

+106
-81
lines changed

4 files changed

+106
-81
lines changed

gcloud/_localstack.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Copyright 2014 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+
"""Thread-local resource stack.
15+
16+
This module is not part of the public API surface of `gcloud`.
17+
"""
18+
19+
try:
20+
from threading import local as Local
21+
except ImportError: # pragma: NO COVER (who doesn't have it?)
22+
class Local(object):
23+
"""Placeholder for non-threaded applications."""
24+
25+
26+
class _LocalStack(Local):
27+
"""Manage a thread-local LIFO stack of resources.
28+
29+
Intended for use in :class:`gcloud.datastore.batch.Batch.__enter__`,
30+
:class:`gcloud.storage.batch.Batch.__enter__`, etc.
31+
"""
32+
def __init__(self):
33+
super(_LocalStack, self).__init__()
34+
self._stack = []
35+
36+
def __iter__(self):
37+
"""Iterate the stack in LIFO order.
38+
"""
39+
return iter(reversed(self._stack))
40+
41+
def push(self, resource):
42+
"""Push a resource onto our stack.
43+
"""
44+
self._stack.append(resource)
45+
46+
def pop(self):
47+
"""Pop a resource from our stack.
48+
49+
:raises: IndexError if the stack is empty.
50+
:returns: the top-most resource, after removing it.
51+
"""
52+
return self._stack.pop()
53+
54+
@property
55+
def top(self):
56+
"""Get the top-most resource
57+
58+
:returns: the top-most item, or None if the stack is empty.
59+
"""
60+
if len(self._stack) > 0:
61+
return self._stack[-1]

gcloud/datastore/batch.py

Lines changed: 2 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -13,66 +13,15 @@
1313
# limitations under the License.
1414

1515
"""Create / interact with a batch of updates / deletes."""
16-
try:
17-
from threading import local as Local
18-
except ImportError: # pragma: NO COVER (who doesn't have it?)
19-
class Local(object):
20-
"""Placeholder for non-threaded applications."""
2116

17+
from gcloud._localstack import _LocalStack
2218
from gcloud.datastore import _implicit_environ
2319
from gcloud.datastore import helpers
2420
from gcloud.datastore.key import _dataset_ids_equal
2521
from gcloud.datastore import _datastore_v1_pb2 as datastore_pb
2622

2723

28-
class _Batches(Local):
29-
"""Manage a thread-local LIFO stack of active batches / transactions.
30-
31-
Intended for use only in :class:`gcloud.datastore.batch.Batch.__enter__`
32-
"""
33-
def __init__(self):
34-
super(_Batches, self).__init__()
35-
self._stack = []
36-
37-
def __iter__(self):
38-
"""Iterate the stack in LIFO order.
39-
"""
40-
return iter(reversed(self._stack))
41-
42-
def push(self, batch):
43-
"""Push a batch / transaction onto our stack.
44-
45-
Intended for use only in :meth:`gcloud.datastore.batch.Batch.__enter__`
46-
47-
:type batch: :class:`gcloud.datastore.batch.Batch` or
48-
:class:`gcloud.datastore.transaction.Transaction`
49-
"""
50-
self._stack.append(batch)
51-
52-
def pop(self):
53-
"""Pop a batch / transaction from our stack.
54-
55-
Intended for use only in :meth:`gcloud.datastore.batch.Batch.__enter__`
56-
57-
:rtype: :class:`gcloud.datastore.batch.Batch` or
58-
:class:`gcloud.datastore.transaction.Transaction`
59-
:raises: IndexError if the stack is empty.
60-
"""
61-
return self._stack.pop()
62-
63-
@property
64-
def top(self):
65-
"""Get the top-most batch / transaction
66-
67-
:rtype: :class:`gcloud.datastore.batch.Batch` or
68-
:class:`gcloud.datastore.transaction.Transaction` or None
69-
:returns: the top-most item, or None if the stack is empty.
70-
"""
71-
if len(self._stack) > 0:
72-
return self._stack[-1]
73-
74-
75-
_BATCHES = _Batches()
24+
_BATCHES = _LocalStack()
7625

7726

7827
class Batch(object):

gcloud/datastore/test_batch.py

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,34 +15,6 @@
1515
import unittest2
1616

1717

18-
class Test_Batches(unittest2.TestCase):
19-
20-
def _getTargetClass(self):
21-
from gcloud.datastore.batch import _Batches
22-
23-
return _Batches
24-
25-
def _makeOne(self):
26-
return self._getTargetClass()()
27-
28-
def test_it(self):
29-
batch1, batch2 = object(), object()
30-
batches = self._makeOne()
31-
self.assertEqual(list(batches), [])
32-
self.assertTrue(batches.top is None)
33-
batches.push(batch1)
34-
self.assertTrue(batches.top is batch1)
35-
batches.push(batch2)
36-
self.assertTrue(batches.top is batch2)
37-
popped = batches.pop()
38-
self.assertTrue(popped is batch2)
39-
self.assertTrue(batches.top is batch1)
40-
self.assertEqual(list(batches), [batch1])
41-
popped = batches.pop()
42-
self.assertTrue(batches.top is None)
43-
self.assertEqual(list(batches), [])
44-
45-
4618
class TestBatch(unittest2.TestCase):
4719

4820
def _getTargetClass(self):

gcloud/test__localstack.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Copyright 2014 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__LocalStack(unittest2.TestCase):
19+
20+
def _getTargetClass(self):
21+
from gcloud._localstack import _LocalStack
22+
23+
return _LocalStack
24+
25+
def _makeOne(self):
26+
return self._getTargetClass()()
27+
28+
def test_it(self):
29+
batch1, batch2 = object(), object()
30+
batches = self._makeOne()
31+
self.assertEqual(list(batches), [])
32+
self.assertTrue(batches.top is None)
33+
batches.push(batch1)
34+
self.assertTrue(batches.top is batch1)
35+
batches.push(batch2)
36+
self.assertTrue(batches.top is batch2)
37+
popped = batches.pop()
38+
self.assertTrue(popped is batch2)
39+
self.assertTrue(batches.top is batch1)
40+
self.assertEqual(list(batches), [batch1])
41+
popped = batches.pop()
42+
self.assertTrue(batches.top is None)
43+
self.assertEqual(list(batches), [])

0 commit comments

Comments
 (0)