@suss/adapter-python 0.12.0 → 0.13.1

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/README.md CHANGED
@@ -81,6 +81,20 @@ FastAPI has no column here: its app has no prefix of its own, and the pack decla
81
81
  | `""`, `None`, `False`, `0` | the blueprint's prefix + the rest | unstated |
82
82
  | a name or a call | whatever it evaluates to | unreadable |
83
83
 
84
+ ### A call that spreads a dictionary
85
+
86
+ `Api(**build_authorizations())` and `include_router(router, **options)`
87
+ spread a value the call does not write out. The reader skips the spread
88
+ and reads the keywords that are written, so the routes underneath keep
89
+ their paths.
90
+
91
+ That leaves one thing it cannot see. If the dictionary sets the prefix
92
+ keyword itself, the composed path is wrong, and nothing in the output
93
+ says so. Reading the prefix as unknown instead would be correct and would
94
+ cost every route under every call that spreads a config dictionary, which
95
+ is most of them. Propagating the uncertainty rather than choosing between
96
+ the two is the work that would settle this.
97
+
84
98
  **At the registration** (`app.register_blueprint(bp, ...)`), which the reader consults only to abstain:
85
99
 
86
100
  | Written | flask-restx serves | The reader says |
package/dist/index.cjs CHANGED
@@ -622,6 +622,9 @@ function readCallArguments(argumentList) {
622
622
  }
623
623
  continue;
624
624
  }
625
+ if (child.type === "dictionary_splat" || child.type === "list_splat") {
626
+ continue;
627
+ }
625
628
  args.push(readArg(child));
626
629
  }
627
630
  return { args, keywordArgs };
@@ -639,18 +642,25 @@ function resolveObjectModule(name, scope) {
639
642
  }
640
643
  if (binding?.kind === "assignment" && binding.value?.type === "call") {
641
644
  const callee = field(binding.value, "function");
642
- if (callee?.type === "identifier") {
643
- const calleeBinding = resolveName(scope, callee.text);
644
- if (calleeBinding?.kind === "importFrom") {
645
- return {
646
- module: calleeBinding.module,
647
- relativeLevel: calleeBinding.relativeLevel
648
- };
649
- }
650
- }
645
+ return callee === null ? null : calleeModule(callee, scope);
651
646
  }
652
647
  return null;
653
648
  }
