Skip to content

Commit 7e7b1f4

Browse files
committed
First attempt
1 parent ff4961d commit 7e7b1f4

16 files changed

+85
-82
lines changed

src/apify/storage_clients/_apify/_alias_resolving.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from collections.abc import Callable
1515
from types import TracebackType
1616

17-
from apify_client.clients import (
17+
from apify_client._resource_clients import (
1818
DatasetClientAsync,
1919
DatasetCollectionClientAsync,
2020
KeyValueStoreClientAsync,
@@ -105,8 +105,8 @@ async def open_by_alias(
105105
# Create new unnamed storage and store alias mapping
106106
raw_metadata = await collection_client.get_or_create()
107107

108-
await alias_resolver.store_mapping(storage_id=raw_metadata['id'])
109-
return get_resource_client_by_id(raw_metadata['id'])
108+
await alias_resolver.store_mapping(storage_id=raw_metadata.id)
109+
return get_resource_client_by_id(raw_metadata.id)
110110

111111

112112
class AliasResolver:

src/apify/storage_clients/_apify/_api_client_creation.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from apify.storage_clients._apify._alias_resolving import open_by_alias
99

1010
if TYPE_CHECKING:
11-
from apify_client.clients import DatasetClientAsync, KeyValueStoreClientAsync, RequestQueueClientAsync
11+
from apify_client._resource_clients import DatasetClientAsync, KeyValueStoreClientAsync, RequestQueueClientAsync
1212

1313
from apify._configuration import Configuration
1414

@@ -137,13 +137,13 @@ def get_resource_client(storage_id: str) -> DatasetClientAsync:
137137
# Default storage does not exist. Create a new one.
138138
if not raw_metadata:
139139
raw_metadata = await collection_client.get_or_create()
140-
resource_client = get_resource_client(raw_metadata['id'])
140+
resource_client = get_resource_client(raw_metadata.id)
141141
return resource_client
142142

143143
# Open by name.
144144
case (None, str(), None, _):
145145
raw_metadata = await collection_client.get_or_create(name=name)
146-
return get_resource_client(raw_metadata['id'])
146+
return get_resource_client(raw_metadata.id)
147147

148148
# Open by ID.
149149
case (None, None, str(), _):

src/apify/storage_clients/_apify/_dataset_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
if TYPE_CHECKING:
1818
from collections.abc import AsyncIterator
1919

20-
from apify_client.clients import DatasetClientAsync
20+
from apify_client._resource_clients import DatasetClientAsync
2121
from crawlee._types import JsonSerializable
2222

2323
from apify import Configuration

src/apify/storage_clients/_apify/_key_value_store_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
if TYPE_CHECKING:
1717
from collections.abc import AsyncIterator
1818

19-
from apify_client.clients import KeyValueStoreClientAsync
19+
from apify_client._resource_clients import KeyValueStoreClientAsync
2020

2121
from apify import Configuration
2222

src/apify/storage_clients/_apify/_request_queue_client.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
from datetime import datetime
34
from logging import getLogger
45
from typing import TYPE_CHECKING, Final, Literal
56

@@ -15,7 +16,7 @@
1516
if TYPE_CHECKING:
1617
from collections.abc import Sequence
1718

18-
from apify_client.clients import RequestQueueClientAsync
19+
from apify_client._resource_clients import RequestQueueClientAsync
1920
from crawlee import Request
2021
from crawlee.storage_clients.models import AddRequestsResponse, ProcessedRequest, RequestQueueMetadata
2122

@@ -82,21 +83,26 @@ async def get_metadata(self) -> ApifyRequestQueueMetadata:
8283
if response is None:
8384
raise ValueError('Failed to fetch request queue metadata from the API.')
8485

86+
total_request_count = int(response.total_request_count)
87+
handled_request_count = int(response.handled_request_count)
88+
pending_request_count = int(response.pending_request_count)
89+
created_at = datetime.fromisoformat(response.created_at)
90+
modified_at = datetime.fromisoformat(response.modified_at)
91+
accessed_at = datetime.fromisoformat(response.accessed_at)
92+
8593
# Enhance API response with local estimations to account for propagation delays (API data can be delayed
8694
# by a few seconds, while local estimates are immediately accurate).
8795
return ApifyRequestQueueMetadata(
88-
id=response['id'],
89-
name=response['name'],
90-
total_request_count=max(response['totalRequestCount'], self._implementation.metadata.total_request_count),
91-
handled_request_count=max(
92-
response['handledRequestCount'], self._implementation.metadata.handled_request_count
93-
),
94-
pending_request_count=response['pendingRequestCount'],
95-
created_at=min(response['createdAt'], self._implementation.metadata.created_at),
96-
modified_at=max(response['modifiedAt'], self._implementation.metadata.modified_at),
97-
accessed_at=max(response['accessedAt'], self._implementation.metadata.accessed_at),
98-
had_multiple_clients=response['hadMultipleClients'] or self._implementation.metadata.had_multiple_clients,
99-
stats=RequestQueueStats.model_validate(response['stats'], by_alias=True),
96+
id=response.id,
97+
name=response.name,
98+
total_request_count=max(total_request_count, self._implementation.metadata.total_request_count),
99+
handled_request_count=max(handled_request_count, self._implementation.metadata.handled_request_count),
100+
pending_request_count=pending_request_count,
101+
created_at=min(created_at, self._implementation.metadata.created_at),
102+
modified_at=max(modified_at, self._implementation.metadata.modified_at),
103+
accessed_at=max(accessed_at, self._implementation.metadata.accessed_at),
104+
had_multiple_clients=response.had_multiple_clients or self._implementation.metadata.had_multiple_clients,
105+
stats=RequestQueueStats.model_validate(response.stats, by_alias=True), # ty: ignore[possibly-missing-attribute]
100106
)
101107

102108
@classmethod

src/apify/storage_clients/_apify/_request_queue_shared_client.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
if TYPE_CHECKING:
1818
from collections.abc import Callable, Coroutine, Sequence
1919

20-
from apify_client.clients import RequestQueueClientAsync
20+
from apify_client._resource_clients import RequestQueueClientAsync
2121

2222
logger = getLogger(__name__)
2323

@@ -388,7 +388,7 @@ async def _update_request(
388388
)
389389

390390
return ProcessedRequest.model_validate(
391-
{'uniqueKey': request.unique_key} | response,
391+
{'uniqueKey': request.unique_key} | response.model_dump(by_alias=True),
392392
)
393393

394394
async def _list_head(
@@ -431,19 +431,19 @@ async def _list_head(
431431
self._should_check_for_forefront_requests = False
432432

433433
# Otherwise fetch from API
434-
response = await self._api_client.list_and_lock_head(
434+
list_and_lost_data = await self._api_client.list_and_lock_head(
435435
lock_secs=int(self._DEFAULT_LOCK_TIME.total_seconds()),
436436
limit=limit,
437437
)
438438

439439
# Update the queue head cache
440-
self._queue_has_locked_requests = response.get('queueHasLockedRequests', False)
440+
self._queue_has_locked_requests = list_and_lost_data.queue_has_locked_requests
441441
# Check if there is another client working with the RequestQueue
442-
self.metadata.had_multiple_clients = response.get('hadMultipleClients', False)
442+
self.metadata.had_multiple_clients = list_and_lost_data.had_multiple_clients
443443

444-
for request_data in response.get('items', []):
444+
for request_data in list_and_lost_data.items:
445445
request = Request.model_validate(request_data)
446-
request_id = request_data.get('id')
446+
request_id = request_data.id
447447

448448
# Skip requests without ID or unique key
449449
if not request.unique_key or not request_id:
@@ -473,7 +473,7 @@ async def _list_head(
473473
# After adding new requests to the forefront, any existing leftover locked request is kept in the end.
474474
self._queue_head.append(leftover_id)
475475

476-
return RequestQueueHead.model_validate(response)
476+
return RequestQueueHead.model_validate(list_and_lost_data)
477477

478478
def _cache_request(
479479
self,

src/apify/storage_clients/_apify/_request_queue_single_client.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
if TYPE_CHECKING:
1616
from collections.abc import Sequence
1717

18-
from apify_client.clients import RequestQueueClientAsync
18+
from apify_client._resource_clients import RequestQueueClientAsync
1919

2020
logger = getLogger(__name__)
2121

@@ -288,16 +288,17 @@ async def _list_head(self) -> None:
288288

289289
# Update metadata
290290
# Check if there is another client working with the RequestQueue
291-
self.metadata.had_multiple_clients = response.get('hadMultipleClients', False)
291+
self.metadata.had_multiple_clients = response.had_multiple_clients
292292
# Should warn once? This might be outside expected context if the other consumers consumes at the same time
293293

294-
if modified_at := response.get('queueModifiedAt'):
294+
if response.queue_modified_at:
295+
modified_at = datetime.fromisoformat(response.queue_modified_at)
295296
self.metadata.modified_at = max(self.metadata.modified_at, modified_at)
296297

297298
# Update the cached data
298-
for request_data in response.get('items', []):
299+
for request_data in response.items:
299300
request = Request.model_validate(request_data)
300-
request_id = request_data['id']
301+
request_id = request_data.id
301302

302303
if request_id in self._requests_in_progress:
303304
# Ignore requests that are already in progress, we will not process them again.
@@ -365,7 +366,7 @@ async def _update_request(
365366
)
366367

367368
return ProcessedRequest.model_validate(
368-
{'uniqueKey': request.unique_key} | response,
369+
{'uniqueKey': request.unique_key} | response.model_dump(by_alias=True),
369370
)
370371

371372
async def _init_caches(self) -> None:
@@ -378,9 +379,9 @@ async def _init_caches(self) -> None:
378379
Local deduplication is cheaper, it takes 1 API call for whole cache and 1 read operation per request.
379380
"""
380381
response = await self._api_client.list_requests(limit=10_000)
381-
for request_data in response.get('items', []):
382+
for request_data in response.items:
382383
request = Request.model_validate(request_data)
383-
request_id = request_data['id']
384+
request_id = request_data.id
384385

385386
if request.was_already_handled:
386387
# Cache just id for deduplication

tests/integration/actor/conftest.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from collections.abc import Awaitable, Callable, Coroutine, Iterator, Mapping
2323
from decimal import Decimal
2424

25-
from apify_client.clients.resource_clients import ActorClientAsync
25+
from apify_client._resource_clients import ActorClientAsync
2626

2727
_TOKEN_ENV_VAR = 'APIFY_TEST_USER_API_TOKEN'
2828
_API_URL_ENV_VAR = 'APIFY_INTEGRATION_TESTS_API_URL'
@@ -236,19 +236,19 @@ async def _make_actor(
236236
],
237237
)
238238

239-
actor_client = client.actor(created_actor['id'])
239+
actor_client = client.actor(created_actor.id)
240240

241241
print(f'Building Actor {actor_name}...')
242242
build_result = await actor_client.build(version_number='0.0')
243-
build_client = client.build(build_result['id'])
243+
build_client = client.build(build_result.id)
244244
build_client_result = await build_client.wait_for_finish(wait_secs=600)
245245

246246
assert build_client_result is not None
247-
assert build_client_result['status'] == ActorJobStatus.SUCCEEDED
247+
assert build_client_result.status == ActorJobStatus.SUCCEEDED
248248

249249
# We only mark the client for cleanup if the build succeeded, so that if something goes wrong here,
250250
# you have a chance to check the error.
251-
actors_for_cleanup.append(created_actor['id'])
251+
actors_for_cleanup.append(created_actor.id)
252252

253253
return actor_client
254254

@@ -259,14 +259,9 @@ async def _make_actor(
259259
actor_client = ApifyClient(token=apify_token, api_url=os.getenv(_API_URL_ENV_VAR)).actor(actor_id)
260260

261261
if (actor := actor_client.get()) is not None:
262-
actor_client.update(
263-
pricing_infos=[
264-
*actor.get('pricingInfos', []),
265-
{
266-
'pricingModel': 'FREE',
267-
},
268-
]
269-
)
262+
assert actor.pricing_infos is not None
263+
new_pricing_infos = [*actor.pricing_infos, {'pricingModel': 'FREE'}]
264+
actor_client.update(pricing_infos=new_pricing_infos)
270265

271266
actor_client.delete()
272267

tests/integration/actor/test_actor_api_helpers.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ async def main_outer() -> None:
130130

131131
inner_run_status = await Actor.apify_client.actor(inner_actor_id).last_run().get()
132132
assert inner_run_status is not None
133-
assert inner_run_status.get('status') in ['READY', 'RUNNING']
133+
assert inner_run_status.status in ['READY', 'RUNNING']
134134

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)
@@ -175,7 +175,7 @@ async def main_outer() -> None:
175175

176176
inner_run_status = await Actor.apify_client.actor(inner_actor_id).last_run().get()
177177
assert inner_run_status is not None
178-
assert inner_run_status.get('status') == 'SUCCEEDED'
178+
assert inner_run_status.status == 'SUCCEEDED'
179179

180180
inner_actor = await make_actor(label='call-inner', main_func=main_inner)
181181
outer_actor = await make_actor(label='call-outer', main_func=main_outer)
@@ -221,7 +221,7 @@ async def main_outer() -> None:
221221

222222
inner_run_status = await Actor.apify_client.task(inner_task_id).last_run().get()
223223
assert inner_run_status is not None
224-
assert inner_run_status.get('status') == 'SUCCEEDED'
224+
assert inner_run_status.status == 'SUCCEEDED'
225225

226226
inner_actor = await make_actor(label='call-task-inner', main_func=main_inner)
227227
outer_actor = await make_actor(label='call-task-outer', main_func=main_outer)
@@ -237,7 +237,7 @@ async def main_outer() -> None:
237237

238238
run_result_outer = await run_actor(
239239
outer_actor,
240-
run_input={'test_value': test_value, 'inner_task_id': task['id']},
240+
run_input={'test_value': test_value, 'inner_task_id': task.id},
241241
)
242242

243243
assert run_result_outer.status == 'SUCCEEDED'
@@ -248,7 +248,7 @@ async def main_outer() -> None:
248248
assert inner_output_record is not None
249249
assert inner_output_record['value'] == f'{test_value}_XXX_{test_value}'
250250

251-
await apify_client_async.task(task['id']).delete()
251+
await apify_client_async.task(task.id).delete()
252252

253253

254254
@pytest.mark.skip(reason='Requires Actor permissions beyond limited permissions, see #715.')
@@ -274,7 +274,8 @@ async def main_outer() -> None:
274274
inner_actor = await make_actor(label='abort-inner', main_func=main_inner)
275275
outer_actor = await make_actor(label='abort-outer', main_func=main_outer)
276276

277-
inner_run_id = (await inner_actor.start())['id']
277+
actor_run = await inner_actor.start()
278+
inner_run_id = actor_run.id
278279

279280
run_result_outer = await run_actor(
280281
outer_actor,

tests/integration/actor/test_actor_charge.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from collections.abc import Iterable
1616

1717
from apify_client import ApifyClientAsync
18-
from apify_client.clients import ActorClientAsync
18+
from apify_client._resource_clients import ActorClientAsync
1919

2020
from .conftest import MakeActorFunction, RunActorFunction
2121

@@ -54,7 +54,7 @@ async def main() -> None:
5454
actor = await actor_client.get()
5555

5656
assert actor is not None
57-
return str(actor['id'])
57+
return str(actor.id)
5858

5959

6060
@pytest_asyncio.fixture(scope='function', loop_scope='module')

0 commit comments

Comments
 (0)