apify 3.0.2b5__py3-none-any.whl → 3.0.2b6__py3-none-any.whl
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.
Potentially problematic release.
This version of apify might be problematic. Click here for more details.
apify/_actor.py
CHANGED
|
@@ -32,7 +32,7 @@ from apify._consts import EVENT_LISTENERS_TIMEOUT
|
|
|
32
32
|
from apify._crypto import decrypt_input_secrets, load_private_key
|
|
33
33
|
from apify._models import ActorRun
|
|
34
34
|
from apify._proxy_configuration import ProxyConfiguration
|
|
35
|
-
from apify._utils import docs_group, docs_name, get_system_info, is_running_in_ipython
|
|
35
|
+
from apify._utils import docs_group, docs_name, get_system_info, is_running_in_ipython
|
|
36
36
|
from apify.events import ApifyEventManager, EventManager, LocalEventManager
|
|
37
37
|
from apify.log import _configure_logging, logger
|
|
38
38
|
from apify.storage_clients import ApifyStorageClient
|
|
@@ -48,10 +48,10 @@ if TYPE_CHECKING:
|
|
|
48
48
|
from typing_extensions import Self
|
|
49
49
|
|
|
50
50
|
from crawlee.proxy_configuration import _NewUrlFunction
|
|
51
|
+
from crawlee.storage_clients import StorageClient
|
|
51
52
|
|
|
52
53
|
from apify._models import Webhook
|
|
53
54
|
|
|
54
|
-
|
|
55
55
|
MainReturnType = TypeVar('MainReturnType')
|
|
56
56
|
|
|
57
57
|
|
|
@@ -98,7 +98,10 @@ class _ActorType:
|
|
|
98
98
|
"""
|
|
99
99
|
|
|
100
100
|
_is_rebooting = False
|
|
101
|
+
"""Whether the Actor is currently rebooting."""
|
|
102
|
+
|
|
101
103
|
_is_any_instance_initialized = False
|
|
104
|
+
"""Whether any Actor instance was initialized."""
|
|
102
105
|
|
|
103
106
|
def __init__(
|
|
104
107
|
self,
|
|
@@ -106,63 +109,149 @@ class _ActorType:
|
|
|
106
109
|
*,
|
|
107
110
|
configure_logging: bool = True,
|
|
108
111
|
exit_process: bool | None = None,
|
|
112
|
+
exit_code: int = 0,
|
|
113
|
+
status_message: str | None = None,
|
|
114
|
+
event_listeners_timeout: timedelta | None = EVENT_LISTENERS_TIMEOUT,
|
|
115
|
+
cleanup_timeout: timedelta = timedelta(seconds=30),
|
|
109
116
|
) -> None:
|
|
110
|
-
"""
|
|
111
|
-
|
|
112
|
-
Note that you don't have to do this, all the functionality is accessible using the default instance
|
|
113
|
-
(e.g. `Actor.open_dataset()`).
|
|
117
|
+
"""Initialize a new instance.
|
|
114
118
|
|
|
115
119
|
Args:
|
|
116
|
-
configuration: The Actor configuration to
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
configuration: The Actor configuration to use. If not provided, a default configuration is created.
|
|
121
|
+
configure_logging: Whether to set up the default logging configuration.
|
|
122
|
+
exit_process: Whether the Actor should call `sys.exit` when the context manager exits.
|
|
123
|
+
Defaults to True, except in IPython, Pytest, and Scrapy environments.
|
|
124
|
+
exit_code: The exit code the Actor should use when exiting.
|
|
125
|
+
status_message: Final status message to display upon Actor termination.
|
|
126
|
+
event_listeners_timeout: Maximum time to wait for Actor event listeners to complete before exiting.
|
|
127
|
+
cleanup_timeout: Maximum time to wait for cleanup tasks to finish.
|
|
121
128
|
"""
|
|
129
|
+
self._configuration = configuration
|
|
130
|
+
self._configure_logging = configure_logging
|
|
122
131
|
self._exit_process = self._get_default_exit_process() if exit_process is None else exit_process
|
|
123
|
-
self.
|
|
132
|
+
self._exit_code = exit_code
|
|
133
|
+
self._status_message = status_message
|
|
134
|
+
self._event_listeners_timeout = event_listeners_timeout
|
|
135
|
+
self._cleanup_timeout = cleanup_timeout
|
|
124
136
|
|
|
125
137
|
# Actor state when this method is being executed is unpredictable.
|
|
126
138
|
# Actor can be initialized by lazy object proxy or by user directly, or by both.
|
|
127
139
|
# Until `init` method is run, this state of uncertainty remains. This is the reason why any setting done here in
|
|
128
140
|
# `__init__` method should not be considered final.
|
|
129
141
|
|
|
130
|
-
self._configuration = configuration
|
|
131
|
-
self._configure_logging = configure_logging
|
|
132
142
|
self._apify_client: ApifyClientAsync | None = None
|
|
143
|
+
self._local_storage_client: StorageClient | None = None
|
|
133
144
|
|
|
145
|
+
self._is_exiting = False
|
|
134
146
|
self._is_initialized = False
|
|
135
147
|
|
|
136
148
|
async def __aenter__(self) -> Self:
|
|
137
|
-
"""
|
|
149
|
+
"""Enter the Actor context.
|
|
150
|
+
|
|
151
|
+
Initializes the Actor when used in an `async with` block. This method:
|
|
138
152
|
|
|
139
|
-
|
|
153
|
+
- Sets up local or cloud storage clients depending on whether the Actor runs locally or on the Apify platform.
|
|
154
|
+
- Configures the event manager and starts periodic state persistence.
|
|
155
|
+
- Initializes the charging manager for handling charging events.
|
|
156
|
+
- Configures logging after all core services are registered.
|
|
140
157
|
|
|
141
|
-
|
|
142
|
-
|
|
158
|
+
This method must be called exactly once per Actor instance. Re-initializing an Actor or having multiple
|
|
159
|
+
active Actor instances is not standard usage and may lead to warnings or unexpected behavior.
|
|
143
160
|
"""
|
|
144
|
-
|
|
161
|
+
if self._is_initialized:
|
|
162
|
+
raise RuntimeError('The Actor was already initialized!')
|
|
163
|
+
|
|
164
|
+
# Initialize configuration first - it's required for the next steps.
|
|
165
|
+
if self._configuration:
|
|
166
|
+
# User provided explicit configuration - register it in the service locator.
|
|
167
|
+
service_locator.set_configuration(self.configuration)
|
|
168
|
+
else:
|
|
169
|
+
# No explicit configuration provided - trigger creation of default configuration.
|
|
170
|
+
_ = self.configuration
|
|
171
|
+
|
|
172
|
+
# Configure logging based on the configuration, any logs before this point are lost.
|
|
173
|
+
if self._configure_logging:
|
|
174
|
+
_configure_logging()
|
|
175
|
+
self.log.debug('Logging configured')
|
|
176
|
+
|
|
177
|
+
self.log.info('Initializing Actor', extra=get_system_info())
|
|
178
|
+
self.log.debug('Configuration initialized')
|
|
179
|
+
|
|
180
|
+
# Warn about non-standard usage patterns.
|
|
181
|
+
if _ActorType._is_any_instance_initialized:
|
|
182
|
+
self.log.warning('Repeated Actor initialization detected - this is non-standard usage, proceed with care.')
|
|
183
|
+
|
|
184
|
+
# Update the global Actor proxy to refer to this instance.
|
|
185
|
+
cast('Proxy', Actor).__wrapped__ = self
|
|
186
|
+
self._is_exiting = False
|
|
187
|
+
self._was_final_persist_state_emitted = False
|
|
188
|
+
|
|
189
|
+
# Initialize the storage client and register it in the service locator.
|
|
190
|
+
_ = self._storage_client
|
|
191
|
+
self.log.debug('Storage client initialized')
|
|
192
|
+
|
|
193
|
+
# Initialize the event manager and register it in the service locator.
|
|
194
|
+
await self.event_manager.__aenter__()
|
|
195
|
+
self.log.debug('Event manager initialized')
|
|
196
|
+
|
|
197
|
+
# Initialize the charging manager.
|
|
198
|
+
await self._charging_manager_implementation.__aenter__()
|
|
199
|
+
self.log.debug('Charging manager initialized')
|
|
200
|
+
|
|
201
|
+
# Mark initialization as complete and update global state.
|
|
202
|
+
self._is_initialized = True
|
|
203
|
+
_ActorType._is_any_instance_initialized = True
|
|
145
204
|
return self
|
|
146
205
|
|
|
147
206
|
async def __aexit__(
|
|
148
207
|
self,
|
|
149
|
-
|
|
208
|
+
exc_type: type[BaseException] | None,
|
|
150
209
|
exc_value: BaseException | None,
|
|
151
|
-
|
|
210
|
+
exc_traceback: TracebackType | None,
|
|
152
211
|
) -> None:
|
|
153
|
-
"""Exit the Actor
|
|
212
|
+
"""Exit the Actor context.
|
|
213
|
+
|
|
214
|
+
If the block exits with an exception, the Actor fails with a non-zero exit code.
|
|
215
|
+
Otherwise, it exits cleanly. In both cases the Actor:
|
|
154
216
|
|
|
155
|
-
|
|
156
|
-
|
|
217
|
+
- Cancels periodic `PERSIST_STATE` events.
|
|
218
|
+
- Sends a final `PERSIST_STATE` event.
|
|
219
|
+
- Waits for all event listeners to finish.
|
|
220
|
+
- Stops the event manager and the charging manager.
|
|
221
|
+
- Optionally terminates the process with the selected exit code.
|
|
157
222
|
"""
|
|
158
|
-
if
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
223
|
+
if self._is_exiting:
|
|
224
|
+
return
|
|
225
|
+
|
|
226
|
+
self._raise_if_not_initialized()
|
|
227
|
+
|
|
228
|
+
if exc_value and not is_running_in_ipython():
|
|
229
|
+
# In IPython, we don't run `sys.exit()` during Actor exits,
|
|
230
|
+
# so the exception traceback will be printed on its own
|
|
231
|
+
self.log.exception('Actor failed with an exception', exc_info=exc_value)
|
|
232
|
+
self.exit_code = ActorExitCodes.ERROR_USER_FUNCTION_THREW.value
|
|
233
|
+
|
|
234
|
+
self._is_exiting = True
|
|
235
|
+
self.log.info('Exiting Actor', extra={'exit_code': self.exit_code})
|
|
236
|
+
|
|
237
|
+
async def finalize() -> None:
|
|
238
|
+
if self.status_message is not None:
|
|
239
|
+
await self.set_status_message(self.status_message, is_terminal=True)
|
|
240
|
+
|
|
241
|
+
# Sleep for a bit so that the listeners have a chance to trigger
|
|
242
|
+
await asyncio.sleep(0.1)
|
|
243
|
+
|
|
244
|
+
if self._event_listeners_timeout:
|
|
245
|
+
await self.event_manager.wait_for_all_listeners_to_complete(timeout=self._event_listeners_timeout)
|
|
246
|
+
|
|
247
|
+
await self.event_manager.__aexit__(None, None, None)
|
|
248
|
+
await self._charging_manager_implementation.__aexit__(None, None, None)
|
|
249
|
+
|
|
250
|
+
await asyncio.wait_for(finalize(), self._cleanup_timeout.total_seconds())
|
|
251
|
+
self._is_initialized = False
|
|
252
|
+
|
|
253
|
+
if self._exit_process:
|
|
254
|
+
sys.exit(self.exit_code)
|
|
166
255
|
|
|
167
256
|
def __repr__(self) -> str:
|
|
168
257
|
if self is cast('Proxy', Actor).__wrapped__:
|
|
@@ -176,24 +265,58 @@ class _ActorType:
|
|
|
176
265
|
*,
|
|
177
266
|
configure_logging: bool = True,
|
|
178
267
|
exit_process: bool | None = None,
|
|
268
|
+
exit_code: int = 0,
|
|
269
|
+
event_listeners_timeout: timedelta | None = EVENT_LISTENERS_TIMEOUT,
|
|
270
|
+
status_message: str | None = None,
|
|
271
|
+
cleanup_timeout: timedelta = timedelta(seconds=30),
|
|
179
272
|
) -> Self:
|
|
180
|
-
"""Make a new Actor instance with a non-default configuration.
|
|
273
|
+
"""Make a new Actor instance with a non-default configuration.
|
|
274
|
+
|
|
275
|
+
This is necessary due to the lazy object proxying of the global `Actor` instance.
|
|
276
|
+
"""
|
|
181
277
|
return self.__class__(
|
|
182
278
|
configuration=configuration,
|
|
183
279
|
configure_logging=configure_logging,
|
|
184
280
|
exit_process=exit_process,
|
|
281
|
+
exit_code=exit_code,
|
|
282
|
+
event_listeners_timeout=event_listeners_timeout,
|
|
283
|
+
status_message=status_message,
|
|
284
|
+
cleanup_timeout=cleanup_timeout,
|
|
185
285
|
)
|
|
186
286
|
|
|
287
|
+
@property
|
|
288
|
+
def log(self) -> logging.Logger:
|
|
289
|
+
"""Logger configured for this Actor."""
|
|
290
|
+
return logger
|
|
291
|
+
|
|
292
|
+
@property
|
|
293
|
+
def exit_code(self) -> int:
|
|
294
|
+
"""The exit code the Actor will use when exiting."""
|
|
295
|
+
return self._exit_code
|
|
296
|
+
|
|
297
|
+
@exit_code.setter
|
|
298
|
+
def exit_code(self, value: int) -> None:
|
|
299
|
+
self._exit_code = value
|
|
300
|
+
|
|
301
|
+
@property
|
|
302
|
+
def status_message(self) -> str | None:
|
|
303
|
+
"""The final status message that the Actor will display upon termination."""
|
|
304
|
+
return self._status_message
|
|
305
|
+
|
|
306
|
+
@status_message.setter
|
|
307
|
+
def status_message(self, value: str | None) -> None:
|
|
308
|
+
self._status_message = value
|
|
309
|
+
|
|
187
310
|
@property
|
|
188
311
|
def apify_client(self) -> ApifyClientAsync:
|
|
189
|
-
"""
|
|
312
|
+
"""Asynchronous Apify client for interacting with the Apify API."""
|
|
190
313
|
if not self._apify_client:
|
|
191
314
|
self._apify_client = self.new_client()
|
|
192
315
|
return self._apify_client
|
|
193
316
|
|
|
194
317
|
@cached_property
|
|
195
318
|
def configuration(self) -> Configuration:
|
|
196
|
-
"""
|
|
319
|
+
"""Actor configuration, uses the default instance if not explicitly set."""
|
|
197
320
|
if self._configuration:
|
|
198
321
|
return self._configuration
|
|
199
322
|
|
|
@@ -214,8 +337,11 @@ class _ActorType:
|
|
|
214
337
|
|
|
215
338
|
@cached_property
|
|
216
339
|
def event_manager(self) -> EventManager:
|
|
217
|
-
"""
|
|
218
|
-
|
|
340
|
+
"""Manages Apify platform events.
|
|
341
|
+
|
|
342
|
+
It uses `ApifyEventManager` on the Apify platform and `LocalEventManager` otherwise.
|
|
343
|
+
"""
|
|
344
|
+
event_manager = (
|
|
219
345
|
ApifyEventManager(
|
|
220
346
|
configuration=self.configuration,
|
|
221
347
|
persist_state_interval=self.configuration.persist_state_interval,
|
|
@@ -226,19 +352,16 @@ class _ActorType:
|
|
|
226
352
|
persist_state_interval=self.configuration.persist_state_interval,
|
|
227
353
|
)
|
|
228
354
|
)
|
|
355
|
+
service_locator.set_event_manager(event_manager)
|
|
356
|
+
return event_manager
|
|
229
357
|
|
|
230
|
-
@
|
|
231
|
-
def
|
|
232
|
-
|
|
233
|
-
return logger
|
|
234
|
-
|
|
235
|
-
def _raise_if_not_initialized(self) -> None:
|
|
236
|
-
if not self._is_initialized:
|
|
237
|
-
raise RuntimeError('The Actor was not initialized!')
|
|
358
|
+
@cached_property
|
|
359
|
+
def _charging_manager_implementation(self) -> ChargingManagerImplementation:
|
|
360
|
+
return ChargingManagerImplementation(self.configuration, self.apify_client)
|
|
238
361
|
|
|
239
362
|
@cached_property
|
|
240
363
|
def _storage_client(self) -> SmartApifyStorageClient:
|
|
241
|
-
"""Storage client used by the
|
|
364
|
+
"""Storage client used by the Actor.
|
|
242
365
|
|
|
243
366
|
Depending on the initialization of the service locator the client can be created in different ways.
|
|
244
367
|
"""
|
|
@@ -250,7 +373,7 @@ class _ActorType:
|
|
|
250
373
|
service_locator.set_storage_client(implicit_storage_client)
|
|
251
374
|
except ServiceConflictError:
|
|
252
375
|
self.log.debug(
|
|
253
|
-
'Storage client in service locator was set explicitly before Actor.init was called.'
|
|
376
|
+
'Storage client in service locator was set explicitly before Actor.init was called. '
|
|
254
377
|
'Using the existing storage client as implicit storage client for the Actor.'
|
|
255
378
|
)
|
|
256
379
|
else:
|
|
@@ -270,100 +393,35 @@ class _ActorType:
|
|
|
270
393
|
)
|
|
271
394
|
|
|
272
395
|
async def init(self) -> None:
|
|
273
|
-
"""Initialize the Actor
|
|
274
|
-
|
|
275
|
-
This initializes the Actor instance. It configures the right storage client based on whether the Actor is
|
|
276
|
-
running locally or on the Apify platform, it initializes the event manager for processing Actor events,
|
|
277
|
-
and starts an interval for regularly sending `PERSIST_STATE` events, so that the Actor can regularly persist
|
|
278
|
-
its state in response to these events.
|
|
396
|
+
"""Initialize the Actor without using context-manager syntax.
|
|
279
397
|
|
|
280
|
-
|
|
281
|
-
called only once.
|
|
398
|
+
Equivalent to `await Actor.__aenter__()`.
|
|
282
399
|
"""
|
|
283
|
-
self.
|
|
284
|
-
if self._configuration:
|
|
285
|
-
# Set explicitly the configuration in the service locator
|
|
286
|
-
service_locator.set_configuration(self.configuration)
|
|
287
|
-
else:
|
|
288
|
-
# Ensure that the configuration (cached property) is set
|
|
289
|
-
_ = self.configuration
|
|
290
|
-
|
|
291
|
-
if self._is_initialized:
|
|
292
|
-
raise RuntimeError('The Actor was already initialized!')
|
|
293
|
-
|
|
294
|
-
if _ActorType._is_any_instance_initialized:
|
|
295
|
-
self.log.warning('Repeated Actor initialization detected - this is non-standard usage, proceed with care')
|
|
296
|
-
|
|
297
|
-
# Make sure that the currently initialized instance is also available through the global `Actor` proxy
|
|
298
|
-
cast('Proxy', Actor).__wrapped__ = self
|
|
299
|
-
|
|
300
|
-
self._is_exiting = False
|
|
301
|
-
self._was_final_persist_state_emitted = False
|
|
302
|
-
|
|
303
|
-
self.log.debug(f'Storage client set to {self._storage_client}')
|
|
304
|
-
|
|
305
|
-
service_locator.set_event_manager(self.event_manager)
|
|
306
|
-
|
|
307
|
-
# The logging configuration has to be called after all service_locator set methods.
|
|
308
|
-
if self._configure_logging:
|
|
309
|
-
_configure_logging()
|
|
310
|
-
|
|
311
|
-
self.log.info('System info', extra=get_system_info())
|
|
312
|
-
|
|
313
|
-
await self.event_manager.__aenter__()
|
|
314
|
-
self.log.debug('Event manager initialized')
|
|
315
|
-
|
|
316
|
-
await self._charging_manager_implementation.__aenter__()
|
|
317
|
-
self.log.debug('Charging manager initialized')
|
|
318
|
-
|
|
319
|
-
self._is_initialized = True
|
|
320
|
-
_ActorType._is_any_instance_initialized = True
|
|
400
|
+
await self.__aenter__()
|
|
321
401
|
|
|
322
402
|
async def exit(
|
|
323
403
|
self,
|
|
324
404
|
*,
|
|
325
405
|
exit_code: int = 0,
|
|
326
|
-
event_listeners_timeout: timedelta | None = EVENT_LISTENERS_TIMEOUT,
|
|
327
406
|
status_message: str | None = None,
|
|
407
|
+
event_listeners_timeout: timedelta | None = EVENT_LISTENERS_TIMEOUT,
|
|
328
408
|
cleanup_timeout: timedelta = timedelta(seconds=30),
|
|
329
409
|
) -> None:
|
|
330
|
-
"""Exit the Actor
|
|
410
|
+
"""Exit the Actor without using context-manager syntax.
|
|
331
411
|
|
|
332
|
-
|
|
333
|
-
sends a final `PERSIST_STATE` event, waits for all the event listeners to finish, and stops the event manager.
|
|
412
|
+
Equivalent to `await Actor.__aexit__()`.
|
|
334
413
|
|
|
335
414
|
Args:
|
|
336
|
-
exit_code: The exit code
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
cleanup_timeout:
|
|
415
|
+
exit_code: The exit code the Actor should use when exiting.
|
|
416
|
+
status_message: Final status message to display upon Actor termination.
|
|
417
|
+
event_listeners_timeout: Maximum time to wait for Actor event listeners to complete before exiting.
|
|
418
|
+
cleanup_timeout: Maximum time to wait for cleanup tasks to finish.
|
|
340
419
|
"""
|
|
341
|
-
self.
|
|
342
|
-
|
|
343
|
-
self.
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
self.log.info('Exiting Actor', extra={'exit_code': exit_code})
|
|
348
|
-
|
|
349
|
-
async def finalize() -> None:
|
|
350
|
-
if status_message is not None:
|
|
351
|
-
await self.set_status_message(status_message, is_terminal=True)
|
|
352
|
-
|
|
353
|
-
# Sleep for a bit so that the listeners have a chance to trigger
|
|
354
|
-
await asyncio.sleep(0.1)
|
|
355
|
-
|
|
356
|
-
if event_listeners_timeout:
|
|
357
|
-
await self.event_manager.wait_for_all_listeners_to_complete(timeout=event_listeners_timeout)
|
|
358
|
-
|
|
359
|
-
await self.event_manager.__aexit__(None, None, None)
|
|
360
|
-
await self._charging_manager_implementation.__aexit__(None, None, None)
|
|
361
|
-
|
|
362
|
-
await asyncio.wait_for(finalize(), cleanup_timeout.total_seconds())
|
|
363
|
-
self._is_initialized = False
|
|
364
|
-
|
|
365
|
-
if self._exit_process:
|
|
366
|
-
sys.exit(exit_code)
|
|
420
|
+
self.exit_code = exit_code
|
|
421
|
+
self.status_message = status_message
|
|
422
|
+
self._event_listeners_timeout = event_listeners_timeout
|
|
423
|
+
self._cleanup_timeout = cleanup_timeout
|
|
424
|
+
await self.__aexit__(None, None, None)
|
|
367
425
|
|
|
368
426
|
async def fail(
|
|
369
427
|
self,
|
|
@@ -372,23 +430,24 @@ class _ActorType:
|
|
|
372
430
|
exception: BaseException | None = None,
|
|
373
431
|
status_message: str | None = None,
|
|
374
432
|
) -> None:
|
|
375
|
-
"""Fail the Actor instance.
|
|
433
|
+
"""Fail the Actor instance without using context-manager syntax.
|
|
376
434
|
|
|
377
|
-
|
|
435
|
+
Equivalent to setting the `self.exit_code` and `self.status_message` properties and using
|
|
436
|
+
`await Actor.__aexit__()`.
|
|
378
437
|
|
|
379
438
|
Args:
|
|
380
439
|
exit_code: The exit code with which the Actor should fail (defaults to `1`).
|
|
381
440
|
exception: The exception with which the Actor failed.
|
|
382
441
|
status_message: The final status message that the Actor should display.
|
|
383
442
|
"""
|
|
384
|
-
self.
|
|
385
|
-
|
|
386
|
-
# In IPython, we don't run `sys.exit()` during Actor exits,
|
|
387
|
-
# so the exception traceback will be printed on its own
|
|
388
|
-
if exception and not is_running_in_ipython():
|
|
389
|
-
self.log.exception('Actor failed with an exception', exc_info=exception)
|
|
443
|
+
self.exit_code = exit_code
|
|
444
|
+
self.status_message = status_message
|
|
390
445
|
|
|
391
|
-
await self.
|
|
446
|
+
await self.__aexit__(
|
|
447
|
+
exc_type=type(exception) if exception else None,
|
|
448
|
+
exc_value=exception,
|
|
449
|
+
exc_traceback=exception.__traceback__ if exception else None,
|
|
450
|
+
)
|
|
392
451
|
|
|
393
452
|
def new_client(
|
|
394
453
|
self,
|
|
@@ -626,10 +685,6 @@ class _ActorType:
|
|
|
626
685
|
self._raise_if_not_initialized()
|
|
627
686
|
return self._charging_manager_implementation
|
|
628
687
|
|
|
629
|
-
@cached_property
|
|
630
|
-
def _charging_manager_implementation(self) -> ChargingManagerImplementation:
|
|
631
|
-
return ChargingManagerImplementation(self.configuration, self.apify_client)
|
|
632
|
-
|
|
633
688
|
async def charge(self, event_name: str, count: int = 1) -> ChargeResult:
|
|
634
689
|
"""Charge for a specified number of events - sub-operations of the Actor.
|
|
635
690
|
|
|
@@ -822,18 +877,6 @@ class _ActorType:
|
|
|
822
877
|
|
|
823
878
|
return ActorRun.model_validate(api_result)
|
|
824
879
|
|
|
825
|
-
def _get_remaining_time(self) -> timedelta | None:
|
|
826
|
-
"""Get time remaining from the Actor timeout. Returns `None` if not on an Apify platform."""
|
|
827
|
-
if self.is_at_home() and self.configuration.timeout_at:
|
|
828
|
-
return self.configuration.timeout_at - datetime.now(tz=timezone.utc)
|
|
829
|
-
|
|
830
|
-
self.log.warning(
|
|
831
|
-
'Returning `None` instead of remaining time. Using `RemainingTime` argument is only possible when the Actor'
|
|
832
|
-
' is running on the Apify platform and when the timeout for the Actor run is set. '
|
|
833
|
-
f'{self.is_at_home()=}, {self.configuration.timeout_at=}'
|
|
834
|
-
)
|
|
835
|
-
return None
|
|
836
|
-
|
|
837
880
|
async def abort(
|
|
838
881
|
self,
|
|
839
882
|
run_id: str,
|
|
@@ -1242,6 +1285,10 @@ class _ActorType:
|
|
|
1242
1285
|
|
|
1243
1286
|
return proxy_configuration
|
|
1244
1287
|
|
|
1288
|
+
def _raise_if_not_initialized(self) -> None:
|
|
1289
|
+
if not self._is_initialized:
|
|
1290
|
+
raise RuntimeError('The Actor was not initialized!')
|
|
1291
|
+
|
|
1245
1292
|
def _get_default_exit_process(self) -> bool:
|
|
1246
1293
|
"""Return False for IPython, Pytest, and Scrapy environments, True otherwise."""
|
|
1247
1294
|
if is_running_in_ipython():
|
|
@@ -1262,6 +1309,18 @@ class _ActorType:
|
|
|
1262
1309
|
|
|
1263
1310
|
return True
|
|
1264
1311
|
|
|
1312
|
+
def _get_remaining_time(self) -> timedelta | None:
|
|
1313
|
+
"""Get time remaining from the Actor timeout. Returns `None` if not on an Apify platform."""
|
|
1314
|
+
if self.is_at_home() and self.configuration.timeout_at:
|
|
1315
|
+
return self.configuration.timeout_at - datetime.now(tz=timezone.utc)
|
|
1316
|
+
|
|
1317
|
+
self.log.warning(
|
|
1318
|
+
'Returning `None` instead of remaining time. Using `RemainingTime` argument is only possible when the Actor'
|
|
1319
|
+
' is running on the Apify platform and when the timeout for the Actor run is set. '
|
|
1320
|
+
f'{self.is_at_home()=}, {self.configuration.timeout_at=}'
|
|
1321
|
+
)
|
|
1322
|
+
return None
|
|
1323
|
+
|
|
1265
1324
|
|
|
1266
1325
|
Actor = cast('_ActorType', Proxy(_ActorType))
|
|
1267
1326
|
"""The entry point of the SDK, through which all the Actor operations should be done."""
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
apify/__init__.py,sha256=HpgKg2FZWJuSPfDygzJ62psylhw4NN4tKFnoYUIhcd4,838
|
|
2
|
-
apify/_actor.py,sha256=
|
|
2
|
+
apify/_actor.py,sha256=kfrwD8gaeN4NcdNMD_Pj66agNh78jJjwMuNOuwLdo-E,57370
|
|
3
3
|
apify/_charging.py,sha256=KjZ2DnEMS0Tt8ibizmmt0RwBq8FOAsD1z-hKFgdazcY,13143
|
|
4
4
|
apify/_configuration.py,sha256=7ZHhgRp98kr35zx4k4EB2aImq7Dq1FJjPg7r5bucv_M,14984
|
|
5
5
|
apify/_consts.py,sha256=CjhyEJ4Mi0lcIrzfqz8dN7nPJWGjCeBrrXQy1PZ6zRI,440
|
|
@@ -51,7 +51,7 @@ apify/storage_clients/_smart_apify/__init__.py,sha256=614B2AaWY-dx6RQ6mod7VVR8gF
|
|
|
51
51
|
apify/storage_clients/_smart_apify/_storage_client.py,sha256=GCPmVe_xWAFcO2Cuej4su4i97_d33Q9Ih_Sc5xW2Wa4,4674
|
|
52
52
|
apify/storages/__init__.py,sha256=-9tEYJVabVs_eRVhUehxN58GH0UG8OfuGjGwuDieP2M,122
|
|
53
53
|
apify/storages/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
|
-
apify-3.0.
|
|
55
|
-
apify-3.0.
|
|
56
|
-
apify-3.0.
|
|
57
|
-
apify-3.0.
|
|
54
|
+
apify-3.0.2b6.dist-info/METADATA,sha256=VvY5YhIVBaPQf2fk7f62zq8RW0ss9R8mwqJZV2Wwchk,22582
|
|
55
|
+
apify-3.0.2b6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
56
|
+
apify-3.0.2b6.dist-info/licenses/LICENSE,sha256=AsFjHssKjj4LGd2ZCqXn6FBzMqcWdjQre1byPPSypVw,11355
|
|
57
|
+
apify-3.0.2b6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|