@@ -167,7 +167,7 @@ def dataset(self, *args, **kwargs):
167167 kwargs ['connection' ] = self
168168 return Dataset (* args , ** kwargs )
169169
170- def lookup (self , dataset_id , key_pbs ):
170+ def lookup (self , dataset_id , key_pbs , missing = None , deferred = None ):
171171 """Lookup keys from a dataset in the Cloud Datastore.
172172
173173 Maps the ``DatastoreService.Lookup`` protobuf RPC.
@@ -201,6 +201,16 @@ def lookup(self, dataset_id, key_pbs):
201201 (or a single Key)
202202 :param key_pbs: The key (or keys) to retrieve from the datastore.
203203
204+ :type missing: an empty list or None.
205+ :param missing: If a list is passed, the key-only entity protobufs
206+ returned by the backend as "missing" will be copied
207+ into it. Use only as a keyword param.
208+
209+ :type deferred: an empty list or None.
210+ :param deferred: If a list is passed, the key protobufs returned
211+ by the backend as "deferred" will be copied into it.
212+ Use only as a keyword param.
213+
204214 :rtype: list of :class:`gcloud.datastore.datastore_v1_pb2.Entity`
205215 (or a single Entity)
206216 :returns: The entities corresponding to the keys provided.
@@ -209,6 +219,12 @@ def lookup(self, dataset_id, key_pbs):
209219 If multiple keys were provided and no results matched,
210220 this will return an empty list.
211221 """
222+ if missing is not None and missing != []:
223+ raise ValueError ('missing must be None or an empty list' )
224+
225+ if deferred is not None and deferred != []:
226+ raise ValueError ('deferred must be None or an empty list' )
227+
212228 lookup_request = datastore_pb .LookupRequest ()
213229
214230 single_key = isinstance (key_pbs , datastore_pb .Key )
@@ -219,10 +235,28 @@ def lookup(self, dataset_id, key_pbs):
219235 for key_pb in key_pbs :
220236 lookup_request .key .add ().CopyFrom (key_pb )
221237
222- lookup_response = self ._rpc (dataset_id , 'lookup' , lookup_request ,
223- datastore_pb .LookupResponse )
238+ results = []
239+ while True : # loop against possible deferred.
240+ lookup_response = self ._rpc (dataset_id , 'lookup' , lookup_request ,
241+ datastore_pb .LookupResponse )
242+
243+ results .extend (
244+ [result .entity for result in lookup_response .found ])
245+
246+ if missing is not None :
247+ missing .extend (
248+ [result .entity for result in lookup_response .missing ])
224249
225- results = [result .entity for result in lookup_response .found ]
250+ if deferred is not None :
251+ deferred .extend ([key for key in lookup_response .deferred ])
252+ break
253+
254+ if not lookup_response .deferred :
255+ break
256+
257+ # We have deferred keys, and the user didn't ask to know about
258+ # them, so retry (but only with the deferred ones).
259+ _copy_deferred_keys (lookup_request , lookup_response )
226260
227261 if single_key :
228262 if results :
@@ -476,3 +510,14 @@ def delete_entities(self, dataset_id, key_pbs):
476510 self .commit (dataset_id , mutation )
477511
478512 return True
513+
514+
515+ def _copy_deferred_keys (lookup_request , lookup_response ):
516+ """Clear requested keys and copy deferred keys back in.
517+
518+ Helper ``Connection.lookup()``.
519+ """
520+ for old_key in list (lookup_request .key ):
521+ lookup_request .key .remove (old_key )
522+ for def_key in lookup_response .deferred :
523+ lookup_request .key .add ().CopyFrom (def_key )
0 commit comments