Skip to content

Commit ac74fdf

Browse files
committed
Deprecate Session.last_bookmark instead of removing it
Added `neo4j.Bookmarks` to replace (now deprecated) `neo4j.Bookmark` and use if for managing bookmarks instead of raw bookmark stings.
1 parent a874ea7 commit ac74fdf

File tree

14 files changed

+361
-59
lines changed

14 files changed

+361
-59
lines changed

CHANGELOG.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,13 @@
5454
was silently ignored.
5555
- `Result.single` now raises `ResultNotSingleError` if not exactly one result is
5656
available.
57-
- `Session.last_bookmark` was renamed to `Session.last_bookmarks`.
58-
It returns a list of strings (previously string or None).
57+
- Bookmarks
58+
- `Session.last_bookmark` was deprecated. Its behaviour is partially incorrect
59+
and cannot be fixed without breaking its signature.
60+
Use `Session.last_bookmarks` instead.
61+
- `neo4j.Bookmark` was deprecated.
62+
Use `neo4j.Bookmarks` instead.
63+
5964

6065
## Version 4.4
6166

docs/source/api.rst

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,8 @@ Session
447447

448448
.. automethod:: run
449449

450+
.. automethod:: last_bookmarks
451+
450452
.. automethod:: last_bookmark
451453

452454
.. automethod:: begin_transaction
@@ -1366,9 +1368,12 @@ This example shows how to suppress the :class:`neo4j.ExperimentalWarning` using
13661368
warnings.filterwarnings("ignore", category=ExperimentalWarning)
13671369
13681370
1369-
********
1370-
Bookmark
1371-
********
1371+
*********
1372+
Bookmarks
1373+
*********
1374+
1375+
.. autoclass:: neo4j.Bookmarks
1376+
:members:
13721377
13731378
.. autoclass:: neo4j.Bookmark
13741379
:members:

docs/source/async_api.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@ AsyncSession
299299

300300
.. automethod:: run
301301

302+
.. automethod:: last_bookmarks
303+
302304
.. automethod:: last_bookmark
303305

304306
.. automethod:: begin_transaction

neo4j/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"bearer_auth",
3333
"BoltDriver",
3434
"Bookmark",
35+
"Bookmarks",
3536
"Config",
3637
"custom_auth",
3738
"DEFAULT_DATABASE",
@@ -97,6 +98,7 @@
9798
basic_auth,
9899
bearer_auth,
99100
Bookmark,
101+
Bookmarks,
100102
custom_auth,
101103
DEFAULT_DATABASE,
102104
kerberos_auth,

neo4j/_async/work/session.py

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
from ..._async_compat import async_sleep
2525
from ...api import (
26+
Bookmarks,
2627
READ_ACCESS,
2728
WRITE_ACCESS,
2829
)
@@ -37,6 +38,10 @@
3738
TransactionError,
3839
TransientError,
3940
)
41+
from ...meta import (
42+
deprecated,
43+
deprecation_warn,
44+
)
4045
from ...work import Query
4146
from .result import AsyncResult
4247
from .transaction import AsyncTransaction
@@ -85,7 +90,7 @@ class AsyncSession(AsyncWorkspace):
8590
def __init__(self, pool, session_config):
8691
super().__init__(pool, session_config)
8792
assert isinstance(session_config, SessionConfig)
88-
self._bookmarks = tuple(session_config.bookmarks)
93+
self._bookmarks = self._prepare_bookmarks(session_config.bookmarks)
8994

9095
def __del__(self):
9196
if asyncio.iscoroutinefunction(self.close):
@@ -103,6 +108,18 @@ async def __aexit__(self, exception_type, exception_value, traceback):
103108
self._state_failed = True
104109
await self.close()
105110

