@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 CHANGED
@@ -159,6 +159,25 @@ function children(node) {
159
159
  function bodyStatements(body) {
160
160
  return children(body);
161
161
  }
162
+ function nestedStatements(stmt) {
163
+ const found = [];
164
+ for (const child of children(stmt)) {
165
+ if (child.type === "block") {
166
+ for (const inner of children(child)) {
167
+ if (inner.type.endsWith("_clause")) {
168
+ found.push(...nestedStatements(inner));
169
+ continue;
170
+ }
171
+ found.push(inner);
172
+ }
173
+ continue;
174
+ }
175
+ if (child.type.endsWith("_clause")) {
176
+ found.push(...nestedStatements(child));
177
+ }
178
+ }
179
+ return found;
180
+ }
162
181
  function stripDecorators(node) {
163
182
  if (node.type !== "decorated_definition") {
164
183
  return { definition: node, decorators: [] };
@@ -519,8 +538,36 @@ function resolveNonlocal(scope, name) {
519
538
  }
520
539
 
521
540
  // src/annotations.ts
522
- function createAnnotationContext(scopeFor) {
523
- return { scopeFor, definitions: /* @__PURE__ */ new Map() };
541
+ function createAnnotationContext(scopeFor, importedDefinition = null) {
542
+ return {
543
+ scopeMaps: [scopeFor],
544
+ definitions: /* @__PURE__ */ new Map(),
545
+ importedDefinition,
546
+ expanding: /* @__PURE__ */ new Set()
547
+ };
548
+ }
549
+ function scopeOfNode(ctx, node) {
550
+ for (const scopeFor of ctx.scopeMaps) {
551
+ const scope = scopeFor.get(node.id);
552
+ if (scope !== void 0) {
553
+ return scope;
554
+ }
555
+ }
556
+ return void 0;
557
+ }
558
+ function aliasValueOf(name, scope, ctx) {
559
+ const binding = resolveName(scope, name);
560
+ if (binding?.kind === "assignment") {
561
+ return binding.value === null ? null : { node: binding.value, scope };
562
+ }
563
+ if (binding?.kind !== "import" && binding?.kind !== "importFrom") {
564
+ return null;
565
+ }
566
+ const imported = ctx.importedDefinition?.(scope, name) ?? null;
567
+ if (imported === null || imported.node.type === "class_definition") {
568
+ return null;
569
+ }
570
+ return { node: imported.node, scope: imported.moduleScope };
524
571
  }
525
572
  function collectedDefinitions(ctx) {
526
573
  const out = {};
@@ -563,6 +610,7 @@ var EXPRESSION_SHAPERS = {
563
610
  identifier: (node, scope, ctx) => shapeFromName(node.text, scope, ctx),
564
611
  none: () => ({ type: "null" }),
565
612
  generic_type: shapeFromGenericType,
613
+ subscript: shapeFromSubscript,
566
614
  binary_operator: shapeFromBinaryOperator
567
615
  };
568
616
  function shapeFromExpression(node, scope, ctx) {
@@ -584,8 +632,46 @@ function shapeFromName(name, scope, ctx) {
584
632
  if (binding?.kind === "classDef") {
585
633
  return recordShapeRef(name, binding.node, ctx);
586
634
  }
635
+ if (binding?.kind === "import" || binding?.kind === "importFrom") {
636
+ const imported = ctx.importedDefinition?.(scope, name) ?? null;
637
+ if (imported?.node.type === "class_definition") {
638
+ if (!ctx.scopeMaps.includes(imported.scopeFor)) {
639
+ ctx.scopeMaps.push(imported.scopeFor);
640
+ }
641
+ return recordShapeRef(name, imported.node, ctx);
642
+ }
643
+ }
644
+ const alias = aliasValueOf(name, scope, ctx);
645
+ if (alias !== null) {
646
+ return shapeFromAliasValue(name, alias, ctx);
647
+ }
587
648
  return { type: "ref", name };
588
649
  }
650
+ function shapeFromAliasValue(name, alias, ctx) {
651
+ if (ctx.expanding.has(alias.node.id)) {
652
+ return { type: "ref", name };
653
+ }
654
+ ctx.expanding.add(alias.node.id);
655
+ try {
656
+ return shapeFromExpression(alias.node, alias.scope, ctx);
657
+ } finally {
658
+ ctx.expanding.delete(alias.node.id);
659
+ }
660
+ }
661
+ function shapeFromSubscript(node, scope, ctx) {
662
+ const base = field(node, "value");
663
+ const args = fields(node, "subscript");
664
+ const baseName = base?.type === "identifier" ? base.text : null;
665
+ if (baseName === "Annotated") {
666
+ const first = args[0];
667
+ return first === void 0 ? { type: "unknown" } : shapeFromExpression(first, scope, ctx);
668
+ }
669
+ return shapeFromGeneric(
670
+ baseName,
671
+ node.text,
672
+ args.map((arg) => shapeFromExpression(arg, scope, ctx))
673
+ );
674
+ }
589
675
  function genericTypeArgs(node) {
590
676
  const typeParameter = node.namedChildren.find(
591
677
  (child) => child !== null && child.type === "type_parameter"
@@ -596,14 +682,20 @@ function genericTypeArgs(node) {
596
682
  }
597
683
  function shapeFromGenericType(node, scope, ctx) {
598
684
  const base = node.namedChild(0);
599
- const args = genericTypeArgs(node);
600
- const shapeOf = (arg) => arg !== void 0 ? annotationToShape(arg, scope, ctx) : { type: "unknown" };
601
685
  const baseName = base?.type === "identifier" ? base.text : null;
686
+ return shapeFromGeneric(
687
+ baseName,
688
+ node.text,
689
+ genericTypeArgs(node).map((arg) => annotationToShape(arg, scope, ctx))
690
+ );
691
+ }
692
+ function shapeFromGeneric(baseName, text2, args) {
693
+ const shapeOf = (arg) => arg ?? { type: "unknown" };
602
694
  if (baseName === "Optional") {
603
695
  return { type: "union", variants: [shapeOf(args[0]), { type: "null" }] };
604
696
  }
605
697
  if (baseName === "Union") {
606
- return { type: "union", variants: args.map((arg) => shapeOf(arg)) };
698
+ return { type: "union", variants: args };
607
699
  }
608
700
  if (baseName !== null && LIST_NAMES.has(baseName)) {
609
701
  return { type: "array", items: shapeOf(args[0]) };
@@ -614,7 +706,7 @@ function shapeFromGenericType(node, scope, ctx) {
614
706
  if (baseName !== null && TUPLE_SET_NAMES.has(baseName)) {
615
707
  return { type: "array", items: shapeOf(args[0]) };
616
708
  }
617
- return { type: "ref", name: baseName ?? node.text };
709
+ return { type: "ref", name: baseName ?? text2 };
618
710
  }
619
711
  function shapeFromBinaryOperator(node, scope, ctx) {
620
712
  const operator = field(node, "operator");
@@ -639,7 +731,7 @@ function recordShapeRef(name, classNode, ctx) {
639
731
  }
640
732
  ctx.definitions.set(key, null);
641
733
  const bodyNode = field(classNode, "body");
642
- const classScope = ctx.scopeFor.get(classNode.id);
734
+ const classScope = scopeOfNode(ctx, classNode);
643
735
  ctx.definitions.set(
644
736
  key,
645
737
  bodyNode !== null && classScope !== void 0 ? recordShapeOf(bodyNode, classScope, ctx) : { type: "record", properties: {} }
@@ -1431,6 +1523,13 @@ function writtenValueOf(db, key) {
1431
1523
  resolveCalls(db, [key]);
1432
1524
  return (0, import_resolution.writtenValueOf)(db, key, (keys) => resolveCalls(db, keys));
1433
1525
  }
1526
+ function originsOf(db, nameKey) {
1527
+ if (!db.facts("wantedOrigin").some((row) => String(row[0]) === nameKey)) {
1528
+ db.add("wantedOrigin", [nameKey]);
1529
+ (0, import_datalog.evaluate)(db, RESOLUTION_PROGRAM.rules);
1530
+ }
1531
+ return db.facts("wantedComesFrom").filter((row) => String(row[0]) === nameKey).map((row) => ({ module: String(row[1]), name: String(row[2]) }));
1532
+ }
1434
1533
  function subjectConstructions(db, valueKeys) {
1435
1534
  const found = /* @__PURE__ */ new Map();
1436
1535
  if (valueKeys.length === 0) {
@@ -1766,7 +1865,7 @@ function emitBinding(emitter, assignment2, receiver) {
1766
1865
  emitter,
1767
1866
  "binds",
1768
1867
  nameId(emitter.filePath, left.text),
1769
- valueKey(emitter, right)
1868
+ valueKey(emitter, assignedValue(right))
1770
1869
  );
1771
1870
  return;
1772
1871
  }
@@ -1798,9 +1897,17 @@ function emitAssignment(emitter, assignment2) {
1798
1897
  "exportsAs",
1799
1898
  emitter.filePath,
1800
1899
  left.text,
1801
- valueKey(emitter, right)
1900
+ valueKey(emitter, assignedValue(right))
1802
1901
  );
1803
1902
  }
1903
+ function assignedValue(right) {
1904
+ if (right.type !== "subscript") {
1905
+ return right;
1906
+ }
1907
+ const outer = field(right, "value");
1908
+ const first = fields(right, "subscript")[0];
1909
+ return outer?.text === "Annotated" && first !== void 0 ? first : right;
1910
+ }
1804
1911
  function declaredBy(statement) {
1805
1912
  if (statement.type === "decorated_definition") {
1806
1913
  return field(statement, "definition") ?? statement;
@@ -3375,11 +3482,15 @@ var WALK_DESCENTS = {
3375
3482
  scan,
3376
3483
  visit
3377
3484
  );
3378
- }
3485
+ },
3486
+ if_statement: descendInPlace,
3487
+ try_statement: descendInPlace,
3488
+ while_statement: descendInPlace,
3489
+ with_statement: descendInPlace,
3490
+ match_statement: descendInPlace
3379
3491
  };
3380
- function nestedStatements(stmt) {
3381
- const body = field(stmt, "body");
3382
- return body === null ? [] : bodyStatements(body);
3492
+ function descendInPlace(stmt, position, scan, visit) {
3493
+ walkStatements(nestedStatements(stmt), position, scan, visit);
3383
3494
  }
3384
3495
  function walkStatements(statements, position, scan, visit) {
3385
3496
  for (const stmt of statements) {
@@ -4460,7 +4571,7 @@ function rawSqlEffects(calls, options) {
4460
4571
  binding: (0, import_ir_core.storageBinding)({
4461
4572
  recognition: `python-${pattern.module}`,
4462
4573
  storageSystem: pattern.storageSystem,
4463
- scope: pattern.module,
4574
+ scope: "default",
4464
4575
  container: access.table
4465
4576
  }),
4466
4577
  callee: field(call, "function")?.text ?? "",
@@ -4604,16 +4715,23 @@ function typeNameOf(annotation) {
4604
4715
  if (quoted !== null) {
4605
4716
  return quoted;
4606
4717
  }
4718
+ if (annotation.type === "binary_operator") {
4719
+ const named = [field(annotation, "left"), field(annotation, "right")].find(
4720
+ (side) => side !== null && side.type !== "none"
4721
+ );
4722
+ return named === void 0 || named === null ? null : typeNameOf(named);
4723
+ }
4607
4724
  if (annotation.type === "generic_type") {
4608
4725
  const outer = annotation.namedChildren[0];
4609
4726
  const first = genericTypeArgs(annotation)[0];
4610
4727
  if ((outer?.text === "Annotated" || outer?.text === "Optional") && first !== void 0) {
4611
4728
  return typeNameOf(first);
4612
4729
  }
4730
+ return outer?.type === "identifier" ? outer.text : null;
4613
4731
  }
4614
4732
  return null;
4615
4733
  }
4616
- function typeGivenBy(statement, name) {
4734
+ function typeGivenBy(statement, name, options) {
4617
4735
  if (statement.type === "assignment") {
4618
4736
  if (field(statement, "left")?.text !== name) {
4619
4737
  return null;
@@ -4623,8 +4741,7 @@ function typeGivenBy(statement, name) {
4623
4741
  return typeNameOf(annotation);
4624
4742
  }
4625
4743
  const right = field(statement, "right");
4626
- const callee = right?.type === "call" ? field(right, "function") : null;
4627
- return callee?.type === "identifier" ? callee.text : null;
4744
+ return right?.type === "call" ? builtType(right, options) : null;
4628
4745
  }
4629
4746
  if (statement.type === "as_pattern") {
4630
4747
  const alias = field(statement, "alias");
@@ -4634,7 +4751,34 @@ function typeGivenBy(statement, name) {
4634
4751
  }
4635
4752
  return null;
4636
4753
  }
4637
- function declaredTypeName(name, from) {
4754
+ function builtType(call, options) {
4755
+ const root = rootOf(call);
4756
+ const callee = field(root, "function");
4757
+ if (callee?.type === "identifier") {
4758
+ return isClassName(callee.text) ? callee.text : resolvedReturnType(options, callee);
4759
+ }
4760
+ const object = callee?.type === "attribute" ? field(callee, "object") : null;
4761
+ if (object?.type === "identifier" && isClassName(object.text)) {
4762
+ return object.text;
4763
+ }
4764
+ const args = field(root, "arguments");
4765
+ const first = (args === null ? [] : children(args))[0];
4766
+ if (first?.type === "identifier" && isClassName(first.text)) {
4767
+ return first.text;
4768
+ }
4769
+ return callee === null ? null : resolvedReturnType(options, callee);
4770
+ }
4771
+ function resolvedReturnType(options, callee) {
4772
+ const settled = settledCallee(options, callee);
4773
+ return settled === void 0 ? null : returnTypeName(options.definitionAt(settled));
4774
+ }
4775
+ function settledCallee(options, callee) {
4776
+ const key = readKey(options.filePath, callee, enclosingFunction(callee));
4777
+ resolveCalls(options.facts, [key]);
4778
+ const resolved = options.facts.facts("wantedResolves").filter((row) => String(row[0]) === key).map((row) => String(row[1]));
4779
+ return resolved.length === 1 ? resolved[0] : void 0;
4780
+ }
4781
+ function declaredTypeName(name, from, options) {
4638
4782
  const fn = enclosingFunction(from);
4639
4783
  if (fn === null) {
4640
4784
  return null;
@@ -4647,7 +4791,7 @@ function declaredTypeName(name, from) {
4647
4791
  }
4648
4792
  }
4649
4793
  const visit = (node) => {
4650
- const given = typeGivenBy(node, name);
4794
+ const given = typeGivenBy(node, name, options);
4651
4795
  if (given !== null) {
4652
4796
  return given;
4653
4797
  }
@@ -4676,16 +4820,27 @@ function typedReceiverPattern(options, chain) {
4676
4820
  if (receiver === null) {
4677
4821
  return void 0;
4678
4822
  }
4679
- const typeName = declaredTypeName(receiver, chain.root);
4823
+ const typeName = declaredTypeName(receiver, chain.root, options);
4680
4824
  if (typeName === null) {
4681
4825
  return void 0;
4682
4826
  }
4683
- const from = options.facts.facts("pyImportedName").find((row) => String(row[0]) === `${options.filePath}#${typeName}`);
4684
- if (from === void 0) {
4685
- return void 0;
4827
+ const typeKey = `${options.filePath}#${typeName}`;
4828
+ const imported = options.facts.facts("pyImportedName").find((row) => String(row[0]) === typeKey);
4829
+ const direct = imported === void 0 ? void 0 : queryTypePattern(options, String(imported[1]), String(imported[2]));
4830
+ if (direct !== void 0) {
4831
+ return direct;
4832
+ }
4833
+ for (const origin of originsOf(options.facts, typeKey)) {
4834
+ const pattern = queryTypePattern(options, origin.module, origin.name);
4835
+ if (pattern !== void 0) {
4836
+ return pattern;
4837
+ }
4686
4838
  }
4839
+ return void 0;
4840
+ }
4841
+ function queryTypePattern(options, module2, name) {
4687
4842
  return options.patterns.find(
4688
- (pattern) => String(from[1]) === pattern.module && pattern.queryTypes.includes(String(from[2]))
4843
+ (pattern) => module2 === pattern.module && pattern.queryTypes.includes(name)
4689
4844
  );
4690
4845
  }
4691
4846
  function fieldsOf(chain, valueMethods) {
@@ -4740,7 +4895,71 @@ function selectorOf(chain, valueMethods) {
4740
4895
  )
4741
4896
  ];
4742
4897
  }
4743
- function effectFor(pattern, chain, operation) {
4898
+ var TABLE_METHODS = ["select_from", "join"];
4899
+ function modelOf(chain, options) {
4900
+ const fromRoot = modelArgument(chain.root, chain.root, options);
4901
+ if (fromRoot !== null) {
4902
+ return fromRoot;
4903
+ }
4904
+ for (const call of laterCalls(chain)) {
4905
+ if (TABLE_METHODS.includes(methodNameOf(call))) {
4906
+ const named = modelArgument(call, chain.root, options);
4907
+ if (named !== null) {
4908
+ return named;
4909
+ }
4910
+ }
4911
+ }
4912
+ return isClassName(chain.subject) ? chain.subject : null;
4913
+ }
4914
+ function modelArgument(call, from, options) {
4915
+ const args = field(call, "arguments");
4916
+ const first = (args === null ? [] : children(args)).find(
4917
+ (argument) => argument.type !== "keyword_argument"
4918
+ );
4919
+ return first === void 0 ? null : modelNamed(first, from, options);
4920
+ }
4921
+ function modelNamed(node, from, options) {
4922
+ if (node.type === "identifier") {
4923
+ if (isClassName(node.text)) {
4924
+ return node.text;
4925
+ }
4926
+ const declared = declaredTypeName(node.text, from, options);
4927
+ return declared === null ? null : classBehind(declared, options);
4928
+ }
4929
+ if (node.type === "attribute") {
4930
+ const property = field(node, "attribute");
4931
+ if (property !== null && isClassName(property.text)) {
4932
+ return property.text;
4933
+ }
4934
+ const object = field(node, "object");
4935
+ return object === null ? null : modelNamed(object, from, options);
4936
+ }
4937
+ if (node.type === "call") {
4938
+ const callee = field(node, "function");
4939
+ const built = callee === null ? null : modelNamed(callee, from, options);
4940
+ return built ?? modelArgument(node, from, options);
4941
+ }
4942
+ return null;
4943
+ }
4944
+ function classBehind(typeName, options) {
4945
+ const key = `${options.filePath}#${typeName}`;
4946
+ originsOf(options.facts, key);
4947
+ const reached = new Set(
4948
+ options.facts.facts("wantedComesTo").filter((row) => String(row[0]) === key).map((row) => String(row[1]))
4949
+ );
4950
+ const classes = new Set(
4951
+ options.facts.facts("objectValue").map((row) => String(row[0])).filter((value) => reached.has(value))
4952
+ );
4953
+ const exported = options.facts.facts("exportsAs").find((row) => classes.has(String(row[2])));
4954
+ if (exported !== void 0) {
4955
+ return String(exported[1]);
4956
+ }
4957
+ return isClassName(typeName) ? typeName : null;
4958
+ }
4959
+ function isClassName(name) {
4960
+ return /^[A-Z][A-Za-z0-9_]*$/.test(name) && name !== name.toUpperCase();
4961
+ }
4962
+ function effectFor(pattern, chain, operation, options) {
4744
4963
  const valueMethods = pattern.valueMethods ?? [];
4745
4964
  const picked = selectorOf(chain, valueMethods);
4746
4965
  return {
@@ -4748,8 +4967,8 @@ function effectFor(pattern, chain, operation) {
4748
4967
  binding: (0, import_ir_core2.storageBinding)({
4749
4968
  recognition: "python-storage",
4750
4969
  storageSystem: pattern.storageSystem,
4751
- scope: pattern.module,
4752
- container: chain.subject
4970
+ scope: "default",
4971
+ container: modelOf(chain, options)
4753
4972
  }),
4754
4973
  callee: chain.last.text,
4755
4974
  interaction: {
@@ -4766,23 +4985,14 @@ function fileOf(key) {
4766
4985
  return at === -1 ? key : key.slice(0, at);
4767
4986
  }
4768
4987
  function returnTypeName(node) {
4769
- if (node === void 0) {
4770
- return null;
4771
- }
4772
- const annotation = field(node, "return_type");
4773
- if (annotation === null) {
4774
- return null;
4775
- }
4776
- return annotation.type === "subscript" ? field(annotation, "value")?.text ?? null : annotation.text;
4988
+ const annotation = node === void 0 ? null : field(node, "return_type");
4989
+ return annotation === null ? null : typeNameOf(annotation);
4777
4990
  }
4778
4991
  function storageCallees(calls, filePath, couldMatch) {
4779
4992
  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)));
4780
4993
  }
4781
4994
  function resolvedMethodPattern(options, callee) {
4782
- const resolved = options.facts.facts("wantedResolves").filter(
4783
- (row) => String(row[0]) === readKey(options.filePath, callee, enclosingFunction(callee))
4784
- ).map((row) => String(row[1]));
4785
- const settled = resolved.length === 1 ? resolved[0] : void 0;
4995
+ const settled = settledCallee(options, callee);
4786
4996
  if (settled === void 0) {
4787
4997
  return void 0;
4788
4998
  }
@@ -4859,7 +5069,7 @@ function storageCallIds(calls, options) {
4859
5069
  }
4860
5070
  function storageEffects(calls, options) {
4861
5071
  return matchedChains(calls, options).flatMap(
4862
- ({ chain, pattern, operation }) => operation === null ? [] : [effectFor(pattern, chain, operation)]
5072
+ ({ chain, pattern, operation }) => operation === null ? [] : [effectFor(pattern, chain, operation, options)]
4863
5073
  );
4864
5074
  }
4865
5075
 
@@ -5155,6 +5365,7 @@ function classRouteUnits(pattern, pack, classification, classNode, module2, opti
5155
5365
  pattern
5156
5366
  ),
5157
5367
  storage: options.storage,
5368
+ importedDefinition: options.importedDefinition,
5158
5369
  wrappers: wrappersAround(
5159
5370
  pattern,
5160
5371
  pack,
@@ -5484,7 +5695,10 @@ function functionRouteUnits(pattern, pack, verb, classification, functionNode, m
5484
5695
  if (functionName === void 0) {
5485
5696
  return [];
5486
5697
  }
5487
- const ctx = createAnnotationContext(module2.scopeFor);
5698
+ const ctx = createAnnotationContext(
5699
+ module2.scopeFor,
5700
+ options.importedDefinition ?? null
5701
+ );
5488
5702
  return readRoutePaths(
5489
5703
  pattern,
5490
5704
  readPathArgument(classification, options),
@@ -5523,6 +5737,7 @@ function functionRouteUnits(pattern, pack, verb, classification, functionNode, m
5523
5737
  ),
5524
5738
  definitionsCtx: ctx,
5525
5739
  storage: options.storage,
5740
+ importedDefinition: options.importedDefinition,
5526
5741
  wrappers: wrappersAround(
5527
5742
  pattern,
5528
5743
  pack,
@@ -5620,7 +5835,10 @@ function buildRouteUnit(options) {
5620
5835
  storage: storageLookup
5621
5836
  } = options;
5622
5837
  const template = (0, import_extractor5.valueToReadFurtherFrom)(routePath);
5623
- const ctx = definitionsCtx ?? createAnnotationContext(module2.scopeFor);
5838
+ const ctx = definitionsCtx ?? createAnnotationContext(
5839
+ module2.scopeFor,
5840
+ options.importedDefinition ?? null
5841
+ );
5624
5842
  const parameters = readParameters(
5625
5843
  definitionNode,
5626
5844
  enclosingScope,
@@ -5701,7 +5919,7 @@ function buildRouteUnit(options) {
5701
5919
  declaredContract: null,
5702
5920
  readings: [
5703
5921
  routePath,
5704
- ...unreadRoleReadings(parameters, rangeOf(definitionNode)),
5922
+ ...template === null ? unreadRoleReadings(parameters, rangeOf(definitionNode)) : [],
5705
5923
  ...responseShape.passedOver
5706
5924
  ],
5707
5925
  ...collectedDefinitions(ctx) !== null ? { definitions: collectedDefinitions(ctx) } : {},
@@ -5796,7 +6014,7 @@ function readParameter(param, scope, ctx, pathParamNames, requestBodyFromAnnotat
5796
6014
  }
5797
6015
  const { name, typeNode } = info;
5798
6016
  const shape = typeNode !== null ? annotationToShape(typeNode, scope, ctx) : null;
5799
- const role = isInjectedParameter(param, injectedCallees) ? null : roleOf(name, shape, pathParamNames, requestBodyFromAnnotatedClass);
6017
+ const role = isInjectedParameter(param, injectedCallees, scope, ctx) ? null : roleOf(name, shape, pathParamNames, requestBodyFromAnnotatedClass);
5800
6018
  return {
5801
6019
  name,
5802
6020
  position,
@@ -5830,7 +6048,7 @@ function calleeName2(call) {
5830
6048
  }
5831
6049
  return null;
5832
6050
  }
5833
- function isInjectedParameter(param, injectedCallees) {
6051
+ function isInjectedParameter(param, injectedCallees, scope, ctx) {
5834
6052
  if (injectedCallees.size === 0) {
5835
6053
  return false;
5836
6054
  }
@@ -5844,18 +6062,26 @@ function isInjectedParameter(param, injectedCallees) {
5844
6062
  if (callsAnInjector(field(param, "value"))) {
5845
6063
  return true;
5846
6064
  }
5847
- const containsInjectorCall = (node, depth) => {
6065
+ const followed = /* @__PURE__ */ new Set();
6066
+ const containsInjectorCall = (node, nodeScope, depth) => {
5848
6067
  if (node === null || depth > MAX_ANNOTATION_DEPTH) {
5849
6068
  return false;
5850
6069
  }
5851
6070
  if (callsAnInjector(node)) {
5852
6071
  return true;
5853
6072
  }
6073
+ if (node.type === "identifier" && !followed.has(node.id)) {
6074
+ followed.add(node.id);
6075
+ const alias = aliasValueOf(node.text, nodeScope, ctx);
6076
+ if (alias !== null && containsInjectorCall(alias.node, alias.scope, 0)) {
6077
+ return true;
6078
+ }
6079
+ }
5854
6080
  return node.namedChildren.some(
5855
- (child) => containsInjectorCall(child, depth + 1)
6081
+ (child) => containsInjectorCall(child, nodeScope, depth + 1)
5856
6082
  );
5857
6083
  };
5858
- return containsInjectorCall(field(param, "type"), 0);
6084
+ return containsInjectorCall(field(param, "type"), scope, 0);
5859
6085
  }
5860
6086
 
5861
6087
  // src/facts/why.ts
@@ -5991,6 +6217,58 @@ function buildPythonExtractionReport(args) {
5991
6217
  });
5992
6218
  }
5993
6219
 
6220
+ // src/importedDefinitions.ts
6221
+ function importedDefinitionLookup(db, files) {
6222
+ const fileOfModuleScope = /* @__PURE__ */ new Map();
6223
+ const byFile = /* @__PURE__ */ new Map();
6224
+ for (const file of files) {
6225
+ fileOfModuleScope.set(file.module.moduleScope.node.id, file.file);
6226
+ byFile.set(file.file, file);
6227
+ }
6228
+ const found = /* @__PURE__ */ new Map();
6229
+ return (scope, name) => {
6230
+ const fromFile = fileOfModuleScope.get(moduleScopeOf(scope).node.id);
6231
+ if (fromFile === void 0) {
6232
+ return null;
6233
+ }
6234
+ const key = `${fromFile}#${name}`;
6235
+ const known = found.get(key);
6236
+ if (known !== void 0) {
6237
+ return known;
6238
+ }
6239
+ const definition = definitionAmong(originsOf(db, key), byFile);
6240
+ found.set(key, definition);
6241
+ return definition;
6242
+ };
6243
+ }
6244
+ function definitionAmong(origins, byFile) {
6245
+ for (const origin of origins) {
6246
+ const file = byFile.get(origin.module);
6247
+ const binding = file?.module.moduleScope.bindings.get(origin.name);
6248
+ if (file === void 0 || binding === void 0) {
6249
+ continue;
6250
+ }
6251
+ const node = definedNodeOf(binding);
6252
+ if (node !== null) {
6253
+ return {
6254
+ node,
6255
+ moduleScope: file.module.moduleScope,
6256
+ scopeFor: file.module.scopeFor
6257
+ };
6258
+ }
6259
+ }
6260
+ return null;
6261
+ }
6262
+ function definedNodeOf(binding) {
6263
+ if (binding.kind === "classDef") {
6264
+ return binding.node;
6265
+ }
6266
+ if (binding.kind === "assignment") {
6267
+ return binding.value;
6268
+ }
6269
+ return null;
6270
+ }
6271
+
5994
6272
  // src/reach/closure.ts
5995
6273
  var import_behavioral_ir4 = require("@suss/behavioral-ir");
5996
6274
  var import_datalog2 = require("@suss/datalog");
@@ -6048,14 +6326,9 @@ var EXPRESSION_RESOLVERS = {
6048
6326
  }
6049
6327
  return memberOf(resolveExpression(object, site, ctx), attribute.text, ctx);
6050
6328
  },
6051
- // `Service().run()` runs a method on whatever the constructor built.
6052
6329
  call: (node, site, ctx) => {
6053
6330
  const callee = field(node, "function");
6054
- if (callee === null) {
6055
- return stop("noDeclaration");
6056
- }
6057
- const built = resolveExpression(callee, site, ctx);
6058
- return built.kind === "class" ? built : stop("noDeclaration");
6331
+ return callee === null ? stop("noDeclaration") : instanceBuiltBy(resolveExpression(callee, site, ctx), "noDeclaration");
6059
6332
  },
6060
6333
  parenthesized_expression: (node, site, ctx) => {
6061
6334
  const inner = node.namedChildren[0];
@@ -6066,6 +6339,12 @@ function resolveExpression(node, site, ctx) {
6066
6339
  const resolver = EXPRESSION_RESOLVERS[node.type];
6067
6340
  return resolver === void 0 ? stop("noDeclaration") : resolver(node, site, ctx);
6068
6341
  }
6342
+ function instanceBuiltBy(built, otherwise) {
6343
+ if (built.kind === "class" || built.kind === "stop") {
6344
+ return built;
6345
+ }
6346
+ return built.kind === "package" ? stop("outsideRun") : stop(otherwise);
6347
+ }
6069
6348
  function resolveIdentifier(name, site, ctx) {
6070
6349
  if (site.rebound.has(name)) {
6071
6350
  return stop("unsettledValue");
@@ -6131,8 +6410,10 @@ function aliasedValue(value, file, scope, ctx, visited) {
6131
6410
  }
6132
6411
  if (value.type === "call") {
6133
6412
  const callee = field(value, "function");
6134
- const built = callee === null ? stop("unsettledValue") : aliasedValue(callee, file, scope, ctx, visited);
6135
- return built.kind === "class" ? built : stop("unsettledValue");
6413
+ return callee === null ? stop("unsettledValue") : instanceBuiltBy(
6414
+ aliasedValue(callee, file, scope, ctx, visited),
6415
+ "unsettledValue"
6416
+ );
6136
6417
  }
6137
6418
  if (value.type === "attribute") {
6138
6419
  const object = field(value, "object");
@@ -7222,6 +7503,7 @@ async function extractPythonProject(options) {
7222
7503
  storageFor
7223
7504
  })
7224
7505
  );
7506
+ const importedDefinition = importedDefinitionLookup(db, bound);
7225
7507
  const discovered = /* @__PURE__ */ new Map();
7226
7508
  for (const boundFile of bound) {
7227
7509
  const { file, root, module: moduleBinding } = boundFile;
@@ -7235,6 +7517,7 @@ async function extractPythonProject(options) {
7235
7517
  routerIndex,
7236
7518
  gapHandling,
7237
7519
  wrappers: wrapperIndex,
7520
+ importedDefinition,
7238
7521
  ...needsValues ? { facts: db } : {},
7239
7522
  ...storage === void 0 ? {} : { storage }
7240
7523
  })