driftdetect-core 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/boundaries/boundary-scanner.d.ts +76 -0
- package/dist/boundaries/boundary-scanner.d.ts.map +1 -0
- package/dist/boundaries/boundary-scanner.js +801 -0
- package/dist/boundaries/boundary-scanner.js.map +1 -0
- package/dist/boundaries/data-access-learner.d.ts +126 -0
- package/dist/boundaries/data-access-learner.d.ts.map +1 -0
- package/dist/boundaries/data-access-learner.js +486 -0
- package/dist/boundaries/data-access-learner.js.map +1 -0
- package/dist/boundaries/index.d.ts +6 -0
- package/dist/boundaries/index.d.ts.map +1 -1
- package/dist/boundaries/index.js +6 -0
- package/dist/boundaries/index.js.map +1 -1
- package/dist/boundaries/security-prioritizer.d.ts +118 -0
- package/dist/boundaries/security-prioritizer.d.ts.map +1 -0
- package/dist/boundaries/security-prioritizer.js +316 -0
- package/dist/boundaries/security-prioritizer.js.map +1 -0
- package/dist/call-graph/analysis/coverage-analyzer.d.ts +201 -0
- package/dist/call-graph/analysis/coverage-analyzer.d.ts.map +1 -0
- package/dist/call-graph/analysis/coverage-analyzer.js +553 -0
- package/dist/call-graph/analysis/coverage-analyzer.js.map +1 -0
- package/dist/call-graph/analysis/dead-code-detector.d.ts +145 -0
- package/dist/call-graph/analysis/dead-code-detector.d.ts.map +1 -0
- package/dist/call-graph/analysis/dead-code-detector.js +391 -0
- package/dist/call-graph/analysis/dead-code-detector.js.map +1 -0
- package/dist/call-graph/analysis/graph-builder.d.ts +142 -0
- package/dist/call-graph/analysis/graph-builder.d.ts.map +1 -0
- package/dist/call-graph/analysis/graph-builder.js +624 -0
- package/dist/call-graph/analysis/graph-builder.js.map +1 -0
- package/dist/call-graph/analysis/impact-analyzer.d.ts +150 -0
- package/dist/call-graph/analysis/impact-analyzer.d.ts.map +1 -0
- package/dist/call-graph/analysis/impact-analyzer.js +329 -0
- package/dist/call-graph/analysis/impact-analyzer.js.map +1 -0
- package/dist/call-graph/analysis/index.d.ts +11 -0
- package/dist/call-graph/analysis/index.d.ts.map +1 -0
- package/dist/call-graph/analysis/index.js +9 -0
- package/dist/call-graph/analysis/index.js.map +1 -0
- package/dist/call-graph/analysis/path-finder.d.ts +117 -0
- package/dist/call-graph/analysis/path-finder.d.ts.map +1 -0
- package/dist/call-graph/analysis/path-finder.js +360 -0
- package/dist/call-graph/analysis/path-finder.js.map +1 -0
- package/dist/call-graph/analysis/reachability.d.ts +56 -0
- package/dist/call-graph/analysis/reachability.d.ts.map +1 -0
- package/dist/call-graph/analysis/reachability.js +357 -0
- package/dist/call-graph/analysis/reachability.js.map +1 -0
- package/dist/call-graph/demo.d.ts +11 -0
- package/dist/call-graph/demo.d.ts.map +1 -0
- package/dist/call-graph/demo.js +339 -0
- package/dist/call-graph/demo.js.map +1 -0
- package/dist/call-graph/enrichment/enrichment-engine.d.ts +126 -0
- package/dist/call-graph/enrichment/enrichment-engine.d.ts.map +1 -0
- package/dist/call-graph/enrichment/enrichment-engine.js +760 -0
- package/dist/call-graph/enrichment/enrichment-engine.js.map +1 -0
- package/dist/call-graph/enrichment/impact-scorer.d.ts +59 -0
- package/dist/call-graph/enrichment/impact-scorer.d.ts.map +1 -0
- package/dist/call-graph/enrichment/impact-scorer.js +328 -0
- package/dist/call-graph/enrichment/impact-scorer.js.map +1 -0
- package/dist/call-graph/enrichment/index.d.ts +12 -0
- package/dist/call-graph/enrichment/index.d.ts.map +1 -0
- package/dist/call-graph/enrichment/index.js +15 -0
- package/dist/call-graph/enrichment/index.js.map +1 -0
- package/dist/call-graph/enrichment/remediation-generator.d.ts +41 -0
- package/dist/call-graph/enrichment/remediation-generator.d.ts.map +1 -0
- package/dist/call-graph/enrichment/remediation-generator.js +609 -0
- package/dist/call-graph/enrichment/remediation-generator.js.map +1 -0
- package/dist/call-graph/enrichment/sensitivity-classifier.d.ts +71 -0
- package/dist/call-graph/enrichment/sensitivity-classifier.d.ts.map +1 -0
- package/dist/call-graph/enrichment/sensitivity-classifier.js +454 -0
- package/dist/call-graph/enrichment/sensitivity-classifier.js.map +1 -0
- package/dist/call-graph/enrichment/types.d.ts +402 -0
- package/dist/call-graph/enrichment/types.d.ts.map +1 -0
- package/dist/call-graph/enrichment/types.js +9 -0
- package/dist/call-graph/enrichment/types.js.map +1 -0
- package/dist/call-graph/extractors/base-extractor.d.ts +112 -0
- package/dist/call-graph/extractors/base-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/base-extractor.js +140 -0
- package/dist/call-graph/extractors/base-extractor.js.map +1 -0
- package/dist/call-graph/extractors/csharp-data-access-extractor.d.ts +76 -0
- package/dist/call-graph/extractors/csharp-data-access-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/csharp-data-access-extractor.js +387 -0
- package/dist/call-graph/extractors/csharp-data-access-extractor.js.map +1 -0
- package/dist/call-graph/extractors/csharp-extractor.d.ts +87 -0
- package/dist/call-graph/extractors/csharp-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/csharp-extractor.js +470 -0
- package/dist/call-graph/extractors/csharp-extractor.js.map +1 -0
- package/dist/call-graph/extractors/data-access-extractor.d.ts +76 -0
- package/dist/call-graph/extractors/data-access-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/data-access-extractor.js +234 -0
- package/dist/call-graph/extractors/data-access-extractor.js.map +1 -0
- package/dist/call-graph/extractors/index.d.ts +26 -0
- package/dist/call-graph/extractors/index.d.ts.map +1 -0
- package/dist/call-graph/extractors/index.js +36 -0
- package/dist/call-graph/extractors/index.js.map +1 -0
- package/dist/call-graph/extractors/java-data-access-extractor.d.ts +101 -0
- package/dist/call-graph/extractors/java-data-access-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/java-data-access-extractor.js +611 -0
- package/dist/call-graph/extractors/java-data-access-extractor.js.map +1 -0
- package/dist/call-graph/extractors/java-extractor.d.ts +87 -0
- package/dist/call-graph/extractors/java-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/java-extractor.js +510 -0
- package/dist/call-graph/extractors/java-extractor.js.map +1 -0
- package/dist/call-graph/extractors/php-data-access-extractor.d.ts +93 -0
- package/dist/call-graph/extractors/php-data-access-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/php-data-access-extractor.js +589 -0
- package/dist/call-graph/extractors/php-data-access-extractor.js.map +1 -0
- package/dist/call-graph/extractors/php-extractor.d.ts +104 -0
- package/dist/call-graph/extractors/php-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/php-extractor.js +619 -0
- package/dist/call-graph/extractors/php-extractor.js.map +1 -0
- package/dist/call-graph/extractors/python-data-access-extractor.d.ts +90 -0
- package/dist/call-graph/extractors/python-data-access-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/python-data-access-extractor.js +537 -0
- package/dist/call-graph/extractors/python-data-access-extractor.js.map +1 -0
- package/dist/call-graph/extractors/python-extractor.d.ts +98 -0
- package/dist/call-graph/extractors/python-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/python-extractor.js +681 -0
- package/dist/call-graph/extractors/python-extractor.js.map +1 -0
- package/dist/call-graph/extractors/semantic-data-access-scanner.d.ts +91 -0
- package/dist/call-graph/extractors/semantic-data-access-scanner.d.ts.map +1 -0
- package/dist/call-graph/extractors/semantic-data-access-scanner.js +498 -0
- package/dist/call-graph/extractors/semantic-data-access-scanner.js.map +1 -0
- package/dist/call-graph/extractors/typescript-data-access-extractor.d.ts +122 -0
- package/dist/call-graph/extractors/typescript-data-access-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/typescript-data-access-extractor.js +788 -0
- package/dist/call-graph/extractors/typescript-data-access-extractor.js.map +1 -0
- package/dist/call-graph/extractors/typescript-extractor.d.ts +145 -0
- package/dist/call-graph/extractors/typescript-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/typescript-extractor.js +904 -0
- package/dist/call-graph/extractors/typescript-extractor.js.map +1 -0
- package/dist/call-graph/index.d.ts +127 -0
- package/dist/call-graph/index.d.ts.map +1 -0
- package/dist/call-graph/index.js +247 -0
- package/dist/call-graph/index.js.map +1 -0
- package/dist/call-graph/store/call-graph-store.d.ts +70 -0
- package/dist/call-graph/store/call-graph-store.d.ts.map +1 -0
- package/dist/call-graph/store/call-graph-store.js +210 -0
- package/dist/call-graph/store/call-graph-store.js.map +1 -0
- package/dist/call-graph/store/index.d.ts +7 -0
- package/dist/call-graph/store/index.d.ts.map +1 -0
- package/dist/call-graph/store/index.js +7 -0
- package/dist/call-graph/store/index.js.map +1 -0
- package/dist/call-graph/types.d.ts +376 -0
- package/dist/call-graph/types.d.ts.map +1 -0
- package/dist/call-graph/types.js +8 -0
- package/dist/call-graph/types.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/dist/lake/callgraph-shard-store.d.ts +168 -0
- package/dist/lake/callgraph-shard-store.d.ts.map +1 -0
- package/dist/lake/callgraph-shard-store.js +466 -0
- package/dist/lake/callgraph-shard-store.js.map +1 -0
- package/dist/lake/examples-store.d.ts +127 -0
- package/dist/lake/examples-store.d.ts.map +1 -0
- package/dist/lake/examples-store.js +389 -0
- package/dist/lake/examples-store.js.map +1 -0
- package/dist/lake/index-store.d.ts +82 -0
- package/dist/lake/index-store.d.ts.map +1 -0
- package/dist/lake/index-store.js +359 -0
- package/dist/lake/index-store.js.map +1 -0
- package/dist/lake/index.d.ts +93 -0
- package/dist/lake/index.d.ts.map +1 -0
- package/dist/lake/index.js +138 -0
- package/dist/lake/index.js.map +1 -0
- package/dist/lake/lake.bak/index-store.d.ts +82 -0
- package/dist/lake/lake.bak/index-store.d.ts.map +1 -0
- package/dist/lake/lake.bak/index-store.js +357 -0
- package/dist/lake/lake.bak/index-store.js.map +1 -0
- package/dist/lake/lake.bak/index.d.ts +81 -0
- package/dist/lake/lake.bak/index.d.ts.map +1 -0
- package/dist/lake/lake.bak/index.js +114 -0
- package/dist/lake/lake.bak/index.js.map +1 -0
- package/dist/lake/lake.bak/manifest-store.d.ts +51 -0
- package/dist/lake/lake.bak/manifest-store.d.ts.map +1 -0
- package/dist/lake/lake.bak/manifest-store.js +347 -0
- package/dist/lake/lake.bak/manifest-store.js.map +1 -0
- package/dist/lake/lake.bak/query-engine.d.ts +112 -0
- package/dist/lake/lake.bak/query-engine.d.ts.map +1 -0
- package/dist/lake/lake.bak/query-engine.js +370 -0
- package/dist/lake/lake.bak/query-engine.js.map +1 -0
- package/dist/lake/lake.bak/types.d.ts +428 -0
- package/dist/lake/lake.bak/types.d.ts.map +1 -0
- package/dist/lake/lake.bak/types.js +46 -0
- package/dist/lake/lake.bak/types.js.map +1 -0
- package/dist/lake/lake.bak/view-materializer.d.ts +70 -0
- package/dist/lake/lake.bak/view-materializer.d.ts.map +1 -0
- package/dist/lake/lake.bak/view-materializer.js +314 -0
- package/dist/lake/lake.bak/view-materializer.js.map +1 -0
- package/dist/lake/lake.bak/view-store.d.ts +57 -0
- package/dist/lake/lake.bak/view-store.d.ts.map +1 -0
- package/dist/lake/lake.bak/view-store.js +348 -0
- package/dist/lake/lake.bak/view-store.js.map +1 -0
- package/dist/lake/manifest-store.d.ts +51 -0
- package/dist/lake/manifest-store.d.ts.map +1 -0
- package/dist/lake/manifest-store.js +348 -0
- package/dist/lake/manifest-store.js.map +1 -0
- package/dist/lake/pattern-shard-store.d.ts +87 -0
- package/dist/lake/pattern-shard-store.d.ts.map +1 -0
- package/dist/lake/pattern-shard-store.js +347 -0
- package/dist/lake/pattern-shard-store.js.map +1 -0
- package/dist/lake/query-engine.d.ts +124 -0
- package/dist/lake/query-engine.d.ts.map +1 -0
- package/dist/lake/query-engine.js +453 -0
- package/dist/lake/query-engine.js.map +1 -0
- package/dist/lake/security-shard-store.d.ts +156 -0
- package/dist/lake/security-shard-store.d.ts.map +1 -0
- package/dist/lake/security-shard-store.js +498 -0
- package/dist/lake/security-shard-store.js.map +1 -0
- package/dist/lake/types.d.ts +428 -0
- package/dist/lake/types.d.ts.map +1 -0
- package/dist/lake/types.js +46 -0
- package/dist/lake/types.js.map +1 -0
- package/dist/lake/view-materializer.d.ts +70 -0
- package/dist/lake/view-materializer.d.ts.map +1 -0
- package/dist/lake/view-materializer.js +314 -0
- package/dist/lake/view-materializer.js.map +1 -0
- package/dist/lake/view-store.d.ts +57 -0
- package/dist/lake/view-store.d.ts.map +1 -0
- package/dist/lake/view-store.js +348 -0
- package/dist/lake/view-store.js.map +1 -0
- package/dist/parsers/tree-sitter/index.d.ts +1 -0
- package/dist/parsers/tree-sitter/index.d.ts.map +1 -1
- package/dist/parsers/tree-sitter/index.js +4 -0
- package/dist/parsers/tree-sitter/index.js.map +1 -1
- package/dist/parsers/tree-sitter/typescript-loader.d.ts +58 -0
- package/dist/parsers/tree-sitter/typescript-loader.d.ts.map +1 -0
- package/dist/parsers/tree-sitter/typescript-loader.js +250 -0
- package/dist/parsers/tree-sitter/typescript-loader.js.map +1 -0
- package/dist/store/project-config.d.ts +154 -0
- package/dist/store/project-config.d.ts.map +1 -0
- package/dist/store/project-config.js +235 -0
- package/dist/store/project-config.js.map +1 -0
- package/dist/store/project-registry.d.ts +241 -0
- package/dist/store/project-registry.d.ts.map +1 -0
- package/dist/store/project-registry.js +557 -0
- package/dist/store/project-registry.js.map +1 -0
- package/package.json +4 -2
|
@@ -0,0 +1,681 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python Call Graph Extractor
|
|
3
|
+
*
|
|
4
|
+
* Extracts functions, calls, imports, and exports from Python
|
|
5
|
+
* using tree-sitter for AST parsing.
|
|
6
|
+
*
|
|
7
|
+
* Handles:
|
|
8
|
+
* - Function definitions (def, async def)
|
|
9
|
+
* - Nested functions (closures)
|
|
10
|
+
* - Class methods and static methods
|
|
11
|
+
* - Module-level calls (top-level code)
|
|
12
|
+
* - Callback patterns (functions passed as arguments)
|
|
13
|
+
* - Dependency injection (FastAPI Depends, etc.)
|
|
14
|
+
* - Lambda expressions
|
|
15
|
+
*/
|
|
16
|
+
import { BaseCallGraphExtractor } from './base-extractor.js';
|
|
17
|
+
import { isTreeSitterAvailable, createPythonParser } from '../../parsers/tree-sitter/loader.js';
|
|
18
|
+
/**
|
|
19
|
+
* Python call graph extractor using tree-sitter
|
|
20
|
+
*/
|
|
21
|
+
export class PythonCallGraphExtractor extends BaseCallGraphExtractor {
|
|
22
|
+
language = 'python';
|
|
23
|
+
extensions = ['.py', '.pyw', '.pyi'];
|
|
24
|
+
parser = null;
|
|
25
|
+
/**
|
|
26
|
+
* Check if tree-sitter is available
|
|
27
|
+
*/
|
|
28
|
+
static isAvailable() {
|
|
29
|
+
return isTreeSitterAvailable();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Extract call graph information from Python source
|
|
33
|
+
*/
|
|
34
|
+
extract(source, filePath) {
|
|
35
|
+
const result = this.createEmptyResult(filePath);
|
|
36
|
+
if (!isTreeSitterAvailable()) {
|
|
37
|
+
result.errors.push('Tree-sitter not available for Python parsing');
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
if (!this.parser) {
|
|
42
|
+
this.parser = createPythonParser();
|
|
43
|
+
}
|
|
44
|
+
const tree = this.parser.parse(source);
|
|
45
|
+
this.visitNode(tree.rootNode, result, source, null, null);
|
|
46
|
+
// Extract module-level calls (top-level code not inside any function)
|
|
47
|
+
this.extractModuleLevelCalls(tree.rootNode, result, source);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
result.errors.push(error instanceof Error ? error.message : 'Unknown parse error');
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Extract calls at module level (not inside any function or class)
|
|
56
|
+
* This catches patterns like: app = FastAPI(), if __name__ == "__main__": main()
|
|
57
|
+
*/
|
|
58
|
+
extractModuleLevelCalls(rootNode, result, source) {
|
|
59
|
+
const moduleCalls = [];
|
|
60
|
+
for (const child of rootNode.children) {
|
|
61
|
+
// Skip function and class definitions
|
|
62
|
+
if (child.type === 'function_definition' || child.type === 'class_definition') {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
// Skip import statements
|
|
66
|
+
if (child.type === 'import_statement' || child.type === 'import_from_statement') {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
// Extract calls from expression statements, assignments, if statements, etc.
|
|
70
|
+
this.extractCallsRecursive(child, moduleCalls, source);
|
|
71
|
+
}
|
|
72
|
+
// Add module-level calls to result
|
|
73
|
+
result.calls.push(...moduleCalls);
|
|
74
|
+
// If there are module-level calls, create a synthetic module function
|
|
75
|
+
if (moduleCalls.length > 0) {
|
|
76
|
+
const moduleFunc = this.createFunction({
|
|
77
|
+
name: '__module__',
|
|
78
|
+
startLine: 1,
|
|
79
|
+
endLine: rootNode.endPosition.row + 1,
|
|
80
|
+
startColumn: 0,
|
|
81
|
+
endColumn: 0,
|
|
82
|
+
parameters: [],
|
|
83
|
+
isMethod: false,
|
|
84
|
+
isStatic: false,
|
|
85
|
+
isExported: false,
|
|
86
|
+
isConstructor: false,
|
|
87
|
+
isAsync: false,
|
|
88
|
+
decorators: [],
|
|
89
|
+
bodyStartLine: 1,
|
|
90
|
+
bodyEndLine: rootNode.endPosition.row + 1,
|
|
91
|
+
});
|
|
92
|
+
result.functions.push(moduleFunc);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Recursively extract calls from a node (for module-level code)
|
|
97
|
+
*/
|
|
98
|
+
extractCallsRecursive(node, calls, source) {
|
|
99
|
+
if (node.type === 'call') {
|
|
100
|
+
const call = this.extractCallExpressionToArray(node, source);
|
|
101
|
+
if (call) {
|
|
102
|
+
calls.push(...call);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Don't recurse into function or class definitions
|
|
106
|
+
if (node.type === 'function_definition' || node.type === 'class_definition') {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
for (const child of node.children) {
|
|
110
|
+
this.extractCallsRecursive(child, calls, source);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Visit a tree-sitter node and extract information
|
|
115
|
+
* @param parentFunction - The name of the containing function (for nested functions)
|
|
116
|
+
*/
|
|
117
|
+
visitNode(node, result, source, currentClass, parentFunction) {
|
|
118
|
+
switch (node.type) {
|
|
119
|
+
case 'function_definition':
|
|
120
|
+
this.extractFunctionDefinition(node, result, source, currentClass, parentFunction);
|
|
121
|
+
break;
|
|
122
|
+
case 'class_definition':
|
|
123
|
+
this.extractClassDefinition(node, result, source);
|
|
124
|
+
break;
|
|
125
|
+
case 'import_statement':
|
|
126
|
+
this.extractImportStatement(node, result);
|
|
127
|
+
break;
|
|
128
|
+
case 'import_from_statement':
|
|
129
|
+
this.extractImportFromStatement(node, result);
|
|
130
|
+
break;
|
|
131
|
+
case 'call':
|
|
132
|
+
this.extractCallExpression(node, result, source);
|
|
133
|
+
break;
|
|
134
|
+
default:
|
|
135
|
+
// Recurse into children
|
|
136
|
+
for (const child of node.children) {
|
|
137
|
+
this.visitNode(child, result, source, currentClass, parentFunction);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Extract a function definition
|
|
143
|
+
*/
|
|
144
|
+
extractFunctionDefinition(node, result, source, currentClass, parentFunction) {
|
|
145
|
+
const nameNode = node.childForFieldName('name');
|
|
146
|
+
if (!nameNode)
|
|
147
|
+
return;
|
|
148
|
+
const name = nameNode.text;
|
|
149
|
+
const isMethod = currentClass !== null;
|
|
150
|
+
const isConstructor = name === '__init__';
|
|
151
|
+
// Get decorators
|
|
152
|
+
const decorators = [];
|
|
153
|
+
let prevSibling = node.previousNamedSibling;
|
|
154
|
+
while (prevSibling && prevSibling.type === 'decorator') {
|
|
155
|
+
decorators.unshift(prevSibling.text);
|
|
156
|
+
prevSibling = prevSibling.previousNamedSibling;
|
|
157
|
+
}
|
|
158
|
+
// Check for async
|
|
159
|
+
const isAsync = node.children.some(c => c.type === 'async');
|
|
160
|
+
// Check for staticmethod/classmethod decorators
|
|
161
|
+
const isStatic = decorators.some(d => d.includes('@staticmethod') || d.includes('@classmethod'));
|
|
162
|
+
// Extract parameters
|
|
163
|
+
const parametersNode = node.childForFieldName('parameters');
|
|
164
|
+
const parameters = parametersNode ? this.extractParameters(parametersNode) : [];
|
|
165
|
+
// Get return type
|
|
166
|
+
const returnTypeNode = node.childForFieldName('return_type');
|
|
167
|
+
const returnType = returnTypeNode?.text;
|
|
168
|
+
// Get body for line range
|
|
169
|
+
const bodyNode = node.childForFieldName('body');
|
|
170
|
+
// Build qualified name including parent function for nested functions
|
|
171
|
+
let qualifiedName = name;
|
|
172
|
+
if (parentFunction) {
|
|
173
|
+
qualifiedName = `${parentFunction}.${name}`;
|
|
174
|
+
}
|
|
175
|
+
else if (currentClass) {
|
|
176
|
+
qualifiedName = `${currentClass}.${name}`;
|
|
177
|
+
}
|
|
178
|
+
const func = this.createFunction({
|
|
179
|
+
name,
|
|
180
|
+
qualifiedName,
|
|
181
|
+
startLine: node.startPosition.row + 1,
|
|
182
|
+
endLine: node.endPosition.row + 1,
|
|
183
|
+
startColumn: node.startPosition.column,
|
|
184
|
+
endColumn: node.endPosition.column,
|
|
185
|
+
parameters,
|
|
186
|
+
returnType,
|
|
187
|
+
isMethod,
|
|
188
|
+
isStatic,
|
|
189
|
+
isExported: parentFunction ? false : (!name.startsWith('_') || name.startsWith('__') && name.endsWith('__')),
|
|
190
|
+
isConstructor,
|
|
191
|
+
isAsync,
|
|
192
|
+
className: currentClass ?? undefined,
|
|
193
|
+
decorators,
|
|
194
|
+
bodyStartLine: bodyNode ? bodyNode.startPosition.row + 1 : node.startPosition.row + 1,
|
|
195
|
+
bodyEndLine: bodyNode ? bodyNode.endPosition.row + 1 : node.endPosition.row + 1,
|
|
196
|
+
});
|
|
197
|
+
result.functions.push(func);
|
|
198
|
+
// Extract calls from parameter default values (for DI patterns like Depends(get_current_user))
|
|
199
|
+
if (parametersNode) {
|
|
200
|
+
this.extractCallsFromParameters(parametersNode, result, source);
|
|
201
|
+
}
|
|
202
|
+
// Extract calls from function body and nested functions
|
|
203
|
+
if (bodyNode) {
|
|
204
|
+
this.extractCallsFromBody(bodyNode, result, source);
|
|
205
|
+
// Extract nested functions
|
|
206
|
+
this.extractNestedFunctions(bodyNode, result, source, currentClass, qualifiedName);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Extract nested functions from a function body
|
|
211
|
+
*/
|
|
212
|
+
extractNestedFunctions(bodyNode, result, source, currentClass, parentFunctionName) {
|
|
213
|
+
for (const child of bodyNode.children) {
|
|
214
|
+
if (child.type === 'function_definition') {
|
|
215
|
+
this.extractFunctionDefinition(child, result, source, currentClass, parentFunctionName);
|
|
216
|
+
}
|
|
217
|
+
// Also check for nested functions in if/else/for/while/with blocks
|
|
218
|
+
else if (['if_statement', 'for_statement', 'while_statement', 'with_statement', 'try_statement'].includes(child.type)) {
|
|
219
|
+
this.extractNestedFunctionsRecursive(child, result, source, currentClass, parentFunctionName);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Recursively extract nested functions from control flow blocks
|
|
225
|
+
*/
|
|
226
|
+
extractNestedFunctionsRecursive(node, result, source, currentClass, parentFunctionName) {
|
|
227
|
+
for (const child of node.children) {
|
|
228
|
+
if (child.type === 'function_definition') {
|
|
229
|
+
this.extractFunctionDefinition(child, result, source, currentClass, parentFunctionName);
|
|
230
|
+
}
|
|
231
|
+
else if (child.type === 'block') {
|
|
232
|
+
this.extractNestedFunctions(child, result, source, currentClass, parentFunctionName);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
this.extractNestedFunctionsRecursive(child, result, source, currentClass, parentFunctionName);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Extract calls from parameter default values (for DI patterns)
|
|
241
|
+
*/
|
|
242
|
+
extractCallsFromParameters(node, result, source) {
|
|
243
|
+
const visit = (n) => {
|
|
244
|
+
if (n.type === 'call') {
|
|
245
|
+
this.extractCallExpression(n, result, source);
|
|
246
|
+
}
|
|
247
|
+
for (const child of n.children) {
|
|
248
|
+
visit(child);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
for (const child of node.children) {
|
|
252
|
+
// Look for default_parameter and typed_default_parameter
|
|
253
|
+
if (child.type === 'default_parameter' || child.type === 'typed_default_parameter') {
|
|
254
|
+
const valueNode = child.childForFieldName('value');
|
|
255
|
+
if (valueNode) {
|
|
256
|
+
visit(valueNode);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Extract a call expression and return as array (for module-level calls)
|
|
263
|
+
*/
|
|
264
|
+
extractCallExpressionToArray(node, _source) {
|
|
265
|
+
const funcNode = node.childForFieldName('function');
|
|
266
|
+
if (!funcNode)
|
|
267
|
+
return null;
|
|
268
|
+
const calls = [];
|
|
269
|
+
let calleeName;
|
|
270
|
+
let receiver;
|
|
271
|
+
let fullExpression = funcNode.text;
|
|
272
|
+
let isConstructorCall = false;
|
|
273
|
+
// Attribute access: obj.method() or module.func()
|
|
274
|
+
if (funcNode.type === 'attribute') {
|
|
275
|
+
const objectNode = funcNode.childForFieldName('object');
|
|
276
|
+
const attrNode = funcNode.childForFieldName('attribute');
|
|
277
|
+
if (objectNode && attrNode) {
|
|
278
|
+
receiver = objectNode.text;
|
|
279
|
+
calleeName = attrNode.text;
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
calleeName = funcNode.text;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
// Direct call: func()
|
|
286
|
+
else if (funcNode.type === 'identifier') {
|
|
287
|
+
calleeName = funcNode.text;
|
|
288
|
+
isConstructorCall = /^[A-Z]/.test(calleeName);
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
calleeName = funcNode.text;
|
|
292
|
+
}
|
|
293
|
+
fullExpression = node.text;
|
|
294
|
+
const argsNode = node.childForFieldName('arguments');
|
|
295
|
+
let argumentCount = 0;
|
|
296
|
+
const functionRefs = [];
|
|
297
|
+
if (argsNode) {
|
|
298
|
+
for (const child of argsNode.children) {
|
|
299
|
+
if (child.type !== '(' && child.type !== ')' && child.type !== ',') {
|
|
300
|
+
argumentCount++;
|
|
301
|
+
if (child.type === 'identifier') {
|
|
302
|
+
const argName = child.text;
|
|
303
|
+
if (!['True', 'False', 'None', 'self', 'cls'].includes(argName)) {
|
|
304
|
+
functionRefs.push(argName);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
else if (child.type === 'keyword_argument') {
|
|
308
|
+
const valueNode = child.childForFieldName('value');
|
|
309
|
+
if (valueNode?.type === 'identifier') {
|
|
310
|
+
const argName = valueNode.text;
|
|
311
|
+
if (!['True', 'False', 'None', 'self', 'cls'].includes(argName)) {
|
|
312
|
+
functionRefs.push(argName);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
calls.push(this.createCall({
|
|
320
|
+
calleeName,
|
|
321
|
+
receiver,
|
|
322
|
+
fullExpression,
|
|
323
|
+
line: node.startPosition.row + 1,
|
|
324
|
+
column: node.startPosition.column,
|
|
325
|
+
argumentCount,
|
|
326
|
+
isMethodCall: !!receiver,
|
|
327
|
+
isConstructorCall,
|
|
328
|
+
}));
|
|
329
|
+
// For DI patterns, also create implicit calls
|
|
330
|
+
if (calleeName === 'Depends' || calleeName === 'inject' || calleeName === 'Inject') {
|
|
331
|
+
for (const funcRef of functionRefs) {
|
|
332
|
+
calls.push(this.createCall({
|
|
333
|
+
calleeName: funcRef,
|
|
334
|
+
receiver: undefined,
|
|
335
|
+
fullExpression: `${calleeName}(${funcRef})`,
|
|
336
|
+
line: node.startPosition.row + 1,
|
|
337
|
+
column: node.startPosition.column,
|
|
338
|
+
argumentCount: 0,
|
|
339
|
+
isMethodCall: false,
|
|
340
|
+
isConstructorCall: /^[A-Z]/.test(funcRef),
|
|
341
|
+
}));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return calls;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Extract calls from a function body
|
|
348
|
+
* Also extracts callback patterns where functions are passed as arguments
|
|
349
|
+
*/
|
|
350
|
+
extractCallsFromBody(node, result, source) {
|
|
351
|
+
const visit = (n) => {
|
|
352
|
+
if (n.type === 'call') {
|
|
353
|
+
this.extractCallExpression(n, result, source);
|
|
354
|
+
// Also extract callback references
|
|
355
|
+
this.extractCallbackReferences(n, result, source);
|
|
356
|
+
}
|
|
357
|
+
// Don't recurse into nested function definitions (they're handled separately)
|
|
358
|
+
if (n.type !== 'function_definition' && n.type !== 'class_definition') {
|
|
359
|
+
for (const child of n.children) {
|
|
360
|
+
visit(child);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
for (const child of node.children) {
|
|
365
|
+
visit(child);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Extract callback references from function call arguments
|
|
370
|
+
* Handles patterns like: map(process_item, items), threading.Thread(target=my_func)
|
|
371
|
+
*/
|
|
372
|
+
extractCallbackReferences(node, result, _source) {
|
|
373
|
+
const argsNode = node.childForFieldName('arguments');
|
|
374
|
+
if (!argsNode)
|
|
375
|
+
return;
|
|
376
|
+
for (const child of argsNode.children) {
|
|
377
|
+
// Direct function reference passed as argument
|
|
378
|
+
if (child.type === 'identifier') {
|
|
379
|
+
const argName = child.text;
|
|
380
|
+
// Skip common non-function arguments
|
|
381
|
+
if (!['True', 'False', 'None', 'self', 'cls'].includes(argName) &&
|
|
382
|
+
!/^[A-Z]/.test(argName)) { // Skip class names (handled as constructor calls)
|
|
383
|
+
result.calls.push(this.createCall({
|
|
384
|
+
calleeName: argName,
|
|
385
|
+
receiver: undefined,
|
|
386
|
+
fullExpression: argName,
|
|
387
|
+
line: child.startPosition.row + 1,
|
|
388
|
+
column: child.startPosition.column,
|
|
389
|
+
argumentCount: 0,
|
|
390
|
+
isMethodCall: false,
|
|
391
|
+
isConstructorCall: false,
|
|
392
|
+
}));
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
// Keyword argument with function reference: target=my_func
|
|
396
|
+
else if (child.type === 'keyword_argument') {
|
|
397
|
+
const keyNode = child.childForFieldName('name');
|
|
398
|
+
const valueNode = child.childForFieldName('value');
|
|
399
|
+
// Common callback keyword arguments
|
|
400
|
+
const callbackKeywords = ['target', 'callback', 'func', 'function', 'handler', 'key', 'default'];
|
|
401
|
+
if (keyNode && valueNode?.type === 'identifier' &&
|
|
402
|
+
callbackKeywords.includes(keyNode.text)) {
|
|
403
|
+
const argName = valueNode.text;
|
|
404
|
+
if (!['True', 'False', 'None', 'self', 'cls'].includes(argName)) {
|
|
405
|
+
result.calls.push(this.createCall({
|
|
406
|
+
calleeName: argName,
|
|
407
|
+
receiver: undefined,
|
|
408
|
+
fullExpression: `${keyNode.text}=${argName}`,
|
|
409
|
+
line: valueNode.startPosition.row + 1,
|
|
410
|
+
column: valueNode.startPosition.column,
|
|
411
|
+
argumentCount: 0,
|
|
412
|
+
isMethodCall: false,
|
|
413
|
+
isConstructorCall: false,
|
|
414
|
+
}));
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Extract a call expression
|
|
422
|
+
*/
|
|
423
|
+
extractCallExpression(node, result, _source) {
|
|
424
|
+
const funcNode = node.childForFieldName('function');
|
|
425
|
+
if (!funcNode)
|
|
426
|
+
return;
|
|
427
|
+
let calleeName;
|
|
428
|
+
let receiver;
|
|
429
|
+
let fullExpression = funcNode.text;
|
|
430
|
+
let isConstructorCall = false;
|
|
431
|
+
// Attribute access: obj.method() or module.func()
|
|
432
|
+
if (funcNode.type === 'attribute') {
|
|
433
|
+
const objectNode = funcNode.childForFieldName('object');
|
|
434
|
+
const attrNode = funcNode.childForFieldName('attribute');
|
|
435
|
+
if (objectNode && attrNode) {
|
|
436
|
+
receiver = objectNode.text;
|
|
437
|
+
calleeName = attrNode.text;
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
calleeName = funcNode.text;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
// Direct call: func()
|
|
444
|
+
else if (funcNode.type === 'identifier') {
|
|
445
|
+
calleeName = funcNode.text;
|
|
446
|
+
// Check if it looks like a class instantiation (PascalCase)
|
|
447
|
+
isConstructorCall = /^[A-Z]/.test(calleeName);
|
|
448
|
+
}
|
|
449
|
+
// Other (subscript, call result, etc.)
|
|
450
|
+
else {
|
|
451
|
+
calleeName = funcNode.text;
|
|
452
|
+
}
|
|
453
|
+
// Get full expression including arguments for DI pattern detection
|
|
454
|
+
fullExpression = node.text;
|
|
455
|
+
// Count arguments and extract function references
|
|
456
|
+
const argsNode = node.childForFieldName('arguments');
|
|
457
|
+
let argumentCount = 0;
|
|
458
|
+
const functionRefs = [];
|
|
459
|
+
if (argsNode) {
|
|
460
|
+
for (const child of argsNode.children) {
|
|
461
|
+
if (child.type !== '(' && child.type !== ')' && child.type !== ',') {
|
|
462
|
+
argumentCount++;
|
|
463
|
+
// Check if argument is a function reference (identifier that's not a keyword)
|
|
464
|
+
if (child.type === 'identifier') {
|
|
465
|
+
const argName = child.text;
|
|
466
|
+
// Skip common non-function arguments
|
|
467
|
+
if (!['True', 'False', 'None', 'self', 'cls'].includes(argName)) {
|
|
468
|
+
functionRefs.push(argName);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
// Check for keyword argument with function reference: Depends(get_current_user)
|
|
472
|
+
else if (child.type === 'keyword_argument') {
|
|
473
|
+
const valueNode = child.childForFieldName('value');
|
|
474
|
+
if (valueNode?.type === 'identifier') {
|
|
475
|
+
const argName = valueNode.text;
|
|
476
|
+
if (!['True', 'False', 'None', 'self', 'cls'].includes(argName)) {
|
|
477
|
+
functionRefs.push(argName);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
const call = this.createCall({
|
|
485
|
+
calleeName,
|
|
486
|
+
receiver,
|
|
487
|
+
fullExpression,
|
|
488
|
+
line: node.startPosition.row + 1,
|
|
489
|
+
column: node.startPosition.column,
|
|
490
|
+
argumentCount,
|
|
491
|
+
isMethodCall: !!receiver,
|
|
492
|
+
isConstructorCall,
|
|
493
|
+
});
|
|
494
|
+
result.calls.push(call);
|
|
495
|
+
// For DI patterns like Depends(func), also create implicit calls to the referenced functions
|
|
496
|
+
// This helps the call graph understand that the route handler "calls" the dependency
|
|
497
|
+
if (calleeName === 'Depends' || calleeName === 'inject' || calleeName === 'Inject') {
|
|
498
|
+
for (const funcRef of functionRefs) {
|
|
499
|
+
const implicitCall = this.createCall({
|
|
500
|
+
calleeName: funcRef,
|
|
501
|
+
receiver: undefined,
|
|
502
|
+
fullExpression: `${calleeName}(${funcRef})`,
|
|
503
|
+
line: node.startPosition.row + 1,
|
|
504
|
+
column: node.startPosition.column,
|
|
505
|
+
argumentCount: 0,
|
|
506
|
+
isMethodCall: false,
|
|
507
|
+
isConstructorCall: /^[A-Z]/.test(funcRef),
|
|
508
|
+
});
|
|
509
|
+
result.calls.push(implicitCall);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Extract a class definition
|
|
515
|
+
*/
|
|
516
|
+
extractClassDefinition(node, result, source) {
|
|
517
|
+
const nameNode = node.childForFieldName('name');
|
|
518
|
+
if (!nameNode)
|
|
519
|
+
return;
|
|
520
|
+
const name = nameNode.text;
|
|
521
|
+
// Get base classes
|
|
522
|
+
const baseClasses = [];
|
|
523
|
+
const superclassNode = node.childForFieldName('superclasses');
|
|
524
|
+
if (superclassNode) {
|
|
525
|
+
for (const child of superclassNode.children) {
|
|
526
|
+
if (child.type === 'identifier' || child.type === 'attribute') {
|
|
527
|
+
baseClasses.push(child.text);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
// Get methods
|
|
532
|
+
const methods = [];
|
|
533
|
+
const bodyNode = node.childForFieldName('body');
|
|
534
|
+
if (bodyNode) {
|
|
535
|
+
for (const child of bodyNode.children) {
|
|
536
|
+
if (child.type === 'function_definition') {
|
|
537
|
+
const methodNameNode = child.childForFieldName('name');
|
|
538
|
+
if (methodNameNode) {
|
|
539
|
+
methods.push(methodNameNode.text);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
const classInfo = this.createClass({
|
|
545
|
+
name,
|
|
546
|
+
startLine: node.startPosition.row + 1,
|
|
547
|
+
endLine: node.endPosition.row + 1,
|
|
548
|
+
baseClasses,
|
|
549
|
+
methods,
|
|
550
|
+
isExported: !name.startsWith('_'),
|
|
551
|
+
});
|
|
552
|
+
result.classes.push(classInfo);
|
|
553
|
+
// Visit class body with class context
|
|
554
|
+
if (bodyNode) {
|
|
555
|
+
for (const child of bodyNode.children) {
|
|
556
|
+
this.visitNode(child, result, source, name, null);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Extract an import statement (import foo, import foo.bar)
|
|
562
|
+
*/
|
|
563
|
+
extractImportStatement(node, result) {
|
|
564
|
+
for (const child of node.children) {
|
|
565
|
+
if (child.type === 'dotted_name') {
|
|
566
|
+
const moduleName = child.text;
|
|
567
|
+
result.imports.push(this.createImport({
|
|
568
|
+
source: moduleName,
|
|
569
|
+
names: [{
|
|
570
|
+
imported: moduleName,
|
|
571
|
+
local: moduleName.split('.').pop() ?? moduleName,
|
|
572
|
+
}],
|
|
573
|
+
line: node.startPosition.row + 1,
|
|
574
|
+
}));
|
|
575
|
+
}
|
|
576
|
+
else if (child.type === 'aliased_import') {
|
|
577
|
+
const nameNode = child.childForFieldName('name');
|
|
578
|
+
const aliasNode = child.childForFieldName('alias');
|
|
579
|
+
if (nameNode) {
|
|
580
|
+
const moduleName = nameNode.text;
|
|
581
|
+
result.imports.push(this.createImport({
|
|
582
|
+
source: moduleName,
|
|
583
|
+
names: [{
|
|
584
|
+
imported: moduleName,
|
|
585
|
+
local: aliasNode?.text ?? moduleName.split('.').pop() ?? moduleName,
|
|
586
|
+
}],
|
|
587
|
+
line: node.startPosition.row + 1,
|
|
588
|
+
}));
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Extract a from...import statement
|
|
595
|
+
*/
|
|
596
|
+
extractImportFromStatement(node, result) {
|
|
597
|
+
const moduleNode = node.childForFieldName('module_name');
|
|
598
|
+
const moduleName = moduleNode?.text ?? '';
|
|
599
|
+
const names = [];
|
|
600
|
+
for (const child of node.children) {
|
|
601
|
+
if (child.type === 'dotted_name' && child !== moduleNode) {
|
|
602
|
+
names.push({
|
|
603
|
+
imported: child.text,
|
|
604
|
+
local: child.text,
|
|
605
|
+
isDefault: false,
|
|
606
|
+
isNamespace: false,
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
else if (child.type === 'aliased_import') {
|
|
610
|
+
const nameNode = child.childForFieldName('name');
|
|
611
|
+
const aliasNode = child.childForFieldName('alias');
|
|
612
|
+
if (nameNode) {
|
|
613
|
+
names.push({
|
|
614
|
+
imported: nameNode.text,
|
|
615
|
+
local: aliasNode?.text ?? nameNode.text,
|
|
616
|
+
isDefault: false,
|
|
617
|
+
isNamespace: false,
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
else if (child.type === 'wildcard_import') {
|
|
622
|
+
names.push({
|
|
623
|
+
imported: '*',
|
|
624
|
+
local: '*',
|
|
625
|
+
isDefault: false,
|
|
626
|
+
isNamespace: true,
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
if (names.length > 0) {
|
|
631
|
+
result.imports.push(this.createImport({
|
|
632
|
+
source: moduleName,
|
|
633
|
+
names,
|
|
634
|
+
line: node.startPosition.row + 1,
|
|
635
|
+
}));
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Extract parameters from a parameters node
|
|
640
|
+
*/
|
|
641
|
+
extractParameters(node) {
|
|
642
|
+
const params = [];
|
|
643
|
+
for (const child of node.children) {
|
|
644
|
+
if (child.type === 'identifier') {
|
|
645
|
+
// Skip 'self' and 'cls' for methods
|
|
646
|
+
if (child.text !== 'self' && child.text !== 'cls') {
|
|
647
|
+
params.push(this.parseParameter(child.text));
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
else if (child.type === 'typed_parameter') {
|
|
651
|
+
const nameNode = child.children.find(c => c.type === 'identifier');
|
|
652
|
+
const typeNode = child.childForFieldName('type');
|
|
653
|
+
if (nameNode && nameNode.text !== 'self' && nameNode.text !== 'cls') {
|
|
654
|
+
params.push(this.parseParameter(nameNode.text, typeNode?.text));
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
else if (child.type === 'default_parameter') {
|
|
658
|
+
const nameNode = child.childForFieldName('name');
|
|
659
|
+
const typeNode = child.childForFieldName('type');
|
|
660
|
+
if (nameNode && nameNode.text !== 'self' && nameNode.text !== 'cls') {
|
|
661
|
+
params.push(this.parseParameter(nameNode.text, typeNode?.text, true));
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
else if (child.type === 'typed_default_parameter') {
|
|
665
|
+
const nameNode = child.childForFieldName('name');
|
|
666
|
+
const typeNode = child.childForFieldName('type');
|
|
667
|
+
if (nameNode && nameNode.text !== 'self' && nameNode.text !== 'cls') {
|
|
668
|
+
params.push(this.parseParameter(nameNode.text, typeNode?.text, true));
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
else if (child.type === 'list_splat_pattern' || child.type === 'dictionary_splat_pattern') {
|
|
672
|
+
const nameNode = child.children.find(c => c.type === 'identifier');
|
|
673
|
+
if (nameNode) {
|
|
674
|
+
params.push(this.parseParameter(nameNode.text, undefined, false, true));
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return params;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
//# sourceMappingURL=python-extractor.js.map
|