Skip to content

Commit b5d9468

Browse files
jsamuel1zastrowm
andauthored
fix: emit deprecation warning only when deprecated aliases are accessed (#1380)
Previously, the deprecation warning was emitted at module import time, which triggered whenever `strands` was imported because other modules import from `experimental.hooks`. Changed to use `__getattr__` to lazily emit the warning only when the deprecated aliases (BeforeToolInvocationEvent, AfterToolInvocationEvent, BeforeModelInvocationEvent, AfterModelInvocationEvent) are actually accessed. Fixes #1236 --------- Co-authored-by: Mackenzie Zastrow <zastrowm@users.noreply.github.com>
1 parent db01eee commit b5d9468

File tree

3 files changed

+45
-24
lines changed

3 files changed

+45
-24
lines changed

src/strands/experimental/hooks/__init__.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
11
"""Experimental hook functionality that has not yet reached stability."""
22

3+
from typing import Any
4+
35
from .events import (
4-
AfterModelInvocationEvent,
5-
AfterToolInvocationEvent,
6-
BeforeModelInvocationEvent,
7-
BeforeToolInvocationEvent,
6+
BidiAfterConnectionRestartEvent,
87
BidiAfterInvocationEvent,
98
BidiAfterToolCallEvent,
109
BidiAgentInitializedEvent,
10+
BidiBeforeConnectionRestartEvent,
1111
BidiBeforeInvocationEvent,
1212
BidiBeforeToolCallEvent,
1313
BidiInterruptionEvent,
1414
BidiMessageAddedEvent,
1515
)
1616

17+
# Deprecated aliases are accessed via __getattr__ to emit warnings only on use
18+
19+
20+
def __getattr__(name: str) -> Any:
21+
from . import events
22+
23+
return getattr(events, name)
24+
25+
1726
__all__ = [
1827
"BeforeToolInvocationEvent",
1928
"AfterToolInvocationEvent",
@@ -27,4 +36,6 @@
2736
"BidiBeforeToolCallEvent",
2837
"BidiAfterToolCallEvent",
2938
"BidiInterruptionEvent",
39+
"BidiBeforeConnectionRestartEvent",
40+
"BidiAfterConnectionRestartEvent",
3041
]

src/strands/experimental/hooks/events.py

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import warnings
77
from dataclasses import dataclass
8-
from typing import TYPE_CHECKING, Any, Literal, TypeAlias
8+
from typing import TYPE_CHECKING, Any, Literal
99

1010
from ...hooks.events import AfterModelCallEvent, AfterToolCallEvent, BeforeModelCallEvent, BeforeToolCallEvent
1111
from ...hooks.registry import BaseHookEvent
@@ -16,17 +16,25 @@
1616
from ..bidi.agent.agent import BidiAgent
1717
from ..bidi.models import BidiModelTimeoutError
1818

19-
warnings.warn(
20-
"BeforeModelCallEvent, AfterModelCallEvent, BeforeToolCallEvent, and AfterToolCallEvent are no longer experimental."
21-
"Import from strands.hooks instead.",
22-
DeprecationWarning,
23-
stacklevel=2,
24-
)
25-
26-
BeforeToolInvocationEvent: TypeAlias = BeforeToolCallEvent
27-
AfterToolInvocationEvent: TypeAlias = AfterToolCallEvent
28-
BeforeModelInvocationEvent: TypeAlias = BeforeModelCallEvent
29-
AfterModelInvocationEvent: TypeAlias = AfterModelCallEvent
19+
# Deprecated aliases - warning emitted on access via __getattr__
20+
_DEPRECATED_ALIASES = {
21+
"BeforeToolInvocationEvent": BeforeToolCallEvent,
22+
"AfterToolInvocationEvent": AfterToolCallEvent,
23+
"BeforeModelInvocationEvent": BeforeModelCallEvent,
24+
"AfterModelInvocationEvent": AfterModelCallEvent,
25+
}
26+
27+
28+
def __getattr__(name: str) -> Any:
29+
if name in _DEPRECATED_ALIASES:
30+
warnings.warn(
31+
f"{name} has been moved to production with an updated name. "
32+
f"Use {_DEPRECATED_ALIASES[name].__name__} from strands.hooks instead.",
33+
DeprecationWarning,
34+
stacklevel=2,
35+
)
36+
return _DEPRECATED_ALIASES[name]
37+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
3038

3139

3240
# BidiAgent Hook Events

tests/strands/experimental/hooks/test_hook_aliases.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,18 +112,20 @@ def experimental_callback(event: BeforeToolInvocationEvent):
112112
assert received_event is test_event
113113

114114

115-
def test_deprecation_warning_on_import(captured_warnings):
116-
"""Verify that importing from experimental module emits deprecation warning."""
115+
def test_deprecation_warning_on_access(captured_warnings):
116+
"""Verify that accessing deprecated aliases emits deprecation warning."""
117+
import strands.experimental.hooks.events as events_module
117118

118-
module = sys.modules.get("strands.experimental.hooks.events")
119-
if module:
120-
importlib.reload(module)
121-
else:
122-
importlib.import_module("strands.experimental.hooks.events")
119+
# Clear any existing warnings
120+
captured_warnings.clear()
121+
122+
# Access a deprecated alias - this should trigger the warning
123+
_ = events_module.BeforeToolInvocationEvent
123124

124125
assert len(captured_warnings) == 1
125126
assert issubclass(captured_warnings[0].category, DeprecationWarning)
126-
assert "are no longer experimental" in str(captured_warnings[0].message)
127+
assert "BeforeToolInvocationEvent" in str(captured_warnings[0].message)
128+
assert "BeforeToolCallEvent" in str(captured_warnings[0].message)
127129

128130

129131
def test_deprecation_warning_on_import_only_for_experimental(captured_warnings):

0 commit comments

Comments
 (0)