Skip to content
Merged
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
28 changes: 11 additions & 17 deletions src/apify/_actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@
from apify._utils import docs_group, docs_name, get_system_info, is_running_in_ipython
from apify.events import ApifyEventManager, EventManager, LocalEventManager
from apify.log import _configure_logging, logger
from apify.storage_clients import ApifyStorageClient
from apify.storage_clients import ApifyStorageClient, SmartApifyStorageClient
from apify.storage_clients._file_system import ApifyFileSystemStorageClient
from apify.storage_clients._smart_apify._storage_client import SmartApifyStorageClient
from apify.storages import Dataset, KeyValueStore, RequestQueue

if TYPE_CHECKING:
Expand Down Expand Up @@ -95,12 +94,6 @@ async def main() -> None:
```
"""

_is_rebooting = False
"""Whether the Actor is currently rebooting."""

_is_any_instance_initialized = False
"""Whether any Actor instance was initialized."""

def __init__(
self,
configuration: Configuration | None = None,
Expand Down Expand Up @@ -139,8 +132,14 @@ def __init__(

self._apify_client: ApifyClientAsync | None = None

self._is_exiting = False
self._is_initialized = False
"""Whether any Actor instance is currently initialized."""

self._is_rebooting = False
"""Whether the Actor is currently rebooting."""

self._is_exiting = False
"""Whether the Actor is currently exiting."""

async def __aenter__(self) -> Self:
"""Enter the Actor context.
Expand Down Expand Up @@ -169,15 +168,11 @@ async def __aenter__(self) -> Self:
# Configure logging based on the configuration, any logs before this point are lost.
if self._configure_logging:
_configure_logging()
self.log.debug('Logging configured')
self.log.debug('Logging configured')

self.log.info('Initializing Actor', extra=get_system_info())
self.log.debug('Configuration initialized')

# Warn about non-standard usage patterns.
if _ActorType._is_any_instance_initialized:
self.log.warning('Repeated Actor initialization detected - this is non-standard usage, proceed with care.')

# Update the global Actor proxy to refer to this instance.
cast('Proxy', Actor).__wrapped__ = self
self._is_exiting = False
Expand All @@ -197,7 +192,6 @@ async def __aenter__(self) -> Self:

# Mark initialization as complete and update global state.
self._is_initialized = True
_ActorType._is_any_instance_initialized = True
return self

async def __aexit__(
Expand Down Expand Up @@ -1113,11 +1107,11 @@ async def reboot(
self.log.error('Actor.reboot() is only supported when running on the Apify platform.')
return

if _ActorType._is_rebooting:
if self._is_rebooting:
self.log.debug('Actor is already rebooting, skipping the additional reboot call.')
return

_ActorType._is_rebooting = True
self._is_rebooting = True

if not custom_after_sleep:
custom_after_sleep = self.configuration.metamorph_after_sleep
Expand Down
101 changes: 48 additions & 53 deletions tests/unit/actor/test_actor_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,72 +36,67 @@ async def test_actor_logs_messages_correctly(caplog: pytest.LogCaptureFixture) -
# Test that exception in Actor.main is logged with the traceback
raise RuntimeError('Dummy RuntimeError')

# We skip the first entry, as it is related to the initialization of `lazy_object_proxy.Proxy` for `Actor`.
records = caplog.records[1:]
records = caplog.records

# Updated expected number of log records (additional debug messages added)
assert len(records) == 14
# Expected number of log records
assert len(records) == 13

# Record 0: Logging configured
assert records[0].levelno == logging.DEBUG
assert records[0].message == 'Logging configured'
# Record 0: Initializing Actor
assert records[0].levelno == logging.INFO
assert records[0].message == 'Initializing Actor'

# Record 1: Initializing Actor
assert records[1].levelno == logging.INFO
assert records[1].message == 'Initializing Actor'
# Record 1: Configuration initialized
assert records[1].levelno == logging.DEBUG
assert records[1].message == 'Configuration initialized'

# Record 2: Configuration initialized
# Record 2: Storage client initialized
assert records[2].levelno == logging.DEBUG
assert records[2].message == 'Configuration initialized'
assert records[2].message == 'Storage client initialized'

# Record 3: Storage client initialized
# Record 3: Event manager initialized
assert records[3].levelno == logging.DEBUG
assert records[3].message == 'Storage client initialized'
assert records[3].message == 'Event manager initialized'

# Record 4: Event manager initialized
# Record 4: Charging manager initialized
assert records[4].levelno == logging.DEBUG
assert records[4].message == 'Event manager initialized'
assert records[4].message == 'Charging manager initialized'

# Record 5: Charging manager initialized
# Record 5: Debug message
assert records[5].levelno == logging.DEBUG
assert records[5].message == 'Charging manager initialized'
assert records[5].message == 'Debug message'

# Record 6: Debug message
assert records[6].levelno == logging.DEBUG
assert records[6].message == 'Debug message'
# Record 6: Info message
assert records[6].levelno == logging.INFO
assert records[6].message == 'Info message'

# Record 7: Info message
assert records[7].levelno == logging.INFO
assert records[7].message == 'Info message'
# Record 7: Warning message
assert records[7].levelno == logging.WARNING
assert records[7].message == 'Warning message'

# Record 8: Warning message
assert records[8].levelno == logging.WARNING
assert records[8].message == 'Warning message'
# Record 8: Error message
assert records[8].levelno == logging.ERROR
assert records[8].message == 'Error message'

# Record 9: Error message
# Record 9: Exception message with traceback (ValueError)
assert records[9].levelno == logging.ERROR
assert records[9].message == 'Error message'

# Record 10: Exception message with traceback (ValueError)
assert records[10].levelno == logging.ERROR
assert records[10].message == 'Exception message'
assert records[10].exc_info is not None
assert records[10].exc_info[0] is ValueError
assert isinstance(records[10].exc_info[1], ValueError)
assert str(records[10].exc_info[1]) == 'Dummy ValueError'

# Record 11: Multiline log message
assert records[11].levelno == logging.INFO
assert records[11].message == 'Multi\nline\nlog\nmessage'

# Record 12: Actor failed with an exception (RuntimeError)
assert records[12].levelno == logging.ERROR
assert records[12].message == 'Actor failed with an exception'
assert records[12].exc_info is not None
assert records[12].exc_info[0] is RuntimeError
assert isinstance(records[12].exc_info[1], RuntimeError)
assert str(records[12].exc_info[1]) == 'Dummy RuntimeError'

# Record 13: Exiting Actor
assert records[13].levelno == logging.INFO
assert records[13].message == 'Exiting Actor'
assert records[9].message == 'Exception message'
assert records[9].exc_info is not None
assert records[9].exc_info[0] is ValueError
assert isinstance(records[9].exc_info[1], ValueError)
assert str(records[9].exc_info[1]) == 'Dummy ValueError'

# Record 10: Multiline log message
assert records[10].levelno == logging.INFO
assert records[10].message == 'Multi\nline\nlog\nmessage'

# Record 11: Actor failed with an exception (RuntimeError)
assert records[11].levelno == logging.ERROR
assert records[11].message == 'Actor failed with an exception'
assert records[11].exc_info is not None
assert records[11].exc_info[0] is RuntimeError
assert isinstance(records[11].exc_info[1], RuntimeError)
assert str(records[11].exc_info[1]) == 'Dummy RuntimeError'

# Record 12: Exiting Actor
assert records[12].levelno == logging.INFO
assert records[12].message == 'Exiting Actor'
2 changes: 1 addition & 1 deletion tests/unit/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def prepare_test_env(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> Callabl
def _prepare_test_env() -> None:
if hasattr(apify._actor.Actor, '__wrapped__'):
delattr(apify._actor.Actor, '__wrapped__')
apify._actor._ActorType._is_any_instance_initialized = False
apify._actor.Actor._is_initialized = False

# Set the environment variable for the local storage directory to the temporary path.
monkeypatch.setenv(ApifyEnvVars.LOCAL_STORAGE_DIR, str(tmp_path))
Expand Down