@opensip-cli/graph-python 0.1.0
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/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +31 -0
- package/dist/__tests__/body-digest.test.d.ts +18 -0
- package/dist/__tests__/body-digest.test.d.ts.map +1 -0
- package/dist/__tests__/body-digest.test.js +61 -0
- package/dist/__tests__/body-digest.test.js.map +1 -0
- package/dist/__tests__/branch-coverage.test.d.ts +19 -0
- package/dist/__tests__/branch-coverage.test.d.ts.map +1 -0
- package/dist/__tests__/branch-coverage.test.js +292 -0
- package/dist/__tests__/branch-coverage.test.js.map +1 -0
- package/dist/__tests__/cache-key.test.d.ts +8 -0
- package/dist/__tests__/cache-key.test.d.ts.map +1 -0
- package/dist/__tests__/cache-key.test.js +55 -0
- package/dist/__tests__/cache-key.test.js.map +1 -0
- package/dist/__tests__/depends-on-emission.test.d.ts +21 -0
- package/dist/__tests__/depends-on-emission.test.d.ts.map +1 -0
- package/dist/__tests__/depends-on-emission.test.js +189 -0
- package/dist/__tests__/depends-on-emission.test.js.map +1 -0
- package/dist/__tests__/discover.test.d.ts +9 -0
- package/dist/__tests__/discover.test.d.ts.map +1 -0
- package/dist/__tests__/discover.test.js +64 -0
- package/dist/__tests__/discover.test.js.map +1 -0
- package/dist/__tests__/parse.test.d.ts +8 -0
- package/dist/__tests__/parse.test.d.ts.map +1 -0
- package/dist/__tests__/parse.test.js +37 -0
- package/dist/__tests__/parse.test.js.map +1 -0
- package/dist/__tests__/resolve.test.d.ts +11 -0
- package/dist/__tests__/resolve.test.d.ts.map +1 -0
- package/dist/__tests__/resolve.test.js +176 -0
- package/dist/__tests__/resolve.test.js.map +1 -0
- package/dist/__tests__/walk-shapes.test.d.ts +15 -0
- package/dist/__tests__/walk-shapes.test.d.ts.map +1 -0
- package/dist/__tests__/walk-shapes.test.js +156 -0
- package/dist/__tests__/walk-shapes.test.js.map +1 -0
- package/dist/__tests__/walk.test.d.ts +10 -0
- package/dist/__tests__/walk.test.d.ts.map +1 -0
- package/dist/__tests__/walk.test.js +71 -0
- package/dist/__tests__/walk.test.js.map +1 -0
- package/dist/body-digest.d.ts +18 -0
- package/dist/body-digest.d.ts.map +1 -0
- package/dist/body-digest.js +88 -0
- package/dist/body-digest.js.map +1 -0
- package/dist/cache-key.d.ts +22 -0
- package/dist/cache-key.d.ts.map +1 -0
- package/dist/cache-key.js +52 -0
- package/dist/cache-key.js.map +1 -0
- package/dist/discover.d.ts +19 -0
- package/dist/discover.d.ts.map +1 -0
- package/dist/discover.js +36 -0
- package/dist/discover.js.map +1 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/parse.d.ts +22 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +16 -0
- package/dist/parse.js.map +1 -0
- package/dist/resolve.d.ts +35 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +294 -0
- package/dist/resolve.js.map +1 -0
- package/dist/rule-hints.d.ts +13 -0
- package/dist/rule-hints.d.ts.map +1 -0
- package/dist/rule-hints.js +70 -0
- package/dist/rule-hints.js.map +1 -0
- package/dist/walk-dependencies.d.ts +27 -0
- package/dist/walk-dependencies.d.ts.map +1 -0
- package/dist/walk-dependencies.js +152 -0
- package/dist/walk-dependencies.js.map +1 -0
- package/dist/walk.d.ts +42 -0
- package/dist/walk.d.ts.map +1 -0
- package/dist/walk.js +281 -0
- package/dist/walk.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python rule hints — declares language-specific signals for rules.
|
|
3
|
+
*
|
|
4
|
+
* Lands in PR 5 of plan docs/plans/10-graph-language-pluggability.md.
|
|
5
|
+
* Each hint maps a generic rule input ("what counts as a test file?",
|
|
6
|
+
* "what's a side-effect primitive?") onto Python conventions.
|
|
7
|
+
*
|
|
8
|
+
* Conservative on purpose: high-precision, low-recall. Rule authors may
|
|
9
|
+
* extend the lists over time as false negatives surface in practice.
|
|
10
|
+
*/
|
|
11
|
+
import { isTestFile } from './walk.js';
|
|
12
|
+
/**
|
|
13
|
+
* Starter list of well-known Python side-effect primitives for
|
|
14
|
+
* `no-side-effect-path`. Names are textual prefixes a developer would
|
|
15
|
+
* actually write (e.g. `print(`, `os.system(`).
|
|
16
|
+
*/
|
|
17
|
+
const PYTHON_SIDE_EFFECT_PRIMITIVES = [
|
|
18
|
+
'print',
|
|
19
|
+
'os.system',
|
|
20
|
+
'os.remove',
|
|
21
|
+
'os.unlink',
|
|
22
|
+
'os.rmdir',
|
|
23
|
+
'os.write',
|
|
24
|
+
'os.makedirs',
|
|
25
|
+
'os.rename',
|
|
26
|
+
'subprocess.run',
|
|
27
|
+
'subprocess.Popen',
|
|
28
|
+
'subprocess.call',
|
|
29
|
+
'subprocess.check_call',
|
|
30
|
+
'subprocess.check_output',
|
|
31
|
+
'open',
|
|
32
|
+
'sys.exit',
|
|
33
|
+
'sys.stdout.write',
|
|
34
|
+
'sys.stderr.write',
|
|
35
|
+
'random.random',
|
|
36
|
+
'random.randint',
|
|
37
|
+
'random.choice',
|
|
38
|
+
'time.sleep',
|
|
39
|
+
'requests.get',
|
|
40
|
+
'requests.post',
|
|
41
|
+
'requests.put',
|
|
42
|
+
'requests.delete',
|
|
43
|
+
'shutil.rmtree',
|
|
44
|
+
'shutil.copy',
|
|
45
|
+
'shutil.move',
|
|
46
|
+
];
|
|
47
|
+
/**
|
|
48
|
+
* Throw-statement detection for `always-throws-branch`. Python's
|
|
49
|
+
* raise syntax: `raise SomeError(...)` or bare `raise`. We accept
|
|
50
|
+
* both forms by allowing optional whitespace and an optional
|
|
51
|
+
* identifier.
|
|
52
|
+
*/
|
|
53
|
+
const PYTHON_THROW_REGEX = /\braise\b(?:\s+[A-Za-z_][\w.]*)?/;
|
|
54
|
+
/**
|
|
55
|
+
* Generated-file globs Python projects commonly use. The TypeScript
|
|
56
|
+
* adapter has its own list (`dist/`, `build/`, `.generated.`); we add
|
|
57
|
+
* Python-specific ones (`*_pb2.py` from protoc).
|
|
58
|
+
*/
|
|
59
|
+
const PYTHON_GENERATED_FILE_PATTERNS = [
|
|
60
|
+
'**/*_pb2.py',
|
|
61
|
+
'**/*_pb2_grpc.py',
|
|
62
|
+
'**/migrations/**',
|
|
63
|
+
];
|
|
64
|
+
export const pythonRuleHints = {
|
|
65
|
+
isTestFile,
|
|
66
|
+
generatedFilePatterns: PYTHON_GENERATED_FILE_PATTERNS,
|
|
67
|
+
sideEffectPrimitives: PYTHON_SIDE_EFFECT_PRIMITIVES,
|
|
68
|
+
throwSyntaxRegex: PYTHON_THROW_REGEX,
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=rule-hints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-hints.js","sourceRoot":"","sources":["../src/rule-hints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAIvC;;;;GAIG;AACH,MAAM,6BAA6B,GAAsB;IACvD,OAAO;IACP,WAAW;IACX,WAAW;IACX,WAAW;IACX,UAAU;IACV,UAAU;IACV,aAAa;IACb,WAAW;IACX,gBAAgB;IAChB,kBAAkB;IAClB,iBAAiB;IACjB,uBAAuB;IACvB,yBAAyB;IACzB,MAAM;IACN,UAAU;IACV,kBAAkB;IAClB,kBAAkB;IAClB,eAAe;IACf,gBAAgB;IAChB,eAAe;IACf,YAAY;IACZ,cAAc;IACd,eAAe;IACf,cAAc;IACd,iBAAiB;IACjB,eAAe;IACf,aAAa;IACb,aAAa;CACd,CAAC;AAEF;;;;;GAKG;AACH,MAAM,kBAAkB,GAAG,kCAAkC,CAAC;AAE9D;;;;GAIG;AACH,MAAM,8BAA8B,GAAsB;IACxD,aAAa;IACb,kBAAkB;IAClB,kBAAkB;CACnB,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAc;IACxC,UAAU;IACV,qBAAqB,EAAE,8BAA8B;IACrD,oBAAoB,EAAE,6BAA6B;IACnD,gBAAgB,EAAE,kBAAkB;CACrC,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Python import-statement → dependency-site emission.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `walk.ts` so the main walker stays focused on
|
|
5
|
+
* function-occurrence construction.
|
|
6
|
+
*
|
|
7
|
+
* Walk a Python file's top-level statements for `import_statement` and
|
|
8
|
+
* `import_from_statement` nodes; emit one `DependencySiteRecord` per
|
|
9
|
+
* imported module (not per imported name). The owner is the file's
|
|
10
|
+
* synthesized module-init occurrence (every file has exactly one).
|
|
11
|
+
*
|
|
12
|
+
* Phase 4 of opensip's substrate consolidation (DEC-498). The emitted
|
|
13
|
+
* `specifier` preserves the raw dotted module path as written, including
|
|
14
|
+
* any leading dots for relative imports (`'.foo'`, `'..pkg.bar'`).
|
|
15
|
+
*
|
|
16
|
+
* Out of scope at v1:
|
|
17
|
+
* - Wildcard `from foo import *` is treated like any other from-import:
|
|
18
|
+
* one dep site on the source module (`foo`); the `*` semantics
|
|
19
|
+
* (re-exports) are not modelled.
|
|
20
|
+
* - `__all__` semantics, dynamic `__import__` / `importlib.import_module`,
|
|
21
|
+
* and conditional / nested imports inside function bodies (only
|
|
22
|
+
* top-level imports are emitted).
|
|
23
|
+
*/
|
|
24
|
+
import type { PythonParsedFile } from './parse.js';
|
|
25
|
+
import type { DependencySiteRecord } from '@opensip-cli/graph';
|
|
26
|
+
export declare function collectDependencySites(file: PythonParsedFile, moduleInitHash: string, out: DependencySiteRecord[]): void;
|
|
27
|
+
//# sourceMappingURL=walk-dependencies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walk-dependencies.d.ts","sourceRoot":"","sources":["../src/walk-dependencies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAG/D,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,gBAAgB,EACtB,cAAc,EAAE,MAAM,EACtB,GAAG,EAAE,oBAAoB,EAAE,GAC1B,IAAI,CAQN"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Python import-statement → dependency-site emission.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `walk.ts` so the main walker stays focused on
|
|
5
|
+
* function-occurrence construction.
|
|
6
|
+
*
|
|
7
|
+
* Walk a Python file's top-level statements for `import_statement` and
|
|
8
|
+
* `import_from_statement` nodes; emit one `DependencySiteRecord` per
|
|
9
|
+
* imported module (not per imported name). The owner is the file's
|
|
10
|
+
* synthesized module-init occurrence (every file has exactly one).
|
|
11
|
+
*
|
|
12
|
+
* Phase 4 of opensip's substrate consolidation (DEC-498). The emitted
|
|
13
|
+
* `specifier` preserves the raw dotted module path as written, including
|
|
14
|
+
* any leading dots for relative imports (`'.foo'`, `'..pkg.bar'`).
|
|
15
|
+
*
|
|
16
|
+
* Out of scope at v1:
|
|
17
|
+
* - Wildcard `from foo import *` is treated like any other from-import:
|
|
18
|
+
* one dep site on the source module (`foo`); the `*` semantics
|
|
19
|
+
* (re-exports) are not modelled.
|
|
20
|
+
* - `__all__` semantics, dynamic `__import__` / `importlib.import_module`,
|
|
21
|
+
* and conditional / nested imports inside function bodies (only
|
|
22
|
+
* top-level imports are emitted).
|
|
23
|
+
*/
|
|
24
|
+
import { childrenOf, namedChildrenOf } from '@opensip-cli/graph-adapter-common';
|
|
25
|
+
export function collectDependencySites(file, moduleInitHash, out) {
|
|
26
|
+
for (const stmt of namedChildrenOf(file.tree.rootNode)) {
|
|
27
|
+
if (stmt.type === 'import_statement') {
|
|
28
|
+
collectFromImportStatement(stmt, file, moduleInitHash, out);
|
|
29
|
+
}
|
|
30
|
+
else if (stmt.type === 'import_from_statement') {
|
|
31
|
+
collectFromImportFromStatement(stmt, file, moduleInitHash, out);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* `import foo`, `import foo.bar`, `import foo as f`, `import a, b.c, d`.
|
|
37
|
+
* Each name child is either a `dotted_name` or an `aliased_import` whose
|
|
38
|
+
* `name` field is a `dotted_name`. Emits one dep site per top-level
|
|
39
|
+
* comma-separated target.
|
|
40
|
+
*/
|
|
41
|
+
function collectFromImportStatement(stmt, file, moduleInitHash, out) {
|
|
42
|
+
for (const child of namedChildrenOf(stmt)) {
|
|
43
|
+
const dotted = resolveImportedNameDotted(child);
|
|
44
|
+
if (!dotted)
|
|
45
|
+
continue;
|
|
46
|
+
pushDependencySite(stmt, file, moduleInitHash, dotted.text, out);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* `from foo import x`, `from foo.bar import a, b`, `from . import x`,
|
|
51
|
+
* `from .pkg import y`, `from ..parent import z`.
|
|
52
|
+
*
|
|
53
|
+
* The module-name field encodes the source module:
|
|
54
|
+
* - `dotted_name` → absolute import (`from foo.bar import …`)
|
|
55
|
+
* - `relative_import` → leading dot(s) + optional `dotted_name`
|
|
56
|
+
*
|
|
57
|
+
* For `from . import name`, the relative_import has only dots (no
|
|
58
|
+
* trailing dotted_name); the specifier is the leading-dot string
|
|
59
|
+
* (e.g. `'.'`) — the *imported names* are themselves modules to resolve.
|
|
60
|
+
* To preserve the one-dep-site-per-imported-module rule for this shape,
|
|
61
|
+
* we walk the `name` field too and emit one site per imported name,
|
|
62
|
+
* each carrying the prefix-dots + name (e.g. `.sibling`).
|
|
63
|
+
*
|
|
64
|
+
* For all other relative shapes (`from .pkg import x`, `from ..pkg.sub
|
|
65
|
+
* import y`), the relative_import already carries the module path; we
|
|
66
|
+
* emit ONE dep site with the raw relative-import text as specifier.
|
|
67
|
+
*/
|
|
68
|
+
function collectFromImportFromStatement(stmt, file, moduleInitHash, out) {
|
|
69
|
+
const moduleNameField = stmt.childForFieldName('module_name');
|
|
70
|
+
if (!moduleNameField)
|
|
71
|
+
return;
|
|
72
|
+
if (moduleNameField.type === 'dotted_name') {
|
|
73
|
+
pushDependencySite(stmt, file, moduleInitHash, moduleNameField.text, out);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (moduleNameField.type === 'relative_import') {
|
|
77
|
+
collectFromRelativeImport(stmt, moduleNameField, file, moduleInitHash, out);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Handle the `from <relative_import> import …` case. Splits into two
|
|
82
|
+
* shapes:
|
|
83
|
+
* - relative_import carries an inner dotted_name (`from .pkg import x`)
|
|
84
|
+
* → ONE dep site with `prefix + inner` as specifier.
|
|
85
|
+
* - relative_import is dots-only (`from . import sibling, other`)
|
|
86
|
+
* → ONE dep site PER imported name, specifier `prefix + name`.
|
|
87
|
+
*/
|
|
88
|
+
function collectFromRelativeImport(stmt, moduleNameField, file, moduleInitHash, out) {
|
|
89
|
+
const prefix = relativeImportPrefix(moduleNameField);
|
|
90
|
+
const innerDotted = relativeImportInnerDotted(moduleNameField);
|
|
91
|
+
if (innerDotted !== null) {
|
|
92
|
+
// `from .pkg import x` — one site, specifier = '.pkg'
|
|
93
|
+
pushDependencySite(stmt, file, moduleInitHash, prefix + innerDotted, out);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// `from . import sibling, other` — one site PER imported name,
|
|
97
|
+
// specifier = `.sibling`, `.other`.
|
|
98
|
+
for (const named of namedChildrenOf(stmt)) {
|
|
99
|
+
// web-tree-sitter returns fresh Node wrappers per access, so `named`
|
|
100
|
+
// is never reference-identical to `moduleNameField`; skip the module
|
|
101
|
+
// node by its stable byte span instead of `===`.
|
|
102
|
+
if (named.startIndex === moduleNameField.startIndex)
|
|
103
|
+
continue;
|
|
104
|
+
const dotted = resolveImportedNameDotted(named);
|
|
105
|
+
if (!dotted)
|
|
106
|
+
continue;
|
|
107
|
+
pushDependencySite(stmt, file, moduleInitHash, prefix + dotted.text, out);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Resolve an imported-name child to its `dotted_name` node, if any.
|
|
112
|
+
* Accepts either a bare `dotted_name` or an `aliased_import` whose
|
|
113
|
+
* `name` field is a `dotted_name`. Returns null for anything else
|
|
114
|
+
* (e.g. punctuation, comments).
|
|
115
|
+
*/
|
|
116
|
+
function resolveImportedNameDotted(named) {
|
|
117
|
+
if (named.type === 'dotted_name')
|
|
118
|
+
return named;
|
|
119
|
+
if (named.type === 'aliased_import') {
|
|
120
|
+
const nameField = named.childForFieldName('name');
|
|
121
|
+
if (nameField?.type === 'dotted_name')
|
|
122
|
+
return nameField;
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
function relativeImportPrefix(node) {
|
|
127
|
+
// The `import_prefix` child carries the leading dots as raw text.
|
|
128
|
+
for (const child of childrenOf(node)) {
|
|
129
|
+
if (child.type === 'import_prefix')
|
|
130
|
+
return child.text;
|
|
131
|
+
}
|
|
132
|
+
/* v8 ignore next */
|
|
133
|
+
return '';
|
|
134
|
+
}
|
|
135
|
+
function relativeImportInnerDotted(node) {
|
|
136
|
+
for (const child of namedChildrenOf(node)) {
|
|
137
|
+
if (child.type === 'dotted_name')
|
|
138
|
+
return child.text;
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
function pushDependencySite(stmt, file, ownerHash, specifier, out) {
|
|
143
|
+
out.push({
|
|
144
|
+
nodeRef: stmt,
|
|
145
|
+
sourceFileRef: file,
|
|
146
|
+
ownerHash,
|
|
147
|
+
specifier,
|
|
148
|
+
line: stmt.startPosition.row + 1,
|
|
149
|
+
column: stmt.startPosition.column,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=walk-dependencies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walk-dependencies.js","sourceRoot":"","sources":["../src/walk-dependencies.ts"],"names":[],"mappings":"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"}
|
package/dist/walk.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python walkProject — emit FunctionOccurrences + CallSiteRecords.
|
|
3
|
+
*
|
|
4
|
+
* One descent per file, mirroring lang-typescript/walk.ts. Identifies
|
|
5
|
+
* five callable shapes:
|
|
6
|
+
*
|
|
7
|
+
* - `function_definition` outside a class body → 'function-declaration'
|
|
8
|
+
* - `function_definition` inside a class body → 'method'
|
|
9
|
+
* - `function_definition` named `__init__` → 'constructor'
|
|
10
|
+
* - `lambda` → 'arrow'
|
|
11
|
+
* - one synthetic `<module-init>` per file
|
|
12
|
+
*
|
|
13
|
+
* Body hashing: sha256 of normalized body text. "Normalized" means:
|
|
14
|
+
* 1. Strip Python comments (`#` to end-of-line).
|
|
15
|
+
* 2. Strip module-level / function-level docstrings — recognized as
|
|
16
|
+
* a leading `expression_statement` whose only child is a `string`.
|
|
17
|
+
* This is the textually-conservative choice; pretty-formatter
|
|
18
|
+
* normalization (e.g. ruff/black-style reflow) is out of scope.
|
|
19
|
+
* 3. Collapse all whitespace runs to a single space, trim.
|
|
20
|
+
*
|
|
21
|
+
* Only string literals **at the top of a function body** are stripped
|
|
22
|
+
* as docstrings. String literals embedded in expressions are preserved.
|
|
23
|
+
*
|
|
24
|
+
* Call-site records:
|
|
25
|
+
* - `call` node — every Python call expression (`foo()`, `obj.method(...)`,
|
|
26
|
+
* `f.g.h()`).
|
|
27
|
+
* - 'creation' edges — for each `lambda` expression nested inside a
|
|
28
|
+
* parent function/method/module-init, emit a creation edge so
|
|
29
|
+
* reachability flows through closures even when the lambda's
|
|
30
|
+
* dispatch is unresolvable. Mirror of lang-typescript's
|
|
31
|
+
* `isInlineCallable` rule applied to lambdas.
|
|
32
|
+
*
|
|
33
|
+
* Test detection happens here too (mirrors lang-typescript): we don't
|
|
34
|
+
* emit special records, but we tag each occurrence's `inTestFile` flag
|
|
35
|
+
* via the path predicate.
|
|
36
|
+
*/
|
|
37
|
+
import type { PythonParsedProject } from './parse.js';
|
|
38
|
+
import type { WalkInput, WalkOutput } from '@opensip-cli/graph';
|
|
39
|
+
declare const isTestFile: (rel: string) => boolean;
|
|
40
|
+
export { isTestFile };
|
|
41
|
+
export declare function walkProject(input: WalkInput<PythonParsedProject>): WalkOutput;
|
|
42
|
+
//# sourceMappingURL=walk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walk.d.ts","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAkBH,OAAO,KAAK,EAAoB,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,KAAK,EAAsC,SAAS,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAOpG,QAAA,MAAQ,UAAU,0BAIhB,CAAC;AAEH,OAAO,EAAE,UAAU,EAAE,CAAC;AAEtB,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,mBAAmB,CAAC,GAAG,UAAU,CAE7E"}
|
package/dist/walk.js
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
// @fitness-ignore-file context-mutation -- `ctx: WalkCtx` here is a function-scoped traversal accumulator (callSites array, occurrence sink, parser refs) threaded through the AST walk, NOT a shared request/execution context. `ctx.callSites.push(...)` is the intended local-accumulator append. The check's `LOCAL_DECLARATION_PATTERNS` heuristic doesn't see it because `ctx` arrives as a typed parameter, not via `const ctx = …`.
|
|
2
|
+
/**
|
|
3
|
+
* Python walkProject — emit FunctionOccurrences + CallSiteRecords.
|
|
4
|
+
*
|
|
5
|
+
* One descent per file, mirroring lang-typescript/walk.ts. Identifies
|
|
6
|
+
* five callable shapes:
|
|
7
|
+
*
|
|
8
|
+
* - `function_definition` outside a class body → 'function-declaration'
|
|
9
|
+
* - `function_definition` inside a class body → 'method'
|
|
10
|
+
* - `function_definition` named `__init__` → 'constructor'
|
|
11
|
+
* - `lambda` → 'arrow'
|
|
12
|
+
* - one synthetic `<module-init>` per file
|
|
13
|
+
*
|
|
14
|
+
* Body hashing: sha256 of normalized body text. "Normalized" means:
|
|
15
|
+
* 1. Strip Python comments (`#` to end-of-line).
|
|
16
|
+
* 2. Strip module-level / function-level docstrings — recognized as
|
|
17
|
+
* a leading `expression_statement` whose only child is a `string`.
|
|
18
|
+
* This is the textually-conservative choice; pretty-formatter
|
|
19
|
+
* normalization (e.g. ruff/black-style reflow) is out of scope.
|
|
20
|
+
* 3. Collapse all whitespace runs to a single space, trim.
|
|
21
|
+
*
|
|
22
|
+
* Only string literals **at the top of a function body** are stripped
|
|
23
|
+
* as docstrings. String literals embedded in expressions are preserved.
|
|
24
|
+
*
|
|
25
|
+
* Call-site records:
|
|
26
|
+
* - `call` node — every Python call expression (`foo()`, `obj.method(...)`,
|
|
27
|
+
* `f.g.h()`).
|
|
28
|
+
* - 'creation' edges — for each `lambda` expression nested inside a
|
|
29
|
+
* parent function/method/module-init, emit a creation edge so
|
|
30
|
+
* reachability flows through closures even when the lambda's
|
|
31
|
+
* dispatch is unresolvable. Mirror of lang-typescript's
|
|
32
|
+
* `isInlineCallable` rule applied to lambdas.
|
|
33
|
+
*
|
|
34
|
+
* Test detection happens here too (mirrors lang-typescript): we don't
|
|
35
|
+
* emit special records, but we tag each occurrence's `inTestFile` flag
|
|
36
|
+
* via the path predicate.
|
|
37
|
+
*/
|
|
38
|
+
import { relative, sep } from 'node:path';
|
|
39
|
+
import { childrenOf, makeFileClassifier, namedChildrenOf, nameOf, record, runWalk, synthesizeModuleInit as buildModuleInit, } from '@opensip-cli/graph-adapter-common';
|
|
40
|
+
import { digestPythonBody, digestSyntheticBody } from './body-digest.js';
|
|
41
|
+
import { collectDependencySites } from './walk-dependencies.js';
|
|
42
|
+
const TEST_PATH_RE = /(?:^|\/)tests?\//;
|
|
43
|
+
const TEST_FILE_NAME_RE = /(?:^|\/)test_[^/]+\.py$|_test\.py$/;
|
|
44
|
+
const GENERATED_PATH_RE = /\bdist\/|\bbuild\/|\.generated\./;
|
|
45
|
+
const { isTestFile, isGeneratedFile } = makeFileClassifier({
|
|
46
|
+
testRe: TEST_FILE_NAME_RE,
|
|
47
|
+
generatedRe: GENERATED_PATH_RE,
|
|
48
|
+
testPathRe: TEST_PATH_RE,
|
|
49
|
+
});
|
|
50
|
+
export { isTestFile };
|
|
51
|
+
export function walkProject(input) {
|
|
52
|
+
return runWalk({ input, walkFile });
|
|
53
|
+
}
|
|
54
|
+
function walkFile(absPath, file, projectDirAbs, sinks) {
|
|
55
|
+
const { occurrences: out, callSites, dependencySites } = sinks;
|
|
56
|
+
const filePathProjectRel = relative(projectDirAbs, absPath).split(sep).join('/');
|
|
57
|
+
const inTestFile = isTestFile(filePathProjectRel);
|
|
58
|
+
const definedInGenerated = isGeneratedFile(filePathProjectRel);
|
|
59
|
+
const qualifiedBase = filePathProjectRel.replace(/\.py$/, '').split('/').join('.');
|
|
60
|
+
const moduleInit = buildModuleInit({
|
|
61
|
+
file,
|
|
62
|
+
filePathProjectRel,
|
|
63
|
+
inTestFile,
|
|
64
|
+
definedInGenerated,
|
|
65
|
+
digestSyntheticBody,
|
|
66
|
+
qualifiedName: `${qualifiedBase}.<module-init>`,
|
|
67
|
+
});
|
|
68
|
+
record(out, moduleInit);
|
|
69
|
+
// Phase 4 (DEC-498): walk top-level imports as dependency sites. Owner
|
|
70
|
+
// is the file's synthesized module-init occurrence.
|
|
71
|
+
collectDependencySites(file, moduleInit.bodyHash, dependencySites);
|
|
72
|
+
const initialFrame = {
|
|
73
|
+
ownerHash: moduleInit.bodyHash,
|
|
74
|
+
enclosingClass: null,
|
|
75
|
+
};
|
|
76
|
+
const ctx = {
|
|
77
|
+
file,
|
|
78
|
+
filePathProjectRel,
|
|
79
|
+
inTestFile,
|
|
80
|
+
definedInGenerated,
|
|
81
|
+
out,
|
|
82
|
+
callSites,
|
|
83
|
+
};
|
|
84
|
+
for (const child of childrenOf(file.tree.rootNode))
|
|
85
|
+
visit(child, initialFrame, ctx);
|
|
86
|
+
}
|
|
87
|
+
// @graph-ignore-next-line graph:cycle -- intentional recursive-descent AST visitor; the cycle is the traversal (visit re-enters via the class/function helpers)
|
|
88
|
+
function visit(node, frame, ctx) {
|
|
89
|
+
if (node.type === 'class_definition') {
|
|
90
|
+
visitClass(node, frame, ctx);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (node.type === 'function_definition') {
|
|
94
|
+
visitFunction(node, frame, ctx);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (node.type === 'lambda' && visitLambdaNode(node, frame, ctx)) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (node.type === 'call') {
|
|
101
|
+
ctx.callSites.push({
|
|
102
|
+
nodeRef: node,
|
|
103
|
+
sourceFileRef: ctx.file,
|
|
104
|
+
ownerHash: frame.ownerHash,
|
|
105
|
+
kind: 'call',
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
for (const child of childrenOf(node))
|
|
109
|
+
visit(child, frame, ctx);
|
|
110
|
+
}
|
|
111
|
+
function visitClass(node, frame, ctx) {
|
|
112
|
+
const className = nameOf(node) ?? '<anon-class>';
|
|
113
|
+
// Don't emit a function for the class itself — Python classes are
|
|
114
|
+
// declarations whose top-level statements run at module load. Keep
|
|
115
|
+
// the module-init as the owner; descend with class context for
|
|
116
|
+
// nested function_definitions to be tagged as methods.
|
|
117
|
+
const childFrame = { ownerHash: frame.ownerHash, enclosingClass: className };
|
|
118
|
+
for (const child of childrenOf(node))
|
|
119
|
+
visit(child, childFrame, ctx);
|
|
120
|
+
}
|
|
121
|
+
function visitFunction(node, frame, ctx) {
|
|
122
|
+
const occ = visitFunctionDefinition(node, frame.enclosingClass, ctx);
|
|
123
|
+
if (!occ)
|
|
124
|
+
return;
|
|
125
|
+
record(ctx.out, occ);
|
|
126
|
+
const childFrame = { ownerHash: occ.bodyHash, enclosingClass: null };
|
|
127
|
+
const body = node.childForFieldName('body');
|
|
128
|
+
if (body) {
|
|
129
|
+
for (const child of childrenOf(body))
|
|
130
|
+
visit(child, childFrame, ctx);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function visitLambdaNode(node, frame, ctx) {
|
|
134
|
+
const occ = visitLambda(node, ctx);
|
|
135
|
+
if (!occ)
|
|
136
|
+
return false;
|
|
137
|
+
record(ctx.out, occ);
|
|
138
|
+
if (frame.ownerHash !== occ.bodyHash) {
|
|
139
|
+
ctx.callSites.push({
|
|
140
|
+
nodeRef: node,
|
|
141
|
+
sourceFileRef: ctx.file,
|
|
142
|
+
ownerHash: frame.ownerHash,
|
|
143
|
+
kind: 'creation',
|
|
144
|
+
childHash: occ.bodyHash,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
const body = node.childForFieldName('body');
|
|
148
|
+
if (body) {
|
|
149
|
+
visit(body, { ownerHash: occ.bodyHash, enclosingClass: null }, ctx);
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
function visitFunctionDefinition(node, enclosingClass, ctx) {
|
|
154
|
+
const { file, filePathProjectRel, inTestFile, definedInGenerated } = ctx;
|
|
155
|
+
const name = nameOf(node) ?? '<anon-fn>';
|
|
156
|
+
const digest = digestPythonBody(file.source.slice(node.startIndex, node.endIndex));
|
|
157
|
+
const kind = classifyFunctionKind(name, enclosingClass);
|
|
158
|
+
const qualifiedBase = filePathProjectRel.replace(/\.py$/, '').split('/').join('.');
|
|
159
|
+
const qualifiedName = enclosingClass === null
|
|
160
|
+
? `${qualifiedBase}.${name}`
|
|
161
|
+
: `${qualifiedBase}.${enclosingClass}.${name}`;
|
|
162
|
+
return {
|
|
163
|
+
bodyHash: digest.hash,
|
|
164
|
+
bodySize: digest.size,
|
|
165
|
+
simpleName: name,
|
|
166
|
+
qualifiedName,
|
|
167
|
+
filePath: filePathProjectRel,
|
|
168
|
+
line: node.startPosition.row + 1,
|
|
169
|
+
column: node.startPosition.column,
|
|
170
|
+
endLine: node.endPosition.row + 1,
|
|
171
|
+
kind,
|
|
172
|
+
params: extractParams(node),
|
|
173
|
+
returnType: null,
|
|
174
|
+
enclosingClass,
|
|
175
|
+
decorators: extractDecorators(node),
|
|
176
|
+
visibility: name.startsWith('_') ? 'module-local' : 'exported',
|
|
177
|
+
inTestFile,
|
|
178
|
+
definedInGenerated,
|
|
179
|
+
calls: [],
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function classifyFunctionKind(name, enclosingClass) {
|
|
183
|
+
if (enclosingClass === null)
|
|
184
|
+
return 'function-declaration';
|
|
185
|
+
if (name === '__init__')
|
|
186
|
+
return 'constructor';
|
|
187
|
+
return 'method';
|
|
188
|
+
}
|
|
189
|
+
function visitLambda(node, ctx) {
|
|
190
|
+
const { file, filePathProjectRel, inTestFile, definedInGenerated } = ctx;
|
|
191
|
+
const digest = digestPythonBody(file.source.slice(node.startIndex, node.endIndex));
|
|
192
|
+
const startLine = node.startPosition.row + 1;
|
|
193
|
+
const startCol = node.startPosition.column;
|
|
194
|
+
const simpleName = `<arrow:${filePathProjectRel}:${String(startLine)}:${String(startCol)}>`;
|
|
195
|
+
const qualifiedBase = filePathProjectRel.replace(/\.py$/, '').split('/').join('.');
|
|
196
|
+
return {
|
|
197
|
+
bodyHash: digest.hash,
|
|
198
|
+
bodySize: digest.size,
|
|
199
|
+
simpleName,
|
|
200
|
+
qualifiedName: `${qualifiedBase}.<lambda:${String(startLine)}:${String(startCol)}>`,
|
|
201
|
+
filePath: filePathProjectRel,
|
|
202
|
+
line: startLine,
|
|
203
|
+
column: startCol,
|
|
204
|
+
endLine: node.endPosition.row + 1,
|
|
205
|
+
kind: 'arrow',
|
|
206
|
+
params: extractParamsFromField(node, 'parameters'),
|
|
207
|
+
returnType: null,
|
|
208
|
+
enclosingClass: null,
|
|
209
|
+
decorators: [],
|
|
210
|
+
visibility: 'private',
|
|
211
|
+
inTestFile,
|
|
212
|
+
definedInGenerated,
|
|
213
|
+
calls: [],
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
// ── helpers ───────────────────────────────────────────────────────
|
|
217
|
+
function extractParams(node) {
|
|
218
|
+
return extractParamsFromField(node, 'parameters');
|
|
219
|
+
}
|
|
220
|
+
function extractParamsFromField(node, fieldName) {
|
|
221
|
+
const params = node.childForFieldName(fieldName);
|
|
222
|
+
if (!params)
|
|
223
|
+
return [];
|
|
224
|
+
const out = [];
|
|
225
|
+
for (const child of namedChildrenOf(params)) {
|
|
226
|
+
const param = extractParam(child);
|
|
227
|
+
if (param)
|
|
228
|
+
out.push(param);
|
|
229
|
+
}
|
|
230
|
+
return out;
|
|
231
|
+
}
|
|
232
|
+
function extractParam(child) {
|
|
233
|
+
switch (child.type) {
|
|
234
|
+
case 'identifier': {
|
|
235
|
+
return { name: child.text, optional: false, rest: false };
|
|
236
|
+
}
|
|
237
|
+
case 'typed_parameter':
|
|
238
|
+
case 'default_parameter':
|
|
239
|
+
case 'typed_default_parameter': {
|
|
240
|
+
const name = child.childForFieldName('name') ?? child.namedChild(0);
|
|
241
|
+
if (!name)
|
|
242
|
+
return null;
|
|
243
|
+
return {
|
|
244
|
+
name: name.text,
|
|
245
|
+
optional: child.type === 'default_parameter' || child.type === 'typed_default_parameter',
|
|
246
|
+
rest: false,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
/* v8 ignore start */
|
|
250
|
+
case 'list_splat_pattern':
|
|
251
|
+
case 'dictionary_splat_pattern': {
|
|
252
|
+
const name = child.namedChild(0);
|
|
253
|
+
if (!name)
|
|
254
|
+
return null;
|
|
255
|
+
return { name: name.text, optional: false, rest: true };
|
|
256
|
+
}
|
|
257
|
+
default: {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
/* v8 ignore stop */
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function extractDecorators(node) {
|
|
264
|
+
// tree-sitter-python wraps a function_definition in a `decorated_definition`
|
|
265
|
+
// node when decorators are present. The decorators are siblings of the
|
|
266
|
+
// function_definition inside that wrapper.
|
|
267
|
+
if (node.parent?.type !== 'decorated_definition')
|
|
268
|
+
return [];
|
|
269
|
+
/* v8 ignore start */
|
|
270
|
+
const out = [];
|
|
271
|
+
for (const child of namedChildrenOf(node.parent)) {
|
|
272
|
+
if (child.type === 'decorator') {
|
|
273
|
+
// Decorator text is `@expr`; trim the leading `@`.
|
|
274
|
+
const text = child.text.trim();
|
|
275
|
+
out.push(text.startsWith('@') ? text.slice(1) : text);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return out;
|
|
279
|
+
/* v8 ignore stop */
|
|
280
|
+
}
|
|
281
|
+
//# sourceMappingURL=walk.js.map
|
package/dist/walk.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walk.js","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opensip-cli/graph-python",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"description": "Python language adapter for the opensip graph engine",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"opensip-cli",
|
|
8
|
+
"static-analysis",
|
|
9
|
+
"code-quality",
|
|
10
|
+
"call-graph",
|
|
11
|
+
"dependency-graph",
|
|
12
|
+
"python"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/opensip-ai/opensip-cli.git",
|
|
17
|
+
"directory": "packages/graph/graph-python"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/opensip-ai/opensip-cli",
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/opensip-ai/opensip-cli/issues"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": "./dist/index.js"
|
|
28
|
+
},
|
|
29
|
+
"opensipTools": {
|
|
30
|
+
"kind": "graph-adapter"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"LICENSE",
|
|
35
|
+
"NOTICE"
|
|
36
|
+
],
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@opensip-cli/core": "0.1.0",
|
|
39
|
+
"@opensip-cli/graph-adapter-common": "0.1.0",
|
|
40
|
+
"@opensip-cli/lang-python": "0.1.0",
|
|
41
|
+
"@opensip-cli/tree-sitter": "0.1.0",
|
|
42
|
+
"@opensip-cli/graph": "0.1.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^24.13.2",
|
|
46
|
+
"vitest": "^4.1.8"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsc",
|
|
50
|
+
"test": "vitest run --passWithNoTests",
|
|
51
|
+
"typecheck": "tsc --noEmit",
|
|
52
|
+
"clean": "rm -rf dist"
|
|
53
|
+
}
|
|
54
|
+
}
|