apify 3.4.2b2__tar.gz → 3.4.2b4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. {apify-3.4.2b2 → apify-3.4.2b4}/CHANGELOG.md +2 -0
  2. {apify-3.4.2b2 → apify-3.4.2b4}/PKG-INFO +2 -3
  3. {apify-3.4.2b2 → apify-3.4.2b4}/pyproject.toml +4 -5
  4. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/__init__.py +7 -2
  5. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/_actor.py +97 -88
  6. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/_charging.py +105 -24
  7. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/_configuration.py +17 -2
  8. apify-3.4.2b4/src/apify/_consts.py +90 -0
  9. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/_proxy_configuration.py +12 -7
  10. apify-3.4.2b4/src/apify/_webhook.py +62 -0
  11. apify-3.4.2b4/src/apify/events/__init__.py +6 -0
  12. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/events/_types.py +8 -0
  13. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/scrapy/requests.py +2 -1
  14. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_apify/_alias_resolving.py +3 -5
  15. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_apify/_api_client_creation.py +7 -9
  16. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_apify/_dataset_client.py +13 -2
  17. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_apify/_key_value_store_client.py +15 -6
  18. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_apify/_models.py +17 -53
  19. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_apify/_request_queue_client.py +19 -18
  20. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_apify/_request_queue_shared_client.py +23 -23
  21. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_apify/_request_queue_single_client.py +26 -24
  22. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_apify/_utils.py +21 -0
  23. apify-3.4.2b2/src/apify/_consts.py +0 -13
  24. apify-3.4.2b2/src/apify/_models.py +0 -266
  25. apify-3.4.2b2/src/apify/events/__init__.py +0 -5
  26. {apify-3.4.2b2 → apify-3.4.2b4}/.gitignore +0 -0
  27. {apify-3.4.2b2 → apify-3.4.2b4}/CONTRIBUTING.md +0 -0
  28. {apify-3.4.2b2 → apify-3.4.2b4}/LICENSE +0 -0
  29. {apify-3.4.2b2 → apify-3.4.2b4}/README.md +0 -0
  30. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/_crypto.py +0 -0
  31. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/_utils.py +0 -0
  32. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/events/_apify_event_manager.py +0 -0
  33. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/events/py.typed +0 -0
  34. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/log.py +0 -0
  35. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/py.typed +0 -0
  36. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/request_loaders/__init__.py +0 -0
  37. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/request_loaders/_apify_request_list.py +0 -0
  38. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/request_loaders/py.typed +0 -0
  39. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/scrapy/__init__.py +0 -0
  40. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/scrapy/_actor_runner.py +0 -0
  41. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/scrapy/_async_thread.py +0 -0
  42. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/scrapy/_logging_config.py +0 -0
  43. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/scrapy/extensions/__init__.py +0 -0
  44. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/scrapy/extensions/_httpcache.py +0 -0
  45. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/scrapy/middlewares/__init__.py +0 -0
  46. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/scrapy/middlewares/apify_proxy.py +0 -0
  47. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/scrapy/middlewares/py.typed +0 -0
  48. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/scrapy/pipelines/__init__.py +0 -0
  49. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/scrapy/pipelines/actor_dataset_push.py +0 -0
  50. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/scrapy/pipelines/py.typed +0 -0
  51. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/scrapy/py.typed +0 -0
  52. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/scrapy/scheduler.py +0 -0
  53. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/scrapy/utils.py +0 -0
  54. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/__init__.py +0 -0
  55. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_apify/__init__.py +0 -0
  56. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_apify/_storage_client.py +0 -0
  57. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_apify/py.typed +0 -0
  58. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_file_system/__init__.py +0 -0
  59. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_file_system/_dataset_client.py +0 -0
  60. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_file_system/_key_value_store_client.py +0 -0
  61. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_file_system/_storage_client.py +0 -0
  62. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_ppe_dataset_mixin.py +0 -0
  63. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_smart_apify/__init__.py +0 -0
  64. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/_smart_apify/_storage_client.py +0 -0
  65. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storage_clients/py.typed +0 -0
  66. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storages/__init__.py +0 -0
  67. {apify-3.4.2b2 → apify-3.4.2b4}/src/apify/storages/py.typed +0 -0
@@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file.
8
8
  ### 🚜 Refactor
