From 9cd910d33b472f17e724a14c4c94d26c633d6630 Mon Sep 17 00:00:00 2001 From: stepskop Date: Tue, 25 Nov 2025 18:38:53 +0100 Subject: [PATCH 1/5] feat: add actor permissions to the configuration --- src/apify/_configuration.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/apify/_configuration.py b/src/apify/_configuration.py index 10109585..53e2c23c 100644 --- a/src/apify/_configuration.py +++ b/src/apify/_configuration.py @@ -61,6 +61,17 @@ class Configuration(CrawleeConfiguration): ), ] = None + actor_permission_level: Annotated[ + str | None, + Field( + validation_alias=AliasChoices( + 'actor_permission_level', + 'apify_actor_permission_level', + ), + description='Permission level the Actor is run under.', + ), + ] = None + actor_run_id: Annotated[ str | None, Field( From 2457458a69a252f5700c8d6a2e32df77b8bc1a41 Mon Sep 17 00:00:00 2001 From: stepskop Date: Tue, 25 Nov 2025 18:40:57 +0100 Subject: [PATCH 2/5] feat: log the actor permission on actor initialization --- src/apify/_actor.py | 8 ++++ tests/unit/actor/test_actor_log.py | 74 ++++++++++++++++-------------- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/src/apify/_actor.py b/src/apify/_actor.py index 3fc99128..b7c6b972 100644 --- a/src/apify/_actor.py +++ b/src/apify/_actor.py @@ -192,6 +192,14 @@ async def __aenter__(self) -> Self: # Mark initialization as complete and update global state. self._is_initialized = True + + self.log.info( + 'Permissions', + extra={ + 'actor_permission_level': self.get_env().get('actor_permission_level'), + }, + ) + return self async def __aexit__( diff --git a/tests/unit/actor/test_actor_log.py b/tests/unit/actor/test_actor_log.py index 7bff15b4..857655a0 100644 --- a/tests/unit/actor/test_actor_log.py +++ b/tests/unit/actor/test_actor_log.py @@ -39,7 +39,7 @@ async def test_actor_logs_messages_correctly(caplog: pytest.LogCaptureFixture) - records = caplog.records # Expected number of log records - assert len(records) == 13 + assert len(records) == 14 # Record 0: Initializing Actor assert records[0].levelno == logging.INFO @@ -61,42 +61,46 @@ async def test_actor_logs_messages_correctly(caplog: pytest.LogCaptureFixture) - assert records[4].levelno == logging.DEBUG assert records[4].message == 'Charging manager initialized' - # Record 5: Debug message - assert records[5].levelno == logging.DEBUG - assert records[5].message == 'Debug message' + # Record 5: Actor Permissions + assert records[5].levelno == logging.INFO + assert records[5].message == 'Permissions' - # Record 6: Info message - assert records[6].levelno == logging.INFO - assert records[6].message == 'Info message' + # Record 6: Debug message + assert records[6].levelno == logging.DEBUG + assert records[6].message == 'Debug message' - # Record 7: Warning message - assert records[7].levelno == logging.WARNING - assert records[7].message == 'Warning message' + # Record 7: Info message + assert records[7].levelno == logging.INFO + assert records[7].message == 'Info message' - # Record 8: Error message - assert records[8].levelno == logging.ERROR - assert records[8].message == 'Error message' + # Record 8: Warning message + assert records[8].levelno == logging.WARNING + assert records[8].message == 'Warning message' - # Record 9: Exception message with traceback (ValueError) + # Record 9: Error message assert records[9].levelno == logging.ERROR - 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' + 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' From f2f0ca7f7ba37699e362a30a99661bb54d301028 Mon Sep 17 00:00:00 2001 From: stepskop Date: Thu, 27 Nov 2025 14:31:46 +0100 Subject: [PATCH 3/5] Revert "feat: log the actor permission on actor initialization" This reverts commit 2457458a69a252f5700c8d6a2e32df77b8bc1a41. --- src/apify/_actor.py | 8 ---- tests/unit/actor/test_actor_log.py | 74 ++++++++++++++---------------- 2 files changed, 35 insertions(+), 47 deletions(-) diff --git a/src/apify/_actor.py b/src/apify/_actor.py index b7c6b972..3fc99128 100644 --- a/src/apify/_actor.py +++ b/src/apify/_actor.py @@ -192,14 +192,6 @@ async def __aenter__(self) -> Self: # Mark initialization as complete and update global state. self._is_initialized = True - - self.log.info( - 'Permissions', - extra={ - 'actor_permission_level': self.get_env().get('actor_permission_level'), - }, - ) - return self async def __aexit__( diff --git a/tests/unit/actor/test_actor_log.py b/tests/unit/actor/test_actor_log.py index 857655a0..7bff15b4 100644 --- a/tests/unit/actor/test_actor_log.py +++ b/tests/unit/actor/test_actor_log.py @@ -39,7 +39,7 @@ async def test_actor_logs_messages_correctly(caplog: pytest.LogCaptureFixture) - records = caplog.records # Expected number of log records - assert len(records) == 14 + assert len(records) == 13 # Record 0: Initializing Actor assert records[0].levelno == logging.INFO @@ -61,46 +61,42 @@ async def test_actor_logs_messages_correctly(caplog: pytest.LogCaptureFixture) - assert records[4].levelno == logging.DEBUG assert records[4].message == 'Charging manager initialized' - # Record 5: Actor Permissions - assert records[5].levelno == logging.INFO - assert records[5].message == 'Permissions' + # Record 5: Debug message + assert records[5].levelno == logging.DEBUG + 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' From a0b42d59a2f9c16cceed8700778c01ce75abc811 Mon Sep 17 00:00:00 2001 From: stepskop Date: Thu, 27 Nov 2025 17:56:45 +0100 Subject: [PATCH 4/5] chore: use single alias --- src/apify/_configuration.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/apify/_configuration.py b/src/apify/_configuration.py index 53e2c23c..d5952d62 100644 --- a/src/apify/_configuration.py +++ b/src/apify/_configuration.py @@ -64,10 +64,7 @@ class Configuration(CrawleeConfiguration): actor_permission_level: Annotated[ str | None, Field( - validation_alias=AliasChoices( - 'actor_permission_level', - 'apify_actor_permission_level', - ), + alias='actor_permission_level', description='Permission level the Actor is run under.', ), ] = None From 7b5c3c65f1fafc6914582033322c90714fd1c25b Mon Sep 17 00:00:00 2001 From: stepskop Date: Fri, 28 Nov 2025 11:48:55 +0100 Subject: [PATCH 5/5] chore: no need for alias as the property name is the same --- src/apify/_configuration.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/apify/_configuration.py b/src/apify/_configuration.py index d5952d62..d3184562 100644 --- a/src/apify/_configuration.py +++ b/src/apify/_configuration.py @@ -64,7 +64,6 @@ class Configuration(CrawleeConfiguration): actor_permission_level: Annotated[ str | None, Field( - alias='actor_permission_level', description='Permission level the Actor is run under.', ), ] = None