Skip to content

Commit d98c224

Browse files
committed
revert: drop SDK-side workaround, skip regression test on Python 3.11
1 parent d846455 commit d98c224

2 files changed

Lines changed: 15 additions & 33 deletions

File tree

src/apify/_actor.py

Lines changed: 7 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import math
55
import sys
66
import warnings
7-
from contextlib import contextmanager
87
from dataclasses import asdict
98
from datetime import UTC, datetime, timedelta
109
from functools import cached_property
@@ -42,7 +41,7 @@
4241

4342
if TYPE_CHECKING:
4443
import logging
45-
from collections.abc import Callable, Iterator, MutableMapping
44+
from collections.abc import Callable, MutableMapping
4645
from decimal import Decimal
4746
from types import TracebackType
4847
from typing import Self
@@ -288,15 +287,12 @@ async def finalize() -> None:
288287
except Exception:
289288
self.log.exception('Failed to save Actor state')
290289

291-
# `exit()` / `fail()` may be called from within an event listener; detach that listener's own task for
292-
# the duration of the cleanup so the waits below don't deadlock on it (see `_detach_current_listener_task`).
293-
with self._detach_current_listener_task():
294-
try:
295-
await asyncio.wait_for(finalize(), self._cleanup_timeout.total_seconds())
296-
except TimeoutError:
297-
self.log.exception('Actor cleanup timed out')
298-
finally:
299-
self._active = False
290+
try:
291+
await asyncio.wait_for(finalize(), self._cleanup_timeout.total_seconds())
292+
except TimeoutError:
293+
self.log.exception('Actor cleanup timed out')
294+
finally:
295+
self._active = False
300296

301297
if reraise_control_flow:
302298
# Return without `sys.exit()` so the original exception re-raises.
@@ -1496,28 +1492,6 @@ def _get_remaining_time(self) -> timedelta | None:
14961492
)
14971493
return None
14981494

1499-
@contextmanager
1500-
def _detach_current_listener_task(self) -> Iterator[None]:
1501-
"""Temporarily remove the current task from the event manager's listener-task set.
1502-
1503-
If `exit()` / `fail()` runs inside an event listener, the current task is that listener's own tracked
1504-
task, so the cleanup waits below would deadlock on it and raise `RecursionError` on the timeout
1505-
cancellation. Detaching it skips it in those waits; restoring it lets the listener wrapper deregister it.
1506-
1507-
Only a direct call on the listener's task is handled, not one from a task the listener itself spawns
1508-
(asyncio exposes no task ancestry).
1509-
"""
1510-
listener_tasks = self.event_manager._listener_tasks # noqa: SLF001
1511-
current_task = asyncio.current_task()
1512-
is_listener_task = current_task is not None and current_task in listener_tasks
1513-
if is_listener_task:
1514-
listener_tasks.discard(current_task)
1515-
try:
1516-
yield
1517-
finally:
1518-
if is_listener_task:
1519-
listener_tasks.add(current_task)
1520-
15211495

15221496
Actor = cast('_ActorType', Proxy(_ActorType))
15231497
"""The entry point of the SDK, through which all the Actor operations should be done."""

tests/unit/actor/test_actor_lifecycle.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import contextlib
55
import json
66
import logging
7+
import sys
78
from datetime import UTC, datetime, timedelta
89
from typing import TYPE_CHECKING, Any
910
from unittest import mock
@@ -112,6 +113,13 @@ async def test_fail_properly_deinitializes_actor(actor: _ActorType) -> None:
112113
assert actor._active is False
113114

114115

116+
@pytest.mark.skipif(
117+
sys.version_info < (3, 12),
118+
reason=(
119+
'On Python 3.11, `asyncio.wait_for` runs the awaited coroutine in a separate task, which defeats '
120+
"crawlee's own self-wait detection in `EventManager.wait_for_all_listeners_to_complete` and deadlocks."
121+
),
122+
)
115123
async def test_exit_from_event_listener_completes_cleanup() -> None:
116124
"""`Actor.exit()` called from an event listener runs cleanup instead of deadlocking into a RecursionError."""
117125
actor = Actor(exit_process=False)

0 commit comments

Comments
 (0)