Skip to content

Commit 0b5c785

Browse files
committed
WIP
1 parent b23dca5 commit 0b5c785

File tree

11 files changed

+124
-327
lines changed

11 files changed

+124
-327
lines changed

src/neo4j/_async/io/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
]
3232

3333

34+
from . import ( # noqa - imports needed to register protocol handlers
35+
_bolt3,
36+
_bolt4,
37+
_bolt5,
38+
)
3439
from ._bolt import AsyncBolt
3540
from ._common import (
3641
check_supported_server_product,

src/neo4j/_async/io/_bolt.py

Lines changed: 32 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@
6060

6161

6262
if t.TYPE_CHECKING:
63+
import typing_extensions as te
64+
6365
from ..._api import TelemetryAPI
6466

6567

@@ -272,83 +274,31 @@ def assert_notification_filtering_support(self):
272274
f"{self.server_info.agent!r}"
273275
)
274276

275-
# [bolt-version-bump] search tag when changing bolt version support
276-
@classmethod
277-
def protocol_handlers(cls):
278-
"""
279-
Return a dictionary of available Bolt protocol handlers.
280-
281-
The handlers are keyed by version tuple. If an explicit protocol
282-
version is provided, the dictionary will contain either zero or one
283-
items, depending on whether that version is supported. If no protocol
284-
version is provided, all available versions will be returned.
285-
286-
:param protocol_version: tuple identifying a specific protocol
287-
version (e.g. (3, 5)) or None
288-
:returns: dictionary of version tuple to handler class for all
289-
relevant and supported protocol versions
290-
:raise TypeError: if protocol version is not passed in a tuple
291-
"""
292-
# Carry out Bolt subclass imports locally to avoid circular dependency
293-
# issues.
294-
from ._bolt3 import AsyncBolt3
295-
from ._bolt4 import (
296-
AsyncBolt4x2,
297-
AsyncBolt4x3,
298-
AsyncBolt4x4,
299-
)
300-
from ._bolt5 import (
301-
AsyncBolt5x0,
302-
AsyncBolt5x1,
303-
AsyncBolt5x2,
304-
AsyncBolt5x3,
305-
AsyncBolt5x4,
306-
AsyncBolt5x5,
307-
AsyncBolt5x6,
308-
)
309-
310-
return {
311-
AsyncBolt3.PROTOCOL_VERSION: AsyncBolt3,
312-
# 4.0-4.1 unsupported because no space left in the handshake
313-
AsyncBolt4x2.PROTOCOL_VERSION: AsyncBolt4x2,
314-
AsyncBolt4x3.PROTOCOL_VERSION: AsyncBolt4x3,
315-
AsyncBolt4x4.PROTOCOL_VERSION: AsyncBolt4x4,
316-
AsyncBolt5x0.PROTOCOL_VERSION: AsyncBolt5x0,
317-
AsyncBolt5x1.PROTOCOL_VERSION: AsyncBolt5x1,
318-
AsyncBolt5x2.PROTOCOL_VERSION: AsyncBolt5x2,
319-
AsyncBolt5x3.PROTOCOL_VERSION: AsyncBolt5x3,
320-
AsyncBolt5x4.PROTOCOL_VERSION: AsyncBolt5x4,
321-
AsyncBolt5x5.PROTOCOL_VERSION: AsyncBolt5x5,
322-
AsyncBolt5x6.PROTOCOL_VERSION: AsyncBolt5x6,
323-
}
324-
325-
@classmethod
326-
def version_list(cls, versions, limit=4):
327-
"""
328-
Return a list of supported protocol versions in order of preference.
277+
protocol_handlers: t.ClassVar[dict[Version, type[AsyncBolt]]] = {}
329278

330-
The number of protocol versions (or ranges) returned is limited to 4.
331-
"""
332-
# In fact, 4.3 is the fist version to support ranges. However, the
333-
# range support got backported to 4.2. But even if the server is too
334-
# old to have the backport, negotiating BOLT 4.1 is no problem as it's
335-
# equivalent to 4.2
336-
first_with_range_support = Version(4, 2)
337-
result = []
338-
for version in versions:
339-
if (
340-
result
341-
and version >= first_with_range_support
342-
and result[-1][0] == version[0]
343-
and result[-1][1][1] == version[1] + 1
344-
):
345-
# can use range to encompass this version
346-
result[-1][1][1] = version[1]
347-
continue
348-
result.append(Version(version[0], [version[1], version[1]]))
349-
if len(result) >= limit:
350-
break
351-
return result
279+
def __init_subclass__(cls: type[te.Self], **kwargs: t.Any) -> None:
280+
protocol_version = cls.PROTOCOL_VERSION
281+
if protocol_version is None:
282+
raise ValueError(
283+
"AsyncBolt subclasses must define PROTOCOL_VERSION"
284+
)
285+
if not (
286+
isinstance(protocol_version, Version)
287+
and len(protocol_version) == 2
288+
and all(isinstance(i, int) for i in protocol_version)
289+
):
290+
raise TypeError(
291+
"PROTOCOL_VERSION must be a 2-tuple of integers, not "
292+
f"{protocol_version!r}"
293+
)
294+
if protocol_version in AsyncBolt.protocol_handlers:
295+
cls_conflict = AsyncBolt.protocol_handlers[protocol_version]
296+
raise TypeError(
297+
f"Multiple classes for the same protocol version "
298+
f"{protocol_version}: {cls}, {cls_conflict}"
299+
)
300+
cls.protocol_handlers[protocol_version] = cls
301+
super().__init_subclass__(**kwargs)
352302

