Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 25 additions & 5 deletions src/apify/_actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def __init__(
*,
configure_logging: bool = True,
exit_process: bool | None = None,
exit_code: int = 0,
exit_code: int | None = None,
status_message: str | None = None,
event_listeners_timeout: timedelta | None = EVENT_LISTENERS_TIMEOUT,
cleanup_timeout: timedelta = timedelta(seconds=30),
Expand All @@ -140,15 +140,35 @@ def __init__(
configure_logging: Whether to set up the default logging configuration.
exit_process: Whether the Actor should call `sys.exit` when the context manager exits.
Defaults to True, except in IPython and Scrapy environments.
exit_code: The exit code the Actor should use when exiting.
status_message: Final status message to display upon Actor termination.
exit_code: Deprecated. Pass `exit_code` to `Actor.exit()` or `Actor.fail()`, or set `Actor.exit_code`
inside the context manager instead. Will be removed in version 5.0.0.
status_message: Deprecated. Pass `status_message` to `Actor.exit()` or `Actor.fail()`, or set
`Actor.status_message` inside the context manager instead. Will be removed in version 5.0.0.
event_listeners_timeout: Maximum time to wait for Actor event listeners to complete before exiting.
cleanup_timeout: Maximum time to wait for cleanup tasks to finish.
"""
if exit_code is not None:
warnings.warn(
'Passing `exit_code` to `Actor()` is deprecated and will be removed in version 5.0.0. '
'Pass it to `Actor.exit()` or `Actor.fail()`, or set `Actor.exit_code` inside the context manager '
'instead.',
DeprecationWarning,
stacklevel=3,
)

if status_message is not None:
warnings.warn(
'Passing `status_message` to `Actor()` is deprecated and will be removed in version 5.0.0. '
'Pass it to `Actor.exit()` or `Actor.fail()`, or set `Actor.status_message` inside the context '
'manager instead.',
DeprecationWarning,
stacklevel=3,
)

self._configuration = configuration
self._configure_logging = configure_logging
self._exit_process = self._get_default_exit_process() if exit_process is None else exit_process
self._exit_code = exit_code
self._exit_code = 0 if exit_code is None else exit_code
self._status_message = status_message
self._event_listeners_timeout = event_listeners_timeout
self._cleanup_timeout = cleanup_timeout
Expand Down Expand Up @@ -347,7 +367,7 @@ def __call__(
*,
configure_logging: bool = True,
exit_process: bool | None = None,
exit_code: int = 0,
exit_code: int | None = None,
event_listeners_timeout: timedelta | None = EVENT_LISTENERS_TIMEOUT,
status_message: str | None = None,
cleanup_timeout: timedelta = timedelta(seconds=30),
Expand Down
38 changes: 38 additions & 0 deletions tests/unit/actor/test_actor_lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import json
import logging
import sys
import warnings
from datetime import UTC, datetime, timedelta
from typing import TYPE_CHECKING, Any
from unittest import mock
Expand Down Expand Up @@ -494,3 +495,40 @@ async def test_actor_sequential_contexts(*, first_with_call: bool, second_with_c

# The mock should have been called twice, once in each context.
assert mock.call_count == 2


@pytest.mark.parametrize(
'exit_code',
[
pytest.param(0, id='zero'),
pytest.param(42, id='non_zero'),
],
)
def test_actor_constructor_exit_code_warns_deprecated(exit_code: int) -> None:
"""Passing `exit_code` to `Actor()` emits a `DeprecationWarning` and points at the caller."""
with pytest.warns(DeprecationWarning, match='Passing `exit_code` to `Actor\\(\\)` is deprecated') as record:
actor = Actor(exit_code=exit_code)

assert len(record) == 1
assert record[0].filename == __file__
assert actor.exit_code == exit_code


def test_actor_constructor_status_message_warns_deprecated() -> None:
"""Passing `status_message` to `Actor()` emits a `DeprecationWarning` and points at the caller."""
with pytest.warns(DeprecationWarning, match='Passing `status_message` to `Actor\\(\\)` is deprecated') as record:
actor = Actor(status_message='Done')

assert len(record) == 1
assert record[0].filename == __file__
assert actor.status_message == 'Done'


def test_actor_constructor_no_warning_without_deprecated_args() -> None:
"""No deprecation warning is emitted when `exit_code` and `status_message` are omitted from `Actor()`."""
with warnings.catch_warnings():
warnings.simplefilter('error', DeprecationWarning)
actor = Actor()

assert actor.exit_code == 0
assert actor.status_message is None
Loading