From 53b07f8ea08990bbc69c7c0456bb4c070a607b0c Mon Sep 17 00:00:00 2001 From: Max Bohomolov Date: Thu, 19 Feb 2026 00:21:41 +0000 Subject: [PATCH 1/3] handle `ServiceConflictError` when reusing `Actor` across sequential context --- src/apify/_actor.py | 29 ++++++++++++++++-------- tests/unit/actor/test_actor_lifecycle.py | 26 +++++++++++++++++++++ 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/apify/_actor.py b/src/apify/_actor.py index 76f4475b..8387fd25 100644 --- a/src/apify/_actor.py +++ b/src/apify/_actor.py @@ -350,18 +350,27 @@ def event_manager(self) -> EventManager: It uses `ApifyEventManager` on the Apify platform and `LocalEventManager` otherwise. """ - event_manager = ( - ApifyEventManager( - configuration=self.configuration, - persist_state_interval=self.configuration.persist_state_interval, + try: + event_manager = ( + ApifyEventManager( + configuration=self.configuration, + persist_state_interval=self.configuration.persist_state_interval, + ) + if self.is_at_home() + else LocalEventManager( + system_info_interval=self.configuration.system_info_interval, + persist_state_interval=self.configuration.persist_state_interval, + ) ) - if self.is_at_home() - else LocalEventManager( - system_info_interval=self.configuration.system_info_interval, - persist_state_interval=self.configuration.persist_state_interval, + service_locator.set_event_manager(event_manager) + except ServiceConflictError: + self.log.debug( + 'Event manager in service locator was set explicitly before Actor.init was called. ' + 'Using the existing event manager as implicit event manager for the Actor.' ) - ) - service_locator.set_event_manager(event_manager) + # Use the event manager from the service locator + event_manager = service_locator.get_event_manager() + return event_manager @cached_property diff --git a/tests/unit/actor/test_actor_lifecycle.py b/tests/unit/actor/test_actor_lifecycle.py index b8fb6ad6..09338514 100644 --- a/tests/unit/actor/test_actor_lifecycle.py +++ b/tests/unit/actor/test_actor_lifecycle.py @@ -4,6 +4,7 @@ import contextlib import logging from typing import TYPE_CHECKING +from unittest.mock import AsyncMock import pytest @@ -232,3 +233,28 @@ async def test_actor_fail_prevents_further_execution(caplog: pytest.LogCaptureFi status_records = [r for r in caplog.records if r.msg == '[Terminal status message]: cde'] assert len(status_records) == 1 assert status_records[0].levelno == logging.INFO + + +@pytest.mark.parametrize( + ('first_with_call', 'second_with_call'), + [ + pytest.param(False, False, id='both_without_call'), + pytest.param(False, True, id='first_without_call'), + pytest.param(True, False, id='second_without_call'), + pytest.param(True, True, id='both_with_call'), + ], +) +async def test_actor_sequential_contexts(*, first_with_call: bool, second_with_call: bool) -> None: + """Test that Actor and Actor() can be used in two sequential async context manager blocks.""" + mock = AsyncMock() + async with Actor(exit_process=False) if first_with_call else Actor as actor: + await mock() + assert actor._is_initialized is True + + # After exiting the context, new Actor instance can be created without conflicts. + async with Actor() if second_with_call else Actor as actor: + await mock() + assert actor._is_initialized is True + + # The mock should have been called twice, once in each context. + assert mock.call_count == 2 From 907ee3c6d76c960ab6934e1cf96f017b5f4a640c Mon Sep 17 00:00:00 2001 From: Max Bohomolov <34358312+Mantisus@users.noreply.github.com> Date: Thu, 19 Feb 2026 02:30:24 +0200 Subject: [PATCH 2/3] Update src/apify/_actor.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/apify/_actor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/apify/_actor.py b/src/apify/_actor.py index 8387fd25..b2da7807 100644 --- a/src/apify/_actor.py +++ b/src/apify/_actor.py @@ -365,8 +365,8 @@ def event_manager(self) -> EventManager: service_locator.set_event_manager(event_manager) except ServiceConflictError: self.log.debug( - 'Event manager in service locator was set explicitly before Actor.init was called. ' - 'Using the existing event manager as implicit event manager for the Actor.' + 'Event manager already exists in service locator (set by previous Actor context or explicitly by user). ' + 'Using the existing event manager.' ) # Use the event manager from the service locator event_manager = service_locator.get_event_manager() From 837b244b9f2e098f95b27ffbcfd772d87df4acca Mon Sep 17 00:00:00 2001 From: Max Bohomolov Date: Thu, 19 Feb 2026 02:21:59 +0000 Subject: [PATCH 3/3] fix --- src/apify/_actor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/apify/_actor.py b/src/apify/_actor.py index b2da7807..dc0fe844 100644 --- a/src/apify/_actor.py +++ b/src/apify/_actor.py @@ -365,8 +365,8 @@ def event_manager(self) -> EventManager: service_locator.set_event_manager(event_manager) except ServiceConflictError: self.log.debug( - 'Event manager already exists in service locator (set by previous Actor context or explicitly by user). ' - 'Using the existing event manager.' + 'Event manager already exists in service locator (set by previous Actor context or explicitly by ' + 'user). Using the existing event manager.' ) # Use the event manager from the service locator event_manager = service_locator.get_event_manager()