Skip to content

Commit 0a72dc4

Browse files
committed
Temp commit with idea to clear up cyclic imports in datastore package.
This is intended for discussion but not to be committed. Some remarks: - This surfaces the fact that the use of Dataset in datastore.key.Key.from_protobuf is not well-tested enough. - The changes to _helpers are made to avoid explicitly referencing the Entity class. These changes end up in uglier code for a minimal gain. - We could likely factor out the Dataset ID/connection concept from the full "dataset" concept. It seems a Dataset and its convenience methods are referenced a lot in the docs but not really tested much.
1 parent 3148468 commit 0a72dc4

File tree

9 files changed

+57
-76
lines changed

9 files changed

+57
-76
lines changed

gcloud/datastore/_dataset.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""Needs module docstring."""
2+
3+
4+
class _Dataset(object):
5+
"""Needs class docstring."""
6+
7+
def __init__(self, id, connection=None):
8+
self._connection = connection
9+
self._id = id
10+
11+
def connection(self):
12+
"""Needs method docstring."""
13+
return self._connection
14+
15+
def id(self):
16+
"""Needs method docstring."""
17+
return self._id

gcloud/datastore/_helpers.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,12 @@
88
from google.protobuf.internal.type_checkers import Int64ValueChecker
99
import pytz
1010

11-
from gcloud.datastore.entity import Entity
1211
from gcloud.datastore.key import Key
1312

1413
INT_VALUE_CHECKER = Int64ValueChecker()
1514

1615

17-
def _get_protobuf_attribute_and_value(val):
16+
def _get_protobuf_attribute_and_value(val, entity_class=type(None)):
1817
"""Given a value, return the protobuf attribute name and proper value.
1918
2019
The Protobuf API uses different attribute names
@@ -64,7 +63,7 @@ def _get_protobuf_attribute_and_value(val):
6463
name, value = 'integer', long(val) # Always cast to a long.
6564
elif isinstance(val, basestring):
6665
name, value = 'string', val
67-
elif isinstance(val, Entity):
66+
elif isinstance(val, entity_class):
6867
name, value = 'entity', val
6968
elif isinstance(val, list):
7069
name, value = 'list', val
@@ -74,7 +73,7 @@ def _get_protobuf_attribute_and_value(val):
7473
return name + '_value', value
7574

7675

