@suss/adapter-python 0.25.0 → 0.27.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/dist/index.cjs +339 -56
- package/dist/index.d.cts +226 -196
- package/dist/index.d.ts +226 -196
- package/dist/index.js +339 -56
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -93,6 +93,25 @@ function children(node) {
|
|
|
93
93
|
function bodyStatements(body) {
|
|
94
94
|
return children(body);
|
|
95
95
|
}
|
|
96
|
+
function nestedStatements(stmt) {
|
|
97
|
+
const found = [];
|
|
98
|
+
for (const child of children(stmt)) {
|
|
99
|
+
if (child.type === "block") {
|
|
100
|
+
for (const inner of children(child)) {
|
|
101
|
+
if (inner.type.endsWith("_clause")) {
|
|
102
|
+
found.push(...nestedStatements(inner));
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
found.push(inner);
|
|
106
|
+
}
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (child.type.endsWith("_clause")) {
|
|
110
|
+
found.push(...nestedStatements(child));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return found;
|
|
114
|
+
}
|
|
96
115
|
function stripDecorators(node) {
|
|
97
116
|
if (node.type !== "decorated_definition") {
|
|
98
117
|
return { definition: node, decorators: [] };
|
|
@@ -453,8 +472,36 @@ function resolveNonlocal(scope, name) {
|
|
|
453
472
|
}
|
|
454
473
|
|
|
455
474
|
// src/annotations.ts
|
|
456
|
-
function createAnnotationContext(scopeFor) {
|
|
457
|
-
return {
|
|
475
|
+
function createAnnotationContext(scopeFor, importedDefinition = null) {
|
|
476
|
+
return {
|
|
477
|
+
scopeMaps: [scopeFor],
|
|
478
|
+
definitions: /* @__PURE__ */ new Map(),
|
|
479
|
+
importedDefinition,
|
|
480
|
+
expanding: /* @__PURE__ */ new Set()
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
function scopeOfNode(ctx, node) {
|
|
484
|
+
for (const scopeFor of ctx.scopeMaps) {
|
|
485
|
+
const scope = scopeFor.get(node.id);
|
|
486
|
+
if (scope !== void 0) {
|
|
487
|
+
return scope;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return void 0;
|
|
491
|
+
}
|
|
492
|
+
function aliasValueOf(name, scope, ctx) {
|
|
493
|
+
const binding = resolveName(scope, name);
|
|
494
|
+
if (binding?.kind === "assignment") {
|
|
495
|
+
return binding.value === null ? null : { node: binding.value, scope };
|
|
496
|
+
}
|
|
497
|
+
if (binding?.kind !== "import" && binding?.kind !== "importFrom") {
|
|
498
|
+
return null;
|
|
499
|
+
}
|
|
500
|
+
const imported = ctx.importedDefinition?.(scope, name) ?? null;
|
|
501
|
+
if (imported === null || imported.node.type === "class_definition") {
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
return { node: imported.node, scope: imported.moduleScope };
|
|
458
505
|
}
|
|
459
506
|
function collectedDefinitions(ctx) {
|
|
460
507
|
const out = {};
|
|
@@ -497,6 +544,7 @@ var EXPRESSION_SHAPERS = {
|
|
|
497
544
|
identifier: (node, scope, ctx) => shapeFromName(node.text, scope, ctx),
|
|
498
545
|
none: () => ({ type: "null" }),
|
|
499
546
|
generic_type: shapeFromGenericType,
|
|
547
|
+
subscript: shapeFromSubscript,
|
|
500
548
|
binary_operator: shapeFromBinaryOperator
|
|
501
549
|
};
|
|
502
550
|
function shapeFromExpression(node, scope, ctx) {
|
|
@@ -518,8 +566,46 @@ function shapeFromName(name, scope, ctx) {
|
|
|
518
566
|
if (binding?.kind === "classDef") {
|
|
519
567
|
return recordShapeRef(name, binding.node, ctx);
|
|
520
568
|
}
|
|
569
|
+
if (binding?.kind === "import" || binding?.kind === "importFrom") {
|
|
570
|
+
const imported = ctx.importedDefinition?.(scope, name) ?? null;
|
|
571
|
+
if (imported?.node.type === "class_definition") {
|
|
572
|
+
if (!ctx.scopeMaps.includes(imported.scopeFor)) {
|
|
573
|
+
ctx.scopeMaps.push(imported.scopeFor);
|
|
574
|
+
}
|
|
575
|
+
return recordShapeRef(name, imported.node, ctx);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
const alias = aliasValueOf(name, scope, ctx);
|
|
579
|
+
if (alias !== null) {
|
|
580
|
+
return shapeFromAliasValue(name, alias, ctx);
|
|
581
|
+
}
|
|
521
582
|
return { type: "ref", name };
|
|
522
583
|
}
|
|
584
|
+
function shapeFromAliasValue(name, alias, ctx) {
|
|
585
|
+
if (ctx.expanding.has(alias.node.id)) {
|
|
586
|
+
return { type: "ref", name };
|
|
587
|
+
}
|
|
588
|
+
ctx.expanding.add(alias.node.id);
|
|
589
|
+
try {
|
|
590
|
+
return shapeFromExpression(alias.node, alias.scope, ctx);
|
|
591
|
+
} finally {
|
|
592
|
+
ctx.expanding.delete(alias.node.id);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
function shapeFromSubscript(node, scope, ctx) {
|
|
596
|
+
const base = field(node, "value");
|
|
597
|
+
const args = fields(node, "subscript");
|
|
598
|
+
const baseName = base?.type === "identifier" ? base.text : null;
|
|
599
|
+
if (baseName === "Annotated") {
|
|
600
|
+
const first = args[0];
|
|
601
|
+
return first === void 0 ? { type: "unknown" } : shapeFromExpression(first, scope, ctx);
|
|
602
|
+
}
|
|
603
|
+
return shapeFromGeneric(
|
|
604
|
+
baseName,
|
|
605
|
+
node.text,
|
|
606
|
+
args.map((arg) => shapeFromExpression(arg, scope, ctx))
|
|
607
|
+
);
|
|
608
|
+
}
|
|
523
609
|
function genericTypeArgs(node) {
|
|
524
610
|
const typeParameter = node.namedChildren.find(
|
|
525
611
|
(child) => child !== null && child.type === "type_parameter"
|
|
@@ -530,14 +616,20 @@ function genericTypeArgs(node) {
|
|
|
530
616
|
}
|
|
531
617
|
function shapeFromGenericType(node, scope, ctx) {
|
|
532
618
|
const base = node.namedChild(0);
|
|
533
|
-
const args = genericTypeArgs(node);
|
|
534
|
-
const shapeOf = (arg) => arg !== void 0 ? annotationToShape(arg, scope, ctx) : { type: "unknown" };
|
|
535
619
|
const baseName = base?.type === "identifier" ? base.text : null;
|
|
620
|
+
return shapeFromGeneric(
|
|
621
|
+
baseName,
|
|
622
|
+
node.text,
|
|
623
|
+
genericTypeArgs(node).map((arg) => annotationToShape(arg, scope, ctx))
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
function shapeFromGeneric(baseName, text2, args) {
|
|
627
|
+
const shapeOf = (arg) => arg ?? { type: "unknown" };
|
|
536
628
|
if (baseName === "Optional") {
|
|
537
629
|
return { type: "union", variants: [shapeOf(args[0]), { type: "null" }] };
|
|
538
630
|
}
|
|
539
631
|
if (baseName === "Union") {
|
|
540
|
-
return { type: "union", variants: args
|
|
632
|
+
return { type: "union", variants: args };
|
|
541
633
|
}
|
|
542
634
|
if (baseName !== null && LIST_NAMES.has(baseName)) {
|
|
543
635
|
return { type: "array", items: shapeOf(args[0]) };
|
|
@@ -548,7 +640,7 @@ function shapeFromGenericType(node, scope, ctx) {
|
|
|
548
640
|
if (baseName !== null && TUPLE_SET_NAMES.has(baseName)) {
|
|
549
641
|
return { type: "array", items: shapeOf(args[0]) };
|
|
550
642
|
}
|
|
551
|
-
return { type: "ref", name: baseName ??
|
|
643
|
+
return { type: "ref", name: baseName ?? text2 };
|
|
552
644
|
}
|
|
553
645
|
function shapeFromBinaryOperator(node, scope, ctx) {
|
|
554
646
|
const operator = field(node, "operator");
|
|
@@ -573,7 +665,7 @@ function recordShapeRef(name, classNode, ctx) {
|
|
|
573
665
|
}
|
|
574
666
|
ctx.definitions.set(key, null);
|
|
575
667
|
const bodyNode = field(classNode, "body");
|
|
576
|
-
const classScope = ctx
|
|
668
|
+
const classScope = scopeOfNode(ctx, classNode);
|
|
577
669
|
ctx.definitions.set(
|
|
578
670
|
key,
|
|
579
671
|
bodyNode !== null && classScope !== void 0 ? recordShapeOf(bodyNode, classScope, ctx) : { type: "record", properties: {} }
|
|
@@ -1382,6 +1474,13 @@ function writtenValueOf(db, key) {
|
|
|
1382
1474
|
resolveCalls(db, [key]);
|
|
1383
1475
|
return sharedWrittenValueOf(db, key, (keys) => resolveCalls(db, keys));
|
|
1384
1476
|
}
|
|
1477
|
+
function originsOf(db, nameKey) {
|
|
1478
|
+
if (!db.facts("wantedOrigin").some((row) => String(row[0]) === nameKey)) {
|
|
1479
|
+
db.add("wantedOrigin", [nameKey]);
|
|
1480
|
+
evaluate(db, RESOLUTION_PROGRAM.rules);
|
|
1481
|
+
}
|
|
1482
|
+
return db.facts("wantedComesFrom").filter((row) => String(row[0]) === nameKey).map((row) => ({ module: String(row[1]), name: String(row[2]) }));
|
|
1483
|
+
}
|
|
1385
1484
|
function subjectConstructions(db, valueKeys) {
|
|
1386
1485
|
const found = /* @__PURE__ */ new Map();
|
|
1387
1486
|
if (valueKeys.length === 0) {
|
|
@@ -1717,7 +1816,7 @@ function emitBinding(emitter, assignment2, receiver) {
|
|
|
1717
1816
|
emitter,
|
|
1718
1817
|
"binds",
|
|
1719
1818
|
nameId(emitter.filePath, left.text),
|
|
1720
|
-
valueKey(emitter, right)
|
|
1819
|
+
valueKey(emitter, assignedValue(right))
|
|
1721
1820
|
);
|
|
1722
1821
|
return;
|
|
1723
1822
|
}
|
|
@@ -1749,9 +1848,17 @@ function emitAssignment(emitter, assignment2) {
|
|
|
1749
1848
|
"exportsAs",
|
|
1750
1849
|
emitter.filePath,
|
|
1751
1850
|
left.text,
|
|
1752
|
-
valueKey(emitter, right)
|
|
1851
|
+
valueKey(emitter, assignedValue(right))
|
|
1753
1852
|
);
|
|
1754
1853
|
}
|
|
1854
|
+
function assignedValue(right) {
|
|
1855
|
+
if (right.type !== "subscript") {
|
|
1856
|
+
return right;
|
|
1857
|
+
}
|
|
1858
|
+
const outer = field(right, "value");
|
|
1859
|
+
const first = fields(right, "subscript")[0];
|
|
1860
|
+
return outer?.text === "Annotated" && first !== void 0 ? first : right;
|
|
1861
|
+
}
|
|
1755
1862
|
function declaredBy(statement) {
|
|
1756
1863
|
if (statement.type === "decorated_definition") {
|
|
1757
1864
|
return field(statement, "definition") ?? statement;
|
|
@@ -3347,11 +3454,15 @@ var WALK_DESCENTS = {
|
|
|
3347
3454
|
scan,
|
|
3348
3455
|
visit
|
|
3349
3456
|
);
|
|
3350
|
-
}
|
|
3457
|
+
},
|
|
3458
|
+
if_statement: descendInPlace,
|
|
3459
|
+
try_statement: descendInPlace,
|
|
3460
|
+
while_statement: descendInPlace,
|
|
3461
|
+
with_statement: descendInPlace,
|
|
3462
|
+
match_statement: descendInPlace
|
|
3351
3463
|
};
|
|
3352
|
-
function
|
|
3353
|
-
|
|
3354
|
-
return body === null ? [] : bodyStatements(body);
|
|
3464
|
+
function descendInPlace(stmt, position, scan, visit) {
|
|
3465
|
+
walkStatements(nestedStatements(stmt), position, scan, visit);
|
|
3355
3466
|
}
|
|
3356
3467
|
function walkStatements(statements, position, scan, visit) {
|
|
3357
3468
|
for (const stmt of statements) {
|
|
@@ -4432,7 +4543,7 @@ function rawSqlEffects(calls, options) {
|
|
|
4432
4543
|
binding: storageBinding({
|
|
4433
4544
|
recognition: `python-${pattern.module}`,
|
|
4434
4545
|
storageSystem: pattern.storageSystem,
|
|
4435
|
-
scope:
|
|
4546
|
+
scope: "default",
|
|
4436
4547
|
container: access.table
|
|
4437
4548
|
}),
|
|
4438
4549
|
callee: field(call, "function")?.text ?? "",
|
|
@@ -4576,16 +4687,23 @@ function typeNameOf(annotation) {
|
|
|
4576
4687
|
if (quoted !== null) {
|
|
4577
4688
|
return quoted;
|
|
4578
4689
|
}
|
|
4690
|
+
if (annotation.type === "binary_operator") {
|
|
4691
|
+
const named = [field(annotation, "left"), field(annotation, "right")].find(
|
|
4692
|
+
(side) => side !== null && side.type !== "none"
|
|
4693
|
+
);
|
|
4694
|
+
return named === void 0 || named === null ? null : typeNameOf(named);
|
|
4695
|
+
}
|
|
4579
4696
|
if (annotation.type === "generic_type") {
|
|
4580
4697
|
const outer = annotation.namedChildren[0];
|
|
4581
4698
|
const first = genericTypeArgs(annotation)[0];
|
|
4582
4699
|
if ((outer?.text === "Annotated" || outer?.text === "Optional") && first !== void 0) {
|
|
4583
4700
|
return typeNameOf(first);
|
|
4584
4701
|
}
|
|
4702
|
+
return outer?.type === "identifier" ? outer.text : null;
|
|
4585
4703
|
}
|
|
4586
4704
|
return null;
|
|
4587
4705
|
}
|
|
4588
|
-
function typeGivenBy(statement, name) {
|
|
4706
|
+
function typeGivenBy(statement, name, options) {
|
|
4589
4707
|
if (statement.type === "assignment") {
|
|
4590
4708
|
if (field(statement, "left")?.text !== name) {
|
|
4591
4709
|
return null;
|
|
@@ -4595,8 +4713,7 @@ function typeGivenBy(statement, name) {
|
|
|
4595
4713
|
return typeNameOf(annotation);
|
|
4596
4714
|
}
|
|
4597
4715
|
const right = field(statement, "right");
|
|
4598
|
-
|
|
4599
|
-
return callee?.type === "identifier" ? callee.text : null;
|
|
4716
|
+
return right?.type === "call" ? builtType(right, options) : null;
|
|
4600
4717
|
}
|
|
4601
4718
|
if (statement.type === "as_pattern") {
|
|
4602
4719
|
const alias = field(statement, "alias");
|
|
@@ -4606,7 +4723,34 @@ function typeGivenBy(statement, name) {
|
|
|
4606
4723
|
}
|
|
4607
4724
|
return null;
|
|
4608
4725
|
}
|
|
4609
|
-
function
|
|
4726
|
+
function builtType(call, options) {
|
|
4727
|
+
const root = rootOf(call);
|
|
4728
|
+
const callee = field(root, "function");
|
|
4729
|
+
if (callee?.type === "identifier") {
|
|
4730
|
+
return isClassName(callee.text) ? callee.text : resolvedReturnType(options, callee);
|
|
4731
|
+
}
|
|
4732
|
+
const object = callee?.type === "attribute" ? field(callee, "object") : null;
|
|
4733
|
+
if (object?.type === "identifier" && isClassName(object.text)) {
|
|
4734
|
+
return object.text;
|
|
4735
|
+
}
|
|
4736
|
+
const args = field(root, "arguments");
|
|
4737
|
+
const first = (args === null ? [] : children(args))[0];
|
|
4738
|
+
if (first?.type === "identifier" && isClassName(first.text)) {
|
|
4739
|
+
return first.text;
|
|
4740
|
+
}
|
|
4741
|
+
return callee === null ? null : resolvedReturnType(options, callee);
|
|
4742
|
+
}
|
|
4743
|
+
function resolvedReturnType(options, callee) {
|
|
4744
|
+
const settled = settledCallee(options, callee);
|
|
4745
|
+
return settled === void 0 ? null : returnTypeName(options.definitionAt(settled));
|
|
4746
|
+
}
|
|
4747
|
+
function settledCallee(options, callee) {
|
|
4748
|
+
const key = readKey(options.filePath, callee, enclosingFunction(callee));
|
|
4749
|
+
resolveCalls(options.facts, [key]);
|
|
4750
|
+
const resolved = options.facts.facts("wantedResolves").filter((row) => String(row[0]) === key).map((row) => String(row[1]));
|
|
4751
|
+
return resolved.length === 1 ? resolved[0] : void 0;
|
|
4752
|
+
}
|
|
4753
|
+
function declaredTypeName(name, from, options) {
|
|
4610
4754
|
const fn = enclosingFunction(from);
|
|
4611
4755
|
if (fn === null) {
|
|
4612
4756
|
return null;
|
|
@@ -4619,7 +4763,7 @@ function declaredTypeName(name, from) {
|
|
|
4619
4763
|
}
|
|
4620
4764
|
}
|
|
4621
4765
|
const visit = (node) => {
|
|
4622
|
-
const given = typeGivenBy(node, name);
|
|
4766
|
+
const given = typeGivenBy(node, name, options);
|
|
4623
4767
|
if (given !== null) {
|
|
4624
4768
|
return given;
|
|
4625
4769
|
}
|
|
@@ -4648,16 +4792,27 @@ function typedReceiverPattern(options, chain) {
|
|
|
4648
4792
|
if (receiver === null) {
|
|
4649
4793
|
return void 0;
|
|
4650
4794
|
}
|
|
4651
|
-
const typeName = declaredTypeName(receiver, chain.root);
|
|
4795
|
+
const typeName = declaredTypeName(receiver, chain.root, options);
|
|
4652
4796
|
if (typeName === null) {
|
|
4653
4797
|
return void 0;
|
|
4654
4798
|
}
|
|
4655
|
-
const
|
|
4656
|
-
|
|
4657
|
-
|
|
4799
|
+
const typeKey = `${options.filePath}#${typeName}`;
|
|
4800
|
+
const imported = options.facts.facts("pyImportedName").find((row) => String(row[0]) === typeKey);
|
|
4801
|
+
const direct = imported === void 0 ? void 0 : queryTypePattern(options, String(imported[1]), String(imported[2]));
|
|
4802
|
+
if (direct !== void 0) {
|
|
4803
|
+
return direct;
|
|
4804
|
+
}
|
|
4805
|
+
for (const origin of originsOf(options.facts, typeKey)) {
|
|
4806
|
+
const pattern = queryTypePattern(options, origin.module, origin.name);
|
|
4807
|
+
if (pattern !== void 0) {
|
|
4808
|
+
return pattern;
|
|
4809
|
+
}
|
|
4658
4810
|
}
|
|
4811
|
+
return void 0;
|
|
4812
|
+
}
|
|
4813
|
+
function queryTypePattern(options, module, name) {
|
|
4659
4814
|
return options.patterns.find(
|
|
4660
|
-
(pattern) =>
|
|
4815
|
+
(pattern) => module === pattern.module && pattern.queryTypes.includes(name)
|
|
4661
4816
|
);
|
|
4662
4817
|
}
|
|
4663
4818
|
function fieldsOf(chain, valueMethods) {
|
|
@@ -4712,7 +4867,71 @@ function selectorOf(chain, valueMethods) {
|
|
|
4712
4867
|
)
|
|
4713
4868
|
];
|
|
4714
4869
|
}
|
|
4715
|
-
|
|
4870
|
+
var TABLE_METHODS = ["select_from", "join"];
|
|
4871
|
+
function modelOf(chain, options) {
|
|
4872
|
+
const fromRoot = modelArgument(chain.root, chain.root, options);
|
|
4873
|
+
if (fromRoot !== null) {
|
|
4874
|
+
return fromRoot;
|
|
4875
|
+
}
|
|
4876
|
+
for (const call of laterCalls(chain)) {
|
|
4877
|
+
if (TABLE_METHODS.includes(methodNameOf(call))) {
|
|
4878
|
+
const named = modelArgument(call, chain.root, options);
|
|
4879
|
+
if (named !== null) {
|
|
4880
|
+
return named;
|
|
4881
|
+
}
|
|
4882
|
+
}
|
|
4883
|
+
}
|
|
4884
|
+
return isClassName(chain.subject) ? chain.subject : null;
|
|
4885
|
+
}
|
|
4886
|
+
function modelArgument(call, from, options) {
|
|
4887
|
+
const args = field(call, "arguments");
|
|
4888
|
+
const first = (args === null ? [] : children(args)).find(
|
|
4889
|
+
(argument) => argument.type !== "keyword_argument"
|
|
4890
|
+
);
|
|
4891
|
+
return first === void 0 ? null : modelNamed(first, from, options);
|
|
4892
|
+
}
|
|
4893
|
+
function modelNamed(node, from, options) {
|
|
4894
|
+
if (node.type === "identifier") {
|
|
4895
|
+
if (isClassName(node.text)) {
|
|
4896
|
+
return node.text;
|
|
4897
|
+
}
|
|
4898
|
+
const declared = declaredTypeName(node.text, from, options);
|
|
4899
|
+
return declared === null ? null : classBehind(declared, options);
|
|
4900
|
+
}
|
|
4901
|
+
if (node.type === "attribute") {
|
|
4902
|
+
const property = field(node, "attribute");
|
|
4903
|
+
if (property !== null && isClassName(property.text)) {
|
|
4904
|
+
return property.text;
|
|
4905
|
+
}
|
|
4906
|
+
const object = field(node, "object");
|
|
4907
|
+
return object === null ? null : modelNamed(object, from, options);
|
|
4908
|
+
}
|
|
4909
|
+
if (node.type === "call") {
|
|
4910
|
+
const callee = field(node, "function");
|
|
4911
|
+
const built = callee === null ? null : modelNamed(callee, from, options);
|
|
4912
|
+
return built ?? modelArgument(node, from, options);
|
|
4913
|
+
}
|
|
4914
|
+
return null;
|
|
4915
|
+
}
|
|
4916
|
+
function classBehind(typeName, options) {
|
|
4917
|
+
const key = `${options.filePath}#${typeName}`;
|
|
4918
|
+
originsOf(options.facts, key);
|
|
4919
|
+
const reached = new Set(
|
|
4920
|
+
options.facts.facts("wantedComesTo").filter((row) => String(row[0]) === key).map((row) => String(row[1]))
|
|
4921
|
+
);
|
|
4922
|
+
const classes = new Set(
|
|
4923
|
+
options.facts.facts("objectValue").map((row) => String(row[0])).filter((value) => reached.has(value))
|
|
4924
|
+
);
|
|
4925
|
+
const exported = options.facts.facts("exportsAs").find((row) => classes.has(String(row[2])));
|
|
4926
|
+
if (exported !== void 0) {
|
|
4927
|
+
return String(exported[1]);
|
|
4928
|
+
}
|
|
4929
|
+
return isClassName(typeName) ? typeName : null;
|
|
4930
|
+
}
|
|
4931
|
+
function isClassName(name) {
|
|
4932
|
+
return /^[A-Z][A-Za-z0-9_]*$/.test(name) && name !== name.toUpperCase();
|
|
4933
|
+
}
|
|
4934
|
+
function effectFor(pattern, chain, operation, options) {
|
|
4716
4935
|
const valueMethods = pattern.valueMethods ?? [];
|
|
4717
4936
|
const picked = selectorOf(chain, valueMethods);
|
|
4718
4937
|
return {
|
|
@@ -4720,8 +4939,8 @@ function effectFor(pattern, chain, operation) {
|
|
|
4720
4939
|
binding: storageBinding2({
|
|
4721
4940
|
recognition: "python-storage",
|
|
4722
4941
|
storageSystem: pattern.storageSystem,
|
|
4723
|
-
scope:
|
|
4724
|
-
container: chain
|
|
4942
|
+
scope: "default",
|
|
4943
|
+
container: modelOf(chain, options)
|
|
4725
4944
|
}),
|
|
4726
4945
|
callee: chain.last.text,
|
|
4727
4946
|
interaction: {
|
|
@@ -4738,23 +4957,14 @@ function fileOf(key) {
|
|
|
4738
4957
|
return at === -1 ? key : key.slice(0, at);
|
|
4739
4958
|
}
|
|
4740
4959
|
function returnTypeName(node) {
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
}
|
|
4744
|
-
const annotation = field(node, "return_type");
|
|
4745
|
-
if (annotation === null) {
|
|
4746
|
-
return null;
|
|
4747
|
-
}
|
|
4748
|
-
return annotation.type === "subscript" ? field(annotation, "value")?.text ?? null : annotation.text;
|
|
4960
|
+
const annotation = node === void 0 ? null : field(node, "return_type");
|
|
4961
|
+
return annotation === null ? null : typeNameOf(annotation);
|
|
4749
4962
|
}
|
|
4750
4963
|
function storageCallees(calls, filePath, couldMatch) {
|
|
4751
4964
|
return chainsIn(calls).filter((chain) => couldMatch.has(methodName(chain.root))).map((chain) => field(chain.root, "function")).filter((callee) => callee !== null).map((callee) => readKey(filePath, callee, enclosingFunction(callee)));
|
|
4752
4965
|
}
|
|
4753
4966
|
function resolvedMethodPattern(options, callee) {
|
|
4754
|
-
const
|
|
4755
|
-
(row) => String(row[0]) === readKey(options.filePath, callee, enclosingFunction(callee))
|
|
4756
|
-
).map((row) => String(row[1]));
|
|
4757
|
-
const settled = resolved.length === 1 ? resolved[0] : void 0;
|
|
4967
|
+
const settled = settledCallee(options, callee);
|
|
4758
4968
|
if (settled === void 0) {
|
|
4759
4969
|
return void 0;
|
|
4760
4970
|
}
|
|
@@ -4831,7 +5041,7 @@ function storageCallIds(calls, options) {
|
|
|
4831
5041
|
}
|
|
4832
5042
|
function storageEffects(calls, options) {
|
|
4833
5043
|
return matchedChains(calls, options).flatMap(
|
|
4834
|
-
({ chain, pattern, operation }) => operation === null ? [] : [effectFor(pattern, chain, operation)]
|
|
5044
|
+
({ chain, pattern, operation }) => operation === null ? [] : [effectFor(pattern, chain, operation, options)]
|
|
4835
5045
|
);
|
|
4836
5046
|
}
|
|
4837
5047
|
|
|
@@ -5127,6 +5337,7 @@ function classRouteUnits(pattern, pack, classification, classNode, module, optio
|
|
|
5127
5337
|
pattern
|
|
5128
5338
|
),
|
|
5129
5339
|
storage: options.storage,
|
|
5340
|
+
importedDefinition: options.importedDefinition,
|
|
5130
5341
|
wrappers: wrappersAround(
|
|
5131
5342
|
pattern,
|
|
5132
5343
|
pack,
|
|
@@ -5456,7 +5667,10 @@ function functionRouteUnits(pattern, pack, verb, classification, functionNode, m
|
|
|
5456
5667
|
if (functionName === void 0) {
|
|
5457
5668
|
return [];
|
|
5458
5669
|
}
|
|
5459
|
-
const ctx = createAnnotationContext(
|
|
5670
|
+
const ctx = createAnnotationContext(
|
|
5671
|
+
module.scopeFor,
|
|
5672
|
+
options.importedDefinition ?? null
|
|
5673
|
+
);
|
|
5460
5674
|
return readRoutePaths(
|
|
5461
5675
|
pattern,
|
|
5462
5676
|
readPathArgument(classification, options),
|
|
@@ -5495,6 +5709,7 @@ function functionRouteUnits(pattern, pack, verb, classification, functionNode, m
|
|
|
5495
5709
|
),
|
|
5496
5710
|
definitionsCtx: ctx,
|
|
5497
5711
|
storage: options.storage,
|
|
5712
|
+
importedDefinition: options.importedDefinition,
|
|
5498
5713
|
wrappers: wrappersAround(
|
|
5499
5714
|
pattern,
|
|
5500
5715
|
pack,
|
|
@@ -5592,7 +5807,10 @@ function buildRouteUnit(options) {
|
|
|
5592
5807
|
storage: storageLookup
|
|
5593
5808
|
} = options;
|
|
5594
5809
|
const template = valueToReadFurtherFrom(routePath);
|
|
5595
|
-
const ctx = definitionsCtx ?? createAnnotationContext(
|
|
5810
|
+
const ctx = definitionsCtx ?? createAnnotationContext(
|
|
5811
|
+
module.scopeFor,
|
|
5812
|
+
options.importedDefinition ?? null
|
|
5813
|
+
);
|
|
5596
5814
|
const parameters = readParameters(
|
|
5597
5815
|
definitionNode,
|
|
5598
5816
|
enclosingScope,
|
|
@@ -5673,7 +5891,7 @@ function buildRouteUnit(options) {
|
|
|
5673
5891
|
declaredContract: null,
|
|
5674
5892
|
readings: [
|
|
5675
5893
|
routePath,
|
|
5676
|
-
...unreadRoleReadings(parameters, rangeOf(definitionNode)),
|
|
5894
|
+
...template === null ? unreadRoleReadings(parameters, rangeOf(definitionNode)) : [],
|
|
5677
5895
|
...responseShape.passedOver
|
|
5678
5896
|
],
|
|
5679
5897
|
...collectedDefinitions(ctx) !== null ? { definitions: collectedDefinitions(ctx) } : {},
|
|
@@ -5768,7 +5986,7 @@ function readParameter(param, scope, ctx, pathParamNames, requestBodyFromAnnotat
|
|
|
5768
5986
|
}
|
|
5769
5987
|
const { name, typeNode } = info;
|
|
5770
5988
|
const shape = typeNode !== null ? annotationToShape(typeNode, scope, ctx) : null;
|
|
5771
|
-
const role = isInjectedParameter(param, injectedCallees) ? null : roleOf(name, shape, pathParamNames, requestBodyFromAnnotatedClass);
|
|
5989
|
+
const role = isInjectedParameter(param, injectedCallees, scope, ctx) ? null : roleOf(name, shape, pathParamNames, requestBodyFromAnnotatedClass);
|
|
5772
5990
|
return {
|
|
5773
5991
|
name,
|
|
5774
5992
|
position,
|
|
@@ -5802,7 +6020,7 @@ function calleeName2(call) {
|
|
|
5802
6020
|
}
|
|
5803
6021
|
return null;
|
|
5804
6022
|
}
|
|
5805
|
-
function isInjectedParameter(param, injectedCallees) {
|
|
6023
|
+
function isInjectedParameter(param, injectedCallees, scope, ctx) {
|
|
5806
6024
|
if (injectedCallees.size === 0) {
|
|
5807
6025
|
return false;
|
|
5808
6026
|
}
|
|
@@ -5816,18 +6034,26 @@ function isInjectedParameter(param, injectedCallees) {
|
|
|
5816
6034
|
if (callsAnInjector(field(param, "value"))) {
|
|
5817
6035
|
return true;
|
|
5818
6036
|
}
|
|
5819
|
-
const
|
|
6037
|
+
const followed = /* @__PURE__ */ new Set();
|
|
6038
|
+
const containsInjectorCall = (node, nodeScope, depth) => {
|
|
5820
6039
|
if (node === null || depth > MAX_ANNOTATION_DEPTH) {
|
|
5821
6040
|
return false;
|
|
5822
6041
|
}
|
|
5823
6042
|
if (callsAnInjector(node)) {
|
|
5824
6043
|
return true;
|
|
5825
6044
|
}
|
|
6045
|
+
if (node.type === "identifier" && !followed.has(node.id)) {
|
|
6046
|
+
followed.add(node.id);
|
|
6047
|
+
const alias = aliasValueOf(node.text, nodeScope, ctx);
|
|
6048
|
+
if (alias !== null && containsInjectorCall(alias.node, alias.scope, 0)) {
|
|
6049
|
+
return true;
|
|
6050
|
+
}
|
|
6051
|
+
}
|
|
5826
6052
|
return node.namedChildren.some(
|
|
5827
|
-
(child) => containsInjectorCall(child, depth + 1)
|
|
6053
|
+
(child) => containsInjectorCall(child, nodeScope, depth + 1)
|
|
5828
6054
|
);
|
|
5829
6055
|
};
|
|
5830
|
-
return containsInjectorCall(field(param, "type"), 0);
|
|
6056
|
+
return containsInjectorCall(field(param, "type"), scope, 0);
|
|
5831
6057
|
}
|
|
5832
6058
|
|
|
5833
6059
|
// src/facts/why.ts
|
|
@@ -5985,6 +6211,58 @@ function buildPythonExtractionReport(args) {
|
|
|
5985
6211
|
});
|
|
5986
6212
|
}
|
|
5987
6213
|
|
|
6214
|
+
// src/importedDefinitions.ts
|
|
6215
|
+
function importedDefinitionLookup(db, files) {
|
|
6216
|
+
const fileOfModuleScope = /* @__PURE__ */ new Map();
|
|
6217
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
6218
|
+
for (const file of files) {
|
|
6219
|
+
fileOfModuleScope.set(file.module.moduleScope.node.id, file.file);
|
|
6220
|
+
byFile.set(file.file, file);
|
|
6221
|
+
}
|
|
6222
|
+
const found = /* @__PURE__ */ new Map();
|
|
6223
|
+
return (scope, name) => {
|
|
6224
|
+
const fromFile = fileOfModuleScope.get(moduleScopeOf(scope).node.id);
|
|
6225
|
+
if (fromFile === void 0) {
|
|
6226
|
+
return null;
|
|
6227
|
+
}
|
|
6228
|
+
const key = `${fromFile}#${name}`;
|
|
6229
|
+
const known = found.get(key);
|
|
6230
|
+
if (known !== void 0) {
|
|
6231
|
+
return known;
|
|
6232
|
+
}
|
|
6233
|
+
const definition = definitionAmong(originsOf(db, key), byFile);
|
|
6234
|
+
found.set(key, definition);
|
|
6235
|
+
return definition;
|
|
6236
|
+
};
|
|
6237
|
+
}
|
|
6238
|
+
function definitionAmong(origins, byFile) {
|
|
6239
|
+
for (const origin of origins) {
|
|
6240
|
+
const file = byFile.get(origin.module);
|
|
6241
|
+
const binding = file?.module.moduleScope.bindings.get(origin.name);
|
|
6242
|
+
if (file === void 0 || binding === void 0) {
|
|
6243
|
+
continue;
|
|
6244
|
+
}
|
|
6245
|
+
const node = definedNodeOf(binding);
|
|
6246
|
+
if (node !== null) {
|
|
6247
|
+
return {
|
|
6248
|
+
node,
|
|
6249
|
+
moduleScope: file.module.moduleScope,
|
|
6250
|
+
scopeFor: file.module.scopeFor
|
|
6251
|
+
};
|
|
6252
|
+
}
|
|
6253
|
+
}
|
|
6254
|
+
return null;
|
|
6255
|
+
}
|
|
6256
|
+
function definedNodeOf(binding) {
|
|
6257
|
+
if (binding.kind === "classDef") {
|
|
6258
|
+
return binding.node;
|
|
6259
|
+
}
|
|
6260
|
+
if (binding.kind === "assignment") {
|
|
6261
|
+
return binding.value;
|
|
6262
|
+
}
|
|
6263
|
+
return null;
|
|
6264
|
+
}
|
|
6265
|
+
|
|
5988
6266
|
// src/reach/closure.ts
|
|
5989
6267
|
import {
|
|
5990
6268
|
functionCallBinding,
|
|
@@ -6055,14 +6333,9 @@ var EXPRESSION_RESOLVERS = {
|
|
|
6055
6333
|
}
|
|
6056
6334
|
return memberOf(resolveExpression(object, site, ctx), attribute.text, ctx);
|
|
6057
6335
|
},
|
|
6058
|
-
// `Service().run()` runs a method on whatever the constructor built.
|
|
6059
6336
|
call: (node, site, ctx) => {
|
|
6060
6337
|
const callee = field(node, "function");
|
|
6061
|
-
|
|
6062
|
-
return stop("noDeclaration");
|
|
6063
|
-
}
|
|
6064
|
-
const built = resolveExpression(callee, site, ctx);
|
|
6065
|
-
return built.kind === "class" ? built : stop("noDeclaration");
|
|
6338
|
+
return callee === null ? stop("noDeclaration") : instanceBuiltBy(resolveExpression(callee, site, ctx), "noDeclaration");
|
|
6066
6339
|
},
|
|
6067
6340
|
parenthesized_expression: (node, site, ctx) => {
|
|
6068
6341
|
const inner = node.namedChildren[0];
|
|
@@ -6073,6 +6346,12 @@ function resolveExpression(node, site, ctx) {
|
|
|
6073
6346
|
const resolver = EXPRESSION_RESOLVERS[node.type];
|
|
6074
6347
|
return resolver === void 0 ? stop("noDeclaration") : resolver(node, site, ctx);
|
|
6075
6348
|
}
|
|
6349
|
+
function instanceBuiltBy(built, otherwise) {
|
|
6350
|
+
if (built.kind === "class" || built.kind === "stop") {
|
|
6351
|
+
return built;
|
|
6352
|
+
}
|
|
6353
|
+
return built.kind === "package" ? stop("outsideRun") : stop(otherwise);
|
|
6354
|
+
}
|
|
6076
6355
|
function resolveIdentifier(name, site, ctx) {
|
|
6077
6356
|
if (site.rebound.has(name)) {
|
|
6078
6357
|
return stop("unsettledValue");
|
|
@@ -6138,8 +6417,10 @@ function aliasedValue(value, file, scope, ctx, visited) {
|
|
|
6138
6417
|
}
|
|
6139
6418
|
if (value.type === "call") {
|
|
6140
6419
|
const callee = field(value, "function");
|
|
6141
|
-
|
|
6142
|
-
|
|
6420
|
+
return callee === null ? stop("unsettledValue") : instanceBuiltBy(
|
|
6421
|
+
aliasedValue(callee, file, scope, ctx, visited),
|
|
6422
|
+
"unsettledValue"
|
|
6423
|
+
);
|
|
6143
6424
|
}
|
|
6144
6425
|
if (value.type === "attribute") {
|
|
6145
6426
|
const object = field(value, "object");
|
|
@@ -7229,6 +7510,7 @@ async function extractPythonProject(options) {
|
|
|
7229
7510
|
storageFor
|
|
7230
7511
|
})
|
|
7231
7512
|
);
|
|
7513
|
+
const importedDefinition = importedDefinitionLookup(db, bound);
|
|
7232
7514
|
const discovered = /* @__PURE__ */ new Map();
|
|
7233
7515
|
for (const boundFile of bound) {
|
|
7234
7516
|
const { file, root, module: moduleBinding } = boundFile;
|
|
@@ -7242,6 +7524,7 @@ async function extractPythonProject(options) {
|
|
|
7242
7524
|
routerIndex,
|
|
7243
7525
|
gapHandling,
|
|
7244
7526
|
wrappers: wrapperIndex,
|
|
7527
|
+
importedDefinition,
|
|
7245
7528
|
...needsValues ? { facts: db } : {},
|
|
7246
7529
|
...storage === void 0 ? {} : { storage }
|
|
7247
7530
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suss/adapter-python",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Read a Python codebase with suss and check what each FastAPI or Flask route returns against the clients, specs and infrastructure that depend on it.",
|
|
6
6
|
"keywords": [
|
|
@@ -49,13 +49,13 @@
|
|
|
49
49
|
"typecheck": "tsc --noEmit"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@suss/behavioral-ir": "0.
|
|
53
|
-
"@suss/datalog": "0.
|
|
54
|
-
"@suss/extractor": "0.
|
|
55
|
-
"@suss/ir-core": "0.
|
|
56
|
-
"@suss/resolution": "0.
|
|
57
|
-
"@suss/sql": "0.
|
|
58
|
-
"@suss/values": "0.
|
|
52
|
+
"@suss/behavioral-ir": "0.27.0",
|
|
53
|
+
"@suss/datalog": "0.27.0",
|
|
54
|
+
"@suss/extractor": "0.27.0",
|
|
55
|
+
"@suss/ir-core": "0.27.0",
|
|
56
|
+
"@suss/resolution": "0.27.0",
|
|
57
|
+
"@suss/sql": "0.27.0",
|
|
58
|
+
"@suss/values": "0.27.0",
|
|
59
59
|
"web-tree-sitter": "^0.26.11"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|