Skip to content

Commit f62c53f

Browse files
committed
PYTHON-2020 Make ClientSession._in_transaction a public property
1 parent 9a88224 commit f62c53f

File tree

6 files changed

+58
-20
lines changed

6 files changed

+58
-20
lines changed

pymongo/client_session.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ def __enter__(self):
267267
return self
268268

269269
def __exit__(self, exc_type, exc_val, exc_tb):
270-
if self.__session._in_transaction:
270+
if self.__session.in_transaction:
271271
if exc_val is None:
272272
self.__session.commit_transaction()
273273
else:
@@ -356,7 +356,7 @@ def end_session(self):
356356
def _end_session(self, lock):
357357
if self._server_session is not None:
358358
try:
359-
if self._in_transaction:
359+
if self.in_transaction:
360360
self.abort_transaction()
361361
finally:
362362
self._client._return_server_session(self._server_session, lock)
@@ -505,7 +505,7 @@ def callback(session, custom_arg, custom_kwarg=None):
505505
try:
506506
ret = callback(self)
507507
except Exception as exc:
508-
if self._in_transaction:
508+
if self.in_transaction:
509509
self.abort_transaction()
510510
if (isinstance(exc, PyMongoError) and
511511
exc.has_error_label("TransientTransactionError") and
@@ -514,8 +514,7 @@ def callback(session, custom_arg, custom_kwarg=None):
514514
continue
515515
raise
516516

517-
if self._transaction.state in (
518-
_TxnState.NONE, _TxnState.COMMITTED, _TxnState.ABORTED):
517+
if not self.in_transaction:
519518
# Assume callback intentionally ended the transaction.
520519
return ret
521520

@@ -551,7 +550,7 @@ def start_transaction(self, read_concern=None, write_concern=None,
551550
"""
552551
self._check_ended()
553552

554-
if self._in_transaction:
553+
if self.in_transaction:
555554
raise InvalidOperation("Transaction already in progress")
556555

557556
read_concern = self._inherit_option("read_concern", read_concern)
@@ -589,7 +588,7 @@ def commit_transaction(self):
589588
"Cannot call commitTransaction after calling abortTransaction")
590589
elif state is _TxnState.COMMITTED:
591590
# We're explicitly retrying the commit, move the state back to
592-
# "in progress" so that _in_transaction returns true.
591+
# "in progress" so that in_transaction returns true.
593592
self._transaction.state = _TxnState.IN_PROGRESS
594593
retry = True
595594

@@ -750,7 +749,7 @@ def _process_response(self, reply):
750749
"""Process a response to a command that was run with this session."""
751750
self._advance_cluster_time(reply.get('$clusterTime'))
752751
self._advance_operation_time(reply.get('operationTime'))
753-
if self._in_transaction and self._transaction.sharded:
752+
if self.in_transaction and self._transaction.sharded:
754753
recovery_token = reply.get('recoveryToken')
755754
if recovery_token:
756755
self._transaction.recovery_token = recovery_token
@@ -761,8 +760,11 @@ def has_ended(self):
761760
return self._server_session is None
762761

763762
@property
764-
def _in_transaction(self):
765-
"""True if this session has an active multi-statement transaction."""
763+
def in_transaction(self):
764+
"""True if this session has an active multi-statement transaction.
765+
766+
.. versionadded:: 3.10
767+
"""
766768
return self._transaction.active()
767769

768770
@property
@@ -783,7 +785,7 @@ def _unpin_mongos(self):
783785

784786
def _txn_read_preference(self):
785787
"""Return read preference of this transaction or None."""
786-
if self._in_transaction:
788+
if self.in_transaction:
787789
return self._transaction.opts.read_preference
788790
return None
789791

@@ -793,14 +795,14 @@ def _apply_to(self, command, is_retryable, read_preference):
793795
self._server_session.last_use = monotonic.time()
794796
command['lsid'] = self._server_session.session_id
795797

796-
if not self._in_transaction:
798+
if not self.in_transaction:
797799
self._transaction.reset()
798800

799801
if is_retryable:
800802
command['txnNumber'] = self._server_session.transaction_id
801803
return
802804

803-
if self._in_transaction:
805+
if self.in_transaction:
804806
if read_preference != ReadPreference.PRIMARY:
805807
raise InvalidOperation(
806808
'read preference in a transaction must be primary, not: '

pymongo/common.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,7 @@ def _write_concern_for(self, session):
824824
"""Read only access to the write concern of this instance or session.
825825
"""
826826
# Override this operation's write concern with the transaction's.
827-
if session and session._in_transaction:
827+
if session and session.in_transaction:
828828
return DEFAULT_WRITE_CONCERN
829829
return self.write_concern
830830

pymongo/message.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ def _gen_find_command(coll, spec, projection, skip, limit, batch_size, options,
205205
cmd['singleBatch'] = True
206206
if batch_size:
207207
cmd['batchSize'] = batch_size
208-
if read_concern.level and not (session and session._in_transaction):
208+
if read_concern.level and not (session and session.in_transaction):
209209
cmd['readConcern'] = read_concern.document
210210
if collation:
211211
cmd['collation'] = collation
@@ -304,7 +304,7 @@ def as_command(self, sock_info):
304304
# Explain does not support readConcern.
305305
if (not explain and session.options.causal_consistency
306306
and session.operation_time is not None
307-
and not session._in_transaction):
307+
and not session.in_transaction):
308308
cmd.setdefault(
309309
'readConcern', {})[
310310
'afterClusterTime'] = session.operation_time

pymongo/mongo_client.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,7 +1255,7 @@ def _select_server(self, server_selector, session, address=None):
12551255
# Pin this session to the selected server if it's performing a
12561256
# sharded transaction.
12571257
if server.description.mongos and (session and
1258-
session._in_transaction):
1258+
session.in_transaction):
12591259
session._pin_mongos(server)
12601260
return server
12611261
except PyMongoError as exc:
@@ -1355,7 +1355,7 @@ def _retry_with_session(self, retryable, func, session, bulk):
13551355
Re-raises any exception thrown by func().
13561356
"""
13571357
retryable = (retryable and self.retry_writes
1358-
and session and not session._in_transaction)
1358+
and session and not session.in_transaction)
13591359
last_error = None
13601360
retrying = False
13611361

@@ -1445,7 +1445,7 @@ def _retryable_read(self, func, read_pref, session, address=None,
14451445
"""
14461446
retryable = (retryable and
14471447
self.retry_reads
1448-
and not (session and session._in_transaction))
1448+
and not (session and session.in_transaction))
14491449
last_error = None
14501450
retrying = False
14511451

pymongo/network.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ def command(sock, dbname, spec, slave_ok, is_mongos,
9898
orig = spec
9999
if is_mongos and not use_op_msg:
100100
spec = message._maybe_add_read_preference(spec, read_preference)
101-
if read_concern and not (session and session._in_transaction):
101+
if read_concern and not (session and session.in_transaction):
102102
if read_concern.level:
103103
spec['readConcern'] = read_concern.document
104104
if (session and session.options.causal_consistency

test/test_transactions.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,42 @@ def callback(session):
334334
self.assertEqual(listener.started_command_names(),
335335
['insert', 'commitTransaction', 'commitTransaction'])
336336

337+
# Tested here because this supports Motor's convenient transactions API.
338+
@client_context.require_transactions
339+
def test_in_transaction_property(self):
340+
client = client_context.client
341+
coll = client.test.testcollection
342+
coll.insert_one({})
343+
self.addCleanup(coll.drop)
344+
345+
with client.start_session() as s:
346+
self.assertFalse(s.in_transaction)
347+
s.start_transaction()
348+
self.assertTrue(s.in_transaction)
349+
coll.insert_one({}, session=s)
350+
self.assertTrue(s.in_transaction)
351+
s.commit_transaction()
352+
self.assertFalse(s.in_transaction)
353+
354+
with client.start_session() as s:
355+
s.start_transaction()
356+
# commit empty transaction
357+
s.commit_transaction()
358+
self.assertFalse(s.in_transaction)
359+
360+
with client.start_session() as s:
361+
s.start_transaction()
362+
s.abort_transaction()
363+
self.assertFalse(s.in_transaction)
364+
365+
# Using a callback
366+
def callback(session):
367+
self.assertTrue(session.in_transaction)
368+
with client.start_session() as s:
369+
self.assertFalse(s.in_transaction)
370+
s.with_transaction(callback)
371+
self.assertFalse(s.in_transaction)
372+
337373

338374
def create_test(scenario_def, test, name):
339375
@client_context.require_test_commands

0 commit comments

Comments
 (0)