Skip to content

Commit fcb9d11

Browse files
authored
Actor.reboot method uses the new reboot endpoint (#109)
1 parent 4cfb6dd commit fcb9d11

File tree

6 files changed

+68
-12
lines changed

6 files changed

+68
-12
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
Changelog
22
=========
33

4+
[1.2.0](../../releases/tag/v1.2.0) - 2023-08-23
5+
-----------------------------------------------
6+
7+
### Added
8+
9+
- Update the `Actor.reboot` method to use the new reboot endpoint
10+
11+
### Internal changes
12+
13+
- Unify indentation in configuration files
14+
415
[1.1.2](../../releases/tag/v1.1.2) - 2023-08-02
516
-----------------------------------------------
617

docs/docs.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -557,14 +557,16 @@ Get the actor input value from the default key-value store associated with the c
557557

558558
***
559559

560-
#### [](#actor-get_value) `async Actor.get_value(key)`
560+
#### [](#actor-get_value) `async Actor.get_value(key, default_value=None)`
561561

562562
Get a value from the default key-value store associated with the current actor run.
563563

564564
* **Parameters**
565565

566566
* **key** (`str`) – The key of the record which to retrieve.
567567

568+
* **default_value** (`Any`, *optional*) – Default value returned in case the record does not exist.
569+
568570
* **Return type**
569571

570572
`Any`
@@ -859,7 +861,7 @@ and the new input is stored under the INPUT-METAMORPH-1 key in the same default
859861

860862
***
861863

862-
#### [](#actor-reboot) `async Actor.reboot(*, event_listeners_timeout_secs=5)`
864+
#### [](#actor-reboot) `async Actor.reboot(*, event_listeners_timeout_secs=5, custom_after_sleep_millis=None)`
863865

864866
Internally reboot this actor.
865867

@@ -869,6 +871,8 @@ The system stops the current container and starts a new one, with the same run I
869871

870872
* **event_listeners_timeout_secs** (`int`, *optional*) – How long should the actor wait for actor event listeners to finish before exiting
871873

874+
* **custom_after_sleep_millis** (`int`, *optional*) – How long to sleep for after the reboot, to wait for the container to be stopped.
875+
872876
* **Return type**
873877

874878
`None`

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ requires-python = ">=3.8"
2525
dependencies = [
2626
"aiofiles >= 22.1.0",
2727
"aioshutil >= 1.0",
28-
"apify-client == 1.3.1",
28+
"apify-client == 1.4.0",
2929
"apify-shared == 1.0.2",
3030
"colorama >= 0.4.6",
3131
"cryptography >= 39.0.0",

src/apify/actor.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from .proxy_configuration import ProxyConfiguration
3232
from .storages import Dataset, KeyValueStore, RequestQueue, StorageClientManager
3333

34+
T = TypeVar('T')
3435
MainReturnType = TypeVar('MainReturnType')
3536

3637
# This metaclass is needed so you can do `async with Actor: ...` instead of `async with Actor() as a: ...`
@@ -629,19 +630,20 @@ async def _get_input_internal(self) -> Any:
629630
return input_value
630631

631632
@classmethod
632-
async def get_value(cls, key: str) -> Any:
633+
async def get_value(cls, key: str, default_value: Optional[T] = None) -> Any:
633634
"""Get a value from the default key-value store associated with the current actor run.
634635
635636
Args:
636637
key (str): The key of the record which to retrieve.
638+
default_value (Any, optional): Default value returned in case the record does not exist.
637639
"""
638-
return await cls._get_default_instance().get_value(key=key)
640+
return await cls._get_default_instance().get_value(key=key, default_value=default_value)
639641

640-
async def _get_value_internal(self, key: str) -> Any:
642+
async def _get_value_internal(self, key: str, default_value: Optional[T] = None) -> Any:
641643
self._raise_if_not_initialized()
642644

643645
key_value_store = await self.open_key_value_store()
644-
value = await key_value_store.get_value(key)
646+
value = await key_value_store.get_value(key, default_value)
645647
return value
646648

647649
@classmethod
@@ -1110,38 +1112,48 @@ async def reboot(
11101112
cls,
11111113
*,
11121114
event_listeners_timeout_secs: Optional[int] = EVENT_LISTENERS_TIMEOUT_SECS,
1115+
custom_after_sleep_millis: Optional[int] = None,
11131116
) -> None:
11141117
"""Internally reboot this actor.
11151118
11161119
The system stops the current container and starts a new one, with the same run ID and default storages.
11171120
11181121
Args:
11191122
event_listeners_timeout_secs (int, optional): How long should the actor wait for actor event listeners to finish before exiting
1123+
custom_after_sleep_millis (int, optional): How long to sleep for after the reboot, to wait for the container to be stopped.
11201124
"""
1121-
return await cls._get_default_instance().reboot(event_listeners_timeout_secs=event_listeners_timeout_secs)
1125+
return await cls._get_default_instance().reboot(
1126+
event_listeners_timeout_secs=event_listeners_timeout_secs,
1127+
custom_after_sleep_millis=custom_after_sleep_millis,
1128+
)
11221129

11231130
async def _reboot_internal(
11241131
self,
11251132
*,
11261133
event_listeners_timeout_secs: Optional[int] = EVENT_LISTENERS_TIMEOUT_SECS,
1134+
custom_after_sleep_millis: Optional[int] = None,
11271135
) -> None:
11281136
self._raise_if_not_initialized()
11291137

11301138
if not self.is_at_home():
11311139
self.log.error('Actor.reboot() is only supported when running on the Apify platform.')
11321140
return
11331141

1142+
if not custom_after_sleep_millis:
1143+
custom_after_sleep_millis = self._config.metamorph_after_sleep_millis
1144+
11341145
await self._cancel_event_emitting_intervals()
11351146

11361147
self._event_manager.emit(ActorEventTypes.PERSIST_STATE, {'isMigrating': True})
11371148
self._was_final_persist_state_emitted = True
11381149

11391150
await self._event_manager.close(event_listeners_timeout_secs=event_listeners_timeout_secs)
11401151

1141-
# If is_at_home() is True, config.actor_id is always set
1142-
assert self._config.actor_id is not None
1152+
assert self._config.actor_run_id is not None
1153+
await self._apify_client.run(self._config.actor_run_id).reboot()
11431154

1144-
await self.metamorph(self._config.actor_id)
1155+
if custom_after_sleep_millis:
1156+
await asyncio.sleep(custom_after_sleep_millis / 1000)
11451157

11461158
@classmethod
11471159
async def add_webhook(

tests/integration/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from apify.storages import Dataset, KeyValueStore, RequestQueue, StorageClientManager
1616
from apify_client import ApifyClientAsync
1717
from apify_client.clients.resource_clients import ActorClientAsync
18-
from apify_client.consts import ActorJobStatus, ActorSourceType
18+
from apify_shared.consts import ActorJobStatus, ActorSourceType
1919

2020
from ._utils import generate_unique_resource_name
2121

tests/integration/test_actor_api_helpers.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,35 @@ async def main_outer() -> None:
323323
assert await inner_actor.last_run().get() is None
324324

325325

326+
class TestActorReboot:
327+
async def test_actor_reboot(self, make_actor: ActorFactory) -> None:
328+
async def main() -> None:
329+
async with Actor:
330+
print('Starting...')
331+
cnt = await Actor.get_value('reboot_counter', 0)
332+
333+
if cnt < 2:
334+
print(f'Rebooting (cnt = {cnt})...')
335+
await Actor.set_value('reboot_counter', cnt + 1)
336+
await Actor.reboot()
337+
await Actor.set_value('THIS_KEY_SHOULD_NOT_BE_WRITTEN', 'XXX')
338+
339+
print('Finishing...')
340+
341+
actor = await make_actor('actor_rebooter', main_func=main)
342+
run_result = await actor.call(run_input={'counter_key': 'reboot_counter'})
343+
344+
assert run_result is not None
345+
assert run_result['status'] == 'SUCCEEDED'
346+
347+
not_written_value = await actor.last_run().key_value_store().get_record('THIS_KEY_SHOULD_NOT_BE_WRITTEN')
348+
assert not_written_value is None
349+
350+
reboot_counter = await actor.last_run().key_value_store().get_record('reboot_counter')
351+
assert reboot_counter is not None
352+
assert reboot_counter['value'] == 2
353+
354+
326355
class TestActorAddWebhook:
327356
async def test_actor_add_webhook(self, make_actor: ActorFactory) -> None:
328357
async def main_server() -> None:

0 commit comments

Comments
 (0)