Skip to content

Commit 6f20183

Browse files
committed
Fixes
1 parent 605c182 commit 6f20183

File tree

7 files changed

+176
-70
lines changed

7 files changed

+176
-70
lines changed

src/apify/_actor.py

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -885,7 +885,8 @@ async def start(
885885
f'Invalid timeout {timeout!r}: expected `None`, `"inherit"`, `"RemainingTime"`, or a `timedelta`.'
886886
)
887887

888-
api_result = await client.actor(actor_id).start(
888+
actor_client = client.actor(actor_id)
889+
run = await actor_client.start(
889890
run_input=run_input,
890891
content_type=content_type,
891892
build=build,
@@ -895,7 +896,11 @@ async def start(
895896
webhooks=serialized_webhooks,
896897
)
897898

898-
return ActorRun.model_validate(api_result)
899+
if run is None:
900+
raise RuntimeError(f'Failed to start Actor with ID "{actor_id}".')
901+
902+
run_dict = run.model_dump(by_alias=True)
903+
return ActorRun.model_validate(run_dict)
899904

900905
async def abort(
901906
self,
@@ -923,13 +928,18 @@ async def abort(
923928
self._raise_if_not_initialized()
924929

925930
client = self.new_client(token=token) if token else self.apify_client
931+
run_client = client.run(run_id)
926932

927933
if status_message:
928-
await client.run(run_id).update(status_message=status_message)
934+
await run_client.update(status_message=status_message)
935+
936+
run = await run_client.abort(gracefully=gracefully)
929937

930-
api_result = await client.run(run_id).abort(gracefully=gracefully)
938+
if run is None:
939+
raise RuntimeError(f'Failed to abort Actor run with ID "{run_id}".')
931940

932-
return ActorRun.model_validate(api_result)
941+
run_dict = run.model_dump(by_alias=True)
942+
return ActorRun.model_validate(run_dict)
933943

934944
async def call(
935945
self,
@@ -1002,7 +1012,8 @@ async def call(
10021012
f'Invalid timeout {timeout!r}: expected `None`, `"inherit"`, `"RemainingTime"`, or a `timedelta`.'
10031013
)
10041014

1005-
api_result = await client.actor(actor_id).call(
1015+
actor_client = client.actor(actor_id)
1016+
run = await actor_client.call(
10061017
run_input=run_input,
10071018
content_type=content_type,
10081019
build=build,
@@ -1013,7 +1024,11 @@ async def call(
10131024
logger=logger,
10141025
)
10151026

1016-
return ActorRun.model_validate(api_result)
1027+
if run is None:
1028+
raise RuntimeError(f'Failed to call Actor with ID "{actor_id}".')
1029+
1030+
run_dict = run.model_dump(by_alias=True)
1031+
return ActorRun.model_validate(run_dict)
10171032

10181033
async def call_task(
10191034
self,
@@ -1075,7 +1090,8 @@ async def call_task(
10751090
else:
10761091
raise ValueError(f'Invalid timeout {timeout!r}: expected `None`, `"inherit"`, or a `timedelta`.')
10771092

1078-
api_result = await client.task(task_id).call(
1093+
task_client = client.task(task_id)
1094+
run = await task_client.call(
10791095
task_input=task_input,
10801096
build=build,
10811097
memory_mbytes=memory_mbytes,
@@ -1084,7 +1100,11 @@ async def call_task(
10841100
wait_secs=int(wait.total_seconds()) if wait is not None else None,
10851101
)
10861102

1087-
return ActorRun.model_validate(api_result)
1103+
if run is None:
1104+
raise RuntimeError(f'Failed to call Task with ID "{task_id}".')
1105+
1106+
run_dict = run.model_dump(by_alias=True)
1107+
return ActorRun.model_validate(run_dict)
10881108

10891109
async def metamorph(
10901110
self,
@@ -1261,11 +1281,19 @@ async def set_status_message(
12611281
if not self.configuration.actor_run_id:
12621282
raise RuntimeError('actor_run_id cannot be None when running on the Apify platform.')
12631283

1264-
api_result = await self.apify_client.run(self.configuration.actor_run_id).update(
1265-
status_message=status_message, is_status_message_terminal=is_terminal
1284+
run_client = self.apify_client.run(self.configuration.actor_run_id)
1285+
run = await run_client.update(
1286+
status_message=status_message,
1287+
is_status_message_terminal=is_terminal,
12661288
)
12671289

1268-
return ActorRun.model_validate(api_result)
1290+
if run is None:
1291+
raise RuntimeError(
1292+
f'Failed to set status message for Actor run with ID "{self.configuration.actor_run_id}".'
1293+
)
1294+
1295+
run_dict = run.model_dump(by_alias=True)
1296+
return ActorRun.model_validate(run_dict)
12691297

12701298
async def create_proxy_configuration(
12711299
self,

src/apify/_models.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,32 +94,82 @@ class ActorRunUsage(BaseModel):
9494

9595
@docs_group('Actor')
9696
class ActorRun(BaseModel):
97+
"""Represents an Actor run and its associated data."""
98+
9799
__model_config__ = ConfigDict(populate_by_name=True)
98100

99101
id: Annotated[str, Field(alias='id')]
102+
"""Unique identifier of the Actor run."""
103+
100104
act_id: Annotated[str, Field(alias='actId')]
105+
"""ID of the Actor that was run."""
106+
101107
user_id: Annotated[str, Field(alias='userId')]
108+
"""ID of the user who started the run."""
109+
102110
actor_task_id: Annotated[str | None, Field(alias='actorTaskId')] = None
111+
"""ID of the Actor task, if the run was started from a task."""
112+
103113
started_at: Annotated[datetime, Field(alias='startedAt')]
114+
"""Time when the Actor run started."""
115+
104116
finished_at: Annotated[datetime | None, Field(alias='finishedAt')] = None
117+
"""Time when the Actor run finished."""
118+
105119
status: Annotated[ActorJobStatus, Field(alias='status')]
120+
"""Current status of the Actor run."""
121+
106122
status_message: Annotated[str | None, Field(alias='statusMessage')] = None
123+
"""Detailed message about the run status."""
124+
107125
is_status_message_terminal: Annotated[bool | None, Field(alias='isStatusMessageTerminal')] = None
126+
"""Whether the status message is terminal (final)."""
127+
108128
meta: Annotated[ActorRunMeta, Field(alias='meta')]
129+
"""Metadata about the Actor run."""
130+
109131
stats: Annotated[ActorRunStats, Field(alias='stats')]
132+
"""Statistics of the Actor run."""
133+
110134
options: Annotated[ActorRunOptions, Field(alias='options')]
135+
"""Configuration options for the Actor run."""
136+
111137
build_id: Annotated[str, Field(alias='buildId')]
138+
"""ID of the Actor build used for this run."""
139+
112140
exit_code: Annotated[int | None, Field(alias='exitCode')] = None
141+
"""Exit code of the Actor run process."""
142+
113143
default_key_value_store_id: Annotated[str, Field(alias='defaultKeyValueStoreId')]
144+
"""ID of the default key-value store for this run."""
145+
114146
default_dataset_id: Annotated[str, Field(alias='defaultDatasetId')]
147+
"""ID of the default dataset for this run."""
148+
115149
default_request_queue_id: Annotated[str, Field(alias='defaultRequestQueueId')]
150+
"""ID of the default request queue for this run."""
151+
116152
build_number: Annotated[str | None, Field(alias='buildNumber')] = None
153+
"""Build number of the Actor build used for this run."""
154+
117155
container_url: Annotated[str, Field(alias='containerUrl')]
156+
"""URL of the container running the Actor."""
157+
118158
is_container_server_ready: Annotated[bool | None, Field(alias='isContainerServerReady')] = None
159+
"""Whether the container's HTTP server is ready to accept requests."""
160+
119161
git_branch_name: Annotated[str | None, Field(alias='gitBranchName')] = None
162+
"""Name of the git branch used for the Actor build."""
163+
120164
usage: Annotated[ActorRunUsage | None, Field(alias='usage')] = None
165+
"""Resource usage statistics for the run."""
166+
121167
usage_total_usd: Annotated[float | None, Field(alias='usageTotalUsd')] = None
168+
"""Total cost of the run in USD."""
169+
122170
usage_usd: Annotated[ActorRunUsage | None, Field(alias='usageUsd')] = None
171+
"""Resource usage costs in USD."""
172+
123173
pricing_info: Annotated[
124174
FreeActorPricingInfo
125175
| FlatPricePerMonthActorPricingInfo
@@ -128,10 +178,13 @@ class ActorRun(BaseModel):
128178
| None,
129179
Field(alias='pricingInfo', discriminator='pricing_model'),
130180
] = None
181+
"""Pricing information for the Actor."""
182+
131183
charged_event_counts: Annotated[
132184
dict[str, int] | None,
133185
Field(alias='chargedEventCounts'),
134186
] = None
187+
"""Count of charged events for pay-per-event pricing model."""
135188

136189

137190
class FreeActorPricingInfo(BaseModel):

tests/integration/actor/test_actor_api_helpers.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,10 @@ async def main_outer() -> None:
135135
inner_actor = await make_actor(label='start-inner', main_func=main_inner)
136136
outer_actor = await make_actor(label='start-outer', main_func=main_outer)
137137

138-
inner_actor_id = (await inner_actor.get() or {})['id']
138+
inner_actor_get_result = await inner_actor.get()
139+
assert inner_actor_get_result is not None, 'Failed to get inner actor ID'
140+
141+
inner_actor_id = inner_actor_get_result.id
139142
test_value = crypto_random_object_id()
140143

141144
run_result_outer = await run_actor(
@@ -180,7 +183,10 @@ async def main_outer() -> None:
180183
inner_actor = await make_actor(label='call-inner', main_func=main_inner)
181184
outer_actor = await make_actor(label='call-outer', main_func=main_outer)
182185

183-
inner_actor_id = (await inner_actor.get() or {})['id']
186+
inner_actor_get_result = await inner_actor.get()
187+
assert inner_actor_get_result is not None, 'Failed to get inner actor ID'
188+
189+
inner_actor_id = inner_actor_get_result.id
184190
test_value = crypto_random_object_id()
185191

186192
run_result_outer = await run_actor(
@@ -226,7 +232,10 @@ async def main_outer() -> None:
226232
inner_actor = await make_actor(label='call-task-inner', main_func=main_inner)
227233
outer_actor = await make_actor(label='call-task-outer', main_func=main_outer)
228234

229-
inner_actor_id = (await inner_actor.get() or {})['id']
235+
inner_actor_get_result = await inner_actor.get()
236+
assert inner_actor_get_result is not None, 'Failed to get inner actor ID'
237+
238+
inner_actor_id = inner_actor_get_result.id
230239
test_value = crypto_random_object_id()
231240

232241
task = await apify_client_async.tasks().create(
@@ -332,7 +341,10 @@ async def main_outer() -> None:
332341
inner_actor = await make_actor(label='metamorph-inner', main_func=main_inner)
333342
outer_actor = await make_actor(label='metamorph-outer', main_func=main_outer)
334343

335-
inner_actor_id = (await inner_actor.get() or {})['id']
344+
inner_actor_get_result = await inner_actor.get()
345+
assert inner_actor_get_result is not None, 'Failed to get inner actor ID'
346+
347+
inner_actor_id = inner_actor_get_result.id
336348
test_value = crypto_random_object_id()
337349

338350
run_result_outer = await run_actor(

tests/integration/actor/test_actor_charge.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ async def main() -> None:
3535
actor_client = await make_actor('ppe', main_func=main)
3636

3737
await actor_client.update(
38+
default_run_build='latest',
3839
pricing_infos=[
3940
{
4041
'pricingModel': 'PAY_PER_EVENT',
@@ -48,7 +49,7 @@ async def main() -> None:
4849
},
4950
},
5051
},
51-
]
52+
],
5253
)
5354

5455
actor = await actor_client.get()
@@ -82,8 +83,12 @@ async def test_actor_charge_basic(
8283
# Refetch until the platform gets its act together
8384
for is_last_attempt, _ in retry_counter(30):
8485
await asyncio.sleep(1)
86+
8587
updated_run = await apify_client_async.run(run.id).get()
86-
run = ActorRun.model_validate(updated_run)
88+
assert updated_run is not None, 'Updated run should not be None'
89+
90+
updated_run_dict = updated_run.model_dump(by_alias=True)
91+
run = ActorRun.model_validate(updated_run_dict)
8792

8893
try:
8994
assert run.status == ActorJobStatus.SUCCEEDED
@@ -104,8 +109,12 @@ async def test_actor_charge_limit(
104109
# Refetch until the platform gets its act together
105110
for is_last_attempt, _ in retry_counter(30):
106111
await asyncio.sleep(1)
112+
107113
updated_run = await apify_client_async.run(run.id).get()
108-
run = ActorRun.model_validate(updated_run)
114+
assert updated_run is not None, 'Updated run should not be None'
115+
116+
updated_run_dict = updated_run.model_dump(by_alias=True)
117+
run = ActorRun.model_validate(updated_run_dict)
109118

110119
try:
111120
assert run.status == ActorJobStatus.SUCCEEDED

0 commit comments

Comments
 (0)