Skip to content

Commit 0165378

Browse files
authored
fix(observability): explicitly initialize LifecycleDispatcher guard attributes for mypyc compatibility (#254)
Fixes `AttributeError: 'LifecycleDispatcher' object has no attribute 'has_pool_create'` exception raised on some drivers after the telemetry integration was released.
1 parent ef9c21b commit 0165378

File tree

2 files changed

+42
-1
lines changed

2 files changed

+42
-1
lines changed

sqlspec/observability/_dispatcher.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,31 @@
3737
class LifecycleDispatcher:
3838
"""Dispatches lifecycle hooks with guard flags and diagnostics counters."""
3939

40-
__slots__ = ("_hooks", "_counters", *GUARD_ATTRS)
40+
__slots__ = (
41+
"_counters",
42+
"_hooks",
43+
"has_connection_create",
44+
"has_connection_destroy",
45+
"has_error",
46+
"has_pool_create",
47+
"has_pool_destroy",
48+
"has_query_complete",
49+
"has_query_start",
50+
"has_session_end",
51+
"has_session_start",
52+
)
4153

4254
def __init__(self, hooks: "dict[str, Iterable[Any]] | None" = None) -> None:
55+
self.has_pool_create = False
56+
self.has_pool_destroy = False
57+
self.has_connection_create = False
58+
self.has_connection_destroy = False
59+
self.has_session_start = False
60+
self.has_session_end = False
61+
self.has_query_start = False
62+
self.has_query_complete = False
63+
self.has_error = False
64+
4365
normalized: dict[LifecycleEvent, tuple[Any, ...]] = {}
4466
for event_name, guard_attr in zip(EVENT_ATTRS, GUARD_ATTRS, strict=False):
4567
callables = hooks.get(event_name) if hooks else None

tests/unit/test_observability.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,25 @@ def observer(_event: Any) -> None:
216216
assert observer_called == [] # observers run via runtime, dispatcher unaffected
217217

218218

219+
def test_lifecycle_dispatcher_guard_attributes_always_accessible() -> None:
220+
"""All guard attributes should be accessible even with no hooks (mypyc compatibility)."""
221+
222+
dispatcher = LifecycleDispatcher(None)
223+
assert dispatcher.has_pool_create is False
224+
assert dispatcher.has_pool_destroy is False
225+
assert dispatcher.has_connection_create is False
226+
assert dispatcher.has_connection_destroy is False
227+
assert dispatcher.has_session_start is False
228+
assert dispatcher.has_session_end is False
229+
assert dispatcher.has_query_start is False
230+
assert dispatcher.has_query_complete is False
231+
assert dispatcher.has_error is False
232+
233+
dispatcher_with_hooks = LifecycleDispatcher(cast("dict[str, Iterable[Any]]", {"on_query_start": [lambda ctx: ctx]}))
234+
assert dispatcher_with_hooks.has_query_start is True
235+
assert dispatcher_with_hooks.has_pool_create is False
236+
237+
219238
def test_lifecycle_dispatcher_counts_events() -> None:
220239
"""Lifecycle dispatcher should count emitted events for diagnostics."""
221240

0 commit comments

Comments
 (0)