77-
def _get_value_from_value_pb(value_pb):
76+
def _get_value_from_value_pb(value_pb, entity_class=type(None)):
7877
"""Given a protobuf for a Value, get the correct value.
7978
8079
The Cloud Datastore Protobuf API returns a Property Protobuf
@@ -113,15 +112,16 @@ def _get_value_from_value_pb(value_pb):
113112
result = value_pb.string_value
114113

115114
elif value_pb.HasField('entity_value'):
116-
result = Entity.from_protobuf(value_pb.entity_value)
115+
result = entity_class.from_protobuf(value_pb.entity_value)
117116

118117
elif value_pb.list_value:
119-
result = [_get_value_from_value_pb(x) for x in value_pb.list_value]
118+
result = [_get_value_from_value_pb(x, entity_class=entity_class)
119+
for x in value_pb.list_value]
120120

121121
return result
122122

123123

124-
def _get_value_from_property_pb(property_pb):
124+
def _get_value_from_property_pb(property_pb, entity_class=type(None)):
125125
"""Given a protobuf for a Property, get the correct value.
126126
127127
The Cloud Datastore Protobuf API returns a Property Protobuf
@@ -136,10 +136,11 @@ def _get_value_from_property_pb(property_pb):
136136
137137
:returns: The value provided by the Protobuf.
138138
"""
139-
return _get_value_from_value_pb(property_pb.value)
139+
return _get_value_from_value_pb(property_pb.value,
140+
entity_class=entity_class)
140141

141142

142-
def _set_protobuf_value(value_pb, val):
143+
def _set_protobuf_value(value_pb, val, entity_class=type(None)):
143144
"""Assign 'val' to the correct subfield of 'value_pb'.
144145
145146
The Protobuf API uses different attribute names
@@ -156,7 +157,8 @@ def _set_protobuf_value(value_pb, val):
156157
:class:`gcloud.datastore.entity.Entity`,
157158
:param val: The value to be assigned.
158159
"""
159-
attr, val = _get_protobuf_attribute_and_value(val)
160+
attr, val = _get_protobuf_attribute_and_value(val,
161+
entity_class=entity_class)
160162
if attr == 'key_value':
161163
value_pb.key_value.CopyFrom(val)
162164
elif attr == 'entity_value':

gcloud/datastore/connection.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from gcloud.datastore import datastore_v1_pb2 as datastore_pb
55
from gcloud.datastore import _helpers
66
from gcloud.datastore.dataset import Dataset
7+
from gcloud.datastore.entity import Entity
78

89

910
class Connection(connection.Connection):
@@ -374,7 +375,8 @@ def save_entity(self, dataset_id, key_pb, properties):
374375
prop.name = name
375376

376377
# Set the appropriate value.
377-
_helpers._set_protobuf_value(prop.value, value)
378+
_helpers._set_protobuf_value(prop.value, value,
379+
entity_class=Entity)
378380

379381
# If this is in a transaction, we should just return True. The
380382
# transaction will handle assigning any keys as necessary.

gcloud/datastore/dataset.py

Lines changed: 7 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
"""Create / interact with gcloud datastore datasets."""
22

33

4-
class Dataset(object):
4+
from gcloud.datastore._dataset import _Dataset
5+
from gcloud.datastore.entity import Entity
6+
from gcloud.datastore.query import Query
7+
from gcloud.datastore.transaction import Transaction
8+
9+
10+
class Dataset(_Dataset):
511
"""A dataset in the Cloud Datastore.
612
713
This class acts as an abstraction of a single dataset
@@ -30,36 +36,6 @@ class Dataset(object):
3036
:param connection: The connection to use for executing API calls.
3137
"""
3238

33-
def __init__(self, id, connection=None):
34-
self._connection = connection
35-
self._id = id
36-
37-
def connection(self):
38-
"""Get the current connection.
39-
40-
>>> dataset = Dataset('dataset-id', connection=conn)
41-
>>> dataset.connection()
42-
<Connection object>
43-
44-
:rtype: :class:`gcloud.datastore.connection.Connection`
45-
:returns: Returns the current connection.
46-
"""
47-
48-
return self._connection
49-
50-
def id(self):
51-
"""Get the current dataset ID.
52-
53-
>>> dataset = Dataset('dataset-id', connection=conn)
54-
>>> dataset.id()
55-
'dataset-id'
56-
57-
:rtype: string
58-
:returns: The current dataset ID.
59-
"""
60-
61-
return self._id
62-
6339
def query(self, *args, **kwargs):
6440
"""Create a query bound to this dataset.
6541
@@ -70,8 +46,6 @@ def query(self, *args, **kwargs):
7046
:rtype: :class:`gcloud.datastore.query.Query`
7147
:returns: a new Query instance, bound to this dataset.
7248
"""
73-
# This import is here to avoid circular references.
74-
from gcloud.datastore.query import Query
7549
kwargs['dataset'] = self
7650
return Query(*args, **kwargs)
7751

@@ -84,8 +58,6 @@ def entity(self, kind):
8458
:rtype: :class:`gcloud.datastore.entity.Entity`
8559
:returns: a new Entity instance, bound to this dataset.
8660
"""
87-
# This import is here to avoid circular references.
88-
from gcloud.datastore.entity import Entity
8961
return Entity(dataset=self, kind=kind)
9062