353303
@classmethod
354304
def get_handshake(cls):
@@ -358,15 +308,9 @@ def get_handshake(cls):
358308
The length is 16 bytes as specified in the Bolt version negotiation.
359309
:returns: bytes
360310
"""
361-
supported_versions = sorted(
362-
cls.protocol_handlers().keys(), reverse=True
363-
)
364-
offered_versions = cls.version_list(supported_versions, limit=3)
365-
versions_bytes = (
366-
Version(0xFF, 1).to_bytes(), # handshake v2
367-
*(v.to_bytes() for v in offered_versions),
311+
return (
312+
b"\x00\x00\x01\xff\x00\x06\x06\x05\x00\x04\x04\x04\x00\x00\x00\x03"
368313
)
369-
return b"".join(versions_bytes).ljust(16, b"\x00")
370314

371315
@classmethod
372316
async def ping(cls, address, *, deadline=None, pool_config=None):
@@ -442,64 +386,16 @@ async def open(
442386
)
443387

444388
pool_config.protocol_version = protocol_version
445-
446-
# Carry out Bolt subclass imports locally to avoid circular dependency
447-
# issues.
448-
449-
# avoid new lines after imports for better readability and conciseness
450-
# fmt: off
451-
if protocol_version == (5, 6):
452-
from ._bolt5 import AsyncBolt5x6
453-
bolt_cls = AsyncBolt5x6
454-
elif protocol_version == (5, 5):
455-
from ._bolt5 import AsyncBolt5x5
456-
bolt_cls = AsyncBolt5x5
457-
elif protocol_version == (5, 4):
458-
from ._bolt5 import AsyncBolt5x4
459-
bolt_cls = AsyncBolt5x4
460-
elif protocol_version == (5, 3):
461-
from ._bolt5 import AsyncBolt5x3
462-
bolt_cls = AsyncBolt5x3
463-
elif protocol_version == (5, 2):
464-
from ._bolt5 import AsyncBolt5x2
465-
bolt_cls = AsyncBolt5x2
466-
elif protocol_version == (5, 1):
467-
from ._bolt5 import AsyncBolt5x1
468-
bolt_cls = AsyncBolt5x1
469-
elif protocol_version == (5, 0):
470-
from ._bolt5 import AsyncBolt5x0
471-
bolt_cls = AsyncBolt5x0
472-
elif protocol_version == (4, 4):
473-
from ._bolt4 import AsyncBolt4x4
474-
bolt_cls = AsyncBolt4x4
475-
elif protocol_version == (4, 3):
476-
from ._bolt4 import AsyncBolt4x3
477-
bolt_cls = AsyncBolt4x3
478-
elif protocol_version == (4, 2):
479-
from ._bolt4 import AsyncBolt4x2
480-
bolt_cls = AsyncBolt4x2
481-
# Implementations for exist, but there was no space left in the
482-
# handshake to offer this version to the server. Hence, the server
483-
# should never request us to speak these bolt versions.
484-
# elif protocol_version == (4, 1):
485-
# from ._bolt4 import AsyncBolt4x1
486-
# bolt_cls = AsyncBolt4x1
487-
# elif protocol_version == (4, 0):
488-
# from ._bolt4 import AsyncBolt4x0
489-
# bolt_cls = AsyncBolt4x0
490-
elif protocol_version == (3, 0):
491-
from ._bolt3 import AsyncBolt3
492-
bolt_cls = AsyncBolt3
493-
# fmt: on
494-
else:
389+
protocol_handlers = AsyncBolt.protocol_handlers
390+
bolt_cls = protocol_handlers.get(protocol_version)
391+
if bolt_cls is None:
495392
log.debug("[#%04X] C: <CLOSE>", s.getsockname()[1])
496393
await AsyncBoltSocket.close_socket(s)
497394

498-
supported_versions = cls.protocol_handlers().keys()
499395
raise BoltHandshakeError(
500396
"The neo4j server does not support communication with this "
501397
"driver. This driver has support for Bolt protocols "
502-
f"{tuple(map(str, supported_versions))}.",
398+
f"{tuple(map(str, AsyncBolt.protocol_handlers.keys()))}.",
503399
address=address,
504400
request_data=handshake,
505401
response_data=data,

src/neo4j/_sync/io/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
]
3232

3333

34+
from . import ( # noqa - imports needed to register protocol handlers
35+
_bolt3,
36+
_bolt4,
37+
_bolt5,
38+
)
3439
from ._bolt import Bolt
3540
from ._common import (
3641
check_supported_server_product,

0 commit comments

Comments
 (0)