9
9
 
10
10
  - [**breaking**] Remove deprecated APIs ([#918](https://github.com/apify/apify-sdk-python/pull/918)) ([3e5728d](https://github.com/apify/apify-sdk-python/commit/3e5728d94cb8fd879d5a76e33a03d55792d835d5)) by [@vdusek](https://github.com/vdusek), closes [#635](https://github.com/apify/apify-sdk-python/issues/635)
11
+ - [**breaking**] Mark secondary arguments as keyword-only ([#917](https://github.com/apify/apify-sdk-python/pull/917)) ([eb94c99](https://github.com/apify/apify-sdk-python/commit/eb94c992ec4aba1cd7cf4dfd7a98731cb304651b)) by [@vdusek](https://github.com/vdusek), closes [#881](https://github.com/apify/apify-sdk-python/issues/881)
12
+ - [**breaking**] Adapt to apify-client v3 ([#719](https://github.com/apify/apify-sdk-python/pull/719)) ([10203bc](https://github.com/apify/apify-sdk-python/commit/10203bc51e67590c97938b37d81614376bc3d29a)) by [@vdusek](https://github.com/vdusek), closes [#697](https://github.com/apify/apify-sdk-python/issues/697), [#736](https://github.com/apify/apify-sdk-python/issues/736), [#770](https://github.com/apify/apify-sdk-python/issues/770), [#853](https://github.com/apify/apify-sdk-python/issues/853)
11
13
 
12
14
  ### ⚙️ Miscellaneous Tasks
13
15
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apify
3
- Version: 3.4.2b2
3
+ Version: 3.4.2b4
4
4
  Summary: Apify SDK for Python
5
5
  Project-URL: Apify Homepage, https://apify.com
6
6
  Project-URL: Changelog, https://docs.apify.com/sdk/python/docs/changelog
@@ -225,8 +225,7 @@ Classifier: Programming Language :: Python :: 3.13
225
225
  Classifier: Programming Language :: Python :: 3.14
226
226
  Classifier: Topic :: Software Development :: Libraries
227
227
  Requires-Python: >=3.11
228
- Requires-Dist: apify-client<3.0.0,>=2.3.0
229
- Requires-Dist: apify-shared<3.0.0,>=2.0.0
228
+ Requires-Dist: apify-client<4.0.0,>=3.0.0
230
229
  Requires-Dist: cachetools>=5.5.0
231
230
  Requires-Dist: crawlee<2.0.0,>=1.0.4
232
231
  Requires-Dist: cryptography>=42.0.0
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "apify"
7
- version = "3.4.2b2"
7
+ version = "3.4.2b4"
8
8
  description = "Apify SDK for Python"
9
9
  authors = [{ name = "Apify Technologies s.r.o.", email = "support@apify.com" }]
10
10
  license = { file = "LICENSE" }
@@ -34,8 +34,7 @@ keywords = [
34
34
  "scraping",
35
35
  ]
36
36
  dependencies = [
37
- "apify-client>=2.3.0,<3.0.0",
38
- "apify-shared>=2.0.0,<3.0.0",
37
+ "apify-client>=3.0.0,<4.0.0",
39
38
  "crawlee>=1.0.4,<2.0.0",
40
39
  "cachetools>=5.5.0",
41
40
  "cryptography>=42.0.0",
@@ -84,7 +83,7 @@ dev = [
84
83
  "ruff~=0.15.0",
85
84
  "setuptools", # setuptools are used by pytest but not explicitly required
86
85
  "ty~=0.0.0",
87
- "types-cachetools<7.0.0",
86
+ "types-cachetools<7.0.1",
88
87
  "uvicorn[standard]",
89
88
  "werkzeug<4.0.0", # Werkzeug is used by httpserver
90
89
  ]
@@ -197,7 +196,7 @@ builtins-ignorelist = ["id"]
197
196
 
198
197
  [tool.ruff.lint.isort]
199
198
  known-local-folder = ["apify"]
200
- known-first-party = ["apify_client", "apify_shared", "crawlee"]
199
+ known-first-party = ["apify_client", "crawlee"]
201
200
 
202
201
  [tool.ruff.lint.pylint]
203
202
  max-branches = 18
@@ -1,6 +1,6 @@
1
1
  from importlib import metadata
2
2
 
3
- from apify_shared.consts import WebhookEventType
3
+ from apify_client._literals import WebhookEventType
4
4
  from crawlee import Request
5
5
  from crawlee.events import (
6
6
  Event,
@@ -14,13 +14,18 @@ from crawlee.events import (
14
14
 
15
15
  from apify._actor import Actor
16
16
  from apify._configuration import Configuration
17
- from apify._models import Webhook
17
+ from apify._consts import ActorEnvVars, ApifyEnvVars
18
18
  from apify._proxy_configuration import ProxyConfiguration, ProxyInfo
19
+ from apify._webhook import Webhook
20
+ from apify.events._types import ActorEventTypes
19
21
 
20
22
  __version__ = metadata.version('apify')
21
23
 
22
24
  __all__ = [
23
25
  'Actor',
26
+ 'ActorEnvVars',
27
+ 'ActorEventTypes',
28
+ 'ApifyEnvVars',
24
29
  'Configuration',
25
30
  'Event',
26
31
  'EventAbortingData',
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import asyncio
4
4
  import sys
5
+ import warnings
5
6
  from contextlib import suppress
6
7
  from datetime import UTC, datetime, timedelta
7
8
  from functools import cached_property
@@ -12,7 +13,6 @@ from more_itertools import flatten
12
13
  from pydantic import AliasChoices
13
14
 
14
15
  from apify_client import ApifyClientAsync
15
- from apify_shared.consts import ActorEnvVars, ActorExitCodes, ApifyEnvVars
16
16
  from crawlee import service_locator
17
17
  from crawlee.errors import ServiceConflictError
18
18
  from crawlee.events import (
@@ -27,11 +27,11 @@ from crawlee.events import (
27
27
 
28
28
  from apify._charging import DEFAULT_DATASET_ITEM_EVENT, ChargeResult, ChargingManager, ChargingManagerImplementation
29
29
  from apify._configuration import Configuration
30
- from apify._consts import EVENT_LISTENERS_TIMEOUT
30
+ from apify._consts import EVENT_LISTENERS_TIMEOUT, EXIT_CODE_ERROR_USER_FUNCTION_THREW, ActorEnvVars, ApifyEnvVars
31
31
  from apify._crypto import decrypt_input_secrets, load_private_key
32
- from apify._models import ActorRun
33
32
  from apify._proxy_configuration import ProxyConfiguration
34
33
  from apify._utils import docs_group, docs_name, ensure_context, get_system_info, is_running_in_ipython
34
+ from apify._webhook import to_client_representations
35
35
  from apify.events import ApifyEventManager, EventManager, LocalEventManager
36
36
  from apify.log import _configure_logging, logger
37
37
  from apify.storage_clients import ApifyStorageClient, SmartApifyStorageClient
@@ -45,11 +45,12 @@ if TYPE_CHECKING:
45
45
  from types import TracebackType
46
46
  from typing import Self
47
47
 
48
- from apify_shared.consts import ActorPermissionLevel
48
+ from apify_client._literals import ActorPermissionLevel
49
+ from apify_client._models import Run
49
50
  from crawlee._types import JsonSerializable
50
51
  from crawlee.proxy_configuration import _NewUrlFunction
51
52
 
52
- from apify._models import Webhook
53
+ from apify._webhook import Webhook
53
54
 
54
55
  MainReturnType = TypeVar('MainReturnType')
55
56
 
@@ -234,7 +235,7 @@ class _ActorType:
234
235
  # In IPython, we don't run `sys.exit()` during Actor exits,
235
236
  # so the exception traceback will be printed on its own
236
237
  self.log.exception('Actor failed with an exception', exc_info=exc_value)
237
- self.exit_code = ActorExitCodes.ERROR_USER_FUNCTION_THREW.value
238
+ self.exit_code = EXIT_CODE_ERROR_USER_FUNCTION_THREW
238
239
 
239
240
  self._is_exiting = True
240
241
  self.log.info('Exiting Actor', extra={'exit_code': self.exit_code})
@@ -504,19 +505,31 @@ class _ActorType:
504
505
  max_retries: How many times to retry a failed request at most.
505
506
  min_delay_between_retries: How long will the client wait between retrying requests
506
507
  (increases exponentially from this value).
507
- timeout: The socket timeout of the HTTP requests sent to the Apify API.
508
+ timeout: Baseline HTTP timeout for medium-duration API operations. The underlying client uses
509
+ separate timeout tiers for short/medium/long/max-duration calls; passing a value here scales
510
+ all four tiers proportionally (short = `timeout / 6`, long = `timeout * 12`,
511
+ max = `timeout * 12`).
508
512
  """
509
- token = token or self.configuration.token
510
- api_url = api_url or self.configuration.api_base_url
511
- return ApifyClientAsync(
512
- token=token,
513
- api_url=api_url,
514
- max_retries=max_retries,
515
- min_delay_between_retries_millis=int(min_delay_between_retries.total_seconds() * 1000)
516
- if min_delay_between_retries is not None
517
- else None,
518
- timeout_secs=int(timeout.total_seconds()) if timeout else None,
519
- )
513
+ # Forward only the explicitly provided options; omitting the rest lets `ApifyClientAsync` apply its
514
+ # own defaults, so the SDK doesn't have to import and re-pass the client's private default constants.
515
+ client_kwargs: dict[str, Any] = {
516
+ 'token': token or self.configuration.token,
517
+ 'api_url': api_url or self.configuration.api_base_url,
518
+ }
519
+ if max_retries is not None:
520
+ client_kwargs['max_retries'] = max_retries
521
+ if min_delay_between_retries is not None:
522
+ client_kwargs['min_delay_between_retries'] = min_delay_between_retries
523
+ if timeout is not None:
524
+ # `apify-client` v3 splits the timeout into four tiers; scale them from the single baseline,
525
+ # mirroring the client's default ratios (medium = baseline, short = baseline / 6,
526
+ # long = max = baseline * 12).
527
+ client_kwargs['timeout_short'] = timeout / 6
528
+ client_kwargs['timeout_medium'] = timeout
529
+ client_kwargs['timeout_long'] = timeout * 12
530
+ client_kwargs['timeout_max'] = timeout * 12
531
+
532
+ return ApifyClientAsync(**client_kwargs)
520
533
 
521
534
  @_ensure_context
522
535
  async def open_dataset(
@@ -624,7 +637,7 @@ class _ActorType:
624
637
  )
625
638
 
626
639
  @_ensure_context
627
- async def push_data(self, data: dict | list[dict], charged_event_name: str | None = None) -> ChargeResult:
640
+ async def push_data(self, data: dict | list[dict], *, charged_event_name: str | None = None) -> ChargeResult:
628
641
  """Store an object or a list of objects to the default dataset of the current Actor run.
629
642
 
630
643
  Args:
@@ -700,7 +713,7 @@ class _ActorType:
700
713
  return input_value
701
714
 
702
715
  @_ensure_context
703
- async def get_value(self, key: str, default_value: Any = None) -> Any:
716
+ async def get_value(self, key: str, *, default_value: Any = None) -> Any:
704
717
  """Get a value from the default key-value store associated with the current Actor run.
705
718
 
706
719
  Args:
@@ -734,7 +747,7 @@ class _ActorType:
734
747
  return self._charging_manager_implementation
735
748
 
736
749
  @_ensure_context
737
- async def charge(self, event_name: str, count: int = 1) -> ChargeResult:
750
+ async def charge(self, event_name: str, *, count: int = 1) -> ChargeResult:
738
751
  """Charge for a specified number of events - sub-operations of the Actor.
739
752
 
740
753
  This is relevant only for the pay-per-event pricing model.
@@ -745,7 +758,7 @@ class _ActorType:
745
758
  """
746
759
  # charging_manager.charge() acquires charge_lock internally.
747
760
  charging_manager = self.get_charging_manager()
748
- return await charging_manager.charge(event_name, count)
761
+ return await charging_manager.charge(event_name, count=count)
749
762
 
750
763
  @overload
751
764
  def on(
@@ -870,7 +883,7 @@ class _ActorType:
870
883
  force_permission_level: ActorPermissionLevel | None = None,
871
884
  wait_for_finish: int | None = None,
872
885
  webhooks: list[Webhook] | None = None,
873
- ) -> ActorRun:
886
+ ) -> Run:
874
887
  """Run an Actor on the Apify platform.
875
888
 
876
889
  Unlike `Actor.call`, this method just starts the run without waiting for finish.
@@ -903,13 +916,6 @@ class _ActorType:
903
916
  """
904
917
  client = self.new_client(token=token) if token else self.apify_client
905
918
 
906
- if webhooks:
907
- serialized_webhooks = [
908
- hook.model_dump(by_alias=True, exclude_unset=True, exclude_defaults=True) for hook in webhooks
909
- ]
910
- else:
911
- serialized_webhooks = None
912
-
913
919
  if timeout == 'inherit':
914
920
  actor_start_timeout = self._get_remaining_time()
915
921
  elif timeout is None:
@@ -919,21 +925,20 @@ class _ActorType:
919
925
  else:
920
926
  raise ValueError(f'Invalid timeout {timeout!r}: expected `None`, `"inherit"`, or a `timedelta`.')
921
927
 
922
- api_result = await client.actor(actor_id).start(
928
+ actor_client = client.actor(actor_id)
929
+ return await actor_client.start(
923
930
  run_input=run_input,
924
931
  content_type=content_type,
925
932
  build=build,
926
933
  max_total_charge_usd=max_total_charge_usd,
927
934
  restart_on_error=restart_on_error,
928
935
  memory_mbytes=memory_mbytes,
929
- timeout_secs=int(actor_start_timeout.total_seconds()) if actor_start_timeout is not None else None,
936
+ run_timeout=actor_start_timeout,
930
937
  force_permission_level=force_permission_level,
931
938
  wait_for_finish=wait_for_finish,
932
- webhooks=serialized_webhooks,
939
+ webhooks=to_client_representations(webhooks),
933
940
  )
934
941
 
935
- return ActorRun.model_validate(api_result)
936
-
937
942
  @_ensure_context
938
943
  async def abort(
939
944
  self,
@@ -942,7 +947,7 @@ class _ActorType:
942
947
  token: str | None = None,
943
948
  status_message: str | None = None,
944
949
  gracefully: bool | None = None,
945
- ) -> ActorRun:
950
+ ) -> Run:
946
951
  """Abort given Actor run on the Apify platform using the current user account.
947
952
 
948
953
  The user account is determined by the `APIFY_TOKEN` environment variable.
@@ -959,13 +964,17 @@ class _ActorType:
959
964
  Info about the aborted Actor run.
960
965
  """
961
966
  client = self.new_client(token=token) if token else self.apify_client
967
+ run_client = client.run(run_id)
962
968
 
963
969
  if status_message:
964
- await client.run(run_id).update(status_message=status_message)
970
+ await run_client.update(status_message=status_message)
971
+
972
+ run = await run_client.abort(gracefully=gracefully)
965
973
 
966
- api_result = await client.run(run_id).abort(gracefully=gracefully)
974
+ if run is None:
975
+ raise RuntimeError(f'Failed to abort Actor run with ID "{run_id}".')
967
976
 
968
- return ActorRun.model_validate(api_result)
977
+ return run
969
978
 
970
979
  @_ensure_context
971
980
  async def call(
@@ -984,7 +993,7 @@ class _ActorType:
984
993
  webhooks: list[Webhook] | None = None,
985
994
  wait: timedelta | None = None,
986
995
  logger: logging.Logger | None | Literal['default'] = 'default',
987
- ) -> ActorRun | None:
996
+ ) -> Run:
988
997
  """Start an Actor on the Apify Platform and wait for it to finish before returning.
989
998
 
990
999
  It waits indefinitely, unless the wait argument is provided.
@@ -1020,13 +1029,6 @@ class _ActorType:
1020
1029
  """
1021
1030
  client = self.new_client(token=token) if token else self.apify_client
1022
1031
 
1023
- if webhooks:
1024
- serialized_webhooks = [
1025
- hook.model_dump(by_alias=True, exclude_unset=True, exclude_defaults=True) for hook in webhooks
1026
- ]
1027
- else:
1028
- serialized_webhooks = None
1029
-
1030
1032
  if timeout == 'inherit':
1031
1033
  actor_call_timeout = self._get_remaining_time()
1032
1034
  elif timeout is None:
@@ -1036,21 +1038,25 @@ class _ActorType:
1036
1038
  else:
1037
1039
  raise ValueError(f'Invalid timeout {timeout!r}: expected `None`, `"inherit"`, or a `timedelta`.')
1038
1040
 
1039
- api_result = await client.actor(actor_id).call(
1041
+ actor_client = client.actor(actor_id)
1042
+ run = await actor_client.call(
1040
1043
  run_input=run_input,
1041
1044
  content_type=content_type,
1042
1045
  build=build,
1043
1046
  max_total_charge_usd=max_total_charge_usd,
1044
1047
  restart_on_error=restart_on_error,
1045
1048
  memory_mbytes=memory_mbytes,
1046
- timeout_secs=int(actor_call_timeout.total_seconds()) if actor_call_timeout is not None else None,
1049
+ run_timeout=actor_call_timeout,
1047
1050
  force_permission_level=force_permission_level,
1048
- webhooks=serialized_webhooks,
1049
- wait_secs=int(wait.total_seconds()) if wait is not None else None,
1051
+ webhooks=to_client_representations(webhooks),
1052
+ wait_duration=wait,
1050
1053
  logger=logger,
1051
1054
  )
1052
1055
 
1053
- return ActorRun.model_validate(api_result)
1056
+ if run is None:
1057
+ raise RuntimeError(f'Failed to call Actor with ID "{actor_id}".')
1058
+
1059
+ return run
1054
1060
 
1055
1061
  @_ensure_context
1056
1062
  async def call_task(
@@ -1065,7 +1071,7 @@ class _ActorType:
1065
1071
  webhooks: list[Webhook] | None = None,
1066
1072
  wait: timedelta | None = None,
1067
1073
  token: str | None = None,
1068
- ) -> ActorRun | None:
1074
+ ) -> Run:
1069
1075
  """Start an Actor task on the Apify Platform and wait for it to finish before returning.
1070
1076
 
1071
1077
  It waits indefinitely, unless the wait argument is provided.
@@ -1098,13 +1104,6 @@ class _ActorType:
1098
1104
  """
1099
1105
  client = self.new_client(token=token) if token else self.apify_client
1100
1106
 
1101
- if webhooks:
1102
- serialized_webhooks = [
1103
- hook.model_dump(by_alias=True, exclude_unset=True, exclude_defaults=True) for hook in webhooks
1104
- ]
1105
- else:
1106
- serialized_webhooks = None
1107
-
1108
1107
  if timeout == 'inherit':
1109
1108
  task_call_timeout = self._get_remaining_time()
1110
1109
  elif timeout is None:
@@ -1114,17 +1113,21 @@ class _ActorType:
1114
1113
  else:
1115
1114
  raise ValueError(f'Invalid timeout {timeout!r}: expected `None`, `"inherit"`, or a `timedelta`.')
1116
1115
 
1117
- api_result = await client.task(task_id).call(
1116
+ task_client = client.task(task_id)
1117
+ run = await task_client.call(
1118
1118
  task_input=task_input,
1119
1119
  build=build,
1120
1120
  restart_on_error=restart_on_error,
1121
1121
  memory_mbytes=memory_mbytes,
1122
- timeout_secs=int(task_call_timeout.total_seconds()) if task_call_timeout is not None else None,
1123
- webhooks=serialized_webhooks,
1124
- wait_secs=int(wait.total_seconds()) if wait is not None else None,
1122
+ run_timeout=task_call_timeout,
1123
+ webhooks=to_client_representations(webhooks),
1124
+ wait_duration=wait,
1125
1125
  )
1126
1126
 
1127
- return ActorRun.model_validate(api_result)
1127
+ if run is None:
1128
+ raise RuntimeError(f'Failed to call Task with ID "{task_id}".')
1129
+
1130
+ return run
1128
1131
 
1129
1132
  @_ensure_context
1130
1133
  async def metamorph(
@@ -1238,14 +1241,7 @@ class _ActorType:
1238
1241
  await asyncio.sleep(custom_after_sleep.total_seconds())
1239
1242
 
1240
1243
  @_ensure_context
1241
- async def add_webhook(
1242
- self,
1243
- webhook: Webhook,
1244
- *,
1245
- ignore_ssl_errors: bool | None = None,
1246
- do_not_retry: bool | None = None,
1247
- idempotency_key: str | None = None,
1248
- ) -> None:
1244
+ async def add_webhook(self, webhook: Webhook, *, idempotency_key: str | None = None) -> None:
1249
1245
  """Create an ad-hoc webhook for the current Actor run.
1250
1246
 
1251
1247
  This webhook lets you receive a notification when the Actor run finished or failed.
@@ -1256,15 +1252,18 @@ class _ActorType:
1256
1252
  For more information about Apify Actor webhooks, please see the [documentation](https://docs.apify.com/webhooks).
1257
1253
 
1258
1254
  Args:
1259
- webhook: The webhook to be added
1260
- ignore_ssl_errors: Whether the webhook should ignore SSL errors returned by request_url
1261
- do_not_retry: Whether the webhook should retry sending the payload to request_url upon failure.
1262
- idempotency_key: A unique identifier of a webhook. You can use it to ensure that you won't create
1263
- the same webhook multiple times.
1264
-
1265
- Returns:
1266
- The created webhook.
1255
+ webhook: The webhook to be added. It is automatically bound to the current Actor run.
1256
+ idempotency_key: Deprecated. Pass `idempotency_key` on the `Webhook` instance instead.
1257
+ Will be removed in version 5.0.0.
1267
1258
  """
1259
+ if idempotency_key is not None:
1260
+ warnings.warn(
1261
+ 'Passing `idempotency_key` to `Actor.add_webhook()` is deprecated and will be removed in version '
1262
+ '5.0.0. Set it on the `Webhook` instance instead.',
1263
+ DeprecationWarning,
1264
+ stacklevel=2,
1265
+ )
1266
+
1268
1267
  if not self.is_at_home():
1269
1268
  self.log.error('Actor.add_webhook() is only supported when running on the Apify platform.')
1270
1269
  return
@@ -1278,9 +1277,11 @@ class _ActorType:
1278
1277
  event_types=webhook.event_types,
1279
1278
  request_url=webhook.request_url,
1280
1279
  payload_template=webhook.payload_template,
1281
- ignore_ssl_errors=ignore_ssl_errors,
1282
- do_not_retry=do_not_retry,
1283
- idempotency_key=idempotency_key,
1280
+ headers_template=webhook.headers_template,
1281
+ ignore_ssl_errors=webhook.ignore_ssl_errors,
1282
+ do_not_retry=webhook.do_not_retry,
1283
+ idempotency_key=idempotency_key if idempotency_key is not None else webhook.idempotency_key,
1284
+ is_ad_hoc=True,
1284
1285
  )
1285
1286
 
1286
1287
  @_ensure_context
@@ -1289,7 +1290,7 @@ class _ActorType:
1289
1290
  status_message: str,
1290
1291
  *,
1291
1292
  is_terminal: bool | None = None,
1292
- ) -> ActorRun | None:
1293
+ ) -> Run | None:
1293
1294
  """Set the status message for the current Actor run.
1294
1295
 
1295
1296
  Args:
@@ -1308,11 +1309,18 @@ class _ActorType:
1308
1309
  if not self.configuration.actor_run_id:
1309
1310
  raise RuntimeError('actor_run_id cannot be None when running on the Apify platform.')
1310
1311
 
1311
- api_result = await self.apify_client.run(self.configuration.actor_run_id).update(
1312
- status_message=status_message, is_status_message_terminal=is_terminal
1312
+ run_client = self.apify_client.run(self.configuration.actor_run_id)
1313
+ run = await run_client.update(
1314
+ status_message=status_message,
1315
+ is_status_message_terminal=is_terminal,
1313
1316
  )
1314
1317
 
1315
- return ActorRun.model_validate(api_result)
1318
+ if run is None:
1319
+ raise RuntimeError(
1320
+ f'Failed to set status message for Actor run with ID "{self.configuration.actor_run_id}".'
1321
+ )
1322
+
1323
+ return run
1316
1324
 
1317
1325
  @_ensure_context
1318
1326
  async def create_proxy_configuration(
@@ -1379,6 +1387,7 @@ class _ActorType:
1379
1387
  async def use_state(
1380
1388
  self,
1381
1389
  default_value: dict[str, JsonSerializable] | None = None,
1390
+ *,
1382
1391
  key: str | None = None,
1383
1392
  kvs_name: str | None = None,
1384
1393
  ) -> MutableMapping[str, JsonSerializable]: