diff --git a/CHANGELOG.md b/CHANGELOG.md index 61106f6af..dc38f90e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -128,6 +128,8 @@ Server-side keep-alives communicated through configuration hints together with the config option `connection_acquisition_timeout` are sufficient to avoid the driver getting stuck. +- Deprecate `Session.read_transaction` and `Session.write_transaction` in favor + of `Session.execute_read` and `Session.execute_write` respectively. ## Version 4.4 diff --git a/docs/source/api.rst b/docs/source/api.rst index 0d6b6831e..f99b2909f 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -550,8 +550,12 @@ Session .. automethod:: read_transaction + .. automethod:: execute_read + .. automethod:: write_transaction + .. automethod:: execute_write + Query ===== diff --git a/docs/source/async_api.rst b/docs/source/async_api.rst index 5b7c5ea3e..c9bdd4985 100644 --- a/docs/source/async_api.rst +++ b/docs/source/async_api.rst @@ -352,8 +352,12 @@ AsyncSession .. automethod:: read_transaction + .. automethod:: execute_read + .. automethod:: write_transaction + .. automethod:: execute_write + .. _async-session-configuration-ref: diff --git a/neo4j/_async/work/result.py b/neo4j/_async/work/result.py index 5e8705850..066687073 100644 --- a/neo4j/_async/work/result.py +++ b/neo4j/_async/work/result.py @@ -354,7 +354,7 @@ async def create_node_tx(tx, name): return value, summary async with driver.session() as session: - node_id, summary = await session.write_transaction( + node_id, summary = await session.execute_write( create_node_tx, "example" ) @@ -376,7 +376,7 @@ async def get_two_tx(tx): return values, summary async with driver.session() as session: - values, summary = await session.read_transaction(get_two_tx) + values, summary = await session.execute_read(get_two_tx) :returns: The :class:`neo4j.ResultSummary` for this result diff --git a/neo4j/_async/work/session.py b/neo4j/_async/work/session.py index 567ea5f7c..4b6dce84d 100644 --- a/neo4j/_async/work/session.py +++ b/neo4j/_async/work/session.py @@ -530,6 +530,75 @@ async def _run_transaction( else: raise ServiceUnavailable("Transaction failed") + async def execute_read( + self, + transaction_function: t.Callable[ + te.Concatenate[AsyncManagedTransaction, _P], t.Awaitable[_R] + ], + *args: _P.args, **kwargs: _P.kwargs + ) -> _R: + """Execute a unit of work in a managed read transaction. + + .. note:: + This does not necessarily imply access control, see the session + configuration option :ref:`default-access-mode-ref`. + + This transaction will automatically be committed when the function + returns, unless an exception is thrown during query execution or by + the user code. Note, that this function performs retries and that the + supplied `transaction_function` might get invoked more than once. + Therefore, it needs to be idempotent (i.e., have the same effect, + regardless if called once or many times). + + Example:: + + async def do_cypher_tx(tx, cypher): + result = await tx.run(cypher) + values = [record.values() async for record in result] + return values + + async with driver.session() as session: + values = await session.execute_read(do_cypher_tx, "RETURN 1 AS x") + + Example:: + + async def get_two_tx(tx): + result = await tx.run("UNWIND [1,2,3,4] AS x RETURN x") + values = [] + async for record in result: + if len(values) >= 2: + break + values.append(record.values()) + # or shorter: values = [record.values() + # for record in await result.fetch(2)] + + # discard the remaining records if there are any + summary = await result.consume() + # use the summary for logging etc. + return values + + async with driver.session() as session: + values = await session.execute_read(get_two_tx) + + :param transaction_function: a function that takes a transaction as an + argument and does work with the transaction. + `transaction_function(tx, *args, **kwargs)` where `tx` is a + :class:`.AsyncManagedTransaction`. + :param args: additional arguments for the `transaction_function` + :param kwargs: key word arguments for the `transaction_function` + + :raises SessionError: if the session has been closed. + + :return: a result as returned by the given unit of work + + .. versionadded:: 5.0 + """ + return await self._run_transaction( + READ_ACCESS, transaction_function, *args, **kwargs + ) + + # TODO 6.0: Remove this method + @deprecated("read_transaction has been renamed to execute_read") async def read_transaction( self, transaction_function: t.Callable[ @@ -590,11 +659,64 @@ async def get_two_tx(tx): :raises SessionError: if the session has been closed. :return: a result as returned by the given unit of work + + .. deprecated:: 5.0 + Method was renamed to :meth:`.execute_read`. """ return await self._run_transaction( READ_ACCESS, transaction_function, *args, **kwargs ) + async def execute_write( + self, + transaction_function: t.Callable[ + te.Concatenate[AsyncManagedTransaction, _P], t.Awaitable[_R] + ], + *args: _P.args, **kwargs: _P.kwargs + ) -> _R: + """Execute a unit of work in a managed write transaction. + + .. note:: + This does not necessarily imply access control, see the session + configuration option :ref:`default-access-mode-ref`. + + This transaction will automatically be committed when the function + returns unless, an exception is thrown during query execution or by + the user code. Note, that this function performs retries and that the + supplied `transaction_function` might get invoked more than once. + Therefore, it needs to be idempotent (i.e., have the same effect, + regardless if called once or many times). + + Example:: + + async def create_node_tx(tx, name): + query = "CREATE (n:NodeExample { name: $name }) RETURN id(n) AS node_id" + result = await tx.run(query, name=name) + record = await result.single() + return record["node_id"] + + async with driver.session() as session: + node_id = await session.execute_write(create_node_tx, "example") + + :param transaction_function: a function that takes a transaction as an + argument and does work with the transaction. + `transaction_function(tx, *args, **kwargs)` where `tx` is a + :class:`.AsyncManagedTransaction`. + :param args: additional arguments for the `transaction_function` + :param kwargs: key word arguments for the `transaction_function` + + :raises SessionError: if the session has been closed. + + :return: a result as returned by the given unit of work + + .. versionadded:: 5.0 + """ + return await self._run_transaction( + WRITE_ACCESS, transaction_function, *args, **kwargs + ) + + # TODO 6.0: Remove this method + @deprecated("write_transaction has been renamed to execute_write") async def write_transaction( self, transaction_function: t.Callable[ @@ -636,6 +758,9 @@ async def create_node_tx(tx, name): :raises SessionError: if the session has been closed. :return: a result as returned by the given unit of work + + .. deprecated:: 5.0 + Method was renamed to :meth:`.execute_write`. """ return await self._run_transaction( WRITE_ACCESS, transaction_function, *args, **kwargs diff --git a/neo4j/_sync/work/result.py b/neo4j/_sync/work/result.py index f1b7cc904..66e6b770c 100644 --- a/neo4j/_sync/work/result.py +++ b/neo4j/_sync/work/result.py @@ -354,7 +354,7 @@ def create_node_tx(tx, name): return value, summary with driver.session() as session: - node_id, summary = session.write_transaction( + node_id, summary = session.execute_write( create_node_tx, "example" ) @@ -376,7 +376,7 @@ def get_two_tx(tx): return values, summary with driver.session() as session: - values, summary = session.read_transaction(get_two_tx) + values, summary = session.execute_read(get_two_tx) :returns: The :class:`neo4j.ResultSummary` for this result diff --git a/neo4j/_sync/work/session.py b/neo4j/_sync/work/session.py index 57ee78fde..743e2965d 100644 --- a/neo4j/_sync/work/session.py +++ b/neo4j/_sync/work/session.py @@ -530,6 +530,75 @@ def _run_transaction( else: raise ServiceUnavailable("Transaction failed") + def execute_read( + self, + transaction_function: t.Callable[ + te.Concatenate[ManagedTransaction, _P], t.Union[_R] + ], + *args: _P.args, **kwargs: _P.kwargs + ) -> _R: + """Execute a unit of work in a managed read transaction. + + .. note:: + This does not necessarily imply access control, see the session + configuration option :ref:`default-access-mode-ref`. + + This transaction will automatically be committed when the function + returns, unless an exception is thrown during query execution or by + the user code. Note, that this function performs retries and that the + supplied `transaction_function` might get invoked more than once. + Therefore, it needs to be idempotent (i.e., have the same effect, + regardless if called once or many times). + + Example:: + + def do_cypher_tx(tx, cypher): + result = tx.run(cypher) + values = [record.values() for record in result] + return values + + with driver.session() as session: + values = session.execute_read(do_cypher_tx, "RETURN 1 AS x") + + Example:: + + def get_two_tx(tx): + result = tx.run("UNWIND [1,2,3,4] AS x RETURN x") + values = [] + for record in result: + if len(values) >= 2: + break + values.append(record.values()) + # or shorter: values = [record.values() + # for record in result.fetch(2)] + + # discard the remaining records if there are any + summary = result.consume() + # use the summary for logging etc. + return values + + with driver.session() as session: + values = session.execute_read(get_two_tx) + + :param transaction_function: a function that takes a transaction as an + argument and does work with the transaction. + `transaction_function(tx, *args, **kwargs)` where `tx` is a + :class:`.ManagedTransaction`. + :param args: additional arguments for the `transaction_function` + :param kwargs: key word arguments for the `transaction_function` + + :raises SessionError: if the session has been closed. + + :return: a result as returned by the given unit of work + + .. versionadded:: 5.0 + """ + return self._run_transaction( + READ_ACCESS, transaction_function, *args, **kwargs + ) + + # TODO 6.0: Remove this method + @deprecated("read_transaction has been renamed to execute_read") def read_transaction( self, transaction_function: t.Callable[ @@ -590,11 +659,64 @@ def get_two_tx(tx): :raises SessionError: if the session has been closed. :return: a result as returned by the given unit of work + + .. deprecated:: 5.0 + Method was renamed to :meth:`.execute_read`. """ return self._run_transaction( READ_ACCESS, transaction_function, *args, **kwargs ) + def execute_write( + self, + transaction_function: t.Callable[ + te.Concatenate[ManagedTransaction, _P], t.Union[_R] + ], + *args: _P.args, **kwargs: _P.kwargs + ) -> _R: + """Execute a unit of work in a managed write transaction. + + .. note:: + This does not necessarily imply access control, see the session + configuration option :ref:`default-access-mode-ref`. + + This transaction will automatically be committed when the function + returns unless, an exception is thrown during query execution or by + the user code. Note, that this function performs retries and that the + supplied `transaction_function` might get invoked more than once. + Therefore, it needs to be idempotent (i.e., have the same effect, + regardless if called once or many times). + + Example:: + + def create_node_tx(tx, name): + query = "CREATE (n:NodeExample { name: $name }) RETURN id(n) AS node_id" + result = tx.run(query, name=name) + record = result.single() + return record["node_id"] + + with driver.session() as session: + node_id = session.execute_write(create_node_tx, "example") + + :param transaction_function: a function that takes a transaction as an + argument and does work with the transaction. + `transaction_function(tx, *args, **kwargs)` where `tx` is a + :class:`.ManagedTransaction`. + :param args: additional arguments for the `transaction_function` + :param kwargs: key word arguments for the `transaction_function` + + :raises SessionError: if the session has been closed. + + :return: a result as returned by the given unit of work + + .. versionadded:: 5.0 + """ + return self._run_transaction( + WRITE_ACCESS, transaction_function, *args, **kwargs + ) + + # TODO 6.0: Remove this method + @deprecated("write_transaction has been renamed to execute_write") def write_transaction( self, transaction_function: t.Callable[ @@ -636,6 +758,9 @@ def create_node_tx(tx, name): :raises SessionError: if the session has been closed. :return: a result as returned by the given unit of work + + .. deprecated:: 5.0 + Method was renamed to :meth:`.execute_write`. """ return self._run_transaction( WRITE_ACCESS, transaction_function, *args, **kwargs diff --git a/testkitbackend/_async/requests.py b/testkitbackend/_async/requests.py index 66da9fe17..8f82758f0 100644 --- a/testkitbackend/_async/requests.py +++ b/testkitbackend/_async/requests.py @@ -354,9 +354,9 @@ async def func(tx): raise FrontendError("Client said no") if is_read: - await session.read_transaction(func) + await session.execute_read(func) else: - await session.write_transaction(func) + await session.execute_write(func) await backend.send_response("RetryableDone", {}) diff --git a/testkitbackend/_sync/requests.py b/testkitbackend/_sync/requests.py index c033acff5..9274479fe 100644 --- a/testkitbackend/_sync/requests.py +++ b/testkitbackend/_sync/requests.py @@ -354,9 +354,9 @@ def func(tx): raise FrontendError("Client said no") if is_read: - session.read_transaction(func) + session.execute_read(func) else: - session.write_transaction(func) + session.execute_write(func) backend.send_response("RetryableDone", {}) diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 82ebe9cf7..43afddfaa 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -35,8 +35,7 @@ def run_and_rollback(tx, cypher, **parameters): def f(cypher, **parameters): with driver.session() as session: try: - session.write_transaction(run_and_rollback, cypher, - **parameters) + session.execute_write(run_and_rollback, cypher, **parameters) raise RuntimeError("Expected rollback") except ForcedRollback as e: return e.return_value diff --git a/tests/integration/examples/test_cypher_error_example.py b/tests/integration/examples/test_cypher_error_example.py index 7551ca0e9..347bfd2ab 100644 --- a/tests/integration/examples/test_cypher_error_example.py +++ b/tests/integration/examples/test_cypher_error_example.py @@ -36,7 +36,7 @@ def __init__(self, driver): def get_employee_number(self, name): with self.driver.session() as session: try: - session.read_transaction(self.select_employee, name) + session.execute_read(self.select_employee, name) except ClientError as error: print(error.message) return -1 diff --git a/tests/integration/examples/test_driver_introduction_example.py b/tests/integration/examples/test_driver_introduction_example.py index b5fe40801..daddd006e 100644 --- a/tests/integration/examples/test_driver_introduction_example.py +++ b/tests/integration/examples/test_driver_introduction_example.py @@ -57,7 +57,7 @@ def enable_log(level, output_stream): def create_friendship(self, person1_name, person2_name, knows_from): with self.driver.session() as session: # Write transactions allow the driver to handle retries and transient errors - result = session.write_transaction( + result = session.execute_write( self._create_and_return_friendship, person1_name, person2_name, knows_from) for row in result: print("Created friendship between: {p1}, {p2} from {knows_from}" @@ -93,7 +93,7 @@ def _create_and_return_friendship(tx, person1_name, person2_name, knows_from): def find_person(self, person_name): with self.driver.session() as session: - result = session.read_transaction(self._find_and_return_person, person_name) + result = session.execute_read(self._find_and_return_person, person_name) for row in result: print("Found person: {row}".format(row=row)) diff --git a/tests/integration/examples/test_geospatial_types_example.py b/tests/integration/examples/test_geospatial_types_example.py index 1f6ca0114..0bfc44634 100644 --- a/tests/integration/examples/test_geospatial_types_example.py +++ b/tests/integration/examples/test_geospatial_types_example.py @@ -45,8 +45,8 @@ def test_cartesian_point(driver): in_point3d = point3d with driver.session() as session: - record_with_2d_point = session.read_transaction(_echo, point2d) - record_with_3d_point = session.read_transaction(_echo, point3d) + record_with_2d_point = session.execute_read(_echo, point2d) + record_with_3d_point = session.execute_read(_echo, point3d) # tag::geospatial-types-cartesian[] @@ -106,8 +106,8 @@ def test_wgs84_point(driver): in_point3d = point3d with driver.session() as session: - record_with_2d_point = session.read_transaction(_echo, point2d) - record_with_3d_point = session.read_transaction(_echo, point3d) + record_with_2d_point = session.execute_read(_echo, point2d) + record_with_3d_point = session.execute_read(_echo, point3d) # tag::geospatial-types-wgs84[] diff --git a/tests/integration/examples/test_hello_world_example.py b/tests/integration/examples/test_hello_world_example.py index be80ec9ed..d599bddc0 100644 --- a/tests/integration/examples/test_hello_world_example.py +++ b/tests/integration/examples/test_hello_world_example.py @@ -45,7 +45,7 @@ def close(self): def print_greeting(self, message): with self.driver.session() as session: - greeting = session.write_transaction(self._create_and_return_greeting, message) + greeting = session.execute_write(self._create_and_return_greeting, message) print(greeting) @staticmethod diff --git a/tests/integration/examples/test_pass_bookmarks_example.py b/tests/integration/examples/test_pass_bookmarks_example.py index a9ca6c4ab..8be7585ef 100644 --- a/tests/integration/examples/test_pass_bookmarks_example.py +++ b/tests/integration/examples/test_pass_bookmarks_example.py @@ -77,20 +77,20 @@ def main(self): # Create the first person and employment relationship. with self.driver.session() as session_a: - session_a.write_transaction(self.create_person, "Alice") - session_a.write_transaction(self.employ, "Alice", "Wayne Enterprises") + session_a.execute_write(self.create_person, "Alice") + session_a.execute_write(self.employ, "Alice", "Wayne Enterprises") saved_bookmarks += session_a.last_bookmarks() # Create the second person and employment relationship. with self.driver.session() as session_b: - session_b.write_transaction(self.create_person, "Bob") - session_b.write_transaction(self.employ, "Bob", "LexCorp") + session_b.execute_write(self.create_person, "Bob") + session_b.execute_write(self.employ, "Bob", "LexCorp") saved_bookmarks += session_a.last_bookmarks() # Create a friendship between the two people created above. with self.driver.session(bookmarks=saved_bookmarks) as session_c: - session_c.write_transaction(self.create_friendship, "Alice", "Bob") - session_c.read_transaction(self.print_friendships) + session_c.execute_write(self.create_friendship, "Alice", "Bob") + session_c.execute_read(self.print_friendships) # end::pass-bookmarks[] diff --git a/tests/integration/examples/test_read_write_transaction_example.py b/tests/integration/examples/test_read_write_transaction_example.py index 89e44cbd1..ddfca5d45 100644 --- a/tests/integration/examples/test_read_write_transaction_example.py +++ b/tests/integration/examples/test_read_write_transaction_example.py @@ -35,8 +35,8 @@ def match_person_node(tx, name): def add_person(name): with driver.session() as session: - session.write_transaction(create_person_node, name) - persons = session.read_transaction(match_person_node, name) + session.execute_write(create_person_node, name) + persons = session.execute_read(match_person_node, name) return persons # end::read-write-transaction[] diff --git a/tests/integration/examples/test_result_consume_example.py b/tests/integration/examples/test_result_consume_example.py index c8f748158..e88f3779c 100644 --- a/tests/integration/examples/test_result_consume_example.py +++ b/tests/integration/examples/test_result_consume_example.py @@ -35,7 +35,7 @@ def match_person_nodes(tx): return [record["a.name"] for record in result] with driver.session() as session: - people = session.read_transaction(match_person_nodes) + people = session.execute_read(match_person_nodes) # end::result-consume[] with driver.session() as session: diff --git a/tests/integration/examples/test_result_retain_example.py b/tests/integration/examples/test_result_retain_example.py index 1d401e5d0..00de56d28 100644 --- a/tests/integration/examples/test_result_retain_example.py +++ b/tests/integration/examples/test_result_retain_example.py @@ -44,10 +44,10 @@ def match_person_nodes(tx): def add_employees(company_name): employees = 0 with driver.session() as session: - persons = session.read_transaction(match_person_nodes) + persons = session.execute_read(match_person_nodes) for person in persons: - employees += session.write_transaction(add_employee_to_company, person, company_name) + employees += session.execute_write(add_employee_to_company, person, company_name) return employees # end::result-retain[] diff --git a/tests/integration/examples/test_service_unavailable_example.py b/tests/integration/examples/test_service_unavailable_example.py index ab4c7d507..3b721e884 100644 --- a/tests/integration/examples/test_service_unavailable_example.py +++ b/tests/integration/examples/test_service_unavailable_example.py @@ -34,7 +34,7 @@ def service_unavailable_example(driver): def add_item(): try: with driver.session() as session: - session.write_transaction(lambda tx: tx.run("CREATE (a:Item)")) + session.execute_write(lambda tx: tx.run("CREATE (a:Item)")) return True except ServiceUnavailable: return False diff --git a/tests/integration/examples/test_temporal_types_example.py b/tests/integration/examples/test_temporal_types_example.py index 1133f09d6..40dced235 100644 --- a/tests/integration/examples/test_temporal_types_example.py +++ b/tests/integration/examples/test_temporal_types_example.py @@ -50,7 +50,7 @@ def test_datetime(driver): in_dt = dt # stored for later assertions with driver.session() as session: - record = session.read_transaction(_echo, dt) + record = session.execute_read(_echo, dt) # tag::temporal-types-datetime[] @@ -69,7 +69,7 @@ def test_datetime(driver): assert native == py_dt with driver.session() as session: - record = session.read_transaction(_echo, py_dt) + record = session.execute_read(_echo, py_dt) dt = record.get("fieldName") assert isinstance(dt, DateTime) @@ -101,7 +101,7 @@ def test_date(driver): in_d = d # stored for later assertions with driver.session() as session: - record = session.read_transaction(_echo, d) + record = session.execute_read(_echo, d) # tag::temporal-types-date[] @@ -120,7 +120,7 @@ def test_date(driver): assert native == py_d with driver.session() as session: - record = session.read_transaction(_echo, py_d) + record = session.execute_read(_echo, py_d) d = record.get("fieldName") assert isinstance(d, Date) @@ -152,7 +152,7 @@ def test_time(driver): in_t = t # stored for later assertions with driver.session() as session: - record = session.read_transaction(_echo, t) + record = session.execute_read(_echo, t) # tag::temporal-types-time[] @@ -171,7 +171,7 @@ def test_time(driver): assert native == py_t with driver.session() as session: - record = session.read_transaction(_echo, py_t) + record = session.execute_read(_echo, py_t) t = record.get("fieldName") assert isinstance(t, Time) @@ -203,7 +203,7 @@ def test_local_datetime(driver): in_dt = dt # stored for later assertions with driver.session() as session: - record = session.read_transaction(_echo, dt) + record = session.execute_read(_echo, dt) # tag::temporal-types-local-datetime[] @@ -222,7 +222,7 @@ def test_local_datetime(driver): assert native == py_dt with driver.session() as session: - record = session.read_transaction(_echo, py_dt) + record = session.execute_read(_echo, py_dt) dt = record.get("fieldName") assert isinstance(dt, DateTime) @@ -253,7 +253,7 @@ def test_local_time(driver): in_t = t # stored for later assertions with driver.session() as session: - record = session.read_transaction(_echo, t) + record = session.execute_read(_echo, t) # tag::temporal-types-local-time[] @@ -272,7 +272,7 @@ def test_local_time(driver): assert native == py_t with driver.session() as session: - record = session.read_transaction(_echo, py_t) + record = session.execute_read(_echo, py_t) t = record.get("fieldName") assert isinstance(t, Time) @@ -300,7 +300,7 @@ def test_duration_example(driver): in_duration = duration # stored for later assertions with driver.session() as session: - record = session.read_transaction(_echo, duration) + record = session.execute_read(_echo, duration) # tag::temporal-types-duration[] @@ -314,7 +314,7 @@ def test_duration_example(driver): assert duration == in_duration with driver.session() as session: - record = session.read_transaction(_echo, py_duration) + record = session.execute_read(_echo, py_duration) duration = record.get("fieldName") assert isinstance(duration, Duration) diff --git a/tests/integration/examples/test_transaction_function_example.py b/tests/integration/examples/test_transaction_function_example.py index 1450f63f0..b11218597 100644 --- a/tests/integration/examples/test_transaction_function_example.py +++ b/tests/integration/examples/test_transaction_function_example.py @@ -33,7 +33,7 @@ def create_person(tx, name): def add_person(driver, name): with driver.session() as session: - return session.write_transaction(create_person, name) + return session.execute_write(create_person, name) # end::transaction-function[] @@ -54,9 +54,9 @@ def work(tx, query, **parameters): def test_example(driver): eg = TransactionFunctionExample(driver) with eg.driver.session() as session: - session.write_transaction(work, "MATCH (_) DETACH DELETE _") + session.execute_write(work, "MATCH (_) DETACH DELETE _") eg.add_person("Alice") - records, _ = session.read_transaction( + records, _ = session.execute_read( work, "MATCH (a:Person) RETURN count(a)" ) assert records == [[1]] diff --git a/tests/integration/examples/test_transaction_metadata_config_example.py b/tests/integration/examples/test_transaction_metadata_config_example.py index 002fd8997..beefd33ce 100644 --- a/tests/integration/examples/test_transaction_metadata_config_example.py +++ b/tests/integration/examples/test_transaction_metadata_config_example.py @@ -31,7 +31,7 @@ def create_person(tx, name): def add_person(driver, name): with driver.session() as session: - return session.write_transaction(create_person, name) + return session.execute_write(create_person, name) # end::transaction-metadata-config[] @@ -52,9 +52,9 @@ def work(tx, query, **parameters): def test_example(driver): eg = TransactionMetadataConfigExample(driver) with eg.driver.session() as session: - session.write_transaction(work, "MATCH (_) DETACH DELETE _") + session.execute_write(work, "MATCH (_) DETACH DELETE _") eg.add_person("Alice") - records, _ = session.read_transaction( + records, _ = session.execute_read( work, "MATCH (a:Person) RETURN count(a)" ) assert records == [[1]] diff --git a/tests/integration/examples/test_transaction_timeout_config_example.py b/tests/integration/examples/test_transaction_timeout_config_example.py index 8a0e8f6af..0c7cb6093 100644 --- a/tests/integration/examples/test_transaction_timeout_config_example.py +++ b/tests/integration/examples/test_transaction_timeout_config_example.py @@ -19,7 +19,7 @@ from neo4j import unit_of_work -# python -m pytest tests/integration/examples/test_transaction_timeout_config_example.py -s -v +# python -m pytest tests/integration/examples/execute_test_timeout_config_example.py -s -v # tag::transaction-timeout-config[] @unit_of_work(timeout=5) @@ -31,7 +31,7 @@ def create_person(tx, name): def add_person(driver, name): with driver.session() as session: - return session.write_transaction(create_person, name) + return session.execute_write(create_person, name) # end::transaction-timeout-config[] @@ -52,9 +52,9 @@ def work(tx, query, **parameters): def test_example(driver): eg = TransactionTimeoutConfigExample(driver) with eg.driver.session() as session: - session.write_transaction(work, "MATCH (_) DETACH DELETE _") + session.execute_write(work, "MATCH (_) DETACH DELETE _") eg.add_person("Alice") - records, _ = session.read_transaction( + records, _ = session.execute_read( work, "MATCH (a:Person) RETURN count(a)" ) assert records == [[1]] diff --git a/tests/integration/mixed/test_async_cancellation.py b/tests/integration/mixed/test_async_cancellation.py index b56443d94..ff0f4c5e3 100644 --- a/tests/integration/mixed/test_async_cancellation.py +++ b/tests/integration/mixed/test_async_cancellation.py @@ -56,7 +56,7 @@ async def work(tx, i=1): async def _do_the_read_tx_func(session_, i=1): - await session_.read_transaction(_get_work(), i=i) + await session_.execute_read(_get_work(), i=i) def _with_retry(outer): diff --git a/tests/integration/test_readme.py b/tests/integration/test_readme.py index bb6e56c95..dd54444bf 100644 --- a/tests/integration/test_readme.py +++ b/tests/integration/test_readme.py @@ -46,10 +46,10 @@ def print_friends(tx, name): with driver.session() as session: session.run("MATCH (a) DETACH DELETE a") - session.write_transaction(add_friend, "Arthur", "Guinevere") - session.write_transaction(add_friend, "Arthur", "Lancelot") - session.write_transaction(add_friend, "Arthur", "Merlin") - session.read_transaction(print_friends, "Arthur") + session.execute_write(add_friend, "Arthur", "Guinevere") + session.execute_write(add_friend, "Arthur", "Lancelot") + session.execute_write(add_friend, "Arthur", "Merlin") + session.execute_read(print_friends, "Arthur") session.run("MATCH (a) DETACH DELETE a") diff --git a/tests/integration/test_tx_functions.py b/tests/integration/test_tx_functions.py index 46421bf20..2528feb33 100644 --- a/tests/integration/test_tx_functions.py +++ b/tests/integration/test_tx_functions.py @@ -30,80 +30,112 @@ # python -m pytest tests/integration/test_tx_functions.py -s -v -def test_simple_read(session): +@pytest.fixture(params=["read_transaction", "execute_read"]) +def read_transaction(request): + def executor(session, *args, **kwargs): + if request.param == "read_transaction": + with pytest.warns( + DeprecationWarning, + match="^read_transaction has been renamed to execute_read$" + ): + return session.read_transaction(*args, **kwargs) + elif request.param == "execute_read": + return session.execute_read(*args, **kwargs) + raise ValueError(request.param) + + return executor + + +@pytest.fixture(params=["write_transaction", "execute_write"]) +def write_transaction(request): + def executor(session, *args, **kwargs): + if request.param == "write_transaction": + with pytest.warns( + DeprecationWarning, + match="^write_transaction has been renamed to execute_write$" + ): + return session.write_transaction(*args, **kwargs) + elif request.param == "execute_write": + return session.execute_write(*args, **kwargs) + raise ValueError(request.param) + + return executor + + +def test_simple_read(session, read_transaction): def work(tx): return tx.run("RETURN 1").single().value() - value = session.read_transaction(work) + value = read_transaction(session, work) assert value == 1 -def test_read_with_arg(session): +def test_read_with_arg(session, read_transaction): def work(tx, x): return tx.run("RETURN $x", x=x).single().value() - value = session.read_transaction(work, x=1) + value = read_transaction(session, work, x=1) assert value == 1 -def test_read_with_arg_and_metadata(session): +def test_read_with_arg_and_metadata(session, read_transaction): @unit_of_work(timeout=25, metadata={"foo": "bar"}) def work(tx): return tx.run("CALL dbms.getTXMetaData").single().value() try: - value = session.read_transaction(work) + value = read_transaction(session, work) except ClientError: pytest.skip("Transaction metadata and timeout only supported in Neo4j EE 3.5+") else: assert value == {"foo": "bar"} -def test_simple_write(session): +def test_simple_write(session, write_transaction): def work(tx): return tx.run("CREATE (a {x: 1}) RETURN a.x").single().value() - value = session.write_transaction(work) + value = write_transaction(session, work) assert value == 1 -def test_write_with_arg(session): +def test_write_with_arg(session, write_transaction): def work(tx, x): return tx.run("CREATE (a {x: $x}) RETURN a.x", x=x).single().value() - value = session.write_transaction(work, x=1) + value = write_transaction(session, work, x=1) assert value == 1 -def test_write_with_arg_and_metadata(session): +def test_write_with_arg_and_metadata(session, write_transaction): @unit_of_work(timeout=25, metadata={"foo": "bar"}) def work(tx, x): return tx.run("CREATE (a {x: $x}) RETURN a.x", x=x).single().value() try: - value = session.write_transaction(work, x=1) + value = write_transaction(session, work, x=1) except ClientError: pytest.skip("Transaction metadata and timeout only supported in Neo4j EE 3.5+") else: assert value == 1 -def test_error_on_write_transaction(session): +def test_error_on_write_transaction(session, write_transaction): def f(tx, uuid): tx.run("CREATE (a:Thing {uuid:$uuid})", uuid=uuid), uuid4() with pytest.raises(TypeError): - session.write_transaction(f) + write_transaction(session, f) -def test_retry_logic(driver): +def test_retry_logic(driver, read_transaction): # python -m pytest tests/integration/test_tx_functions.py -s -v -k test_retry_logic pytest.global_counter = 0 @@ -120,7 +152,7 @@ def get_one(tx): return records with driver.session() as session: - records = session.read_transaction(get_one) + records = read_transaction(session, get_one) assert pytest.global_counter == 3 diff --git a/tests/performance/test_async_results.py b/tests/performance/test_async_results.py index 74ce212e3..ccf062447 100644 --- a/tests/performance/test_async_results.py +++ b/tests/performance/test_async_results.py @@ -25,7 +25,7 @@ def work(async_driver, *units_of_work): async def runner(): async with async_driver.session() as session: for unit_of_work in units_of_work: - await session.read_transaction(unit_of_work) + await session.execute_read(unit_of_work) return runner diff --git a/tests/performance/test_results.py b/tests/performance/test_results.py index f7cbbd624..fd4c4e33a 100644 --- a/tests/performance/test_results.py +++ b/tests/performance/test_results.py @@ -25,7 +25,7 @@ def work(driver, *units_of_work): def runner(): with driver.session() as session: for unit_of_work in units_of_work: - session.read_transaction(unit_of_work) + session.execute_read(unit_of_work) return runner diff --git a/tests/unit/async_/work/test_session.py b/tests/unit/async_/work/test_session.py index 117cb5cbf..70d3805b1 100644 --- a/tests/unit/async_/work/test_session.py +++ b/tests/unit/async_/work/test_session.py @@ -311,7 +311,7 @@ async def test_session_run_with_parameters( elif run_type == "managed": async def work(tx): await tx.run("RETURN $x", **parameters) - await session.write_transaction(work) + await session.execute_write(work) else: raise ValueError(run_type) diff --git a/tests/unit/sync/work/test_session.py b/tests/unit/sync/work/test_session.py index c93646306..dc5f71fc4 100644 --- a/tests/unit/sync/work/test_session.py +++ b/tests/unit/sync/work/test_session.py @@ -311,7 +311,7 @@ def test_session_run_with_parameters( elif run_type == "managed": def work(tx): tx.run("RETURN $x", **parameters) - session.write_transaction(work) + session.execute_write(work) else: raise ValueError(run_type)