diff --git a/src/apify/_actor.py b/src/apify/_actor.py index be3487f9..7a8cb52c 100644 --- a/src/apify/_actor.py +++ b/src/apify/_actor.py @@ -1,6 +1,7 @@ from __future__ import annotations import asyncio +import functools import os import sys from contextlib import suppress @@ -50,6 +51,26 @@ MainReturnType = TypeVar('MainReturnType') +TFun = TypeVar('TFun', bound=Callable[..., Any]) + + +def _add_local_storage_error_hint(function: TFun) -> TFun: + """This decorator adds a local storage error hint in situation where storage was not found locally.""" + + @functools.wraps(function) + async def wrapper( + self: _ActorType, *, id: str | None = None, name: str | None = None, force_cloud: bool = False + ) -> Any: + try: + return await function(self=self, id=id, name=name, force_cloud=force_cloud) + except Exception as e: + if not force_cloud: + e.args = ( + f'{e.args[0]} (If you are trying to retrieve a remote storage, use `force_cloud=True` argument.)', + ) + raise + + return cast(TFun, wrapper) @docs_name('Actor') @@ -362,6 +383,7 @@ def new_client( timeout_secs=int(timeout.total_seconds()) if timeout else None, ) + @_add_local_storage_error_hint async def open_dataset( self, *, @@ -398,6 +420,7 @@ async def open_dataset( storage_client=storage_client, ) + @_add_local_storage_error_hint async def open_key_value_store( self, *, @@ -432,6 +455,7 @@ async def open_key_value_store( storage_client=storage_client, ) + @_add_local_storage_error_hint async def open_request_queue( self, *, diff --git a/tests/unit/actor/test_actor_dataset.py b/tests/unit/actor/test_actor_dataset.py index ef6282bb..da427830 100644 --- a/tests/unit/actor/test_actor_dataset.py +++ b/tests/unit/actor/test_actor_dataset.py @@ -62,3 +62,12 @@ async def test_push_data_to_dataset() -> None: list_page = await dataset.get_data(limit=desired_item_count) assert {item['id'] for item in list_page.items} == set(range(desired_item_count)) + + +async def test_open_local_non_existent_dataset() -> None: + async with Actor as my_actor: + with pytest.raises(RuntimeError) as e: + await my_actor.open_dataset(id='non-existent') + assert str(e.value).endswith( + 'If you are trying to retrieve a remote storage, use `force_cloud=True` argument.)' + ) diff --git a/tests/unit/actor/test_actor_key_value_store.py b/tests/unit/actor/test_actor_key_value_store.py index 821065e1..2943216a 100644 --- a/tests/unit/actor/test_actor_key_value_store.py +++ b/tests/unit/actor/test_actor_key_value_store.py @@ -92,3 +92,12 @@ async def test_get_input_with_encrypted_secrets( input = await my_actor.get_input() # noqa: A001 assert input['foo'] == input_with_secret['foo'] assert input['secret'] == secret_string + + +async def test_open_local_non_existent_kvs() -> None: + async with Actor as my_actor: + with pytest.raises(RuntimeError) as e: + await my_actor.open_key_value_store(id='non-existent') + assert str(e.value).endswith( + 'If you are trying to retrieve a remote storage, use `force_cloud=True` argument.)' + ) diff --git a/tests/unit/actor/test_actor_request_queue.py b/tests/unit/actor/test_actor_request_queue.py index 5504715f..5e288f74 100644 --- a/tests/unit/actor/test_actor_request_queue.py +++ b/tests/unit/actor/test_actor_request_queue.py @@ -27,3 +27,12 @@ async def test_open_returns_same_references() -> None: rq_by_id_2 = await Actor.open_key_value_store(id=rq_by_name_1._id) assert rq_by_id_1 is rq_by_name_1 assert rq_by_id_2 is rq_by_id_1 + + +async def test_open_local_non_existent_rq() -> None: + async with Actor as my_actor: + with pytest.raises(RuntimeError) as e: + await my_actor.open_request_queue(id='non-existent') + assert str(e.value).endswith( + 'If you are trying to retrieve a remote storage, use `force_cloud=True` argument.)' + )