Skip to content

Commit beb9b54

Browse files
🐛 Fix named args
1 parent 346aa66 commit beb9b54

4 files changed

Lines changed: 45 additions & 5 deletions

File tree

src/core/extractors.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -399,26 +399,28 @@ export function importExtractor(node: Node): ImportInfo | null {
399399
const namedImports: ImportedName[] = []
400400

401401
if (node.type === "import_statement") {
402+
let modulePath = ""
402403
// Handle aliased imports: "import fastapi as f"
403404
for (const aliased of findNodesByType(node, "aliased_import")) {
404405
const nameNode = aliased.childForFieldName("name")
405406
const aliasNode = aliased.childForFieldName("alias")
406407
if (nameNode) {
408+
if (!modulePath) modulePath = nameNode.text // preserve full dotted path
407409
const alias = aliasNode?.text ?? null
408410
names.push(alias ?? nameNode.text)
409411
namedImports.push({ name: nameNode.text, alias })
410412
}
411413
}
412-
// Non-aliased: "import fastapi"
414+
// Non-aliased: "import fastapi" or "import fastapi.routing"
413415
const nameNodes = findNodesByType(node, "dotted_name")
414416
for (const nameNode of nameNodes) {
415417
if (!hasAncestor(nameNode, "aliased_import")) {
418+
if (!modulePath) modulePath = nameNode.text // preserve full dotted path
416419
const firstName = nameNode.text.split(".")[0]
417420
names.push(firstName)
418421
namedImports.push({ name: firstName, alias: null })
419422
}
420423
}
421-
const modulePath = namedImports[0]?.name ?? ""
422424
return {
423425
modulePath,
424426
names,
@@ -465,7 +467,7 @@ export function importExtractor(node: Node): ImportInfo | null {
465467
* app.get("/users", response_model=List[User]) → position 0 = string node "/users"
466468
* app.get(path="/users", response_model=List[User]) → keyword "path" = string node "/users"
467469
*/
468-
function resolveArgNode(
470+
export function resolveArgNode(
469471
args: Node[],
470472
position: number,
471473
keywordName: string,

src/test/core/extractors.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,20 @@ router = f.APIRouter(prefix="/items")
649649
assert.strictEqual(result.isRelative, false)
650650
})
651651

652+
test("preserves full dotted modulePath for import fastapi.routing", () => {
653+
const code = "import fastapi.routing"
654+
const tree = parse(code)
655+
const imports = findNodesByType(tree.rootNode, "import_statement")
656+
const result = importExtractor(imports[0])
657+
658+
assert.ok(result)
659+
assert.strictEqual(result.modulePath, "fastapi.routing")
660+
assert.deepStrictEqual(result.names, ["fastapi"])
661+
assert.deepStrictEqual(result.namedImports, [
662+
{ name: "fastapi", alias: null },
663+
])
664+
})
665+
652666
test("extracts aliased module import (import fastapi as f)", () => {
653667
const code = "import fastapi as f"
654668
const tree = parse(code)

src/test/providers/testCodeLensProvider.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,22 @@ def test_something():
287287
assert.strictEqual(lenses.length, 0)
288288
})
289289

290+
test("creates CodeLens for url= keyword argument", async () => {
291+
const app = createMockApp([createRoute("GET", "/users")])
292+
provider.setApps([app])
293+
294+
const doc = await vscode.workspace.openTextDocument({
295+
content: `
296+
def test_get_users():
297+
response = client.get(url="/users")
298+
`,
299+
language: "python",
300+
})
301+
const lenses = provider.provideCodeLenses(doc)
302+
assert.strictEqual(lenses.length, 1)
303+
assert.ok(lenses[0].command?.title.includes("/users"))
304+
})
305+
290306
test("ignores calls with no arguments", async () => {
291307
const app = createMockApp([createRoute("GET", "/users")])
292308
provider.setApps([app])

src/vscode/testCodeLensProvider.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ import {
1414
Uri,
1515
} from "vscode"
1616
import type { Node } from "web-tree-sitter"
17-
import { extractPathFromNode, findNodesByType } from "../core/extractors"
17+
import {
18+
extractPathFromNode,
19+
findNodesByType,
20+
resolveArgNode,
21+
} from "../core/extractors"
1822
import { ROUTE_METHODS } from "../core/internal"
1923
import type { Parser } from "../core/parser"
2024
import {
@@ -134,7 +138,11 @@ export class TestCodeLensProvider implements CodeLensProvider {
134138
continue
135139
}
136140

137-
const pathArg = args[0]
141+
const pathArg = resolveArgNode(args, 0, "url")
142+
143+
if (!pathArg) {
144+
continue
145+
}
138146
// extractPathFromNode always returns a non-empty string for valid AST nodes
139147
const path = extractPathFromNode(pathArg)
140148

0 commit comments

Comments
 (0)