60
60
61
61
62
62
if t .TYPE_CHECKING :
63
+ import typing_extensions as te
64
+
63
65
from ..._api import TelemetryAPI
64
66
65
67
@@ -272,83 +274,31 @@ def assert_notification_filtering_support(self):
272
274
f"{ self .server_info .agent !r} "
273
275
)
274
276
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 ]]] = {}
329
278
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 )
352
302
353
303
@classmethod
354
304
def get_handshake (cls ):
@@ -358,15 +308,9 @@ def get_handshake(cls):
358
308
The length is 16 bytes as specified in the Bolt version negotiation.
359
309
:returns: bytes
360
310
"""
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 "
368
313
)
369
- return b"" .join (versions_bytes ).ljust (16 , b"\x00 " )
370
314
371
315
@classmethod
372
316
async def ping (cls , address , * , deadline = None , pool_config = None ):
@@ -442,64 +386,16 @@ async def open(
442
386
)
443
387
444
388
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 :
495
392
log .debug ("[#%04X] C: <CLOSE>" , s .getsockname ()[1 ])
496
393
await AsyncBoltSocket .close_socket (s )
497
394
498
- supported_versions = cls .protocol_handlers ().keys ()
499
395
raise BoltHandshakeError (
500
396
"The neo4j server does not support communication with this "
501
397
"driver. This driver has support for Bolt protocols "
502
- f"{ tuple (map (str , supported_versions ))} ." ,
398
+ f"{ tuple (map (str , AsyncBolt . protocol_handlers . keys () ))} ." ,
503
399
address = address ,
504
400
request_data = handshake ,
505
401
response_data = data ,
0 commit comments