Skip to content

Commit bd5bf2b

Browse files
rickstaaclaude
andcommitted
fix(live): warn when @pipeline decorates a nested definition
Nested classes/functions produce a dotted __qualname__ which breaks the loader's getattr lookup. Log a warning so developers know to keep @pipeline at the top level. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 428dbdf commit bd5bf2b

1 file changed

Lines changed: 12 additions & 6 deletions

File tree

runner/src/runner/live/pipelines/create.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""@pipeline decorator for creating live pipelines.
22
33
Lifecycle hooks:
4-
54
* ``prepare_models``: called at build time (model download, TensorRT compile)
65
* ``on_ready``: called once at startup
76
* ``transform``: called per frame
@@ -10,6 +9,7 @@
109
"""
1110

1211
import asyncio
12+
import inspect
1313
import logging
1414
from typing import Optional, Type
1515

@@ -20,7 +20,7 @@
2020

2121
async def _invoke(func, *args, **kwargs):
2222
"""Call a function, handling both async and sync. Sync runs in a thread pool."""
23-
if asyncio.iscoroutinefunction(func):
23+
if inspect.iscoroutinefunction(func):
2424
return await func(*args, **kwargs)
2525
return await asyncio.to_thread(func, *args, **kwargs)
2626

@@ -56,7 +56,14 @@ async def transform(self, frame, p):
5656
user_cls = func_or_class
5757
else:
5858
raise TypeError(
59-
f"@pipeline can only decorate a function or class, got {type(func_or_class)}"
59+
"@pipeline can only decorate a function or class, got "
60+
f"{type(func_or_class)}"
61+
)
62+
63+
if "." in user_cls.__qualname__:
64+
logging.warning(
65+
f"@pipeline decorating nested '{user_cls.__qualname__}' — "
66+
f"this may cause import issues if the enclosing scope is not accessible"
6067
)
6168

6269
pipeline_cls = _build_pipeline(user_cls, params_cls)
@@ -73,6 +80,7 @@ async def transform(self, frame, p):
7380
params_cls=params_import,
7481
initial_params=initial_params or {},
7582
)
83+
7684
return pipeline_cls
7785

7886
return decorator
@@ -147,9 +155,7 @@ async def prepare_models(cls):
147155
if has_prepare:
148156
await _invoke(user_cls.prepare_models)
149157
else:
150-
logging.info(
151-
f"{label} pipeline does not require model preparation"
152-
)
158+
logging.info(f"{label} pipeline does not require model preparation")
153159

154160
# Keep the original decorated name so importlib can find it via
155161
# getattr(module, name) after the decorator replaces the symbol.

0 commit comments

Comments
 (0)