9163
def transaction(self, *args, **kwargs):
@@ -98,8 +70,6 @@ def transaction(self, *args, **kwargs):
9870
:rtype: :class:`gcloud.datastore.transaction.Transaction`
9971
:returns: a new Transaction instance, bound to this dataset.
10072
"""
101-
# This import is here to avoid circular references.
102-
from gcloud.datastore.transaction import Transaction
10373
kwargs['dataset'] = self
10474
return Transaction(*args, **kwargs)
10575

@@ -125,9 +95,6 @@ def get_entities(self, keys):
12595
:rtype: list of :class:`gcloud.datastore.entity.Entity`
12696
:return: The requested entities.
12797
"""
128-
# This import is here to avoid circular references.
129-
from gcloud.datastore.entity import Entity
130-
13198
entity_pbs = self.connection().lookup(
13299
dataset_id=self.id(),
133100
key_pbs=[k.to_protobuf() for k in keys]

gcloud/datastore/entity.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
delete or persist the data stored on the entity.
1616
"""
1717

18+
from gcloud.datastore import _helpers
1819
from gcloud.datastore import datastore_v1_pb2 as datastore_pb
1920
from gcloud.datastore.key import Key
2021

@@ -155,15 +156,12 @@ def from_protobuf(cls, pb, dataset=None):
155156
:returns: The :class:`Entity` derived from the
156157
:class:`gcloud.datastore.datastore_v1_pb2.Entity`.
157158
"""
158-
159-
# This is here to avoid circular imports.
160-
from gcloud.datastore import _helpers
161-
162159
key = Key.from_protobuf(pb.key, dataset=dataset)
163160
entity = cls.from_key(key)
164161

165162
for property_pb in pb.property:
166-
value = _helpers._get_value_from_property_pb(property_pb)
163+
value = _helpers._get_value_from_property_pb(property_pb,
164+
entity_class=Entity)
167165
entity[property_pb.name] = value
168166

169167
return entity

gcloud/datastore/key.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import copy
44
from itertools import izip
55

6+
from gcloud.datastore._dataset import _Dataset
67
from gcloud.datastore import datastore_v1_pb2 as datastore_pb
7-
from gcloud.datastore.dataset import Dataset
88

99

1010
class Key(object):
@@ -76,7 +76,7 @@ def from_protobuf(cls, pb, dataset=None):
7676
path.append(element_dict)
7777

7878
if not dataset:
79-
dataset = Dataset(id=pb.partition_id.dataset_id)
79+
dataset = _Dataset(id=pb.partition_id.dataset_id)
8080
namespace = pb.partition_id.namespace
8181
else:
8282
namespace = None

gcloud/datastore/query.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ def filter(self, expression, value):
138138
property_filter.operator = operator
139139

140140
# Set the value to filter on based on the type.
141-
_helpers._set_protobuf_value(property_filter.value, value)
141+
_helpers._set_protobuf_value(property_filter.value, value,
142+
entity_class=Entity)
142143
return clone
143144

144145
def ancestor(self, ancestor):

gcloud/datastore/test__helpers.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33

44
class Test__get_protobuf_attribute_and_value(unittest2.TestCase):
55

6-
def _callFUT(self, val):
6+
def _callFUT(self, val, entity_class=type(None)):
77
from gcloud.datastore._helpers import _get_protobuf_attribute_and_value
88

9-
return _get_protobuf_attribute_and_value(val)
9+
return _get_protobuf_attribute_and_value(val,
10+
entity_class=entity_class)
1011

1112
def test_datetime_naive(self):
1213
import calendar
@@ -86,7 +87,7 @@ def test_unicode(self):
8687
def test_entity(self):
8788
from gcloud.datastore.entity import Entity
8889
entity = Entity()
89-
name, value = self._callFUT(entity)
90+
name, value = self._callFUT(entity, entity_class=Entity)
9091
self.assertEqual(name, 'entity_value')
9192
self.assertTrue(value is entity)
9293

@@ -102,10 +103,10 @@ def test_object(self):
102103

103104
class Test__get_value_from_value_pb(unittest2.TestCase):
104105

105-
def _callFUT(self, pb):
106+
def _callFUT(self, pb, entity_class=type(None)):
106107
from gcloud.datastore._helpers import _get_value_from_value_pb
107108

108-
return _get_value_from_value_pb(pb)
109+
return _get_value_from_value_pb(pb, entity_class=entity_class)
109110

110111
def _makePB(self, attr_name, value):
111112
from gcloud.datastore.datastore_v1_pb2 import Value
@@ -168,7 +169,7 @@ def test_entity(self):
168169
prop_pb = entity_pb.property.add()
169170
prop_pb.name = 'foo'
170171
prop_pb.value.string_value = 'Foo'
171-
entity = self._callFUT(pb)
172+
entity = self._callFUT(pb, entity_class=Entity)
172173
self.assertTrue(isinstance(entity, Entity))
173174
self.assertEqual(entity['foo'], 'Foo')
174175

@@ -208,10 +209,10 @@ def test_it(self):
208209

209210
class Test_set_protobuf_value(unittest2.TestCase):
210211

211-
def _callFUT(self, value_pb, val):
212+
def _callFUT(self, value_pb, val, entity_class=type(None)):
212213
from gcloud.datastore._helpers import _set_protobuf_value
213214

214-
return _set_protobuf_value(value_pb, val)
215+
return _set_protobuf_value(value_pb, val, entity_class=entity_class)
215216

216217
def _makePB(self):
217218
from gcloud.datastore.datastore_v1_pb2 import Value
@@ -286,7 +287,7 @@ def test_entity_empty_wo_key(self):
286287

287288
pb = self._makePB()
288289
entity = Entity()
289-
self._callFUT(pb, entity)
290+
self._callFUT(pb, entity, entity_class=Entity)
290291
value = pb.entity_value
291292
self.assertEqual(value.key.SerializeToString(), '')
292293
props = list(value.property)
@@ -300,7 +301,7 @@ def test_entity_w_key(self):
300301
key = Key(path=[{'kind': 'KIND', 'id': 123}])
301302
entity = Entity().key(key)
302303
entity['foo'] = 'Foo'
303-
self._callFUT(pb, entity)
304+
self._callFUT(pb, entity, entity_class=Entity)
304305
value = pb.entity_value
305306
self.assertEqual(value.key, key.to_protobuf())
306307
props = list(value.property)

gcloud/storage/test_iterator.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -313,13 +313,6 @@ def build_api_url(self, path, query_params=None):
313313
return urlunsplit(('http', 'example.com', path, qs, ''))
314314

315315

316-
class _Bucket(object):
317-
path = '/b/name'
318-
319-
def __init__(self, connection):
320-
self.connection = connection
321-
322-
323316
class _Key(object):
324317
CHUNK_SIZE = 10
325318
path = '/b/name/o/key'

0 commit comments

Comments
 (0)