Skip to content

Commit 7f58a3c

Browse files
author
Chris Rossi
authored
Refactor options. (#78)
Treat "options" in a more or less uniform way for get/put/delete/query.
1 parent 9210fff commit 7f58a3c

10 files changed

Lines changed: 988 additions & 425 deletions

File tree

packages/google-cloud-ndb/src/google/cloud/ndb/_datastore_api.py

Lines changed: 57 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
from google.cloud.ndb import context as context_module
2929
from google.cloud.ndb import _eventloop
30+
from google.cloud.ndb import _options
3031
from google.cloud.ndb import _remote
3132
from google.cloud.ndb import _retry
3233
from google.cloud.ndb import tasklets
@@ -105,7 +106,7 @@ def rpc_call():
105106
return rpc_call()
106107

107108

108-
def lookup(key, **options):
109+
def lookup(key, options):
109110
"""Look up a Datastore entity.
110111
111112
Gets an entity from Datastore, asynchronously. Actually adds the request to
@@ -114,15 +115,13 @@ def lookup(key, **options):
114115
115116
Args:
116117
key (~datastore.Key): The key for the entity to retrieve.
117-
options (Dict[str, Any]): The options for the request. For example,
118-
``{"read_consistency": EVENTUAL}``.
118+
options (_options.ReadOptions): The options for the request. For
119+
example, ``{"read_consistency": EVENTUAL}``.
119120
120121
Returns:
121122
:class:`~tasklets.Future`: If not an exception, future's result will be
122123
either an entity protocol buffer or _NOT_FOUND.
123124
"""
124-
_check_unsupported_options(options)
125-
126125
batch = _get_batch(_LookupBatch, options)
127126
return batch.add(key)
128127

@@ -138,9 +137,8 @@ def _get_batch(batch_cls, options):
138137
Args:
139138
batch_cls (type): Class representing the kind of operation being
140139
batched.
141-
options (Dict[str, Any]): The options for the request. For example,
142-
``{"read_consistency": EVENTUAL}``. Calls with different options
143-
will be placed in different batches.
140+
options (_options.ReadOptions): The options for the request. Calls with
141+
different options will be placed in different batches.
144142
145143
Returns:
146144
batch_cls: An instance of the batch class.
@@ -150,7 +148,15 @@ def _get_batch(batch_cls, options):
150148
if batches is None:
151149
context.batches[batch_cls] = batches = {}
152150

153-
options_key = tuple(sorted(options.items()))
151+
options_key = tuple(
152+
sorted(
153+
(
154+
(key, value)
155+
for key, value in options.items()
156+
if value is not None
157+
)
158+
)
159+
)
154160
batch = batches.get(options_key)
155161
if batch is not None:
156162
return batch
@@ -173,9 +179,8 @@ class _LookupBatch:
173179
protocol buffers to dependent futures.
174180
175181
Args:
176-
options (Dict[str, Any]): The options for the request. For example,
177-
``{"read_consistency": EVENTUAL}``. Calls with different options
178-
will be placed in different batches.
182+
options (_options.ReadOptions): The options for the request. Calls with
183+
different options will be placed in different batches.
179184
"""
180185

181186
def __init__(self, options):
@@ -205,7 +210,7 @@ def idle_callback(self):
205210
keys.append(key_pb)
206211

207212
read_options = _get_read_options(self.options)
208-
retries = self.options.get("retries")
213+
retries = self.options.retries
209214
rpc = _datastore_lookup(keys, read_options, retries=retries)
210215
rpc.add_done_callback(self.lookup_callback)
211216

@@ -288,10 +293,9 @@ def _get_read_options(options):
288293
"""Get the read options for a request.
289294
290295
Args:
291-
options (Dict[str, Any]): The options for the request. For example,
292-
``{"read_consistency": EVENTUAL}``. May contain options unrelated
293-
to creating a :class:`datastore_pb2.ReadOptions` instance, which
294-
will be ignored.
296+
options (_options.ReadOptions): The options for the request. May
297+
contain options unrelated to creating a
298+
:class:`datastore_pb2.ReadOptions` instance, which will be ignored.
295299
296300
Returns:
297301
datastore_pb2.ReadOptions: The options instance for passing to the
@@ -303,13 +307,11 @@ def _get_read_options(options):
303307
"""
304308
transaction = _get_transaction(options)
305309

306-
read_consistency = options.get("read_consistency")
307-
if read_consistency is None:
308-
read_consistency = options.get("read_policy") # Legacy NDB
310+
read_consistency = options.read_consistency
309311

310312
if transaction is not None and read_consistency is EVENTUAL:
311313
raise ValueError(
312-
"read_consistency must be EVENTUAL when in transaction"
314+
"read_consistency must not be EVENTUAL when in transaction"
313315
)
314316

315317
return datastore_pb2.ReadOptions(
@@ -324,25 +326,29 @@ def _get_transaction(options):
324326
it will return the transaction for the current context.
325327
326328
Args:
327-
options (Dict[str, Any]): The options for the request. Only
329+
options (_options.ReadOptions): The options for the request. Only
328330
``transaction`` will have any bearing here.
329331
330332
Returns:
331333
Union[bytes, NoneType]: The transaction identifier, or :data:`None`.
332334
"""
333-
context = context_module.get_context()
334-
return options.get("transaction", context.transaction)
335+
transaction = getattr(options, "transaction", None)
336+
if transaction is None:
337+
context = context_module.get_context()
338+
transaction = context.transaction
339+
340+
return transaction
335341

336342

337-
def put(entity_pb, **options):
343+
def put(entity_pb, options):
338344
"""Store an entity in datastore.
339345
340346
The entity can be a new entity to be saved for the first time or an
341347
existing entity that has been updated.
342348
343349
Args:
344350
entity_pb (datastore_v1.types.Entity): The entity to be stored.
345-
options (Dict[str, Any]): Options for this request.
351+
options (_options.Options): Options for this request.
346352
347353
Returns:
348354
tasklets.Future: Result will be completed datastore key
@@ -357,15 +363,15 @@ def put(entity_pb, **options):
357363
return batch.put(entity_pb)
358364

359365

360-
def delete(key, **options):
366+
def delete(key, options):
361367
"""Delete an entity from Datastore.
362368
363369
Deleting an entity that doesn't exist does not result in an error. The
364370
result is the same regardless.
365371
366372
Args:
367373
key (datastore.Key): The key for the entity to be deleted.
368-
options (Dict[str, Any]): Options for this request.
374+
options (_options.Options): Options for this request.
369375
370376
Returns:
371377
tasklets.Future: Will be finished when entity is deleted. Result will
@@ -384,20 +390,19 @@ class _NonTransactionalCommitBatch:
384390
"""Batch for tracking a set of mutations for a non-transactional commit.
385391
386392
Attributes:
387-
options (Dict[str, Any]): See Args.
393+
options (_options.Options): See Args.
388394
mutations (List[datastore_pb2.Mutation]): Sequence of mutation protocol
389395
buffers accumumlated for this batch.
390396
futures (List[tasklets.Future]): Sequence of futures for return results
391397
of the commit. The i-th element of ``futures`` corresponds to the
392398
i-th element of ``mutations``.
393399
394400
Args:
395-
options (Dict[str, Any]): The options for the request. Calls with
401+
options (_options.Options): The options for the request. Calls with
396402
different options will be placed in different batches.
397403
"""
398404

399405
def __init__(self, options):
400-
_check_unsupported_options(options)
401406
self.options = options
402407
self.mutations = []
403408
self.futures = []
@@ -441,7 +446,7 @@ def idle_callback(self):
441446
def commit_callback(rpc):
442447
_process_commit(rpc, futures)
443448

444-
retries = self.options.get("retries")
449+
retries = self.options.retries
445450
rpc = _datastore_commit(self.mutations, None, retries=retries)
446451
rpc.add_done_callback(commit_callback)
447452

@@ -459,7 +464,7 @@ def commit(transaction, retries=None):
459464
tasklets.Future: Result will be none, will finish when the transaction
460465
is committed.
461466
"""
462-
batch = _get_commit_batch(transaction, {})
467+
batch = _get_commit_batch(transaction, _options.Options())
463468
return batch.commit(retries=retries)
464469

465470

@@ -469,27 +474,26 @@ def _get_commit_batch(transaction, options):
469474
Args:
470475
transaction (bytes): The transaction id. Different transactions will
471476
have different batchs.
472-
options (Dict[str, Any]): Options for the batch. Only "transaction" is
473-
supported at this time.
477+
options (_options.Options): Options for the batch. Not supported at
478+
this time.
474479
475480
Returns:
476481
_TransactionalCommitBatch: The batch.
477482
"""
478483
# Support for different options will be tricky if we're in a transaction,
479484
# since we can only do one commit, so any options that affect that gRPC
480-
# call would all need to be identical. For now, only "transaction" is
481-
# suppoorted if there is a transaction.
482-
options = options.copy()
483-
options.pop("transaction", None)
484-
for key in options:
485-
raise NotImplementedError("Passed bad option: {!r}".format(key))
485+
# call would all need to be identical. For now, no options are supported
486+
# here.
487+
for key, value in options.items():
488+
if value:
489+
raise NotImplementedError("Passed bad option: {!r}".format(key))
486490

487491
# Since we're in a transaction, we need to hang on to the batch until
488492
# commit time, so we need to store it separately from other batches.
489493
context = context_module.get_context()
490494
batch = context.commit_batches.get(transaction)
491495
if batch is None:
492-
batch = _TransactionalCommitBatch({"transaction": transaction})
496+
batch = _TransactionalCommitBatch(transaction, options)
493497
context.commit_batches[transaction] = batch
494498

495499
return batch
@@ -499,14 +503,14 @@ class _TransactionalCommitBatch(_NonTransactionalCommitBatch):
499503
"""Batch for tracking a set of mutations to be committed for a transaction.
500504
501505
Attributes:
502-
options (Dict[str, Any]): See Args.
506+
options (_options.Options): See Args.
503507
mutations (List[datastore_pb2.Mutation]): Sequence of mutation protocol
504508
buffers accumumlated for this batch.
505509
futures (List[tasklets.Future]): Sequence of futures for return results
506510
of the commit. The i-th element of ``futures`` corresponds to the
507511
i-th element of ``mutations``.
508512
transaction (bytes): The transaction id of the transaction for this
509-
commit, if in a transaction.
513+
commit.
510514
allocating_ids (List[tasklets.Future]): Futures for any calls to
511515
AllocateIds that are fired off before commit.
512516
incomplete_mutations (List[datastore_pb2.Mutation]): List of mutations
@@ -519,13 +523,15 @@ class _TransactionalCommitBatch(_NonTransactionalCommitBatch):
519523
receive results of id allocation.
520524
521525
Args:
522-
options (Dict[str, Any]): The options for the request. Calls with
526+
transaction (bytes): The transaction id of the transaction for this
527+
commit.
528+
options (_options.Options): The options for the request. Calls with
523529
different options will be placed in different batches.
524530
"""
525531

526-
def __init__(self, options):
532+
def __init__(self, transaction, options):
527533
super(_TransactionalCommitBatch, self).__init__(options)
528-
self.transaction = _get_transaction(options)
534+
self.transaction = transaction
529535
self.allocating_ids = []
530536
self.incomplete_mutations = []
531537
self.incomplete_futures = []
@@ -582,7 +588,7 @@ def callback(rpc):
582588
# Signal that we're done allocating these ids
583589
allocating_ids.set_result(None)
584590

585-
retries = self.options.get("retries")
591+
retries = self.options.retries
586592
keys = [mutation.upsert.key for mutation in mutations]
587593
rpc = _datastore_allocate_ids(keys, retries=retries)
588594
rpc.add_done_callback(callback)
@@ -612,8 +618,9 @@ def commit(self, retries=None):
612618
613619
Args:
614620
retries (int): Number of times to potentially retry the call. If
615-
:data:`None` is passed, will use :data:`_retry._DEFAULT_RETRIES`.
616-
If :data:`0` is passed, the call is attempted only once.
621+
:data:`None` is passed, will use
622+
:data:`_retry._DEFAULT_RETRIES`. If :data:`0` is passed, the
623+
call is attempted only once.
617624
"""
618625
if not self.mutations:
619626
return
@@ -851,46 +858,3 @@ def _datastore_rollback(transaction, retries=None):
851858
)
852859

853860
return make_call("Rollback", request, retries=retries)
854-
855-
856-
_OPTIONS_SUPPORTED = {
857-
"transaction",
858-
"read_consistency",
859-
"read_policy",
860-
"retries",
861-
}
862-
863-
_OPTIONS_NOT_IMPLEMENTED = {
864-
"deadline",
865-
"force_writes",
866-
"use_cache",
867-
"use_memcache",
868-
"use_datastore",
869-
"memcache_timeout",
870-
"max_memcache_items",
871-
"xg",
872-
"propagation",
873-
"retries",
874-
}
875-
876-
877-
def _check_unsupported_options(options):
878-
"""Check to see if any passed options are not supported.
879-
880-
options (Dict[str, Any]): The options for the request. For example,
881-
``{"read_consistency": EVENTUAL}``.
882-
883-
Raises: NotImplementedError if any options are not supported.
884-
"""
885-
for key in options:
886-
if key in _OPTIONS_NOT_IMPLEMENTED:
887-
# option is used in Legacy NDB, but has not yet been implemented in
888-
# the rewrite, nor have we determined it won't be used, yet.
889-
raise NotImplementedError(
890-
"Support for option {!r} has not yet been implemented".format(
891-
key
892-
)
893-
)
894-
895-
elif key not in _OPTIONS_SUPPORTED:
896-
raise NotImplementedError("Passed bad option: {!r}".format(key))

0 commit comments

Comments
 (0)