Skip to content

Commit c9991fa

Browse files
authored
fix: deprecate max_items in favor of max_total_charge_usd (#1168)
`max_items` is a leftover from the pay-per-result pricing model. The platform applies it to pay-per-event Actors only when `max_total_charge_usd` is not set, by turning it into a charge limit priced by the Actor's `apify-default-dataset-item` event or its last pay-per-result price. On any other Actor it does nothing, and the name doesn't suggest that. - The `max_items` docstrings on `Actor.start`, `Actor.call`, `Actor.start_task`, `Actor.call_task` and `Actor.resurrect` mark it deprecated (removal in 5.0.0) and describe what it really does. - Passing `max_items` emits a `FutureWarning` that recommends `max_total_charge_usd`. I picked `FutureWarning` over `DeprecationWarning` because Python hides `DeprecationWarning` unless the caller is `__main__`, so it would never show up in an Actor's run log. Stacked on #1167, which adds `max_total_charge_usd` to the task methods. Raised in #1166 (comment); the JS counterpart is apify/apify-sdk-js#768. *✍️ Drafted by Claude Code*
1 parent 1a13da8 commit c9991fa

2 files changed

Lines changed: 103 additions & 20 deletions

File tree

‎src/apify/_actor.py‎

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@
6666
_ensure_context = ensure_context('_active')
6767

6868

69+
def _warn_max_items_deprecated() -> None:
70+
warnings.warn(
71+
'`max_items` is deprecated and will be removed in version 5.0.0. It works only with legacy pay-per-result '
72+
'Actors. Use `max_total_charge_usd` instead.',
73+
FutureWarning,
74+
# This helper, the SDK method, and its `ensure_context` wrapper sit between the warning and the caller.
75+
stacklevel=4,
76+
)
77+
78+
6979
@docs_name('Actor')
7080
@docs_group('Actor')
7181
class _ActorType:
@@ -956,8 +966,8 @@ async def start(
956966
content_type: The content type of the input.
957967
build: Specifies the Actor build to run. It can be either a build tag or build number. By default,
958968
the run uses the build specified in the default run configuration for the Actor (typically latest).
959-
max_items: Maximum number of dataset items you are charged for, for pay-per-result Actors. It caps the
960-
charge, not the output, so the run can return fewer or more items than this.
969+
max_items: Deprecated, use `max_total_charge_usd` instead. Will be removed in version 5.0.0. Works only with
970+
legacy pay-per-result Actors.
961971
max_total_charge_usd: A limit on the total charged amount, in USD. Once the run exceeds it, the platform
962972
aborts the run, which takes a few seconds, so the final charge can slightly overshoot the limit.
963973
restart_on_error: If true, the Actor run process will be restarted whenever it exits with
@@ -976,6 +986,9 @@ async def start(
976986
Returns:
977987
Info about the started Actor run
978988
"""
989+
if max_items is not None:
990+
_warn_max_items_deprecated()
991+
979992
client = self.new_client(token=token) if token else self.apify_client
980993

981994
actor_client = client.actor(actor_id)
@@ -1057,8 +1070,9 @@ async def resurrect(
10571070
timeout: New timeout for the resurrected run. By default, the resurrected run uses the same timeout as
10581071
before. Using `inherit` will set timeout of the resurrected run to the time remaining from this Actor
10591072
timeout.
1060-
max_items: Maximum number of items that the resurrected pay-per-result run will return. By default, the
1061-
resurrected run uses the same limit as before. The limit can only be increased.
1073+
max_items: Deprecated, use `max_total_charge_usd` instead. Will be removed in version 5.0.0. Works only with
1074+
legacy pay-per-result Actors. By default, the resurrected run uses the same limit as before. The limit
1075+
can only be increased.
10621076
max_total_charge_usd: A limit on the total charged amount of the resurrected run, in USD. Once the run
10631077
exceeds it, the platform aborts the run, which takes a few seconds, so the final charge can slightly
10641078
overshoot the limit. By default, the resurrected run uses the same limit as before. The limit can only
@@ -1069,6 +1083,9 @@ async def resurrect(
10691083
Returns:
10701084
Info about the resurrected Actor run.
10711085
"""
1086+
if max_items is not None:
1087+
_warn_max_items_deprecated()
1088+
10721089
client = self.new_client(token=token) if token else self.apify_client
10731090

10741091
return await client.run(run_id).resurrect(
@@ -1110,8 +1127,8 @@ async def call(
11101127
content_type: The content type of the input.
11111128
build: Specifies the Actor build to run. It can be either a build tag or build number. By default,
11121129
the run uses the build specified in the default run configuration for the Actor (typically latest).
1113-
max_items: Maximum number of dataset items you are charged for, for pay-per-result Actors. It caps the
1114-
charge, not the output, so the run can return fewer or more items than this.
1130+
max_items: Deprecated, use `max_total_charge_usd` instead. Will be removed in version 5.0.0. Works only with
1131+
legacy pay-per-result Actors.
11151132
max_total_charge_usd: A limit on the total charged amount, in USD. Once the run exceeds it, the platform
11161133
aborts the run, which takes a few seconds, so the final charge can slightly overshoot the limit.
11171134
restart_on_error: If true, the Actor run process will be restarted whenever it exits with
@@ -1134,6 +1151,9 @@ async def call(
11341151
Returns:
11351152
Info about the started Actor run.
11361153
"""
1154+
if max_items is not None:
1155+
_warn_max_items_deprecated()
1156+
11371157
client = self.new_client(token=token) if token else self.apify_client
11381158

11391159
actor_client = client.actor(actor_id)
@@ -1186,8 +1206,8 @@ async def start_task(
11861206
token: The Apify API token to use for this request (defaults to the `APIFY_TOKEN` environment variable).
11871207
build: Specifies the Actor build to run. It can be either a build tag or build number. By default,
11881208
the run uses the build specified in the default run configuration for the Actor (typically latest).
1189-
max_items: Maximum number of dataset items you are charged for, for pay-per-result Actors. It caps the
1190-
charge, not the output, so the run can return fewer or more items than this.
1209+
max_items: Deprecated, use `max_total_charge_usd` instead. Will be removed in version 5.0.0. Works only with
1210+
legacy pay-per-result Actors.
11911211
max_total_charge_usd: A limit on the total charged amount, in USD. Once the run exceeds it, the platform
11921212
aborts the run, which takes a few seconds, so the final charge can slightly overshoot the limit.
11931213
restart_on_error: If true, the Task run process will be restarted whenever it exits with
@@ -1204,6 +1224,9 @@ async def start_task(
12041224
Returns:
12051225
Info about the started Actor run.
12061226
"""
1227+
if max_items is not None:
1228+
_warn_max_items_deprecated()
1229+
12071230
client = self.new_client(token=token) if token else self.apify_client
12081231
task_client = client.task(task_id)
12091232
return await task_client.start(
@@ -1246,8 +1269,8 @@ async def call_task(
12461269
token: The Apify API token to use for this request (defaults to the `APIFY_TOKEN` environment variable).
12471270
build: Specifies the Actor build to run. It can be either a build tag or build number. By default,
12481271
the run uses the build specified in the default run configuration for the Actor (typically latest).
1249-
max_items: Maximum number of dataset items you are charged for, for pay-per-result Actors. It caps the
1250-
charge, not the output, so the run can return fewer or more items than this.
1272+
max_items: Deprecated, use `max_total_charge_usd` instead. Will be removed in version 5.0.0. Works only with
1273+
legacy pay-per-result Actors.
12511274
max_total_charge_usd: A limit on the total charged amount, in USD. Once the run exceeds it, the platform
12521275
aborts the run, which takes a few seconds, so the final charge can slightly overshoot the limit.
12531276
restart_on_error: If true, the Task run process will be restarted whenever it exits with
@@ -1265,6 +1288,9 @@ async def call_task(
12651288
Returns:
12661289
Info about the started Actor run.
12671290
"""
1291+
if max_items is not None:
1292+
_warn_max_items_deprecated()
1293+
12681294
client = self.new_client(token=token) if token else self.apify_client
12691295

12701296
task_client = client.task(task_id)

‎tests/unit/actor/test_actor_helpers.py‎

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import asyncio
44
import logging
5+
import warnings
56
from datetime import UTC, datetime, timedelta
67
from decimal import Decimal
78
from typing import TYPE_CHECKING
@@ -153,11 +154,66 @@ async def test_max_items_forwarded_to_client(
153154
apify_client_async_patcher.patch(client_resource, client_method, return_value=fake_actor_run)
154155

155156
async with Actor:
156-
await getattr(Actor, sdk_method)('some-id', max_items=42)
157+
with pytest.warns(FutureWarning):
158+
await getattr(Actor, sdk_method)('some-id', max_items=42)
157159

158160
assert apify_client_async_patcher.calls[client_resource][client_method][0][1]['max_items'] == 42
159161

160162

163+
@pytest.mark.parametrize(
164+
('client_resource', 'client_method', 'sdk_method'),
165+
[
166+
pytest.param('actor', 'start', 'start', id='start'),
167+
pytest.param('actor', 'call', 'call', id='call'),
168+
pytest.param('task', 'start', 'start_task', id='start_task'),
169+
pytest.param('task', 'call', 'call_task', id='call_task'),
170+
pytest.param('run', 'resurrect', 'resurrect', id='resurrect'),
171+
],
172+
)
173+
async def test_max_items_warns_deprecated(
174+
apify_client_async_patcher: ApifyClientAsyncPatcher,
175+
fake_actor_run: Run,
176+
client_resource: str,
177+
client_method: str,
178+
sdk_method: str,
179+
) -> None:
180+
"""Passing `max_items` emits a `FutureWarning` that recommends `max_total_charge_usd` and points at the caller."""
181+
apify_client_async_patcher.patch(client_resource, client_method, return_value=fake_actor_run)
182+
183+
async with Actor:
184+
with pytest.warns(FutureWarning, match='`max_total_charge_usd`') as record:
185+
await getattr(Actor, sdk_method)('some-id', max_items=42)
186+
187+
assert len(record) == 1
188+
assert record[0].filename == __file__
189+
190+
191+
@pytest.mark.parametrize(
192+
('client_resource', 'client_method', 'sdk_method'),
193+
[
194+
pytest.param('actor', 'start', 'start', id='start'),
195+
pytest.param('actor', 'call', 'call', id='call'),
196+
pytest.param('task', 'start', 'start_task', id='start_task'),
197+
pytest.param('task', 'call', 'call_task', id='call_task'),
198+
pytest.param('run', 'resurrect', 'resurrect', id='resurrect'),
199+
],
200+
)
201+
async def test_no_max_items_warning_without_max_items(
202+
apify_client_async_patcher: ApifyClientAsyncPatcher,
203+
fake_actor_run: Run,
204+
client_resource: str,
205+
client_method: str,
206+
sdk_method: str,
207+
) -> None:
208+
"""The `max_items` deprecation warning is not emitted when `max_items` is not passed."""
209+
apify_client_async_patcher.patch(client_resource, client_method, return_value=fake_actor_run)
210+
211+
async with Actor:
212+
with warnings.catch_warnings():
213+
warnings.simplefilter('error', FutureWarning)
214+
await getattr(Actor, sdk_method)('some-id')
215+
216+
161217
@pytest.mark.parametrize(
162218
('client_resource', 'client_method', 'sdk_method'),
163219
[
@@ -201,15 +257,16 @@ async def test_resurrect_actor_run(apify_client_async_patcher: ApifyClientAsyncP
201257
run_id = 'some-run-id'
202258

203259
async with Actor:
204-
run = await Actor.resurrect(
205-
run_id,
206-
build='beta',
207-
memory_mbytes=1024,
208-
timeout=timedelta(seconds=120),
209-
max_items=100,
210-
max_total_charge_usd=Decimal('2.5'),
211-
restart_on_error=True,
212-
)
260+
with pytest.warns(FutureWarning):
261+
run = await Actor.resurrect(
262+
run_id,
263+
build='beta',
264+
memory_mbytes=1024,
265+
timeout=timedelta(seconds=120),
266+
max_items=100,
267+
max_total_charge_usd=Decimal('2.5'),
268+
restart_on_error=True,
269+
)
213270

214271
assert run is fake_actor_run
215272
calls = apify_client_async_patcher.calls['run']['resurrect']

0 commit comments

Comments
 (0)