From 11ffc9ae3b8503f26601f259e1dbfd0d6cf27d2b Mon Sep 17 00:00:00 2001 From: Vyacheslav-Tomashevskiy Date: Wed, 22 Jul 2026 04:48:40 +0200 Subject: [PATCH] fix(camera): blur CLI/self-verify calls undefined edge_detect -> NameError on NPU blur_pipeline.py defines the design function , but _run_and_verify() and main() still referenced (a leftover from the AMD example this was adapted from), which is never defined or imported in the module. The module imports aie.iron at the top, so a host without the NPU toolchain fails at that import first and CI's import-boundary check stays green. But on a real NPU box, `python camera/blur_pipeline.py` reaches main() and crashes with `NameError: name 'edge_detect' is not defined` instead of running the blur design or its self-verify. Rename the two runtime references to `blur` (the function the daemon and test_blur.py already import). Add a pure-AST regression test (no aie/numpy needed, runs anywhere) asserting the design symbol the CLI hands to the NPU is one the module actually defines. --- camera/blur_pipeline.py | 4 +- camera/test_blur_pipeline_cli_symbol.py | 68 +++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 camera/test_blur_pipeline_cli_symbol.py diff --git a/camera/blur_pipeline.py b/camera/blur_pipeline.py index 540b411..2907d77 100644 --- a/camera/blur_pipeline.py +++ b/camera/blur_pipeline.py @@ -317,7 +317,7 @@ def _run_and_verify(opts): b_t = iron.zeros(16 * 16, dtype=np.int32, device="npu") out_t = iron.zeros(tensor_size, dtype=np.int8, device="npu") - edge_detect(in_t, b_t, out_t, **_compile_kwargs(opts)) + blur(in_t, b_t, out_t, **_compile_kwargs(opts)) in_uint8 = in_np.view(np.uint8) expected_uint8 = _edge_detect_ref(in_uint8, opts.height, opts.width) @@ -339,7 +339,7 @@ def _run_and_verify(opts): def main(): opts = _make_argparser().parse_args() run_design_cli( - edge_detect, + blur, opts, compile_kwargs=_compile_kwargs, run_and_verify=_run_and_verify, diff --git a/camera/test_blur_pipeline_cli_symbol.py b/camera/test_blur_pipeline_cli_symbol.py new file mode 100644 index 0000000..1b9d653 --- /dev/null +++ b/camera/test_blur_pipeline_cli_symbol.py @@ -0,0 +1,68 @@ +# Regression guard for the blur_pipeline CLI / self-verify design symbol. +# +# blur_pipeline.py was adapted from AMD's `edge_detect` IRON example. The design +# it actually defines is `blur` (see `def blur(...)` and +# `from blur_pipeline import blur` in npu_camera_daemon.py / test_blur.py), but +# `_run_and_verify()` and `main()` still called the old name `edge_detect`, +# which is never defined or imported in this module. Because the module imports +# `aie.iron` at the top, a host without the NPU toolchain fails at that import +# first (so CI's import-boundary check stays green) -- but on a real NPU box +# `python blur_pipeline.py` reaches main() and dies with +# `NameError: name 'edge_detect' is not defined` instead of running the design. +# +# This test is pure-AST (no aie/NPU toolchain, no numpy) so it runs anywhere, +# and it asserts the design symbol the CLI hands to the NPU is one the module +# actually defines. +import ast +import builtins +import os + +HERE = os.path.dirname(os.path.abspath(__file__)) +SRC = os.path.join(HERE, "blur_pipeline.py") + + +def _module_bound_names(tree): + names = set() + for node in tree.body: + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)): + names.add(node.name) + elif isinstance(node, (ast.Import, ast.ImportFrom)): + for alias in node.names: + names.add(alias.asname or alias.name.split(".")[0]) + elif isinstance(node, ast.Assign): + for tgt in node.targets: + if isinstance(tgt, ast.Name): + names.add(tgt.id) + return names + + +def _cli_referenced_names(tree): + """Bare names called (or handed to run_design_cli) inside the CLI paths.""" + refs = set() + for node in ast.walk(tree): + if isinstance(node, ast.FunctionDef) and node.name in ("_run_and_verify", "main"): + for sub in ast.walk(node): + if isinstance(sub, ast.Call) and isinstance(sub.func, ast.Name): + refs.add(sub.func.id) + # run_design_cli(, ...) -> first positional arg is the design fn + if sub.func.id == "run_design_cli" and sub.args and isinstance(sub.args[0], ast.Name): + refs.add(sub.args[0].id) + return refs + + +def test_cli_design_symbol_is_defined(): + tree = ast.parse(open(SRC, encoding="utf-8").read()) + bound = _module_bound_names(tree) + assert "blur" in bound, "expected the design function `blur` to be defined" + refs = _cli_referenced_names(tree) + assert "edge_detect" not in refs, ( + "blur_pipeline.py references undefined `edge_detect` (leftover from the " + "AMD edge_detect example) in its CLI/self-verify path; it should call `blur`" + ) + undefined = {r for r in refs if r not in bound and not hasattr(builtins, r)} + assert not undefined, f"CLI/self-verify references undefined names: {sorted(undefined)}" + + +if __name__ == "__main__": + test_cli_design_symbol_is_defined() + print("OK: blur_pipeline CLI design symbol is defined")