Skip to content

Commit 7926339

Browse files
authored
Merge pull request #657 from ably/stop-retrying-failures
ci: some improvements to CI stability and warnings
2 parents b799b9e + b8c41e7 commit 7926339

File tree

6 files changed

+30
-48
lines changed

6 files changed

+30
-48
lines changed

.github/workflows/check.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,4 @@ jobs:
4646
- name: Generate rest sync code and tests
4747
run: uv run unasync
4848
- name: Test with pytest
49-
run: uv run pytest --verbose --tb=short --reruns 3
49+
run: uv run pytest --verbose --tb=short --capture=no

ably/realtime/realtimepresence.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -468,10 +468,6 @@ async def subscribe(self, *args) -> None:
468468
Raises:
469469
AblyException: If channel state prevents subscription
470470
"""
471-
# RTP6d: Implicitly attach
472-
if self.channel.state in [ChannelState.INITIALIZED, ChannelState.DETACHED, ChannelState.DETACHING]:
473-
asyncio.create_task(self.channel.attach())
474-
475471
# Parse arguments: similar to channel subscribe
476472
if len(args) == 1:
477473
# subscribe(listener)
@@ -485,6 +481,10 @@ async def subscribe(self, *args) -> None:
485481
else:
486482
raise ValueError('Invalid subscribe arguments')
487483

484+
# RTP6d: Implicitly attach
485+
if self.channel.state in [ChannelState.INITIALIZED, ChannelState.DETACHED, ChannelState.DETACHING]:
486+
await self.channel.attach()
487+
488488
def unsubscribe(self, *args) -> None:
489489
"""
490490
Unsubscribe from presence events on this channel (RTP7).

ably/transport/websockettransport.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,9 @@ async def ws_read_loop(self):
203203
log.exception(
204204
f"WebSocketTransport.decode(): Unexpected exception handling channel message: {e}"
205205
)
206-
except ConnectionClosedOK:
206+
except (ConnectionClosedOK, GeneratorExit):
207+
# ConnectionClosedOK: normal websocket closure
208+
# GeneratorExit: coroutine being closed (e.g., during event loop shutdown)
207209
return
208210

209211
def decode_raw_websocket_frame(self, raw: str | bytes) -> dict:
@@ -229,18 +231,38 @@ def on_read_loop_done(self, task: asyncio.Task):
229231

230232
async def dispose(self):
231233
self.is_disposed = True
234+
235+
# Cancel tasks but don't await them yet to avoid deadlock
236+
tasks_to_await = []
237+
232238
if self.read_loop:
233239
self.read_loop.cancel()
240+
tasks_to_await.append(self.read_loop)
234241
if self.ws_connect_task:
235242
self.ws_connect_task.cancel()
243+
tasks_to_await.append(self.ws_connect_task)
236244
if self.idle_timer:
237245
self.idle_timer.cancel()
246+
247+
# Schedule cleanup of cancelled tasks in the background to avoid blocking dispose()
248+
# This prevents deadlock when dispose() is called from within these tasks
249+
if tasks_to_await:
250+
asyncio.create_task(self._cleanup_tasks(tasks_to_await))
251+
238252
if self.websocket:
239253
try:
240254
await self.websocket.close()
241255
except asyncio.CancelledError:
242256
return
243257

258+
async def _cleanup_tasks(self, tasks):
259+
"""Wait for cancelled tasks to complete their cleanup."""
260+
for task in tasks:
261+
try:
262+
await task
263+
except Exception:
264+
pass # Ignore all exceptions from cancelled/failed tasks
265+
244266
async def close(self):
245267
await self.send({'action': ProtocolMessageAction.CLOSE})
246268

pyproject.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@ dev = [
5252
"respx>=0.22.0,<0.23.0; python_version>='3.8'",
5353
"importlib-metadata>=4.12,<5.0",
5454
"pytest-timeout>=2.1.0,<3.0.0",
55-
"pytest-rerunfailures>=13.0,<14.0; python_version=='3.7'",
56-
"pytest-rerunfailures>=14.0,<15.0; python_version>='3.8'",
5755
"async-case>=10.1.0,<11.0.0; python_version=='3.7'",
5856
"tokenize_rt",
5957
"vcdiff-decoder>=0.1.0a1",

test/ably/realtime/realtimepresence_test.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,8 @@ async def test_presence_message_action(self):
342342
received = asyncio.Future()
343343

344344
def on_presence(msg):
345-
received.set_result(msg)
345+
if msg.action == PresenceAction.ENTER:
346+
received.set_result(msg)
346347

347348
await channel1.presence.subscribe(on_presence)
348349
await channel1.presence.enter()

uv.lock

Lines changed: 0 additions & 39 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)