From f6c047a94dddcad11543e01ae7c0593b701b5572 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Thu, 11 Jun 2026 16:16:36 +0200 Subject: [PATCH] fix: respect explicit zero custom_after_sleep in metamorph and reboot The falsy check treated an explicit timedelta(0) as unset and silently replaced it with the 5-minute default. Check for None instead so callers can opt out of the after-sleep entirely. --- src/apify/_actor.py | 4 +-- tests/unit/actor/test_actor_helpers.py | 37 ++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/apify/_actor.py b/src/apify/_actor.py index befd051d..ff5c1e1f 100644 --- a/src/apify/_actor.py +++ b/src/apify/_actor.py @@ -1166,7 +1166,7 @@ async def metamorph( self.log.error('Actor.metamorph() is only supported when running on the Apify platform.') return - if not custom_after_sleep: + if custom_after_sleep is None: custom_after_sleep = self.configuration.metamorph_after_sleep # If is_at_home() is True, configuration.actor_run_id is always set @@ -1208,7 +1208,7 @@ async def reboot( self._is_rebooting = True - if not custom_after_sleep: + if custom_after_sleep is None: custom_after_sleep = self.configuration.metamorph_after_sleep # Call all the listeners for the PERSIST_STATE and MIGRATING events, and wait for them to finish. diff --git a/tests/unit/actor/test_actor_helpers.py b/tests/unit/actor/test_actor_helpers.py index 633aa028..c81e797d 100644 --- a/tests/unit/actor/test_actor_helpers.py +++ b/tests/unit/actor/test_actor_helpers.py @@ -4,6 +4,7 @@ import logging from datetime import UTC, datetime, timedelta from typing import TYPE_CHECKING +from unittest.mock import AsyncMock, patch import pytest @@ -420,3 +421,39 @@ async def hanging_listener(*_args: object) -> None: # The reboot API call proceeded despite the hanging listener. assert len(apify_client_async_patcher.calls['run']['reboot']) == 1 + + +async def test_metamorph_with_zero_custom_after_sleep_does_not_sleep( + apify_client_async_patcher: ApifyClientAsyncPatcher, +) -> None: + """Test that an explicit `custom_after_sleep=timedelta(0)` is not replaced by the default sleep duration.""" + apify_client_async_patcher.patch('run', 'metamorph', return_value=None) + + async with Actor: + Actor.configuration.is_at_home = True + Actor.configuration.actor_run_id = 'some-run-id' + + with patch('asyncio.sleep', new=AsyncMock()) as sleep_mock: + await Actor.metamorph('target-actor-id', custom_after_sleep=timedelta(0)) + + sleep_mock.assert_not_awaited() + + assert len(apify_client_async_patcher.calls['run']['metamorph']) == 1 + + +async def test_reboot_with_zero_custom_after_sleep_does_not_sleep( + apify_client_async_patcher: ApifyClientAsyncPatcher, +) -> None: + """Test that an explicit `custom_after_sleep=timedelta(0)` is not replaced by the default sleep duration.""" + apify_client_async_patcher.patch('run', 'reboot', return_value=None) + + async with Actor: + Actor.configuration.is_at_home = True + Actor.configuration.actor_run_id = 'some-run-id' + + with patch('asyncio.sleep', new=AsyncMock()) as sleep_mock: + await Actor.reboot(custom_after_sleep=timedelta(0)) + + sleep_mock.assert_not_awaited() + + assert len(apify_client_async_patcher.calls['run']['reboot']) == 1