Skip to content

Commit d1e1dd3

Browse files
committed
Fix googleapis#260: remove import cycles in datastore.
'entity.Entity.from_protobuf' classmethod -> 'helpers.entity_from_protobuf'. 'key.Key.from_protobuf' classmethod -> 'helpers.key_from_protobuf'.
1 parent 949df73 commit d1e1dd3

9 files changed

Lines changed: 186 additions & 167 deletions

File tree

gcloud/datastore/dataset.py

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

3+
from gcloud.datastore import helpers
4+
from gcloud.datastore.entity import Entity
5+
from gcloud.datastore.query import Query
6+
from gcloud.datastore.transaction import Transaction
7+
38

49
class Dataset(object):
510
"""A dataset in the Cloud Datastore.
@@ -70,8 +75,6 @@ def query(self, *args, **kwargs):
7075
:rtype: :class:`gcloud.datastore.query.Query`
7176
:returns: a new Query instance, bound to this dataset.
7277
"""
73-
# This import is here to avoid circular references.
74-
from gcloud.datastore.query import Query
7578
kwargs['dataset'] = self
7679
return Query(*args, **kwargs)
7780

@@ -84,8 +87,6 @@ def entity(self, kind):
8487
:rtype: :class:`gcloud.datastore.entity.Entity`
8588
:returns: a new Entity instance, bound to this dataset.
8689
"""
87-
# This import is here to avoid circular references.
88-
from gcloud.datastore.entity import Entity
8990
return Entity(dataset=self, kind=kind)
9091

9192
def transaction(self, *args, **kwargs):
@@ -98,8 +99,6 @@ def transaction(self, *args, **kwargs):
9899
:rtype: :class:`gcloud.datastore.transaction.Transaction`
99100
:returns: a new Transaction instance, bound to this dataset.
100101
"""
101-
# This import is here to avoid circular references.
102-
from gcloud.datastore.transaction import Transaction
103102
kwargs['dataset'] = self
104103
return Transaction(*args, **kwargs)
105104

@@ -125,15 +124,13 @@ def get_entities(self, keys):
125124
:rtype: list of :class:`gcloud.datastore.entity.Entity`
126125
:return: The requested entities.
127126
"""
128-
# This import is here to avoid circular references.
129-
from gcloud.datastore.entity import Entity
130-
131127
entity_pbs = self.connection().lookup(
132128
dataset_id=self.id(),
133129
key_pbs=[k.to_protobuf() for k in keys]
134130
)
135131

136132
entities = []
137133
for entity_pb in entity_pbs:
138-
entities.append(Entity.from_protobuf(entity_pb, dataset=self))
134+
entities.append(helpers.entity_from_protobuf(
135+
entity_pb, dataset=self))
139136
return entities

gcloud/datastore/entity.py

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -146,32 +146,6 @@ def from_key(cls, key, dataset=None):
146146

147147
return cls(dataset).key(key)
148148

149-
@classmethod
150-
def from_protobuf(cls, pb, dataset=None):
151-
"""Factory method for creating an entity based on a protobuf.
152-
153-
The protobuf should be one returned from the Cloud Datastore
154-
Protobuf API.
155-
156-
:type pb: :class:`gcloud.datastore.datastore_v1_pb2.Entity`
157-
:param pb: The Protobuf representing the entity.
158-
159-
:returns: The :class:`Entity` derived from the
160-
:class:`gcloud.datastore.datastore_v1_pb2.Entity`.
161-
"""
162-
163-
# This is here to avoid circular imports.
164-
from gcloud.datastore import helpers
165-
166-
key = Key.from_protobuf(pb.key)
167-
entity = cls.from_key(key, dataset)
168-
169-
for property_pb in pb.property:
170-
value = helpers._get_value_from_property_pb(property_pb)
171-
entity[property_pb.name] = value
172-
173-
return entity
174-
175149
@property
176150
def _must_key(self):
177151
"""Return our key, or raise NoKey if not set.
@@ -248,9 +222,11 @@ def save(self):
248222
transaction.add_auto_id_entity(self)
249223

