Skip to content

Commit cf7ae93

Browse files
authored
Merge branch '6.x' into errors/value-error-on-invalid-acquisition-timeout
2 parents ebd6ce8 + 6da0a46 commit cf7ae93

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+348
-965
lines changed

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,37 @@ See also https://github.com/neo4j/neo4j-python-driver/wiki for a full changelog.
66
- Python 3.7, 3.8, and 3.9 support has been dropped.
77
- Remove deprecated package alias `neo4j-driver`. Use `pip install neo4j` instead.
88
- Remove `setup.py`. Please use a recent enough packaging/build tool that supports `pyproject.toml`
9+
- Remove deprecated modules:
10+
- `neo4j.conf`
11+
- `neo4j.data`
12+
- `neo4j.meta`
13+
- `neo4j.packstream`
14+
- `neo4j.routing`
15+
- `neo4j.time.arithmetic`
16+
- `neo4j.time.clock_implementation`
17+
- `neo4j.time.hydration`
18+
- `neo4j.time.metaclasses`
19+
- `neo4j.work`
20+
- `neo4j.work.query`
21+
- `neo4j.work.summary`
22+
- Remove deprecated exports from `neo4j`:
23+
- `log`, `Config`, `PoolConfig`, `SessionConfig`, `WorkspaceConfig` (internal - no replacement)
24+
- `SummaryNotificationPosition` (use `SummaryInputPosition` instead)
25+
- `api.Version` has been removed as it's unused now.
26+
`ServerInfo.protocol_version` now is a `tuple[int, int]` insteadof a `api.Version`.
27+
This should be drop-in replacement is most cases:
28+
- `Version` was a sup-type of `tuple[int, int]`
29+
- `ServerInfo.protocol_version` was already documented and typed as `tuple[int, int]`
30+
- `Version`'s additional methods were undocumented and shouldn't have been used
931
- Changed errors raised under certain circumstances
32+
- `ConfigurationError` if the passed `auth` parameters is not valid (instead of `AuthError`)
33+
- This improves the differentiation between `DriverError` for client-side errors and `Neo4jError` for server-side errors.
34+
- `access_mode` configuration option
35+
- `ValueError` on invalid value (instead of `ClientError`)
36+
- Consistently check the value (also for non-routing drivers)
37+
- `neo4j.exceptions.UnsupportedServerProduct` if no common bolt protocol version could be negotiated with the server
38+
(instead of internal `neo4j._exceptions.BoltHandshakeError`).
39+
`UnsupportedServerProduct` is now a subclass of `ServiceUnavailable` (instead of `Exception` directly).
1040
- `connection_acquisition_timeout` configuration option
1141
- `ValueError` on invalid values (instead of `ClientError`)
1242
- Consistently restrict the value to be strictly positive

docs/source/api.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2109,6 +2109,8 @@ Client-side errors
21092109

21102110
* :class:`neo4j.exceptions.ReadServiceUnavailable`
21112111

2112+
* :class:`neo4j.exceptions.UnsupportedServerProduct`
2113+
21122114
* :class:`neo4j.exceptions.IncompleteCommit`
21132115

21142116
* :class:`neo4j.exceptions.ConfigurationError`
@@ -2164,6 +2166,9 @@ Client-side errors
21642166
.. autoexception:: neo4j.exceptions.ReadServiceUnavailable()
21652167
:show-inheritance:
21662168

2169+
.. autoexception:: neo4j.exceptions.UnsupportedServerProduct()
2170+
:show-inheritance:
2171+
21672172
.. autoexception:: neo4j.exceptions.IncompleteCommit()
21682173
:show-inheritance:
21692174

src/neo4j/__init__.py

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616

1717
import typing as _t
18-
from logging import getLogger as _getLogger
1918

