Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/apify_client/_resource_clients/actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,15 +536,16 @@ def default_build(
def last_run(
self,
*,
status: ActorJobStatus | None = None,
status: ActorJobStatus | None = 'SUCCEEDED',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important: Changing the default to SUCCEEDED silently breaks the common start() then last_run().get() / .wait_for_finish() / .iterate_dataset_items() pattern, because a READY or RUNNING run is no longer matched and callers get None or an older run; the Python SDK e2e test asserting the started run is READY/RUNNING fails, and so does this repo's tests/integration/test_run.py:536 (on a fresh Actor whose only run is still running, the iterator yields nothing) - this is a breaking change shipped as plain feat: without ! or a BREAKING CHANGE: footer. (apify-sdk-python tests/e2e/test_actor_api_helpers.py:129)

origin: RunOrigin | None = None,
) -> RunClient:
"""Retrieve the client for the last run of this Actor.

Last run is retrieved based on the start time of the runs.

Args:
status: Consider only runs with this status.
status: Consider only runs with this status. Defaults to SUCCEEDED; pass None to consider runs
of any status.
origin: Consider only runs started with this origin.

Returns:
Expand Down Expand Up @@ -1139,15 +1140,16 @@ async def default_build(
def last_run(
self,
*,
status: ActorJobStatus | None = None,
status: ActorJobStatus | None = 'SUCCEEDED',
origin: RunOrigin | None = None,
) -> RunClientAsync:
"""Retrieve the client for the last run of this Actor.

Last run is retrieved based on the start time of the runs.

Args:
status: Consider only runs with this status.
status: Consider only runs with this status. Defaults to SUCCEEDED; pass None to consider runs
of any status.
origin: Consider only runs started with this origin.

Returns:
Expand Down
12 changes: 8 additions & 4 deletions src/apify_client/_resource_clients/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,13 +429,14 @@ def runs(self) -> RunCollectionClient:
**self._base_client_kwargs,
)

def last_run(self, *, status: ActorJobStatus | None = None, origin: RunOrigin | None = None) -> RunClient:
def last_run(self, *, status: ActorJobStatus | None = 'SUCCEEDED', origin: RunOrigin | None = None) -> RunClient:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: the JS client's lastRun(options = {}) sends no status filter by default for both Actors and tasks, so with this default the same call returns different runs in the two clients. (apify-client-js src/resource_clients/actor.ts:509)

"""Retrieve the client for the last run of this task.

Last run is retrieved based on the start time of the runs.

Args:
status: Consider only runs with this status.
status: Consider only runs with this status. Defaults to SUCCEEDED; pass None to consider runs
of any status.
origin: Consider only runs started with this origin.

Returns:
Expand Down Expand Up @@ -844,13 +845,16 @@ def runs(self) -> RunCollectionClientAsync:
**self._base_client_kwargs,
)

def last_run(self, *, status: ActorJobStatus | None = None, origin: RunOrigin | None = None) -> RunClientAsync:
def last_run(
self, *, status: ActorJobStatus | None = 'SUCCEEDED', origin: RunOrigin | None = None
) -> RunClientAsync:
"""Retrieve the client for the last run of this task.

Last run is retrieved based on the start time of the runs.

Args:
status: Consider only runs with this status.
status: Consider only runs with this status. Defaults to SUCCEEDED; pass None to consider runs
of any status.
origin: Consider only runs started with this origin.

Returns:
Expand Down
64 changes: 64 additions & 0 deletions tests/unit/test_last_run_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,67 @@ async def test_last_run_filters_propagate_to_chained_clients_async(
request, _ = httpserver.log[-1]
assert request.args.get('status') == 'SUCCEEDED'
assert request.args.get('origin') == 'WEB'


@pytest.mark.parametrize(
('resource', 'path_prefix'),
[
pytest.param('actor', 'actors', id='actor'),
pytest.param('task', 'actor-tasks', id='task'),
],
)
@pytest.mark.parametrize(
('kwargs', 'expected_status'),
[
pytest.param({}, 'SUCCEEDED', id='default'),
pytest.param({'status': 'FAILED'}, 'FAILED', id='explicit_status'),
pytest.param({'status': None}, None, id='any_status'),
],
)
def test_last_run_defaults_to_succeeded(
httpserver: HTTPServer,
resource: str,
path_prefix: str,
kwargs: dict,
expected_status: str | None,
) -> None:
"""`last_run()` asks for the last successful run unless the caller picks a different status or opts out."""
httpserver.expect_request(f'/v2/{path_prefix}/resource-id/runs/last').respond_with_json(_NOT_FOUND_BODY, status=404)
client = ApifyClient(token='test-token', api_url=httpserver.url_for('/').removesuffix('/'))

assert getattr(client, resource)('resource-id').last_run(**kwargs).get() is None

request, _ = httpserver.log[-1]
assert request.args.get('status') == expected_status


@pytest.mark.parametrize(
('resource', 'path_prefix'),
[
pytest.param('actor', 'actors', id='actor'),
pytest.param('task', 'actor-tasks', id='task'),
],
)
@pytest.mark.parametrize(
('kwargs', 'expected_status'),
[
pytest.param({}, 'SUCCEEDED', id='default'),
pytest.param({'status': 'FAILED'}, 'FAILED', id='explicit_status'),
pytest.param({'status': None}, None, id='any_status'),
],
)
async def test_last_run_defaults_to_succeeded_async(
httpserver: HTTPServer,
resource: str,
path_prefix: str,
kwargs: dict,
expected_status: str | None,
) -> None:
"""Async variant of `test_last_run_defaults_to_succeeded`."""
httpserver.expect_request(f'/v2/{path_prefix}/resource-id/runs/last').respond_with_json(_NOT_FOUND_BODY, status=404)
client = ApifyClientAsync(token='test-token', api_url=httpserver.url_for('/').removesuffix('/'))

assert await getattr(client, resource)('resource-id').last_run(**kwargs).get() is None

request, _ = httpserver.log[-1]
assert request.args.get('status') == expected_status
Loading