250224
if isinstance(key_pb, datastore_pb.Key):
251-
updated_key = Key.from_protobuf(key_pb)
225+
path = [
226+
{'kind': x.kind, 'id': x.id, 'name': x.name}
227+
for x in key_pb.path_element]
252228
# Update the path (which may have been altered).
253-
self._key = key.path(updated_key.path())
229+
self._key = key.path(path)
254230

255231
return self
256232

gcloud/datastore/helpers.py

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,60 @@
1414
INT_VALUE_CHECKER = Int64ValueChecker()
1515

1616

17+
def entity_from_protobuf(pb, dataset=None):
18+
"""Factory method for creating an entity based on a protobuf.
19+
20+
The protobuf should be one returned from the Cloud Datastore
21+
Protobuf API.
22+
23+
:type pb: :class:`gcloud.datastore.datastore_v1_pb2.Entity`
24+
:param pb: The Protobuf representing the entity.
25+
26+
:rtype: :class:`gcloud.datastore.entity.Entity`
27+
:returns: The entity derived from the protobuf.
28+
"""
29+
key = key_from_protobuf(pb.key)
30+
entity = Entity.from_key(key, dataset)
31+
32+
for property_pb in pb.property:
33+
value = _get_value_from_property_pb(property_pb)
34+
entity[property_pb.name] = value
35+
36+
return entity
37+
38+
39+
def key_from_protobuf(pb):
40+
"""Factory method for creating a key based on a protobuf.
41+
42+
The protobuf should be one returned from the Cloud Datastore
43+
Protobuf API.
44+
45+
:type pb: :class:`gcloud.datastore.datastore_v1_pb2.Key`
46+
:param pb: The Protobuf representing the key.
47+
48+
:rtype: :class:`gcloud.datastore.key.Key`
49+
:returns: a new `Key` instance
50+
"""
51+
path = []
52+
for element in pb.path_element:
53+
element_dict = {'kind': element.kind}
54+
55+
if element.HasField('id'):
56+
element_dict['id'] = element.id
57+
58+
# This is safe: we expect proto objects returned will only have
59+
# one of `name` or `id` set.
60+
if element.HasField('name'):
61+
element_dict['name'] = element.name
62+
63+
path.append(element_dict)
64+
65+
dataset_id = pb.partition_id.dataset_id or None
66+
namespace = pb.partition_id.namespace
67+
68+
return Key(path, namespace, dataset_id)
69+
70+
1771
def _get_protobuf_attribute_and_value(val):
1872
"""Given a value, return the protobuf attribute name and proper value.
1973
@@ -105,7 +159,7 @@ def _get_value_from_value_pb(value_pb):
105159
result = naive.replace(tzinfo=pytz.utc)
106160

107161
elif value_pb.HasField('key_value'):
108-
result = Key.from_protobuf(value_pb.key_value)
162+
result = key_from_protobuf(value_pb.key_value)
109163

110164
elif value_pb.HasField('boolean_value'):
111165
result = value_pb.boolean_value
@@ -123,7 +177,7 @@ def _get_value_from_value_pb(value_pb):
123177
result = value_pb.blob_value
124178

125179
elif value_pb.HasField('entity_value'):
126-
result = Entity.from_protobuf(value_pb.entity_value)
180+
result = entity_from_protobuf(value_pb.entity_value)
127181

128182
elif value_pb.list_value:
129183
result = [_get_value_from_value_pb(x) for x in value_pb.list_value]

gcloud/datastore/key.py

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -42,38 +42,6 @@ def _clone(self):
4242
"""
4343
return copy.deepcopy(self)
4444