2019
from ._api import ( # noqa: F401 dynamic attributes
2120
NotificationCategory,
@@ -37,26 +36,19 @@
3736
AsyncSession,
3837
AsyncTransaction,
3938
)
40-
from ._conf import ( # noqa: F401 dynamic attribute
41-
Config as _Config,
42-
SessionConfig as _SessionConfig,
39+
from ._conf import (
4340
TrustAll,
4441
TrustCustomCAs,
4542
TrustSystemCAs,
46-
WorkspaceConfig as _WorkspaceConfig,
4743
)
4844
from ._data import Record
4945
from ._meta import (
50-
deprecation_warn as _deprecation_warn,
5146
ExperimentalWarning,
5247
get_user_agent,
5348
preview_warn as _preview_warn,
5449
PreviewWarning,
5550
version as __version__,
5651
)
57-
from ._sync.config import ( # noqa: F401 dynamic attribute
58-
PoolConfig as _PoolConfig,
59-
)
6052
from ._sync.driver import (
6153
BoltDriver,
6254
Driver,
@@ -87,7 +79,6 @@
8779
from ._work import (
8880
GqlStatusObject, # noqa: TCH004 false positive (dynamic attribute)
8981
NotificationClassification, # noqa: TCH004 false positive (dynamic attribute)
90-
SummaryInputPosition as SummaryNotificationPosition, # noqa: TCH004 false positive (dynamic attribute)
9182
)
9283

9384
from .addressing import (
@@ -112,7 +103,6 @@
112103
SYSTEM_DATABASE,
113104
TRUST_ALL_CERTIFICATES,
114105
TRUST_SYSTEM_CA_SIGNED_CERTIFICATES,
115-
Version,
116106
WRITE_ACCESS,
117107
)
118108

@@ -138,7 +128,6 @@
138128
"BoltDriver",
139129
"Bookmark",
140130
"Bookmarks",
141-
"Config", # noqa: F822 dynamic attribute
142131
"Driver",
143132
"EagerResult",
144133
"ExperimentalWarning",
@@ -154,7 +143,6 @@
154143
"NotificationDisabledClassification",
155144
"NotificationMinimumSeverity",
156145
"NotificationSeverity",
157-
"PoolConfig", # noqa: F822 dynamic attribute
158146
"PreviewWarning",
159147
"Query",
160148
"Record",
@@ -163,53 +151,25 @@
163151
"RoutingControl",
164152
"ServerInfo",
165153
"Session",
166-
"SessionConfig", # noqa: F822 dynamic attribute
167154
"SummaryCounters",
168155
"SummaryInputPosition",
169156
"SummaryNotification",
170-
"SummaryNotificationPosition",
171157
"Transaction",
172158
"TrustAll",
173159
"TrustCustomCAs",
174160
"TrustSystemCAs",
175-
"Version",
176-
"WorkspaceConfig", # noqa: F822 dynamic attribute
177161
"__version__",
178162
"basic_auth",
179163
"bearer_auth",
180164
"custom_auth",
181165
"get_user_agent",
182166
"kerberos_auth",
183-
"log", # noqa: F822 dynamic attribute
184167
"unit_of_work",
185168
]
186169

187170

188-
_log = _getLogger("neo4j")
189-
190-
191171
def __getattr__(name) -> _t.Any:
192172
# TODO: 6.0 - remove this
193-
if name in {
194-
"log",
195-
"Config",
196-
"PoolConfig",
197-
"SessionConfig",
198-
"WorkspaceConfig",
199-
}:
200-
_deprecation_warn(
201-
f"Importing {name} from neo4j is deprecated without replacement. "
202-
"It's internal and will be removed in a future version.",
203-
stack_level=2,
204-
)
205-
return globals()[f"_{name}"]
206-
if name == "SummaryNotificationPosition":
207-
_deprecation_warn(
208-
"SummaryNotificationPosition is deprecated. "
209-
"Use SummaryInputPosition instead.",
210-
stack_level=2,
211-
)
212-
return SummaryInputPosition
213173
if name in {
214174
"NotificationClassification",
215175
"GqlStatusObject",

src/neo4j/_async/io/_bolt.py

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,18 @@
3535
BoltError,
3636
BoltHandshakeError,
3737
)
38+
from ..._io import BoltProtocolVersion
3839
from ..._meta import USER_AGENT
3940
from ..._sync.config import PoolConfig
4041
from ...addressing import ResolvedAddress
41-
from ...api import (
42-
ServerInfo,
43-
Version,
44-
)
42+
from ...api import ServerInfo
4543
from ...exceptions import (
4644
ConfigurationError,
4745
DriverError,
4846
IncompleteCommit,
4947
ServiceUnavailable,
5048
SessionExpired,
49+
UnsupportedServerProduct,
5150
)
5251
from ..config import AsyncPoolConfig
5352
from ._bolt_socket import AsyncBoltSocket
@@ -109,7 +108,7 @@ class AsyncBolt:
109108

110109
MAGIC_PREAMBLE = b"\x60\x60\xb0\x17"
111110

112-
PROTOCOL_VERSION: Version = None # type: ignore[assignment]
111+
PROTOCOL_VERSION: BoltProtocolVersion = None # type: ignore[assignment]
113112

114113
# flag if connection needs RESET to go back to READY state
115114
is_reset = False
@@ -159,7 +158,7 @@ def __init__(
159158
ResolvedAddress(
160159
sock.getpeername(), host_name=unresolved_address.host
161160
),
162-
self.PROTOCOL_VERSION,
161+
self.PROTOCOL_VERSION.version,
163162
)
164163
self.connection_hints = {}
165164
self.patch = {}
@@ -244,7 +243,7 @@ def assert_re_auth_support(self):
244243
if not self.supports_re_auth:
245244
raise ConfigurationError(
246245
"User switching is not supported for Bolt "
247-
f"Protocol {self.PROTOCOL_VERSION!r}. Server Agent "
246+
f"Protocol {self.PROTOCOL_VERSION}. Server Agent "
248247
f"{self.server_info.agent!r}"
249248
)
250249

@@ -257,11 +256,13 @@ def assert_notification_filtering_support(self):
257256
if not self.supports_notification_filtering:
258257
raise ConfigurationError(
259258
"Notification filtering is not supported for the Bolt "
260-
f"Protocol {self.PROTOCOL_VERSION!r}. Server Agent "
259+
f"Protocol {self.PROTOCOL_VERSION}. Server Agent "
261260
f"{self.server_info.agent!r}"
262261
)
263262

264-
protocol_handlers: t.ClassVar[dict[Version, type[AsyncBolt]]] = {}
263+
protocol_handlers: t.ClassVar[
264+
dict[BoltProtocolVersion, type[AsyncBolt]]
265+
] = {}
265266

266267
def __init_subclass__(cls: type[te.Self], **kwargs: t.Any) -> None:
267268
if cls.SKIP_REGISTRATION:
@@ -272,14 +273,10 @@ def __init_subclass__(cls: type[te.Self], **kwargs: t.Any) -> None:
272273
raise ValueError(
273274
"AsyncBolt subclasses must define PROTOCOL_VERSION"
274275
)
275-
if not (
276-
isinstance(protocol_version, Version)
277-
and len(protocol_version) == 2
278-
and all(isinstance(i, int) for i in protocol_version)
279-
):
276+
if not isinstance(protocol_version, BoltProtocolVersion):
280277
raise TypeError(
281-
"PROTOCOL_VERSION must be a 2-tuple of integers, not "
282-
f"{protocol_version!r}"
278+
"PROTOCOL_VERSION must be a BoltProtocolVersion, found "
279+
f"{type(protocol_version)} for {cls.__name__}"
283280
)
284281
if protocol_version in AsyncBolt.protocol_handlers:
285282
cls_conflict = AsyncBolt.protocol_handlers[protocol_version]
@@ -336,16 +333,15 @@ async def ping(cls, address, *, deadline=None, pool_config=None):
336333
await AsyncBoltSocket.close_socket(s)
337334
return protocol_version
338335

339-
@classmethod
336+
@staticmethod
340337
async def open(
341-
cls,
342338
address,
343339
*,
344340
auth_manager=None,
345341
deadline=None,
346342
routing_context=None,
347343
pool_config=None,
348-
):
344+
) -> AsyncBolt:
349345
"""
350346
Open a new Bolt connection to a given server address.
351347
@@ -366,7 +362,7 @@ async def open(
366362
if deadline is None:
367363
deadline = Deadline(None)
368364

369-
s, protocol_version, handshake, data = await AsyncBoltSocket.connect(
365+
s, protocol_version = await AsyncBoltSocket.connect(
370366
address,
371367
tcp_timeout=pool_config.connection_timeout,
372368
deadline=deadline,
@@ -381,15 +377,10 @@ async def open(
381377
if bolt_cls is None:
382378
log.debug("[#%04X] C: <CLOSE>", s.getsockname()[1])
383379
await AsyncBoltSocket.close_socket(s)
384-
385-
# TODO: 6.0 - raise public DriverError subclass instead
386-
raise BoltHandshakeError(
380+
raise UnsupportedServerProduct(
387381
"The neo4j server does not support communication with this "
388382
"driver. This driver has support for Bolt protocols "
389-
f"{tuple(map(str, AsyncBolt.protocol_handlers.keys()))}.",
390-
address=address,
391-
request_data=handshake,
392-
response_data=data,
383+
f"{tuple(map(str, AsyncBolt.protocol_handlers))}.",
393384
)
394385

395386
try:

src/neo4j/_async/io/_bolt3.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,8 @@
2222
from ssl import SSLSocket
2323

2424
from ..._exceptions import BoltProtocolError
25-
from ...api import (
26-
READ_ACCESS,
27-
Version,
28-
)
25+
from ..._io import BoltProtocolVersion
26+
from ...api import READ_ACCESS
2927
from ...exceptions import (
3028
ConfigurationError,
3129
DatabaseUnavailable,
@@ -146,7 +144,7 @@ class AsyncBolt3(AsyncBolt):
146144
This is supported by Neo4j versions 3.5, 4.0, 4.1, 4.2, 4.3, and 4.4.
147145
"""
148146

