Skip to content

Commit df503f2

Browse files
authored
Python 3.8+ handle cancellation during HELLO (#801)
Turns out `asyncio.CancelledError` inherits from `BaseException` starting with Python 3.8.
1 parent 018b0b8 commit df503f2

File tree

4 files changed

+68
-2
lines changed

4 files changed

+68
-2
lines changed

neo4j/_async/io/_bolt.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,11 @@ def time_remaining():
373373
await connection.hello()
374374
finally:
375375
connection.socket.set_deadline(None)
376-
except Exception as e:
376+
except (
377+
Exception,
378+
# Python 3.8+: CancelledError is a subclass of BaseException
379+
asyncio.CancelledError,
380+
) as e:
377381
log.debug("[#%04X] C: <OPEN FAILED> %r", connection.local_port, e)
378382
connection.kill()
379383
raise

neo4j/_sync/io/_bolt.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,11 @@ def time_remaining():
373373
connection.hello()
374374
finally:
375375
connection.socket.set_deadline(None)
376-
except Exception as e:
376+
except (
377+
Exception,
378+
# Python 3.8+: CancelledError is a subclass of BaseException
379+
asyncio.CancelledError,
380+
) as e:
377381
log.debug("[#%04X] C: <OPEN FAILED> %r", connection.local_port, e)
378382
connection.kill()
379383
raise

tests/unit/async_/io/test_class_bolt.py

+29
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,14 @@
1616
# limitations under the License.
1717

1818

19+
import asyncio
20+
1921
import pytest
2022

2123
from neo4j._async.io import AsyncBolt
24+
from neo4j._async_compat.network import AsyncBoltSocket
25+
26+
from ...._async_compat import AsyncTestDecorators
2227

2328

2429
# python -m pytest tests/unit/io/test_class_bolt.py -s -v
@@ -74,3 +79,27 @@ def test_magic_preamble():
7479
preamble = 0x6060B017
7580
preamble_bytes = preamble.to_bytes(4, byteorder="big")
7681
assert AsyncBolt.MAGIC_PREAMBLE == preamble_bytes
82+
83+
84+
@AsyncTestDecorators.mark_async_only_test
85+
async def test_cancel_hello_in_open(mocker):
86+
address = ("localhost", 7687)
87+
socket_mock = mocker.AsyncMock(spec=AsyncBoltSocket)
88+
89+
socket_cls_mock = mocker.patch("neo4j._async.io._bolt.AsyncBoltSocket",
90+
autospec=True)
91+
socket_cls_mock.connect.return_value = (
92+
socket_mock, (5, 0), None, None
93+
)
94+
socket_mock.getpeername.return_value = address
95+
bolt_cls_mock = mocker.patch("neo4j._async.io._bolt5.AsyncBolt5x0",
96+
autospec=True)
97+
bolt_mock = bolt_cls_mock.return_value
98+
bolt_mock.socket = socket_mock
99+
bolt_mock.hello.side_effect = asyncio.CancelledError()
100+
bolt_mock.local_port = 1234
101+
102+
with pytest.raises(asyncio.CancelledError):
103+
await AsyncBolt.open(address)
104+
105+
bolt_mock.kill.assert_called_once_with()

tests/unit/sync/io/test_class_bolt.py

+29
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,15 @@
1616
# limitations under the License.
1717

1818

19+
import asyncio
20+
1921
import pytest
2022

23+
from neo4j._async_compat.network import BoltSocket
2124
from neo4j._sync.io import Bolt
2225

26+
from ...._async_compat import TestDecorators
27+
2328

2429
# python -m pytest tests/unit/io/test_class_bolt.py -s -v
2530

@@ -74,3 +79,27 @@ def test_magic_preamble():
7479
preamble = 0x6060B017
7580
preamble_bytes = preamble.to_bytes(4, byteorder="big")
7681
assert Bolt.MAGIC_PREAMBLE == preamble_bytes
82+
83+
84+
@TestDecorators.mark_async_only_test
85+
def test_cancel_hello_in_open(mocker):
86+
address = ("localhost", 7687)
87+
socket_mock = mocker.Mock(spec=BoltSocket)
88+
89+
socket_cls_mock = mocker.patch("neo4j._sync.io._bolt.BoltSocket",
90+
autospec=True)
91+
socket_cls_mock.connect.return_value = (
92+
socket_mock, (5, 0), None, None
93+
)
94+
socket_mock.getpeername.return_value = address
95+
bolt_cls_mock = mocker.patch("neo4j._sync.io._bolt5.Bolt5x0",
96+
autospec=True)
97+
bolt_mock = bolt_cls_mock.return_value
98+
bolt_mock.socket = socket_mock
99+
bolt_mock.hello.side_effect = asyncio.CancelledError()
100+
bolt_mock.local_port = 1234
101+
102+
with pytest.raises(asyncio.CancelledError):
103+
Bolt.open(address)
104+
105+
bolt_mock.kill.assert_called_once_with()

0 commit comments

Comments
 (0)