From cd69e9349e56362ee5d0dca4531e9cc7eb229cc7 Mon Sep 17 00:00:00 2001 From: Tirth Kanani Date: Sun, 14 Jun 2026 18:01:45 +0100 Subject: [PATCH] fix(python-extractor): capture type-annotated *args/**kwargs parameters The `typed_parameter` case in extractParams assumed the parameter name was a direct `identifier` child. For type-annotated variadic parameters such as `def f(*args: int, **kwargs: str)`, tree-sitter-python wraps the splat in a `typed_parameter` whose first child is a `list_splat_pattern` (`*args`) or `dictionary_splat_pattern` (`**kwargs`), with the identifier nested inside. Since findChild is shallow, it returned null and the parameter was silently dropped. Now the `typed_parameter` case checks for a nested splat pattern first and prefixes the name with `*`/`**` accordingly, falling back to the plain identifier lookup otherwise. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../__tests__/python-extractor.test.ts | 18 ++++++++++++++++++ .../src/plugins/extractors/python-extractor.ts | 12 ++++++++++++ 2 files changed, 30 insertions(+) diff --git a/understand-anything-plugin/packages/core/src/plugins/extractors/__tests__/python-extractor.test.ts b/understand-anything-plugin/packages/core/src/plugins/extractors/__tests__/python-extractor.test.ts index 3929b05d..699db08a 100644 --- a/understand-anything-plugin/packages/core/src/plugins/extractors/__tests__/python-extractor.test.ts +++ b/understand-anything-plugin/packages/core/src/plugins/extractors/__tests__/python-extractor.test.ts @@ -114,6 +114,24 @@ def flexible(*args, **kwargs): parser.delete(); }); + it("extracts type-annotated *args and **kwargs", () => { + const { tree, parser, root } = parse(` +def f(a: int, *args: str, b: int = 0, **kwargs: bool): + pass +`); + const result = extractor.extractStructure(root); + + expect(result.functions[0].params).toEqual([ + "a", + "*args", + "b", + "**kwargs", + ]); + + tree.delete(); + parser.delete(); + }); + it("extracts decorated functions", () => { const { tree, parser, root } = parse(` @decorator diff --git a/understand-anything-plugin/packages/core/src/plugins/extractors/python-extractor.ts b/understand-anything-plugin/packages/core/src/plugins/extractors/python-extractor.ts index 83ae76cb..e6f41ae4 100644 --- a/understand-anything-plugin/packages/core/src/plugins/extractors/python-extractor.ts +++ b/understand-anything-plugin/packages/core/src/plugins/extractors/python-extractor.ts @@ -26,6 +26,18 @@ function extractParams(paramsNode: TreeSitterNode | null): string[] { break; case "typed_parameter": { + const splat = findChild(child, "list_splat_pattern"); + if (splat) { + const sid = findChild(splat, "identifier"); + if (sid) params.push("*" + sid.text); + break; + } + const dsplat = findChild(child, "dictionary_splat_pattern"); + if (dsplat) { + const did = findChild(dsplat, "identifier"); + if (did) params.push("**" + did.text); + break; + } const ident = findChild(child, "identifier"); if (ident && ident.text !== "self" && ident.text !== "cls") { params.push(ident.text);