149-
PROTOCOL_VERSION = Version(3, 0)
147+
PROTOCOL_VERSION = BoltProtocolVersion(3, 0)
150148

151149
ssr_enabled = False
152150

@@ -270,14 +268,14 @@ async def route(
270268
if database is not None:
271269
raise ConfigurationError(
272270
"Database name parameter for selecting database is not "
273-
f"supported in Bolt Protocol {self.PROTOCOL_VERSION!r}. "
271+
f"supported in Bolt Protocol {self.PROTOCOL_VERSION}. "
274272
f"Database name {database!r}. "
275273
f"Server Agent {self.server_info.agent!r}"
276274
)
277275
if imp_user is not None:
278276
raise ConfigurationError(
279277
"Impersonation is not supported in Bolt Protocol "
280-
f"{self.PROTOCOL_VERSION!r}. Trying to impersonate "
278+
f"{self.PROTOCOL_VERSION}. Trying to impersonate "
281279
f"{imp_user!r}."
282280
)
283281
dehydration_hooks, hydration_hooks = self._default_hydration_hooks(
@@ -332,13 +330,13 @@ def run(
332330
if db is not None:
333331
raise ConfigurationError(
334332
"Database name parameter for selecting database is not "
335-
f"supported in Bolt Protocol {self.PROTOCOL_VERSION!r}. "
333+
f"supported in Bolt Protocol {self.PROTOCOL_VERSION}. "
336334
f"Database name {db!r}."
337335
)
338336
if imp_user is not None:
339337
raise ConfigurationError(
340338
"Impersonation is not supported in Bolt Protocol "
341-
f"{self.PROTOCOL_VERSION!r}. Trying to impersonate "
339+
f"{self.PROTOCOL_VERSION}. Trying to impersonate "
342340
f"{imp_user!r}."
343341
)
344342
if (
@@ -439,13 +437,13 @@ def begin(
439437
if db is not None:
440438
raise ConfigurationError(
441439
"Database name parameter for selecting database is not "
442-
f"supported in Bolt Protocol {self.PROTOCOL_VERSION!r}. "
440+
f"supported in Bolt Protocol {self.PROTOCOL_VERSION}. "
443441
f"Database name {db!r}."
444442
)
445443
if imp_user is not None:
446444
raise ConfigurationError(
447445
"Impersonation is not supported in Bolt Protocol "
448-
f"{self.PROTOCOL_VERSION!r}. Trying to impersonate "
446+
f"{self.PROTOCOL_VERSION}. Trying to impersonate "
449447
f"{imp_user!r}."
450448
)
451449
if (

0 commit comments

Comments
 (0)