Skip to content

Commit a4a046e

Browse files
committed
Move all relevant initialization for Actor from __init__ to init to enforce explicit configuration adn services
1 parent 432c79c commit a4a046e

File tree

1 file changed

+47
-37
lines changed

1 file changed

+47
-37
lines changed

src/apify/_actor.py

Lines changed: 47 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -127,22 +127,12 @@ def __init__(
127127

128128
self._configuration = configuration
129129
self._configure_logging = configure_logging
130-
self._apify_client = self.new_client()
130+
self._apify_client: ApifyClientAsync | None = None
131131

132132
# Set the event manager based on whether the Actor is running on the platform or locally.
133-
self._event_manager = (
134-
ApifyEventManager(
135-
configuration=self.config,
136-
persist_state_interval=self.config.persist_state_interval,
137-
)
138-
if self.is_at_home()
139-
else LocalEventManager(
140-
system_info_interval=self.config.system_info_interval,
141-
persist_state_interval=self.config.persist_state_interval,
142-
)
143-
)
133+
self._event_manager: EventManager | None = None
144134

145-
self._charging_manager = ChargingManagerImplementation(self.config, self._apify_client)
135+
self._charging_manager: ChargingManagerImplementation | None = None
146136

147137
self._is_initialized = False
148138

@@ -200,6 +190,8 @@ def __call__(
200190
@property
201191
def apify_client(self) -> ApifyClientAsync:
202192
"""The ApifyClientAsync instance the Actor instance uses."""
193+
if not self._apify_client:
194+
self._apify_client = self.new_client()
203195
return self._apify_client
204196

205197
@property
@@ -212,15 +204,26 @@ def config(self) -> Configuration:
212204
"""The Configuration instance the Actor instance uses."""
213205
if self._configuration:
214206
return self._configuration
215-
self.log.debug(
216-
'Implicit configuration used.'
217-
"It's recommended to explicitly set the configuration to avoid unexpected behavior."
207+
raise RuntimeError(
208+
'Use of implicit configuration before entering Actor context is no longer allowed.'
209+
'Either pass explicit configuration or enter the Actor context.'
218210
)
219-
return Configuration()
220211

221212
@property
222213
def event_manager(self) -> EventManager:
223214
"""The EventManager instance the Actor instance uses."""
215+
if not self._event_manager:
216+
self._event_manager = (
217+
ApifyEventManager(
218+
configuration=self.config,
219+
persist_state_interval=self.config.persist_state_interval,
220+
)
221+
if self.is_at_home()
222+
else LocalEventManager(
223+
system_info_interval=self.config.system_info_interval,
224+
persist_state_interval=self.config.persist_state_interval,
225+
)
226+
)
224227
return self._event_manager
225228

226229
@property
@@ -305,10 +308,10 @@ async def init(self) -> None:
305308
# TODO: Print outdated SDK version warning (we need a new env var for this)
306309
# https://github.com/apify/apify-sdk-python/issues/146
307310

308-
await self._event_manager.__aenter__()
311+
await self.event_manager.__aenter__()
309312
self.log.debug('Event manager initialized')
310313

311-
await self._charging_manager.__aenter__()
314+
await self._get_charging_manager_implementation().__aenter__()
312315
self.log.debug('Charging manager initialized')
313316

314317
self._is_initialized = True
@@ -349,10 +352,10 @@ async def finalize() -> None:
349352
await asyncio.sleep(0.1)
350353

351354
if event_listeners_timeout:
352-
await self._event_manager.wait_for_all_listeners_to_complete(timeout=event_listeners_timeout)
355+
await self.event_manager.wait_for_all_listeners_to_complete(timeout=event_listeners_timeout)
353356

354-
await self._event_manager.__aexit__(None, None, None)
355-
await self._charging_manager.__aexit__(None, None, None)
357+
await self.event_manager.__aexit__(None, None, None)
358+
await self._get_charging_manager_implementation().__aexit__(None, None, None)
356359

357360
await asyncio.wait_for(finalize(), cleanup_timeout.total_seconds())
358361
self._is_initialized = False
@@ -547,7 +550,9 @@ async def push_data(self, data: dict | list[dict], charged_event_name: str | Non
547550
data = data if isinstance(data, list) else [data]
548551

549552
max_charged_count = (
550-
self._charging_manager.calculate_max_event_charge_count_within_limit(charged_event_name)
553+
self._get_charging_manager_implementation().calculate_max_event_charge_count_within_limit(
554+
charged_event_name
555+
)
551556
if charged_event_name is not None
552557
else None
553558
)
@@ -561,7 +566,7 @@ async def push_data(self, data: dict | list[dict], charged_event_name: str | Non
561566
await dataset.push_data(data)
562567

563568
if charged_event_name:
564-
return await self._charging_manager.charge(
569+
return await self.get_charging_manager().charge(
565570
event_name=charged_event_name,
566571
count=min(max_charged_count, len(data)) if max_charged_count is not None else len(data),
567572
)
@@ -617,7 +622,12 @@ async def set_value(
617622

618623
def get_charging_manager(self) -> ChargingManager:
619624
"""Retrieve the charging manager to access granular pricing information."""
625+
return self._get_charging_manager_implementation()
626+
627+
def _get_charging_manager_implementation(self) -> ChargingManagerImplementation:
620628
self._raise_if_not_initialized()
629+
if not self._charging_manager:
630+
self._charging_manager = ChargingManagerImplementation(self.config, self.apify_client)
621631
return self._charging_manager
622632

623633
async def charge(self, event_name: str, count: int = 1) -> ChargeResult:
@@ -630,7 +640,7 @@ async def charge(self, event_name: str, count: int = 1) -> ChargeResult:
630640
count: Number of events to charge for.
631641
"""
632642
self._raise_if_not_initialized()
633-
return await self._charging_manager.charge(event_name, count)
643+
return await self.get_charging_manager().charge(event_name, count)
634644

635645
@overload
636646
def on(
@@ -681,7 +691,7 @@ def on(self, event_name: Event, listener: EventListener[Any]) -> EventListener[A
681691
"""
682692
self._raise_if_not_initialized()
683693

684-
self._event_manager.on(event=event_name, listener=listener)
694+
self.event_manager.on(event=event_name, listener=listener)
685695
return listener
686696

687697
@overload
@@ -707,7 +717,7 @@ def off(self, event_name: Event, listener: Callable | None = None) -> None:
707717
"""
708718
self._raise_if_not_initialized()
709719

710-
self._event_manager.off(event=event_name, listener=listener)
720+
self.event_manager.off(event=event_name, listener=listener)
711721

712722
def is_at_home(self) -> bool:
713723
"""Return `True` when the Actor is running on the Apify platform, and `False` otherwise (e.g. local run)."""
@@ -782,7 +792,7 @@ async def start(
782792
"""
783793
self._raise_if_not_initialized()
784794

785-
client = self.new_client(token=token) if token else self._apify_client
795+
client = self.new_client(token=token) if token else self.apify_client
786796

787797
if webhooks:
788798
serialized_webhooks = [
@@ -849,7 +859,7 @@ async def abort(
849859
"""
850860
self._raise_if_not_initialized()
851861

852-
client = self.new_client(token=token) if token else self._apify_client
862+
client = self.new_client(token=token) if token else self.apify_client
853863

854864
if status_message:
855865
await client.run(run_id).update(status_message=status_message)
@@ -902,7 +912,7 @@ async def call(
902912
"""
903913
self._raise_if_not_initialized()
904914

905-
client = self.new_client(token=token) if token else self._apify_client
915+
client = self.new_client(token=token) if token else self.apify_client
906916

907917
if webhooks:
908918
serialized_webhooks = [
@@ -974,7 +984,7 @@ async def call_task(
974984
"""
975985
self._raise_if_not_initialized()
976986

977-
client = self.new_client(token=token) if token else self._apify_client
987+
client = self.new_client(token=token) if token else self.apify_client
978988

979989
if webhooks:
980990
serialized_webhooks = [
@@ -1031,7 +1041,7 @@ async def metamorph(
10311041
if not self.config.actor_run_id:
10321042
raise RuntimeError('actor_run_id cannot be None when running on the Apify platform.')
10331043

1034-
await self._apify_client.run(self.config.actor_run_id).metamorph(
1044+
await self.apify_client.run(self.config.actor_run_id).metamorph(
10351045
target_actor_id=target_actor_id,
10361046
run_input=run_input,
10371047
target_actor_build=target_actor_build,
@@ -1077,10 +1087,10 @@ async def reboot(
10771087
# We can't just emit the events and wait for all listeners to finish,
10781088
# because this method might be called from an event listener itself, and we would deadlock.
10791089
persist_state_listeners = flatten(
1080-
(self._event_manager._listeners_to_wrappers[Event.PERSIST_STATE] or {}).values() # noqa: SLF001
1090+
(self.event_manager._listeners_to_wrappers[Event.PERSIST_STATE] or {}).values() # noqa: SLF001
10811091
)
10821092
migrating_listeners = flatten(
1083-
(self._event_manager._listeners_to_wrappers[Event.MIGRATING] or {}).values() # noqa: SLF001
1093+
(self.event_manager._listeners_to_wrappers[Event.MIGRATING] or {}).values() # noqa: SLF001
10841094
)
10851095

10861096
await asyncio.gather(
@@ -1091,7 +1101,7 @@ async def reboot(
10911101
if not self.config.actor_run_id:
10921102
raise RuntimeError('actor_run_id cannot be None when running on the Apify platform.')
10931103

1094-
await self._apify_client.run(self.config.actor_run_id).reboot()
1104+
await self.apify_client.run(self.config.actor_run_id).reboot()
10951105

10961106
if custom_after_sleep:
10971107
await asyncio.sleep(custom_after_sleep.total_seconds())
@@ -1133,7 +1143,7 @@ async def add_webhook(
11331143
if not self.config.actor_run_id:
11341144
raise RuntimeError('actor_run_id cannot be None when running on the Apify platform.')
11351145

1136-
await self._apify_client.webhooks().create(
1146+
await self.apify_client.webhooks().create(
11371147
actor_run_id=self.config.actor_run_id,
11381148
event_types=webhook.event_types,
11391149
request_url=webhook.request_url,
@@ -1169,7 +1179,7 @@ async def set_status_message(
11691179
if not self.config.actor_run_id:
11701180
raise RuntimeError('actor_run_id cannot be None when running on the Apify platform.')
11711181

1172-
api_result = await self._apify_client.run(self.config.actor_run_id).update(
1182+
api_result = await self.apify_client.run(self.config.actor_run_id).update(
11731183
status_message=status_message, is_status_message_terminal=is_terminal
11741184
)
11751185

0 commit comments

Comments
 (0)