1313
1414from apify_client import ApifyClientAsync
1515from apify_shared .consts import ActorEnvVars , ActorExitCodes , ApifyEnvVars
16+ from crawlee .errors import ServiceConflictError
1617from crawlee .events import (
1718 Event ,
1819 EventAbortingData ,
@@ -118,9 +119,12 @@ def __init__(
118119 self ._exit_process = self ._get_default_exit_process () if exit_process is None else exit_process
119120 self ._is_exiting = False
120121
121- if configuration :
122- service_locator .set_configuration (configuration )
123- self ._configuration = service_locator .get_configuration ()
122+ # Actor state when this method is being executed is unpredictable.
123+ # Actor can be initialized by lazy object proxy or by user directly, or by both.
124+ # Until `init` method is run, this state of uncertainty remains. This is the reason why any setting done here in
125+ # `__init__` method should not be considered final.
126+
127+ self ._configuration = configuration
124128 self ._configure_logging = configure_logging
125129 self ._apify_client = self .new_client ()
126130
@@ -131,17 +135,17 @@ def __init__(
131135 # Set the event manager based on whether the Actor is running on the platform or locally.
132136 self ._event_manager = (
133137 ApifyEventManager (
134- configuration = self ._configuration ,
135- persist_state_interval = self ._configuration .persist_state_interval ,
138+ configuration = self .config ,
139+ persist_state_interval = self .config .persist_state_interval ,
136140 )
137141 if self .is_at_home ()
138142 else LocalEventManager (
139- system_info_interval = self ._configuration .system_info_interval ,
140- persist_state_interval = self ._configuration .persist_state_interval ,
143+ system_info_interval = self .config .system_info_interval ,
144+ persist_state_interval = self .config .persist_state_interval ,
141145 )
142146 )
143147
144- self ._charging_manager = ChargingManagerImplementation (self ._configuration , self ._apify_client )
148+ self ._charging_manager = ChargingManagerImplementation (self .config , self ._apify_client )
145149
146150 self ._is_initialized = False
147151
@@ -204,12 +208,18 @@ def apify_client(self) -> ApifyClientAsync:
204208 @property
205209 def configuration (self ) -> Configuration :
206210 """The Configuration instance the Actor instance uses."""
207- return self ._configuration
211+ return self .config
208212
209213 @property
210214 def config (self ) -> Configuration :
211215 """The Configuration instance the Actor instance uses."""
212- return self ._configuration
216+ if self ._configuration :
217+ return self ._configuration
218+ self .log .debug (
219+ 'Implicit configuration used.'
220+ "It's recommended to explicitly set the configuration to avoid unexpected behavior."
221+ )
222+ return Configuration ()
213223
214224 @property
215225 def event_manager (self ) -> EventManager :
@@ -251,6 +261,21 @@ async def init(self) -> None:
251261 This method should be called immediately before performing any additional Actor actions, and it should be
252262 called only once.
253263 """
264+ if self ._configuration :
265+ # Set explicitly the configuration in the service locator
266+ service_locator .set_configuration (self .configuration )
267+ else :
268+ try :
269+ # Set implicit default Apify configuration, unless configuration was already set.
270+ service_locator .set_configuration (self .configuration )
271+ except ServiceConflictError :
272+ self .log .info (
273+ 'Configuration in service locator was set explicitly before Actor. '
274+ 'Using the existing configuration.'
275+ )
276+ # Use the configuration from the service locator
277+ self ._configuration = service_locator .get_configuration ()
278+
254279 if self ._is_initialized :
255280 raise RuntimeError ('The Actor was already initialized!' )
256281
@@ -268,7 +293,6 @@ async def init(self) -> None:
268293 service_locator .set_storage_client (self ._cloud_storage_client )
269294
270295 service_locator .set_event_manager (self .event_manager )
271- service_locator .set_configuration (self .configuration )
272296
273297 # The logging configuration has to be called after all service_locator set methods.
274298 if self ._configure_logging :
@@ -386,8 +410,8 @@ def new_client(
386410 (increases exponentially from this value).
387411 timeout: The socket timeout of the HTTP requests sent to the Apify API.
388412 """
389- token = token or self ._configuration .token
390- api_url = api_url or self ._configuration .api_base_url
413+ token = token or self .config .token
414+ api_url = api_url or self .config .api_base_url
391415 return ApifyClientAsync (
392416 token = token ,
393417 api_url = api_url ,
@@ -547,9 +571,9 @@ async def get_input(self) -> Any:
547571 """Get the Actor input value from the default key-value store associated with the current Actor run."""
548572 self ._raise_if_not_initialized ()
549573
550- input_value = await self .get_value (self ._configuration .input_key )
551- input_secrets_private_key = self ._configuration .input_secrets_private_key_file
552- input_secrets_key_passphrase = self ._configuration .input_secrets_private_key_passphrase
574+ input_value = await self .get_value (self .config .input_key )
575+ input_secrets_private_key = self .config .input_secrets_private_key_file
576+ input_secrets_key_passphrase = self .config .input_secrets_private_key_passphrase
553577 if input_secrets_private_key and input_secrets_key_passphrase :
554578 private_key = load_private_key (
555579 input_secrets_private_key ,
@@ -686,7 +710,7 @@ def off(self, event_name: Event, listener: Callable | None = None) -> None:
686710
687711 def is_at_home (self ) -> bool :
688712 """Return `True` when the Actor is running on the Apify platform, and `False` otherwise (e.g. local run)."""
689- return self ._configuration .is_at_home
713+ return self .config .is_at_home
690714
691715 def get_env (self ) -> dict :
692716 """Return a dictionary with information parsed from all the `APIFY_XXX` environment variables.
@@ -712,7 +736,7 @@ def get_env(self) -> dict:
712736 aliases = [field_name ]
713737
714738 for alias in aliases :
715- config [alias ] = getattr (self ._configuration , field_name )
739+ config [alias ] = getattr (self .config , field_name )
716740
717741 env_vars = {env_var .value .lower (): env_var .name .lower () for env_var in [* ActorEnvVars , * ApifyEnvVars ]}
718742 return {option_name : config [env_var ] for env_var , option_name in env_vars .items () if env_var in config }
@@ -1000,13 +1024,13 @@ async def metamorph(
10001024 return
10011025
10021026 if not custom_after_sleep :
1003- custom_after_sleep = self ._configuration .metamorph_after_sleep
1027+ custom_after_sleep = self .config .metamorph_after_sleep
10041028
10051029 # If is_at_home() is True, config.actor_run_id is always set
1006- if not self ._configuration .actor_run_id :
1030+ if not self .config .actor_run_id :
10071031 raise RuntimeError ('actor_run_id cannot be None when running on the Apify platform.' )
10081032
1009- await self ._apify_client .run (self ._configuration .actor_run_id ).metamorph (
1033+ await self ._apify_client .run (self .config .actor_run_id ).metamorph (
10101034 target_actor_id = target_actor_id ,
10111035 run_input = run_input ,
10121036 target_actor_build = target_actor_build ,
@@ -1043,7 +1067,7 @@ async def reboot(
10431067 _ActorType ._is_rebooting = True
10441068
10451069 if not custom_after_sleep :
1046- custom_after_sleep = self ._configuration .metamorph_after_sleep
1070+ custom_after_sleep = self .config .metamorph_after_sleep
10471071
10481072 # Call all the listeners for the PERSIST_STATE and MIGRATING events, and wait for them to finish.
10491073 # PERSIST_STATE listeners are called to allow the Actor to persist its state before the reboot.
@@ -1063,10 +1087,10 @@ async def reboot(
10631087 * [listener (EventMigratingData ()) for listener in migrating_listeners ],
10641088 )
10651089
1066- if not self ._configuration .actor_run_id :
1090+ if not self .config .actor_run_id :
10671091 raise RuntimeError ('actor_run_id cannot be None when running on the Apify platform.' )
10681092
1069- await self ._apify_client .run (self ._configuration .actor_run_id ).reboot ()
1093+ await self ._apify_client .run (self .config .actor_run_id ).reboot ()
10701094
10711095 if custom_after_sleep :
10721096 await asyncio .sleep (custom_after_sleep .total_seconds ())
@@ -1105,11 +1129,11 @@ async def add_webhook(
11051129 return
11061130
11071131 # If is_at_home() is True, config.actor_run_id is always set
1108- if not self ._configuration .actor_run_id :
1132+ if not self .config .actor_run_id :
11091133 raise RuntimeError ('actor_run_id cannot be None when running on the Apify platform.' )
11101134
11111135 await self ._apify_client .webhooks ().create (
1112- actor_run_id = self ._configuration .actor_run_id ,
1136+ actor_run_id = self .config .actor_run_id ,
11131137 event_types = webhook .event_types ,
11141138 request_url = webhook .request_url ,
11151139 payload_template = webhook .payload_template ,
@@ -1141,10 +1165,10 @@ async def set_status_message(
11411165 return None
11421166
11431167 # If is_at_home() is True, config.actor_run_id is always set
1144- if not self ._configuration .actor_run_id :
1168+ if not self .config .actor_run_id :
11451169 raise RuntimeError ('actor_run_id cannot be None when running on the Apify platform.' )
11461170
1147- api_result = await self ._apify_client .run (self ._configuration .actor_run_id ).update (
1171+ api_result = await self ._apify_client .run (self .config .actor_run_id ).update (
11481172 status_message = status_message , is_status_message_terminal = is_terminal
11491173 )
11501174
@@ -1199,7 +1223,7 @@ async def create_proxy_configuration(
11991223 country_code = country_code ,
12001224 proxy_urls = proxy_urls ,
12011225 new_url_function = new_url_function ,
1202- _actor_config = self ._configuration ,
1226+ _actor_config = self .config ,
12031227 _apify_client = self ._apify_client ,
12041228 )
12051229
0 commit comments