111+
def _prepare_bookmarks(self, bookmarks):
112+
if not bookmarks:
113+
return ()
114+
if isinstance(bookmarks, Bookmarks):
115+
return tuple(bookmarks.raw_values)
116+
if hasattr(bookmarks, "__iter__"):
117+
deprecation_warn("Passing an iterable to `bookmarks` is "
118+
"deprecated. Please use a Bookmarks instance.")
119+
return tuple(bookmarks)
120+
raise TypeError("Bookmarks must be an instance of Bookmarks or an "
121+
"iterable of raw bookmarks (deprecated).")
122+
106123
async def _connect(self, access_mode):
107124
if access_mode is None:
108125
access_mode = self._config.default_access_mode
@@ -222,6 +239,40 @@ async def run(self, query, parameters=None, **kwargs):
222239

223240
return self._auto_result
224241

242+
@deprecated(
243+
"`last_bookmark` has been deprecated in favor of `last_bookmarks`. "
244+
"This method can lead to unexpected behaviour."
245+
)
246+
async def last_bookmark(self):
247+
"""Return the bookmark received following the last completed transaction.
248+
249+
Note: For auto-transactions (:meth:`Session.run`), this will trigger
250+
:meth:`Result.consume` for the current result.
251+
252+
.. warning::
253+
This method can lead to unexpected behaviour if the session has not
254+
yet successfully completed a transaction.
255+
256+
.. deprecated:: 5.0
257+
:meth:`last_bookmark` will be removed in version 6.0.
258+
Use :meth:`last_bookmarks` instead.
259+
260+
:returns: last bookmark
261+
:rtype: str or None
262+
"""
263+
# The set of bookmarks to be passed into the next transaction.
264+
265+
if self._auto_result:
266+
await self._auto_result.consume()
267+
268+
if self._transaction and self._transaction._closed:
269+
self._collect_bookmark(self._transaction._bookmark)
270+
self._transaction = None
271+
272+
if self._bookmarks:
273+
return self._bookmarks[-1]
274+
return None
275+
225276
async def last_bookmarks(self):
226277
"""Return most recent bookmarks of the session.
227278
@@ -231,6 +282,12 @@ async def last_bookmarks(self):
231282
``session2 = driver.session(bookmarks=session1.last_bookmarks())`` to
232283
achieve this.
233284
285+
Combine the bookmarks of multiple sessions like so::
286+
287+
bookmarks1 = await session1.last_bookmarks()
288+
bookmarks2 = await session2.last_bookmarks()
289+
session3 = driver.session(bookmarks=bookmarks1 + bookmarks2)
290+
234291
A session automatically manages bookmarks, so this method is rarely
235292
needed. If you need causal consistency, try to run the relevant queries
236293
in the same session.
@@ -239,11 +296,11 @@ async def last_bookmarks(self):
239296
or creation, or the last bookmark the session received after committing
240297
a transaction to the server.
241298
242-
Note: For auto-transaction (Session.run) this will trigger a
243-
``consume`` for the current result.
299+
Note: For auto-transactions (:meth:`Session.run`), this will trigger
300+
:meth:`Result.consume` for the current result.
244301
245-
:returns: list of bookmarks
246-
:rtype: list[str]
302+
:returns: the session's last known bookmarks
303+
:rtype: Bookmarks
247304
"""
248305
# The set of bookmarks to be passed into the next transaction.
249306

@@ -254,7 +311,7 @@ async def last_bookmarks(self):
254311
self._collect_bookmark(self._transaction._bookmark)
255312
self._transaction = None
256313

257-
return list(self._bookmarks)
314+
return Bookmarks.from_raw_values(self._bookmarks)
258315

259316
async def _transaction_closed_handler(self):
260317
if self._transaction:

neo4j/_sync/work/session.py

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
from ..._async_compat import sleep
2525
from ...api import (
26+
Bookmarks,
2627
READ_ACCESS,
2728
WRITE_ACCESS,
2829
)
@@ -37,6 +38,10 @@
3738
TransactionError,
3839
TransientError,
3940
)
41+
from ...meta import (
42+
deprecated,
43+
deprecation_warn,
44+
)
4045
from ...work import Query
4146
from .result import Result
4247
from .transaction import Transaction
@@ -85,7 +90,7 @@ class Session(Workspace):
8590
def __init__(self, pool, session_config):
8691
super().__init__(pool, session_config)
8792
assert isinstance(session_config, SessionConfig)
88-
self._bookmarks = tuple(session_config.bookmarks)
93+
self._bookmarks = self._prepare_bookmarks(session_config.bookmarks)
8994

9095
def __del__(self):
9196
if asyncio.iscoroutinefunction(self.close):
@@ -103,6 +108,18 @@ def __exit__(self, exception_type, exception_value, traceback):
103108
self._state_failed = True
104109
self.close()
105110

111+
def _prepare_bookmarks(self, bookmarks):
112+
if not bookmarks:
113+
return ()
114+
if isinstance(bookmarks, Bookmarks):
115+
return tuple(bookmarks.raw_values)
116+
if hasattr(bookmarks, "__iter__"):
117+
deprecation_warn("Passing an iterable to `bookmarks` is "
118+
"deprecated. Please use a Bookmarks instance.")
119+
return tuple(bookmarks)
120+
raise TypeError("Bookmarks must be an instance of Bookmarks or an "
121+
"iterable of raw bookmarks (deprecated).")
122+
106123
def _connect(self, access_mode):
107124
if access_mode is None:
108125
access_mode = self._config.default_access_mode
@@ -222,6 +239,40 @@ def run(self, query, parameters=None, **kwargs):
222239

223240
return self._auto_result
224241

242+
@deprecated(
243+
"`last_bookmark` has been deprecated in favor of `last_bookmarks`. "
244+
"This method can lead to unexpected behaviour."
245+
)
246+
def last_bookmark(self):
247+
"""Return the bookmark received following the last completed transaction.
248+
249+
Note: For auto-transactions (:meth:`Session.run`), this will trigger
250+
:meth:`Result.consume` for the current result.
251+
252+
.. warning::
253+
This method can lead to unexpected behaviour if the session has not
254+
yet successfully completed a transaction.
255+
256+
.. deprecated:: 5.0
257+
:meth:`last_bookmark` will be removed in version 6.0.
258+
Use :meth:`last_bookmarks` instead.
259+
260+
:returns: last bookmark
261+
:rtype: str or None
262+
"""
263+
# The set of bookmarks to be passed into the next transaction.
264+
265+
if self._auto_result:
266+
self._auto_result.consume()
267+
268+
if self._transaction and self._transaction._closed:
269+
self._collect_bookmark(self._transaction._bookmark)
270+
self._transaction = None
271+
272+
if self._bookmarks:
273+
return self._bookmarks[-1]
274+
return None
275+
225276
def last_bookmarks(self):
226277
"""Return most recent bookmarks of the session.
227278
@@ -231,6 +282,12 @@ def last_bookmarks(self):
231282
``session2 = driver.session(bookmarks=session1.last_bookmarks())`` to
232283
achieve this.
233284
285+
Combine the bookmarks of multiple sessions like so::
286+
287+
bookmarks1 = session1.last_bookmarks()
288+
bookmarks2 = session2.last_bookmarks()
289+
session3 = driver.session(bookmarks=bookmarks1 + bookmarks2)
290+
234291
A session automatically manages bookmarks, so this method is rarely
235292
needed. If you need causal consistency, try to run the relevant queries
236293
in the same session.
@@ -239,11 +296,11 @@ def last_bookmarks(self):
239296
or creation, or the last bookmark the session received after committing
240297
a transaction to the server.
241298
242-
Note: For auto-transaction (Session.run) this will trigger a
243-
``consume`` for the current result.
299+
Note: For auto-transactions (:meth:`Session.run`), this will trigger
300+
:meth:`Result.consume` for the current result.
244301
245-
:returns: list of bookmarks
246-
:rtype: list[str]
302+
:returns: the session's last known bookmarks
303+
:rtype: Bookmarks
247304
"""
248305
# The set of bookmarks to be passed into the next transaction.
249306

@@ -254,7 +311,7 @@ def last_bookmarks(self):
254311
self._collect_bookmark(self._transaction._bookmark)
255312
self._transaction = None
256313

257-
return list(self._bookmarks)
314+
return Bookmarks.from_raw_values(self._bookmarks)
258315

259316
def _transaction_closed_handler(self):
260317
if self._transaction:

0 commit comments

Comments
 (0)