45-
@classmethod
46-
def from_protobuf(cls, pb):
47-
"""Factory method for creating a key based on a protobuf.
48-
49-
The protobuf should be one returned from the Cloud Datastore
50-
Protobuf API.
51-
52-
:type pb: :class:`gcloud.datastore.datastore_v1_pb2.Key`
53-
:param pb: The Protobuf representing the key.
54-
55-
:rtype: :class:`gcloud.datastore.key.Key`
56-
:returns: a new `Key` instance
57-
"""
58-
path = []
59-
for element in pb.path_element:
60-
element_dict = {'kind': element.kind}
61-
62-
if element.HasField('id'):
63-
element_dict['id'] = element.id
64-
65-
# This is safe: we expect proto objects returned will only have
66-
# one of `name` or `id` set.
67-
if element.HasField('name'):
68-
element_dict['name'] = element.name
69-
70-
path.append(element_dict)
71-
72-
dataset_id = pb.partition_id.dataset_id or None
73-
namespace = pb.partition_id.namespace
74-
75-
return cls(path, namespace, dataset_id)
76-
7745
def to_protobuf(self):
7846
"""Return a protobuf corresponding to the key.
7947

gcloud/datastore/query.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
from gcloud.datastore import datastore_v1_pb2 as datastore_pb
66
from gcloud.datastore import helpers
7-
from gcloud.datastore.entity import Entity
87
from gcloud.datastore.key import Key
98

109

@@ -343,7 +342,7 @@ def fetch(self, limit=None):
343342
entity_pbs, end_cursor = query_results[:2]
344343

345344
self._cursor = end_cursor
346-
return [Entity.from_protobuf(entity, dataset=self.dataset())
345+
return [helpers.entity_from_protobuf(entity, dataset=self.dataset())
347346
for entity in entity_pbs]
348347

349348
def cursor(self):

gcloud/datastore/test_entity.py

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -76,48 +76,6 @@ def test_from_key_w_dataset(self):
7676
self.assertEqual(key.kind(), _KIND)
7777
self.assertEqual(key.id(), _ID)
7878

79-
def test_from_protobuf_wo_dataset(self):
80-
from gcloud.datastore import datastore_v1_pb2 as datastore_pb
81-
82-
entity_pb = datastore_pb.Entity()
83-
entity_pb.key.partition_id.dataset_id = _DATASET_ID
84-
entity_pb.key.path_element.add(kind=_KIND, id=_ID)
85-
entity_pb.key.partition_id.dataset_id = _DATASET_ID
86-
prop_pb = entity_pb.property.add()
87-
prop_pb.name = 'foo'
88-
prop_pb.value.string_value = 'Foo'
89-
klass = self._getTargetClass()
90-
entity = klass.from_protobuf(entity_pb)
91-
self.assertTrue(entity.dataset() is None)
92-
self.assertEqual(entity.kind(), _KIND)
93-
self.assertEqual(entity['foo'], 'Foo')
94-
key = entity.key()
95-
self.assertEqual(key._dataset_id, _DATASET_ID)
96-
self.assertEqual(key.kind(), _KIND)
97-
self.assertEqual(key.id(), _ID)
98-
99-
def test_from_protobuf_w_dataset(self):
100-
from gcloud.datastore import datastore_v1_pb2 as datastore_pb
101-
from gcloud.datastore.dataset import Dataset
102-
103-
entity_pb = datastore_pb.Entity()
104-
entity_pb.key.partition_id.dataset_id = _DATASET_ID
105-
entity_pb.key.path_element.add(kind=_KIND, id=_ID)
106-
entity_pb.key.partition_id.dataset_id = _DATASET_ID
107-
prop_pb = entity_pb.property.add()
108-
prop_pb.name = 'foo'
109-
prop_pb.value.string_value = 'Foo'
110-
dataset = Dataset(_DATASET_ID)
111-
klass = self._getTargetClass()
112-
entity = klass.from_protobuf(entity_pb, dataset)
113-
self.assertTrue(entity.dataset() is dataset)
114-
self.assertEqual(entity.kind(), _KIND)
115-
self.assertEqual(entity['foo'], 'Foo')
116-
key = entity.key()
117-
self.assertEqual(key._dataset_id, _DATASET_ID)
118-
self.assertEqual(key.kind(), _KIND)
119-
self.assertEqual(key.id(), _ID)
120-
12179
def test__must_key_no_key(self):
12280
from gcloud.datastore.entity import NoKey
12381

@@ -224,7 +182,7 @@ def test_save_w_returned_key(self):
224182
self.assertEqual(entity['foo'], 'Foo')
225183
self.assertEqual(connection._saved,
226184
(_DATASET_ID, 'KEY', {'foo': 'Foo'}))
227-
self.assertEqual(key._path, [{'kind': _KIND, 'id': _ID}])
185+
self.assertEqual(key._path, [{'kind': _KIND, 'id': _ID, 'name': ''}])
228186

229187
def test_delete_no_key(self):
230188
from gcloud.datastore.entity import NoKey

0 commit comments

Comments
 (0)