Skip to content

Commit c93073f

Browse files
authored
feat(plugin): stabilize plugin interface and otel package (#625)
* feat(plugin): stabilize plugin interface and otel package * docs(plugin): mark payload fields experimental
1 parent 479832b commit c93073f

6 files changed

Lines changed: 60 additions & 15 deletions

File tree

packages/aws-durable-execution-sdk-python-otel/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ license = "Apache-2.0"
1212
keywords = ["opentelemetry", "tracing", "observability", "durable-execution"]
1313
authors = [{ name = "AWS durable-execution-dev", email = "durable-execution-dev@amazon.com" }]
1414
classifiers = [
15-
"Development Status :: 4 - Beta",
15+
"Development Status :: 5 - Production/Stable",
1616
"Programming Language :: Python",
1717
"Programming Language :: Python :: 3.11",
1818
"Programming Language :: Python :: 3.12",
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import tomllib
2+
from pathlib import Path
3+
4+
5+
PACKAGE_ROOT = Path(__file__).resolve().parents[1]
6+
7+
8+
def test_package_is_marked_production_stable() -> None:
9+
with (PACKAGE_ROOT / "pyproject.toml").open("rb") as pyproject:
10+
classifiers = tomllib.load(pyproject)["project"]["classifiers"]
11+
12+
assert "Development Status :: 5 - Production/Stable" in classifiers
13+
assert "Development Status :: 4 - Beta" not in classifiers

packages/aws-durable-execution-sdk-python/src/aws_durable_execution_sdk_python/execution.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import functools
55
import json
66
import logging
7-
import warnings
87
from concurrent.futures import ThreadPoolExecutor
98
from dataclasses import dataclass, field
109
from typing import TYPE_CHECKING, Any
@@ -178,8 +177,7 @@ def durable_execution(
178177
Args:
179178
func: The user function to decorate
180179
boto3_client: Optional boto3 Lambda client to use
181-
plugins: Optional list of plugins to use (EXPERIMENTAL: This
182-
feature has known issues and this parameter may change or be removed.)
180+
plugins: Optional list of instrumentation plugins to use
183181
"""
184182
# Decorator called with parameters
185183
if func is None:
@@ -190,13 +188,6 @@ def durable_execution(
190188

191189
logger.debug("Starting durable execution handler...")
192190

193-
if plugins:
194-
warnings.warn(
195-
"The 'plugins' parameter is provisional and may be altered or removed.",
196-
category=FutureWarning,
197-
stacklevel=2, # point the warning to the caller of durable_execution
198-
)
199-
200191
plugin_executor = PluginExecutor(load_configured_plugins(plugins))
201192

202193
@plugin_executor.handle_durable_output

packages/aws-durable-execution-sdk-python/src/aws_durable_execution_sdk_python/plugin.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,18 @@ class OperationInfo:
6868
is_replayed: bool
6969
status: OperationStatus
7070
end_time: datetime.datetime | None = field(default=None, kw_only=True)
71-
result: str | None = field(default=None, kw_only=True)
72-
error: ErrorObject | None = field(default=None, kw_only=True)
71+
result: str | None = field(
72+
default=None,
73+
kw_only=True,
74+
metadata={"experimental": True},
75+
)
76+
"""EXPERIMENTAL: The serialized operation result, when available."""
77+
error: ErrorObject | None = field(
78+
default=None,
79+
kw_only=True,
80+
metadata={"experimental": True},
81+
)
82+
"""EXPERIMENTAL: The operation error, when available."""
7383
attempt: int | None = field(default=None, kw_only=True)
7484

7585
@staticmethod
@@ -175,7 +185,11 @@ class InvocationStartInfo(InvocationInfo):
175185
@dataclass(frozen=True)
176186
class InvocationEndInfo(InvocationInfo):
177187
status: InvocationStatus = field(kw_only=True)
178-
error: ErrorObject | None = None
188+
error: ErrorObject | None = field(
189+
default=None,
190+
metadata={"experimental": True},
191+
)
192+
"""EXPERIMENTAL: The invocation error, when available."""
179193

180194
@classmethod
181195
def from_durable_execution_invocation_output(

packages/aws-durable-execution-sdk-python/tests/execution_test.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import datetime
44
import json
55
import time
6+
import warnings
67
from typing import Any
78
from unittest.mock import Mock, patch
89

@@ -2914,12 +2915,13 @@ def test_durable_execution_loads_plugins_when_handler_is_initialized():
29142915
resolved_plugin = _RecordingPlugin()
29152916

29162917
with (
2918+
warnings.catch_warnings(),
29172919
patch(
29182920
"aws_durable_execution_sdk_python.execution.load_configured_plugins",
29192921
return_value=[explicit_plugin, resolved_plugin],
29202922
) as load_plugins,
2921-
pytest.warns(FutureWarning),
29222923
):
2924+
warnings.simplefilter("error", FutureWarning)
29232925

29242926
@durable_execution(plugins=[explicit_plugin])
29252927
def test_handler(event: Any, context: DurableContext) -> dict:

packages/aws-durable-execution-sdk-python/tests/plugin_test.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import datetime
22
import logging
33
import unittest
4+
from dataclasses import fields
45
from unittest.mock import MagicMock
56

67
from aws_durable_execution_sdk_python.identifier import OperationIdentifier
@@ -18,6 +19,7 @@
1819
from aws_durable_execution_sdk_python.plugin import (
1920
DurableInstrumentationPlugin,
2021
InvocationEndInfo,
22+
InvocationInfo,
2123
InvocationStartInfo,
2224
OperationChangeInfo,
2325
OperationEndInfo,
@@ -110,6 +112,29 @@
110112

111113

112114
class TestDataClasses(unittest.TestCase):
115+
def test_payload_fields_are_marked_experimental(self):
116+
plugin_info_types = (
117+
OperationInfo,
118+
OperationStartInfo,
119+
OperationEndInfo,
120+
OperationChangeInfo,
121+
UserFunctionStartInfo,
122+
UserFunctionEndInfo,
123+
InvocationInfo,
124+
InvocationStartInfo,
125+
InvocationEndInfo,
126+
)
127+
payload_field_terms = ("input", "output", "result", "error")
128+
129+
for info_type in plugin_info_types:
130+
for info_field in fields(info_type):
131+
if any(term in info_field.name for term in payload_field_terms):
132+
self.assertIs(
133+
info_field.metadata.get("experimental"),
134+
True,
135+
f"{info_type.__name__}.{info_field.name}",
136+
)
137+
113138
def test_operation_start_info(self):
114139
self.assertEqual(OPERATION_START_INFO.sub_type, OperationSubType.CALLBACK)
115140
self.assertEqual(OPERATION_START_INFO.name, "my-op")

0 commit comments

Comments
 (0)