Skip to content

Commit a13bdc6

Browse files
authored
Rename Session.x_transaction to Session.execute_x (#784)
Deprecate `Session.read_transaction` and `Session.write_transaction` in favor of `Session.execute_read` and `Session.execute_write` respectively. Other drivers had to rename this function as there was a breaking change in the API. Namely, transaction functions getting passed a managed transaction object that has no commit/rollback/... methods, but can only run queries. The same change also applies to the Python driver but it's not breaking as the driver would've failed later (with a rather cryptic error coming from the server) when the user tried to manually commit/rollback/... a managed transaction inside a transaction function.
1 parent d04b80a commit a13bdc6

30 files changed

+368
-77
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@
128128
Server-side keep-alives communicated through configuration hints together with
129129
the config option `connection_acquisition_timeout` are sufficient to avoid the
130130
driver getting stuck.
131+
- Deprecate `Session.read_transaction` and `Session.write_transaction` in favor
132+
of `Session.execute_read` and `Session.execute_write` respectively.
131133

132134

133135
## Version 4.4

docs/source/api.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,8 +550,12 @@ Session
550550

551551
.. automethod:: read_transaction
552552

553+
.. automethod:: execute_read
554+
553555
.. automethod:: write_transaction
554556

557+
.. automethod:: execute_write
558+
555559

556560
Query
557561
=====

docs/source/async_api.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,12 @@ AsyncSession
352352

353353
.. automethod:: read_transaction
354354

355+
.. automethod:: execute_read
356+
355357
.. automethod:: write_transaction
356358

359+
.. automethod:: execute_write
360+
357361

358362

359363
.. _async-session-configuration-ref:

neo4j/_async/work/result.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ async def create_node_tx(tx, name):
354354
return value, summary
355355
356356
async with driver.session() as session:
357-
node_id, summary = await session.write_transaction(
357+
node_id, summary = await session.execute_write(
358358
create_node_tx, "example"
359359
)
360360
@@ -376,7 +376,7 @@ async def get_two_tx(tx):
376376
return values, summary
377377
378378
async with driver.session() as session:
379-
values, summary = await session.read_transaction(get_two_tx)
379+
values, summary = await session.execute_read(get_two_tx)
380380
381381
:returns: The :class:`neo4j.ResultSummary` for this result
382382

neo4j/_async/work/session.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,75 @@ async def _run_transaction(
530530
else:
531531
raise ServiceUnavailable("Transaction failed")
532532

533+
async def execute_read(
534+
self,
535+
transaction_function: t.Callable[
536+
te.Concatenate[AsyncManagedTransaction, _P], t.Awaitable[_R]
537+
],
538+
*args: _P.args, **kwargs: _P.kwargs
539+
) -> _R:
540+
"""Execute a unit of work in a managed read transaction.
541+
542+
.. note::
543+
This does not necessarily imply access control, see the session
544+
configuration option :ref:`default-access-mode-ref`.
545+
546+
This transaction will automatically be committed when the function
547+
returns, unless an exception is thrown during query execution or by
548+
the user code. Note, that this function performs retries and that the
549+
supplied `transaction_function` might get invoked more than once.
550+
Therefore, it needs to be idempotent (i.e., have the same effect,
551+
regardless if called once or many times).
552+
553+
Example::
554+
555+
async def do_cypher_tx(tx, cypher):
556+
result = await tx.run(cypher)
557+
values = [record.values() async for record in result]
558+
return values
559+
560+
async with driver.session() as session:
561+
values = await session.execute_read(do_cypher_tx, "RETURN 1 AS x")
562+
563+
Example::
564+
565+
async def get_two_tx(tx):
566+
result = await tx.run("UNWIND [1,2,3,4] AS x RETURN x")
567+
values = []
568+
async for record in result:
569+
if len(values) >= 2:
570+
break
571+
values.append(record.values())
572+
# or shorter: values = [record.values()
573+
# for record in await result.fetch(2)]
574+
575+
# discard the remaining records if there are any
576+
summary = await result.consume()
577+
# use the summary for logging etc.
578+
return values
579+
580+
async with driver.session() as session:
581+
values = await session.execute_read(get_two_tx)
582+
583+
:param transaction_function: a function that takes a transaction as an
584+
argument and does work with the transaction.
585+
`transaction_function(tx, *args, **kwargs)` where `tx` is a
586+
:class:`.AsyncManagedTransaction`.
587+
:param args: additional arguments for the `transaction_function`
588+
:param kwargs: key word arguments for the `transaction_function`
589+
590+
:raises SessionError: if the session has been closed.
591+
592+
:return: a result as returned by the given unit of work
593+
594+
.. versionadded:: 5.0
595+
"""
596+
return await self._run_transaction(
597+
READ_ACCESS, transaction_function, *args, **kwargs
598+
)
599+
600+
# TODO 6.0: Remove this method
601+
@deprecated("read_transaction has been renamed to execute_read")
533602
async def read_transaction(
534603
self,
535604
transaction_function: t.Callable[
@@ -590,11 +659,64 @@ async def get_two_tx(tx):
590659
:raises SessionError: if the session has been closed.
591660
592661
:return: a result as returned by the given unit of work
662+
663+
.. deprecated:: 5.0
664+
Method was renamed to :meth:`.execute_read`.
593665
"""
594666
return await self._run_transaction(
595667
READ_ACCESS, transaction_function, *args, **kwargs
596668
)
597669

670+
async def execute_write(
671+
self,
672+
transaction_function: t.Callable[
673+
te.Concatenate[AsyncManagedTransaction, _P], t.Awaitable[_R]
674+
],
675+
*args: _P.args, **kwargs: _P.kwargs
676+
) -> _R:
677+
"""Execute a unit of work in a managed write transaction.
678+
679+
.. note::
680+
This does not necessarily imply access control, see the session
681+
configuration option :ref:`default-access-mode-ref`.
682+
683+
This transaction will automatically be committed when the function
684+
returns unless, an exception is thrown during query execution or by
685+
the user code. Note, that this function performs retries and that the
686+
supplied `transaction_function` might get invoked more than once.
687+
Therefore, it needs to be idempotent (i.e., have the same effect,
688+
regardless if called once or many times).
689+
690+
Example::
691+
692+
async def create_node_tx(tx, name):
693+
query = "CREATE (n:NodeExample { name: $name }) RETURN id(n) AS node_id"
694+
result = await tx.run(query, name=name)
695+
record = await result.single()
696+
return record["node_id"]
697+
698+
async with driver.session() as session:
699+
node_id = await session.execute_write(create_node_tx, "example")
700+
701+
:param transaction_function: a function that takes a transaction as an
702+
argument and does work with the transaction.
703+
`transaction_function(tx, *args, **kwargs)` where `tx` is a
704+
:class:`.AsyncManagedTransaction`.
705+
:param args: additional arguments for the `transaction_function`
706+
:param kwargs: key word arguments for the `transaction_function`
707+
708+
:raises SessionError: if the session has been closed.
709+
710+
:return: a result as returned by the given unit of work
711+
712+
.. versionadded:: 5.0
713+
"""
714+
return await self._run_transaction(
715+
WRITE_ACCESS, transaction_function, *args, **kwargs
716+
)
717+
718+
# TODO 6.0: Remove this method
719+
@deprecated("write_transaction has been renamed to execute_write")
598720
async def write_transaction(
599721
self,
600722
transaction_function: t.Callable[
@@ -636,6 +758,9 @@ async def create_node_tx(tx, name):
636758
:raises SessionError: if the session has been closed.
637759
638760
:return: a result as returned by the given unit of work
761+
762+
.. deprecated:: 5.0
763+
Method was renamed to :meth:`.execute_write`.
639764
"""
640765
return await self._run_transaction(
641766
WRITE_ACCESS, transaction_function, *args, **kwargs

neo4j/_sync/work/result.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ def create_node_tx(tx, name):
354354
return value, summary
355355
356356
with driver.session() as session:
357-
node_id, summary = session.write_transaction(
357+
node_id, summary = session.execute_write(
358358
create_node_tx, "example"
359359
)
360360
@@ -376,7 +376,7 @@ def get_two_tx(tx):
376376
return values, summary
377377
378378
with driver.session() as session:
379-
values, summary = session.read_transaction(get_two_tx)
379+
values, summary = session.execute_read(get_two_tx)
380380
381381
:returns: The :class:`neo4j.ResultSummary` for this result
382382

neo4j/_sync/work/session.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,75 @@ def _run_transaction(
530530
else:
531531
raise ServiceUnavailable("Transaction failed")
532532

533+
def execute_read(
534+
self,
535+
transaction_function: t.Callable[
536+
te.Concatenate[ManagedTransaction, _P], t.Union[_R]
537+
],
538+
*args: _P.args, **kwargs: _P.kwargs
539+
) -> _R:
540+
"""Execute a unit of work in a managed read transaction.
541+
542+
.. note::
543+
This does not necessarily imply access control, see the session
544+
configuration option :ref:`default-access-mode-ref`.
545+
546+
This transaction will automatically be committed when the function
547+
returns, unless an exception is thrown during query execution or by
548+
the user code. Note, that this function performs retries and that the
549+
supplied `transaction_function` might get invoked more than once.
550+
Therefore, it needs to be idempotent (i.e., have the same effect,
551+
regardless if called once or many times).
552+
553+
Example::
554+
555+
def do_cypher_tx(tx, cypher):
556+
result = tx.run(cypher)
557+
values = [record.values() for record in result]
558+
return values
559+
560+
with driver.session() as session:
561+
values = session.execute_read(do_cypher_tx, "RETURN 1 AS x")
562+
563+
Example::
564+
565+
def get_two_tx(tx):
566+
result = tx.run("UNWIND [1,2,3,4] AS x RETURN x")
567+
values = []
568+
for record in result:
569+
if len(values) >= 2:
570+
break
571+
values.append(record.values())
572+
# or shorter: values = [record.values()
573+
# for record in result.fetch(2)]
574+
575+
# discard the remaining records if there are any
576+
summary = result.consume()
577+
# use the summary for logging etc.
578+
return values
579+
580+
with driver.session() as session:
581+
values = session.execute_read(get_two_tx)
582+
583+
:param transaction_function: a function that takes a transaction as an
584+
argument and does work with the transaction.
585+
`transaction_function(tx, *args, **kwargs)` where `tx` is a
586+
:class:`.ManagedTransaction`.
587+
:param args: additional arguments for the `transaction_function`
588+
:param kwargs: key word arguments for the `transaction_function`
589+
590+
:raises SessionError: if the session has been closed.
591+
592+
:return: a result as returned by the given unit of work
593+
594+
.. versionadded:: 5.0
595+
"""
596+
return self._run_transaction(
597+
READ_ACCESS, transaction_function, *args, **kwargs
598+
)
599+
600+
# TODO 6.0: Remove this method
601+
@deprecated("read_transaction has been renamed to execute_read")
533602
def read_transaction(
534603
self,
535604
transaction_function: t.Callable[
@@ -590,11 +659,64 @@ def get_two_tx(tx):
590659
:raises SessionError: if the session has been closed.
591660
592661
:return: a result as returned by the given unit of work
662+
663+
.. deprecated:: 5.0
664+
Method was renamed to :meth:`.execute_read`.
593665
"""
594666
return self._run_transaction(
595667
READ_ACCESS, transaction_function, *args, **kwargs
596668
)
597669

670+
def execute_write(
671+
self,
672+
transaction_function: t.Callable[
673+
te.Concatenate[ManagedTransaction, _P], t.Union[_R]
674+
],
675+
*args: _P.args, **kwargs: _P.kwargs
676+
) -> _R:
677+
"""Execute a unit of work in a managed write transaction.
678+
679+
.. note::
680+
This does not necessarily imply access control, see the session
681+
configuration option :ref:`default-access-mode-ref`.
682+
683+
This transaction will automatically be committed when the function
684+
returns unless, an exception is thrown during query execution or by
685+
the user code. Note, that this function performs retries and that the
686+
supplied `transaction_function` might get invoked more than once.
687+
Therefore, it needs to be idempotent (i.e., have the same effect,
688+
regardless if called once or many times).
689+
690+
Example::
691+
692+
def create_node_tx(tx, name):
693+
query = "CREATE (n:NodeExample { name: $name }) RETURN id(n) AS node_id"
694+
result = tx.run(query, name=name)
695+
record = result.single()
696+
return record["node_id"]
697+
698+
with driver.session() as session:
699+
node_id = session.execute_write(create_node_tx, "example")
700+
701+
:param transaction_function: a function that takes a transaction as an
702+
argument and does work with the transaction.
703+
`transaction_function(tx, *args, **kwargs)` where `tx` is a
704+
:class:`.ManagedTransaction`.
705+
:param args: additional arguments for the `transaction_function`
706+
:param kwargs: key word arguments for the `transaction_function`
707+
708+
:raises SessionError: if the session has been closed.
709+
710+
:return: a result as returned by the given unit of work
711+
712+
.. versionadded:: 5.0
713+
"""
714+
return self._run_transaction(
715+
WRITE_ACCESS, transaction_function, *args, **kwargs
716+
)
717+
718+
# TODO 6.0: Remove this method
719+
@deprecated("write_transaction has been renamed to execute_write")
598720
def write_transaction(
599721
self,
600722
transaction_function: t.Callable[
@@ -636,6 +758,9 @@ def create_node_tx(tx, name):
636758
:raises SessionError: if the session has been closed.
637759
638760
:return: a result as returned by the given unit of work
761+
762+
.. deprecated:: 5.0
763+
Method was renamed to :meth:`.execute_write`.
639764
"""
640765
return self._run_transaction(
641766
WRITE_ACCESS, transaction_function, *args, **kwargs

testkitbackend/_async/requests.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,9 +354,9 @@ async def func(tx):
354354
raise FrontendError("Client said no")
355355

356356
if is_read:
357-
await session.read_transaction(func)
357+
await session.execute_read(func)
358358
else:
359-
await session.write_transaction(func)
359+
await session.execute_write(func)
360360
await backend.send_response("RetryableDone", {})
361361

362362

testkitbackend/_sync/requests.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,9 +354,9 @@ def func(tx):
354354
raise FrontendError("Client said no")
355355

356356
if is_read:
357-
session.read_transaction(func)
357+
session.execute_read(func)
358358
else:
359-
session.write_transaction(func)
359+
session.execute_write(func)
360360
backend.send_response("RetryableDone", {})
361361

362362

0 commit comments

Comments
 (0)