def _do_enter(cls, state, decorator_args): _init_storage() mandatory = decorator_args.get("mandatory", False) independent = decorator_args.get("independent", False) read_only = decorator_args.get("read_only", False) using = decorator_args.get("using", "default") mandatory = False if mandatory is None else mandatory independent = False if independent is None else independent read_only = False if read_only is None else read_only state.using = using = "default" if using is None else using enable_cache = decorator_args.get("enable_cache", True) context = caching.get_context() state.original_context_enabled = context.context_enabled # Only force-disable cache, do not force enable (e.g. if already disabled # in the context, leave as is) if enable_cache is False: context.context_enabled = enable_cache new_transaction = None connection = connections[using] # Connect if necessary (mainly in tests) if not connection.connection: connection.connect() connection = connection.connection assert (connection) if independent: new_transaction = IndependentTransaction(connection) elif in_atomic_block(): new_transaction = NestedTransaction(connection) elif mandatory: raise TransactionFailedError( "You've specified that an outer transaction is mandatory, but one doesn't exist" ) else: new_transaction = NormalTransaction(connection) _STORAGE.transaction_stack.setdefault(using, []).append(new_transaction) _STORAGE.transaction_stack[using][-1].enter() if isinstance(new_transaction, (IndependentTransaction, NormalTransaction)): caching.get_context().stack.push() # We may have created a new transaction, we may not. current_transaction() returns # the actual active transaction (highest NormalTransaction or lowest IndependentTransaction) # or None if we're in a non_atomic, or there are no transactions return current_transaction()
def _do_enter(cls, state, decorator_args): _init_storage() context = caching.get_context() state.using = using = decorator_args.get("using", "default") connection = connections[using] # Connect if necessary (mainly in tests) if not connection.connection: connection.connect() connection = connection.connection assert (connection) # For non_atomic blocks we pass a Batch as the transaction new_transaction = NoTransaction(connection.gclient.batch()) _STORAGE.transaction_stack.setdefault(using, []).append(new_transaction) _STORAGE.transaction_stack[using][-1].enter() # Store the current state of the stack (aside from the first entry) state.original_stack = copy.deepcopy(context.stack.stack[1:]) # Unwind the in-context stack leaving just the first entry while len(context.stack.stack) > 1: context.stack.pop(discard=True) return current_transaction()
def _do_exit(cls, state, decorator_args, exception): _init_storage() transaction = _STORAGE.transaction_stack[state.using].pop() try: if transaction._datastore_transaction: if exception: transaction._datastore_transaction.rollback() else: try: transaction._datastore_transaction.commit() except exceptions.GoogleCloudError: raise TransactionFailedError() finally: if isinstance(transaction, (IndependentTransaction, NormalTransaction)): context = caching.get_context() # Clear the context cache at the end of a transaction if exception: context.stack.pop(discard=True) else: context.stack.pop(apply_staged=True, clear_staged=True) transaction.exit()
def _do_exit(cls, state, decorator_args, exception): _init_storage() context = caching.get_context() transaction = _STORAGE.transaction_stack[state.using].pop() try: if transaction._datastore_transaction: if exception: transaction._datastore_transaction.rollback() else: try: transaction._datastore_transaction.commit() except exceptions.GoogleAPIError: raise TransactionFailedError() finally: # Restore the context stack as it was context.stack.stack = context.stack.stack + state.original_stack transaction.exit()
def __enter__(self): self.context = caching.get_context() self.context.context_enabled = False return self