649
+ function calleeModule(callee, scope) {
650
+ if (callee.type === "identifier") {
651
+ const binding2 = resolveName(scope, callee.text);
652
+ return binding2?.kind === "importFrom" ? { module: binding2.module, relativeLevel: binding2.relativeLevel } : null;
653
+ }
654
+ if (callee.type !== "attribute") {
655
+ return null;
656
+ }
657
+ const object2 = field(callee, "object");
658
+ if (object2?.type !== "identifier") {
659
+ return null;
660
+ }
661
+ const binding = resolveName(scope, object2.text);
662
+ return binding?.kind === "import" ? { module: binding.module, relativeLevel: binding.relativeLevel } : null;
663
+ }
654
664
  function resolveCallee(expr, scope) {
655
665
  if (expr.type === "identifier") {
656
666
  const binding = resolveName(scope, expr.text);
@@ -18793,14 +18803,11 @@ function constructionOf(name, scope, importModule) {
18793
18803
  return null;
18794
18804
  }
18795
18805
  const callee = field(binding.value, "function");
18796
- if (callee?.type !== "identifier") {
18797
- return null;
18798
- }
18799
- const calleeBinding = resolveName(scope, callee.text);
18800
- if (calleeBinding?.kind !== "importFrom" || !importModule.includes(calleeBinding.module)) {
18806
+ if (callee === null) {
18801
18807
  return null;
18802
18808
  }
18803
- return { constructorName: calleeBinding.importedName, call: binding.value };
18809
+ const constructorName = importedConstructorName(callee, scope, importModule);
18810
+ return constructorName === null ? null : { constructorName, call: binding.value };
18804
18811
  }
18805
18812
  var PREFIX_TRAILING_SLASH_READERS = {
18806
18813
  kept: (prefix) => prefix,
@@ -18812,7 +18819,20 @@ var NO_VALUE_LITERALS = {
18812
18819
  boolean: (arg) => arg.kind === "boolean" && !arg.value,
18813
18820
  none: () => true
18814
18821
  };
18815
- function readPrefixKeyword(keywordArgs, keyword, composition) {
18822
+ function prefixStringOf(arg, scope) {
18823
+ if (arg.kind === "string") {
18824
+ return arg.value;
18825
+ }
18826
+ if (arg.kind !== "identifier" || scope === void 0) {
18827
+ return null;
18828
+ }
18829
+ const binding = resolveName(scope, arg.name);
18830
+ if (binding === null || binding.kind !== "assignment" || binding.value === null) {
18831
+ return null;
18832
+ }
18833
+ return stringLiteralValue(binding.value);
18834
+ }
18835
+ function readPrefixKeyword(keywordArgs, keyword, composition, scope) {
18816
18836
  const arg = keywordArgs[keyword];
18817
18837
  if (arg === void 0) {
18818
18838
  return UNSTATED_PREFIX;
@@ -18820,16 +18840,15 @@ function readPrefixKeyword(keywordArgs, keyword, composition) {
18820
18840
  if ((composition.noValuePrefix ?? "unreadable") === "unstated" && NO_VALUE_LITERALS[arg.kind]?.(arg) === true) {
18821
18841
  return UNSTATED_PREFIX;
18822
18842
  }
18823
- if (arg.kind === "string") {
18824
- return { kind: "stated", value: arg.value };
18825
- }
18826
- return UNREADABLE_PREFIX;
18843
+ const value = prefixStringOf(arg, scope);
18844
+ return value === null ? UNREADABLE_PREFIX : { kind: "stated", value };
18827
18845
  }
18828
- function constructorPrefix(keywordArgs, composition) {
18846
+ function constructorPrefix(keywordArgs, composition, scope) {
18829
18847
  const reading = readPrefixKeyword(
18830
18848
  keywordArgs,
18831
18849
  composition.prefixKeyword,
18832
- composition
18850
+ composition,
18851
+ scope
18833
18852
  );
18834
18853
  if (reading.kind !== "stated") {
18835
18854
  return reading;
@@ -18856,18 +18875,36 @@ function constructionStatement(stmt, scope, importModule) {
18856
18875
  return null;
18857
18876
  }
18858
18877
  const callee = field(right, "function");
18859
- if (callee?.type !== "identifier") {
18878
+ if (callee === null) {
18860
18879
  return null;
18861
18880
  }
18862
- const calleeBinding = resolveName(scope, callee.text);
18863
- if (calleeBinding?.kind !== "importFrom" || !importModule.includes(calleeBinding.module)) {
18881
+ const constructorName = importedConstructorName(callee, scope, importModule);
18882
+ if (constructorName === null) {
18864
18883
  return null;
18865
18884
  }
18866
- return {
18867
- name: left.text,
18868
- constructorName: calleeBinding.importedName,
18869
- call: right
18870
- };
18885
+ return { name: left.text, constructorName, call: right };
18886
+ }
18887
+ function importedConstructorName(callee, scope, importModule) {
18888
+ if (callee.type === "identifier") {
18889
+ const binding2 = resolveName(scope, callee.text);
18890
+ if (binding2?.kind !== "importFrom" || !importModule.includes(binding2.module)) {
18891
+ return null;
18892
+ }
18893
+ return binding2.importedName;
18894
+ }
18895
+ if (callee.type !== "attribute") {
18896
+ return null;
18897
+ }
18898
+ const object2 = field(callee, "object");
18899
+ const attribute = field(callee, "attribute");
18900
+ if (object2?.type !== "identifier" || attribute?.type !== "identifier") {
18901
+ return null;
18902
+ }
18903
+ const binding = resolveName(scope, object2.text);
18904
+ if (binding?.kind !== "import" || !importModule.includes(binding.module)) {
18905
+ return null;
18906
+ }
18907
+ return attribute.text;
18871
18908
  }
18872
18909
  function recordConstruction(construction, module2, file2, prefixOf, into, byValueKey) {
18873
18910
  const perModule = into.get(module2) ?? /* @__PURE__ */ new Map();
@@ -18903,7 +18940,7 @@ function collectConstructions(bound, importModule, composition, index) {
18903
18940
  construction,
18904
18941
  bound.module,
18905
18942
  bound.file,
18906
- (keywordArgs) => constructorPrefix(keywordArgs, composition),
18943
+ (keywordArgs) => constructorPrefix(keywordArgs, composition, bound.module.moduleScope),
18907
18944
  index.constructions,
18908
18945
  index.byValueKey
18909
18946
  );
@@ -18927,7 +18964,12 @@ function collectCarrierConstructions(carrier, composition, scan) {
18927
18964
  construction,
18928
18965
  scan.bound.module,
18929
18966
  scan.bound.file,
18930
- (keywordArgs) => readPrefixKeyword(keywordArgs, carrier.prefixKeyword, composition),
18967
+ (keywordArgs) => readPrefixKeyword(
18968
+ keywordArgs,
18969
+ carrier.prefixKeyword,
18970
+ composition,
18971
+ scan.bound.module.moduleScope
18972
+ ),
18931
18973
  scan.index.carriers,
18932
18974
  scan.index.byValueKey
18933
18975
  );
@@ -19246,13 +19288,15 @@ function recordMountStatement(stmt, position, scan) {
19246
19288
  keywordArgs,
19247
19289
  objectPrefix,
19248
19290
  scan.composition,
19249
- position.site
19291
+ position.site,
19292
+ position.scope
19250
19293
  );
19251
- for (const target of mountedConstructions(args[0], position, scan)) {
19294
+ const mounted = args[0] ?? (scan.composition.routerKeyword === void 0 ? void 0 : keywordArgs[scan.composition.routerKeyword]);
19295
+ for (const target of mountedConstructions(mounted, position, scan)) {
19252
19296
  recordMount(scan.index, target, state);
19253
19297
  }
19254
19298
  }
19255
- function mountStateOf(includerConstructorName, keywordArgs, objectPrefix, composition, site) {
19299
+ function mountStateOf(includerConstructorName, keywordArgs, objectPrefix, composition, site, scope) {
19256
19300
  if (includerConstructorName === composition.routerConstructorName) {
19257
19301
  return {
19258
19302
  kind: "abstain",
@@ -19268,7 +19312,8 @@ function mountStateOf(includerConstructorName, keywordArgs, objectPrefix, compos
19268
19312
  const mountPrefix = readPrefixKeyword(
19269
19313
  keywordArgs,
19270
19314
  composition.prefixKeyword,
19271
- composition
19315
+ composition,
19316
+ scope
19272
19317
  );
19273
19318
  if (mountPrefix.kind === "unreadable") {
19274
19319
  return {
@@ -19424,7 +19469,12 @@ function readMountObjectPrefix(spec, objectName, constructionCall, scope, scan)
19424
19469
  const { args, keywordArgs } = readCallArguments(
19425
19470
  field(constructionCall, "arguments")
19426
19471
  );
19427
- const own = spec.prefixKeyword === void 0 ? UNSTATED_PREFIX : readPrefixKeyword(keywordArgs, spec.prefixKeyword, scan.composition);
19472
+ const own = spec.prefixKeyword === void 0 ? UNSTATED_PREFIX : readPrefixKeyword(
19473
+ keywordArgs,
19474
+ spec.prefixKeyword,
19475
+ scan.composition,
19476
+ scan.bound.module.moduleScope
19477
+ );
19428
19478
  if (own.kind === "unreadable") {
19429
19479
  return {
19430
19480
  kind: "abstain",
package/dist/index.d.cts CHANGED
@@ -304,6 +304,12 @@ interface RouterComposition {
304
304
  routerConstructorName: string;
305
305
  /** Method that mounts a router onto the app, FastAPI's `include_router`. */
306
306
  includeMethodName: string;
307
+ /**
308
+ * What the mount method calls its router parameter, FastAPI's `router`.
309
+ * A call that passes the router by keyword rather than by position is
310
+ * read through this. Unset reads the first argument only.
311
+ */
312
+ routerKeyword?: string;
307
313
  /** One keyword serves the constructor and the mount alike. A library that spells them apart needs two fields here. */
308
314
  prefixKeyword: string;
309
315
  /** Default "prefixes". */
package/dist/index.d.ts CHANGED
@@ -304,6 +304,12 @@ interface RouterComposition {
304
304
  routerConstructorName: string;
305
305
  /** Method that mounts a router onto the app, FastAPI's `include_router`. */
306
306
  includeMethodName: string;
307
+ /**
308
+ * What the mount method calls its router parameter, FastAPI's `router`.
309
+ * A call that passes the router by keyword rather than by position is
310
+ * read through this. Unset reads the first argument only.
311
+ */
312
+ routerKeyword?: string;
307
313
  /** One keyword serves the constructor and the mount alike. A library that spells them apart needs two fields here. */
308
314
  prefixKeyword: string;
309
315
  /** Default "prefixes". */
package/dist/index.js CHANGED
@@ -568,6 +568,9 @@ function readCallArguments(argumentList) {
568
568
  }
569
569
  continue;
570
570
  }
571
+ if (child.type === "dictionary_splat" || child.type === "list_splat") {
572
+ continue;
573
+ }
571
574
  args.push(readArg(child));
572
575
  }
573
576
  return { args, keywordArgs };
@@ -585,18 +588,25 @@ function resolveObjectModule(name, scope) {
585
588
  }
586
589
  if (binding?.kind === "assignment" && binding.value?.type === "call") {
587
590
  const callee = field(binding.value, "function");
588
- if (callee?.type === "identifier") {
589
- const calleeBinding = resolveName(scope, callee.text);
590
- if (calleeBinding?.kind === "importFrom") {
591
- return {
592
- module: calleeBinding.module,
593
- relativeLevel: calleeBinding.relativeLevel
594
- };
595
- }
596
- }
591
+ return callee === null ? null : calleeModule(callee, scope);
597
592
  }
598
593
  return null;
599
594
  }
595
+ function calleeModule(callee, scope) {
596
+ if (callee.type === "identifier") {
597
+ const binding2 = resolveName(scope, callee.text);
598
+ return binding2?.kind === "importFrom" ? { module: binding2.module, relativeLevel: binding2.relativeLevel } : null;
599
+ }
600
+ if (callee.type !== "attribute") {
601
+ return null;
602
+ }
603
+ const object2 = field(callee, "object");
604
+ if (object2?.type !== "identifier") {
605
+ return null;
606
+ }
607
+ const binding = resolveName(scope, object2.text);
608
+ return binding?.kind === "import" ? { module: binding.module, relativeLevel: binding.relativeLevel } : null;
609
+ }
600
610
  function resolveCallee(expr, scope) {
601
611
  if (expr.type === "identifier") {
602
612
  const binding = resolveName(scope, expr.text);
@@ -18749,14 +18759,11 @@ function constructionOf(name, scope, importModule) {
18749
18759
  return null;
18750
18760
  }
18751
18761
  const callee = field(binding.value, "function");
18752
- if (callee?.type !== "identifier") {
18753
- return null;
18754
- }
18755
- const calleeBinding = resolveName(scope, callee.text);
18756
- if (calleeBinding?.kind !== "importFrom" || !importModule.includes(calleeBinding.module)) {
18762
+ if (callee === null) {
18757
18763
  return null;
18758
18764
  }
18759
- return { constructorName: calleeBinding.importedName, call: binding.value };
18765
+ const constructorName = importedConstructorName(callee, scope, importModule);
18766
+ return constructorName === null ? null : { constructorName, call: binding.value };
18760
18767
  }
18761
18768
  var PREFIX_TRAILING_SLASH_READERS = {
18762
18769
  kept: (prefix) => prefix,
@@ -18768,7 +18775,20 @@ var NO_VALUE_LITERALS = {
18768
18775
  boolean: (arg) => arg.kind === "boolean" && !arg.value,
18769
18776
  none: () => true
18770
18777
  };
18771
- function readPrefixKeyword(keywordArgs, keyword, composition) {
18778
+ function prefixStringOf(arg, scope) {
18779
+ if (arg.kind === "string") {
18780
+ return arg.value;
18781
+ }
18782
+ if (arg.kind !== "identifier" || scope === void 0) {
18783
+ return null;
18784
+ }
18785
+ const binding = resolveName(scope, arg.name);
18786
+ if (binding === null || binding.kind !== "assignment" || binding.value === null) {
18787
+ return null;
18788
+ }
18789
+ return stringLiteralValue(binding.value);
18790
+ }
18791
+ function readPrefixKeyword(keywordArgs, keyword, composition, scope) {
18772
18792
  const arg = keywordArgs[keyword];
18773
18793
  if (arg === void 0) {
18774
18794
  return UNSTATED_PREFIX;
@@ -18776,16 +18796,15 @@ function readPrefixKeyword(keywordArgs, keyword, composition) {
18776
18796
  if ((composition.noValuePrefix ?? "unreadable") === "unstated" && NO_VALUE_LITERALS[arg.kind]?.(arg) === true) {
18777
18797
  return UNSTATED_PREFIX;
18778
18798
  }
18779
- if (arg.kind === "string") {
18780
- return { kind: "stated", value: arg.value };
18781
- }
18782
- return UNREADABLE_PREFIX;
18799
+ const value = prefixStringOf(arg, scope);
18800
+ return value === null ? UNREADABLE_PREFIX : { kind: "stated", value };
18783
18801
  }
18784
- function constructorPrefix(keywordArgs, composition) {
18802
+ function constructorPrefix(keywordArgs, composition, scope) {
18785
18803
  const reading = readPrefixKeyword(
18786
18804
  keywordArgs,
18787
18805
  composition.prefixKeyword,
18788
- composition
18806
+ composition,
18807
+ scope
18789
18808
  );
18790
18809
  if (reading.kind !== "stated") {
18791
18810
  return reading;
@@ -18812,18 +18831,36 @@ function constructionStatement(stmt, scope, importModule) {
18812
18831
  return null;
18813
18832
  }
18814
18833
  const callee = field(right, "function");
18815
- if (callee?.type !== "identifier") {
18834
+ if (callee === null) {
18816
18835
  return null;
18817
18836
  }
18818
- const calleeBinding = resolveName(scope, callee.text);
18819
- if (calleeBinding?.kind !== "importFrom" || !importModule.includes(calleeBinding.module)) {
18837
+ const constructorName = importedConstructorName(callee, scope, importModule);
18838
+ if (constructorName === null) {
18820
18839
  return null;
18821
18840
  }
18822
- return {
18823
- name: left.text,
18824
- constructorName: calleeBinding.importedName,
18825
- call: right
18826
- };
18841
+ return { name: left.text, constructorName, call: right };
18842
+ }
18843
+ function importedConstructorName(callee, scope, importModule) {
18844
+ if (callee.type === "identifier") {
18845
+ const binding2 = resolveName(scope, callee.text);
18846
+ if (binding2?.kind !== "importFrom" || !importModule.includes(binding2.module)) {
18847
+ return null;
18848
+ }
18849
+ return binding2.importedName;
18850
+ }
18851
+ if (callee.type !== "attribute") {
18852
+ return null;
18853
+ }
18854
+ const object2 = field(callee, "object");
18855
+ const attribute = field(callee, "attribute");
18856
+ if (object2?.type !== "identifier" || attribute?.type !== "identifier") {
18857
+ return null;
18858
+ }
18859
+ const binding = resolveName(scope, object2.text);
18860
+ if (binding?.kind !== "import" || !importModule.includes(binding.module)) {
18861
+ return null;
18862
+ }
18863
+ return attribute.text;
18827
18864
  }
18828
18865
  function recordConstruction(construction, module, file2, prefixOf, into, byValueKey) {
18829
18866
  const perModule = into.get(module) ?? /* @__PURE__ */ new Map();
@@ -18859,7 +18896,7 @@ function collectConstructions(bound, importModule, composition, index) {
18859
18896
  construction,
18860
18897
  bound.module,
18861
18898
  bound.file,
18862
- (keywordArgs) => constructorPrefix(keywordArgs, composition),
18899
+ (keywordArgs) => constructorPrefix(keywordArgs, composition, bound.module.moduleScope),
18863
18900
  index.constructions,
18864
18901
  index.byValueKey
18865
18902
  );
@@ -18883,7 +18920,12 @@ function collectCarrierConstructions(carrier, composition, scan) {
18883
18920
  construction,
18884
18921
  scan.bound.module,
18885
18922
  scan.bound.file,
18886
- (keywordArgs) => readPrefixKeyword(keywordArgs, carrier.prefixKeyword, composition),
18923
+ (keywordArgs) => readPrefixKeyword(
18924
+ keywordArgs,
18925
+ carrier.prefixKeyword,
18926
+ composition,
18927
+ scan.bound.module.moduleScope
18928
+ ),
18887
18929
  scan.index.carriers,
18888
18930
  scan.index.byValueKey
18889
18931
  );
@@ -19202,13 +19244,15 @@ function recordMountStatement(stmt, position, scan) {
19202
19244
  keywordArgs,
19203
19245
  objectPrefix,
19204
19246
  scan.composition,
19205
- position.site
19247
+ position.site,
19248
+ position.scope
19206
19249
  );
19207
- for (const target of mountedConstructions(args[0], position, scan)) {
19250
+ const mounted = args[0] ?? (scan.composition.routerKeyword === void 0 ? void 0 : keywordArgs[scan.composition.routerKeyword]);
19251
+ for (const target of mountedConstructions(mounted, position, scan)) {
19208
19252
  recordMount(scan.index, target, state);
19209
19253
  }
19210
19254
  }
19211
- function mountStateOf(includerConstructorName, keywordArgs, objectPrefix, composition, site) {
19255
+ function mountStateOf(includerConstructorName, keywordArgs, objectPrefix, composition, site, scope) {
19212
19256
  if (includerConstructorName === composition.routerConstructorName) {
19213
19257
  return {
19214
19258
  kind: "abstain",
@@ -19224,7 +19268,8 @@ function mountStateOf(includerConstructorName, keywordArgs, objectPrefix, compos
19224
19268
  const mountPrefix = readPrefixKeyword(
19225
19269
  keywordArgs,
19226
19270
  composition.prefixKeyword,
19227
- composition
19271
+ composition,
19272
+ scope
19228
19273
  );
19229
19274
  if (mountPrefix.kind === "unreadable") {
19230
19275
  return {
@@ -19380,7 +19425,12 @@ function readMountObjectPrefix(spec, objectName, constructionCall, scope, scan)
19380
19425
  const { args, keywordArgs } = readCallArguments(
19381
19426
  field(constructionCall, "arguments")
19382
19427
  );
19383
- const own = spec.prefixKeyword === void 0 ? UNSTATED_PREFIX : readPrefixKeyword(keywordArgs, spec.prefixKeyword, scan.composition);
19428
+ const own = spec.prefixKeyword === void 0 ? UNSTATED_PREFIX : readPrefixKeyword(
19429
+ keywordArgs,
19430
+ spec.prefixKeyword,
19431
+ scan.composition,
19432
+ scan.bound.module.moduleScope
19433
+ );
19384
19434
  if (own.kind === "unreadable") {
19385
19435
  return {
19386
19436
  kind: "abstain",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/adapter-python",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
4
4
  "type": "module",
5
5
  "description": "Python language adapter for suss — parses source with tree-sitter (WASM), resolves names with a lexical binder, and emits raw extractions for the suss pipeline.",
6
6
  "keywords": [
@@ -47,10 +47,10 @@
47
47
  "typecheck": "tsc --noEmit"
48
48
  },
49
49
  "dependencies": {
50
- "@suss/behavioral-ir": "0.12.0",
51
- "@suss/datalog": "0.12.0",
52
- "@suss/extractor": "0.12.0",
53
- "@suss/sql": "0.12.0",
50
+ "@suss/behavioral-ir": "0.13.1",
51
+ "@suss/datalog": "0.13.1",
52
+ "@suss/extractor": "0.13.1",
53
+ "@suss/sql": "0.13.1",
54
54
  "web-tree-sitter": "^0.26.11"
55
55
  },
56
56
  "devDependencies": {