diff --git a/src/aws_durable_execution_sdk_python/lambda_service.py b/src/aws_durable_execution_sdk_python/lambda_service.py index 51a0049..aa78e4e 100644 --- a/src/aws_durable_execution_sdk_python/lambda_service.py +++ b/src/aws_durable_execution_sdk_python/lambda_service.py @@ -36,6 +36,15 @@ logger = logging.getLogger(__name__) +def _is_in_var_dir(module_file: str = __file__) -> bool: + """Return True if this SDK is installed under /var/lang/. + + Lambda bundled Python runtimes install packages at + /var/lang/lib/pythonX.Y/site-packages/. + """ + return module_file.startswith("/var/lang/") + + # region model class OperationAction(Enum): START = "START" @@ -1061,7 +1070,7 @@ def initialize_client(cls) -> LambdaClient: config=Config( connect_timeout=5, read_timeout=50, - user_agent_extra=f"aws-durable-execution-sdk-python/{__version__}", + user_agent_extra=f"aws-durable-execution-sdk-python/{__version__}{'-bundled' if _is_in_var_dir() else ''}", ), ) return cls(client=cls._cached_boto_client) diff --git a/tests/lambda_service_test.py b/tests/lambda_service_test.py index 9c9def5..626be29 100644 --- a/tests/lambda_service_test.py +++ b/tests/lambda_service_test.py @@ -38,6 +38,7 @@ TimestampConverter, WaitDetails, WaitOptions, + _is_in_var_dir, ) @@ -2047,6 +2048,76 @@ def test_lambda_client_initialize_client_no_endpoint( assert isinstance(client, LambdaClient) +@patch( + "aws_durable_execution_sdk_python.lambda_service._is_in_var_dir", + return_value=True, +) +@patch("boto3.client") +def test_lambda_client_user_agent_runtime_bundled( + mock_boto_client, _mock_is_in_var_dir, reset_lambda_client_cache +): + """user_agent_extra includes -bundled when SDK is in /var/lang/.""" + mock_client = Mock() + mock_boto_client.return_value = mock_client + + client = LambdaClient.initialize_client() + + call_args = mock_boto_client.call_args + config = call_args[1]["config"] + assert ( + config.user_agent_extra + == f"aws-durable-execution-sdk-python/{__version__}-bundled" + ) + assert isinstance(client, LambdaClient) + + +@patch( + "aws_durable_execution_sdk_python.lambda_service._is_in_var_dir", + return_value=False, +) +@patch("boto3.client") +def test_lambda_client_user_agent_not_runtime_bundled( + mock_boto_client, _mock_is_in_var_dir, reset_lambda_client_cache +): + """user_agent_extra omits -bundled when SDK is not in /var/lang.""" + mock_client = Mock() + mock_boto_client.return_value = mock_client + + client = LambdaClient.initialize_client() + + call_args = mock_boto_client.call_args + config = call_args[1]["config"] + assert config.user_agent_extra == f"aws-durable-execution-sdk-python/{__version__}" + assert isinstance(client, LambdaClient) + + +@pytest.mark.parametrize( + "path,expected", + [ + # Lambda bundled runtime site-packages + ( + "/var/lang/lib/python3.13/site-packages/aws_durable_execution_sdk_python/lambda_service.py", + True, + ), + ( + "/var/lang/lib/python3.12/site-packages/aws_durable_execution_sdk_python/lambda_service.py", + True, + ), + # Customer deployment package + ("/var/task/aws_durable_execution_sdk_python/lambda_service.py", False), + # Lambda Layer + ("/opt/python/aws_durable_execution_sdk_python/lambda_service.py", False), + # Trailing-slash guard: /var/langsurprise must not match + ("/var/langsurprise/lib/python3.13/site-packages/x.py", False), + # Local dev + ("/Users/me/project/.venv/lib/python3.12/site-packages/x.py", False), + ("", False), + ], +) +def test_is_in_var_dir(path, expected): + assert _is_in_var_dir(path) is expected + + def test_lambda_client_checkpoint_with_non_none_client_token(): """Test LambdaClient.checkpoint with non-None client_token.""" mock_client = Mock()