@suss/adapter-python 0.3.2-0 → 0.4.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.d.cts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { TypeShape, BehavioralSummary } from '@suss/behavioral-ir';
2
2
  import { Tree, Node } from 'web-tree-sitter';
3
3
  export { Node as PyNode, Tree as PyTree } from 'web-tree-sitter';
4
- import { ExtractorOptions, RawCodeStructure } from '@suss/extractor';
5
4
  import { Database } from '@suss/datalog';
5
+ import { ExtractorOptions, RawCodeStructure } from '@suss/extractor';
6
6
 
7
7
  /**
8
8
  * A new `Parser` for every call, so that parses running at the same time do
@@ -107,15 +107,6 @@ declare function annotationToShape(typeNode: Node, scope: Scope, ctx: Annotation
107
107
  /** Exported so a decorator keyword that gives a class name reads the same way as that name written in annotation position. */
108
108
  declare function shapeFromName(name: string, scope: Scope, ctx: AnnotationContext): TypeShape;
109
109
 
110
- /**
111
- * Small helpers for reading a tree-sitter-python parse tree.
112
- *
113
- * The node-type strings here come from tree-sitter-python's grammar, at the
114
- * version the grammar README gives. Read a node through these helpers rather
115
- * than calling `childForFieldName` or `.type` inline, so that a grammar upgrade
116
- * that renames a field only has to be fixed in one place.
117
- */
118
-
119
110
  interface Range {
120
111
  start: number;
121
112
  end: number;
@@ -175,6 +166,24 @@ interface PythonPack {
175
166
  /** Wire protocol for the produced boundary bindings, e.g. "http". */
176
167
  protocol: string;
177
168
  discovery: PythonDiscoveryPattern[];
169
+ /** What the library's own database queries look like. The README says how one is matched. */
170
+ storage?: StoragePattern[];
171
+ }
172
+ /**
173
+ * A call talks to the database when the method behind it says it returns one
174
+ * of the library's query types. Matching on the return rather than on the
175
+ * import is what reads a project base class that wraps the library, which is
176
+ * how the measured corpus writes every one of its queries.
177
+ */
178
+ interface StoragePattern {
179
+ /** The module a query type is imported from, `sqlalchemy.orm` for a Session. */
180
+ module: string;
181
+ /** Type names that mean the value is a query against the database. */
182
+ queryTypes: string[];
183
+ /** Chain-ending methods that change what is stored. Anything else reads. */
184
+ writes: string[];
185
+ /** Which database the library is talking to, for the boundary binding. */
186
+ storageSystem: "postgres" | "mysql" | "sqlite";
178
187
  }
179
188
  type PythonDiscoveryPattern = DecoratedClassRoute | DecoratedFunctionRoute;
180
189
  /** Conventions both kinds of route share. Each one describes what the library does, never a project's choice. */
@@ -327,6 +336,18 @@ type NoValuePrefix = "unstated" | "unreadable";
327
336
  */
328
337
  type PathRepeatedSlashes = "kept" | "merged";
329
338
 
339
+ /**
340
+ * What discovery uses for one file. `factsPath` is the path the facts were
341
+ * keyed under, which is the absolute one, while a summary shows the short one.
342
+ */
343
+ interface StorageLookup {
344
+ readonly facts: Database;
345
+ readonly factsPath: string;
346
+ readonly patterns: readonly StoragePattern[];
347
+ readonly definitionAt: (key: string) => Node | undefined;
348
+ readonly couldMatch: ReadonlySet<string>;
349
+ }
350
+
330
351
  /**
331
352
  * Maps a dotted import onto the file in the repo it refers to.
332
353
  *
@@ -394,7 +415,11 @@ type RoutePrefixResolution = {
394
415
  interface RouterIndex {
395
416
  resolve(pattern: PythonDiscoveryPattern, module: ModuleBinding, objectName: string): RoutePrefixResolution;
396
417
  }
397
- declare function buildRouterIndex(files: BoundPythonFile[], packs: PythonPack[], resolverOptions: ModuleResolverOptions): RouterIndex;
418
+ interface RouterIndexOptions extends ModuleResolverOptions {
419
+ /** The project's facts, so a loop over a call can be settled by the rules. */
420
+ facts?: Database;
421
+ }
422
+ declare function buildRouterIndex(files: BoundPythonFile[], packs: PythonPack[], resolverOptions: RouterIndexOptions): RouterIndex;
398
423
 
399
424
  /**
400
425
  * Finds decorated routes and turns each one into a `RawCodeStructure`.
@@ -418,9 +443,37 @@ interface DiscoveryOptions {
418
443
  routerIndex?: RouterIndex;
419
444
  /** Under "strict" a route whose unit cannot be built stops the run instead of abstaining. */
420
445
  gapHandling?: ExtractorOptions["gapHandling"];
446
+ /** What a pack needs to say a call talks to the database. Absent when no pack does. */
447
+ storage?: StorageLookup | undefined;
421
448
  }
422
449
  declare function discoverUnits(root: Node, module: ModuleBinding, options: DiscoveryOptions): RawCodeStructure[];
423
450
 
451
+ /**
452
+ * Ask what these calls come down to, then derive. Asking about every call in
453
+ * a project costs seconds on a large one and answers questions nobody has,
454
+ * so a caller names the handful it needs.
455
+ */
456
+ declare function resolveCalls(db: Database, callKeys: readonly string[]): void;
457
+ /**
458
+ * The values an object contains under its own keys, in the order the source
459
+ * writes them. Empty when nothing said what the object contains.
460
+ */
461
+ declare function containedValues(db: Database, objectKey: string): string[];
462
+ /** What a call comes down to, when the rules settled it on an object. */
463
+ declare function objectReturnedBy(db: Database, callKey: string): string | null;
464
+
465
+ /**
466
+ * A node's identity across the whole run. The end is part of it because a
467
+ * call and its callee start at the same offset.
468
+ */
469
+ declare function nodeId(filePath: string, node: Node): string;
470
+ /**
471
+ * Walk a module and emit the value facts. A nested function is walked in its
472
+ * own right, so a body's returns and calls belong to the function that wrote
473
+ * them.
474
+ */
475
+ declare function emitValueFacts(db: Database, filePath: string, root: Node): void;
476
+
424
477
  /**
425
478
  * The name is part of the key because the range is measured in lines, two
426
479
  * units can start on the same line, and `entry` is a set, so keying on the range
@@ -434,7 +487,11 @@ declare function emitEntryFact(db: Database, filePath: string, range: {
434
487
  start: number;
435
488
  end: number;
436
489
  }, name: string): void;
437
- /** Module scope only. A function- or class-scoped import is not recorded. */
490
+ /**
491
+ * Every import in the file, wherever it is written. Python code puts an import
492
+ * inside a function to break a cycle between two modules, and a chain through
493
+ * one of those functions stops dead without it.
494
+ */
438
495
  declare function emitModuleImportFacts(db: Database, filePath: string, module: ModuleBinding, resolverOptions: ModuleResolverOptions): void;
439
496
 
440
497
  interface ExtractPythonOptions {
@@ -456,4 +513,4 @@ declare function extractPythonProject(options: ExtractPythonOptions): Promise<Ex
456
513
  /** Every `.py` file under `root`, depth-first, skipping the usual non-source directories. */
457
514
  declare function findPythonFiles(root: string): string[];
458
515
 
459
- export { type AnnotationContext, type Binding, type BoundPythonFile, type DecoratedClassRoute, type DecoratedFunctionRoute, type DecoratorArg, type DecoratorClassification, type DiscoveryOptions, type ExtractPythonOptions, type ExtractPythonResult, type ModuleBinding, type ModuleResolution, type ModuleResolverOptions, type MountObjectCarrier, type MountObjectPrefix, type MountPrefixEffect, type NoValuePrefix, type PathRepeatedSlashes, type PrefixTrailingSlash, type PythonDiscoveryPattern, type PythonPack, type RelativeModuleSpec, type RouteConventions, type RoutePrefixResolution, type RouterComposition, type RouterIndex, type Scope, type ScopeKind, annotationToShape, bindModule, buildRouterIndex, classifyDecorator, discoverUnits, emitEntryFact, emitModuleImportFacts, extractPythonProject, findPythonFiles, parsePython, resolveAbsoluteModule, resolveModule, resolveName, resolveRelativeModule, shapeFromName, unitKey };
516
+ export { type AnnotationContext, type Binding, type BoundPythonFile, type DecoratedClassRoute, type DecoratedFunctionRoute, type DecoratorArg, type DecoratorClassification, type DiscoveryOptions, type ExtractPythonOptions, type ExtractPythonResult, type ModuleBinding, type ModuleResolution, type ModuleResolverOptions, type MountObjectCarrier, type MountObjectPrefix, type MountPrefixEffect, type NoValuePrefix, type PathRepeatedSlashes, type PrefixTrailingSlash, type PythonDiscoveryPattern, type PythonPack, type RelativeModuleSpec, type RouteConventions, type RoutePrefixResolution, type RouterComposition, type RouterIndex, type Scope, type ScopeKind, type StoragePattern, annotationToShape, bindModule, buildRouterIndex, classifyDecorator, containedValues, discoverUnits, emitEntryFact, emitModuleImportFacts, emitValueFacts, extractPythonProject, findPythonFiles, nodeId, objectReturnedBy, parsePython, resolveAbsoluteModule, resolveCalls, resolveModule, resolveName, resolveRelativeModule, shapeFromName, unitKey };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { TypeShape, BehavioralSummary } from '@suss/behavioral-ir';
2
2
  import { Tree, Node } from 'web-tree-sitter';
3
3
  export { Node as PyNode, Tree as PyTree } from 'web-tree-sitter';
4
- import { ExtractorOptions, RawCodeStructure } from '@suss/extractor';
5
4
  import { Database } from '@suss/datalog';
5
+ import { ExtractorOptions, RawCodeStructure } from '@suss/extractor';
6
6
 
7
7
  /**
8
8
  * A new `Parser` for every call, so that parses running at the same time do
@@ -107,15 +107,6 @@ declare function annotationToShape(typeNode: Node, scope: Scope, ctx: Annotation
107
107
  /** Exported so a decorator keyword that gives a class name reads the same way as that name written in annotation position. */
108
108
  declare function shapeFromName(name: string, scope: Scope, ctx: AnnotationContext): TypeShape;
109
109
 
110
- /**
111
- * Small helpers for reading a tree-sitter-python parse tree.
112
- *
113
- * The node-type strings here come from tree-sitter-python's grammar, at the
114
- * version the grammar README gives. Read a node through these helpers rather
115
- * than calling `childForFieldName` or `.type` inline, so that a grammar upgrade
116
- * that renames a field only has to be fixed in one place.
117
- */
118
-
119
110
  interface Range {
120
111
  start: number;
121
112
  end: number;
@@ -175,6 +166,24 @@ interface PythonPack {
175
166
  /** Wire protocol for the produced boundary bindings, e.g. "http". */
176
167
  protocol: string;
177
168
  discovery: PythonDiscoveryPattern[];
169
+ /** What the library's own database queries look like. The README says how one is matched. */
170
+ storage?: StoragePattern[];
171
+ }
172
+ /**
173
+ * A call talks to the database when the method behind it says it returns one
174
+ * of the library's query types. Matching on the return rather than on the
175
+ * import is what reads a project base class that wraps the library, which is
176
+ * how the measured corpus writes every one of its queries.
177
+ */
178
+ interface StoragePattern {
179
+ /** The module a query type is imported from, `sqlalchemy.orm` for a Session. */
180
+ module: string;
181
+ /** Type names that mean the value is a query against the database. */
182
+ queryTypes: string[];
183
+ /** Chain-ending methods that change what is stored. Anything else reads. */
184
+ writes: string[];
185
+ /** Which database the library is talking to, for the boundary binding. */
186
+ storageSystem: "postgres" | "mysql" | "sqlite";
178
187
  }
179
188
  type PythonDiscoveryPattern = DecoratedClassRoute | DecoratedFunctionRoute;
180
189
  /** Conventions both kinds of route share. Each one describes what the library does, never a project's choice. */
@@ -327,6 +336,18 @@ type NoValuePrefix = "unstated" | "unreadable";
327
336
  */
328
337
  type PathRepeatedSlashes = "kept" | "merged";
329
338
 
339
+ /**
340
+ * What discovery uses for one file. `factsPath` is the path the facts were
341
+ * keyed under, which is the absolute one, while a summary shows the short one.
342
+ */
343
+ interface StorageLookup {
344
+ readonly facts: Database;
345
+ readonly factsPath: string;
346
+ readonly patterns: readonly StoragePattern[];
347
+ readonly definitionAt: (key: string) => Node | undefined;
348
+ readonly couldMatch: ReadonlySet<string>;
349
+ }
350
+
330
351
  /**
331
352
  * Maps a dotted import onto the file in the repo it refers to.
332
353
  *
@@ -394,7 +415,11 @@ type RoutePrefixResolution = {
394
415
  interface RouterIndex {
395
416
  resolve(pattern: PythonDiscoveryPattern, module: ModuleBinding, objectName: string): RoutePrefixResolution;
396
417
  }
397
- declare function buildRouterIndex(files: BoundPythonFile[], packs: PythonPack[], resolverOptions: ModuleResolverOptions): RouterIndex;
418
+ interface RouterIndexOptions extends ModuleResolverOptions {
419
+ /** The project's facts, so a loop over a call can be settled by the rules. */
420
+ facts?: Database;
421
+ }
422
+ declare function buildRouterIndex(files: BoundPythonFile[], packs: PythonPack[], resolverOptions: RouterIndexOptions): RouterIndex;
398
423
 
399
424
  /**
400
425
  * Finds decorated routes and turns each one into a `RawCodeStructure`.
@@ -418,9 +443,37 @@ interface DiscoveryOptions {
418
443
  routerIndex?: RouterIndex;
419
444
  /** Under "strict" a route whose unit cannot be built stops the run instead of abstaining. */
420
445
  gapHandling?: ExtractorOptions["gapHandling"];
446
+ /** What a pack needs to say a call talks to the database. Absent when no pack does. */
447
+ storage?: StorageLookup | undefined;
421
448
  }
422
449
  declare function discoverUnits(root: Node, module: ModuleBinding, options: DiscoveryOptions): RawCodeStructure[];
423
450
 
451
+ /**
452
+ * Ask what these calls come down to, then derive. Asking about every call in
453
+ * a project costs seconds on a large one and answers questions nobody has,
454
+ * so a caller names the handful it needs.
455
+ */
456
+ declare function resolveCalls(db: Database, callKeys: readonly string[]): void;
457
+ /**
458
+ * The values an object contains under its own keys, in the order the source
459
+ * writes them. Empty when nothing said what the object contains.
460
+ */
461
+ declare function containedValues(db: Database, objectKey: string): string[];
462
+ /** What a call comes down to, when the rules settled it on an object. */
463
+ declare function objectReturnedBy(db: Database, callKey: string): string | null;
464
+
465
+ /**
466
+ * A node's identity across the whole run. The end is part of it because a
467
+ * call and its callee start at the same offset.
468
+ */
469
+ declare function nodeId(filePath: string, node: Node): string;
470
+ /**
471
+ * Walk a module and emit the value facts. A nested function is walked in its
472
+ * own right, so a body's returns and calls belong to the function that wrote
473
+ * them.
474
+ */
475
+ declare function emitValueFacts(db: Database, filePath: string, root: Node): void;
476
+
424
477
  /**
425
478
  * The name is part of the key because the range is measured in lines, two
426
479
  * units can start on the same line, and `entry` is a set, so keying on the range
@@ -434,7 +487,11 @@ declare function emitEntryFact(db: Database, filePath: string, range: {
434
487
  start: number;
435
488
  end: number;
436
489
  }, name: string): void;
437
- /** Module scope only. A function- or class-scoped import is not recorded. */
490
+ /**
491
+ * Every import in the file, wherever it is written. Python code puts an import
492
+ * inside a function to break a cycle between two modules, and a chain through
493
+ * one of those functions stops dead without it.
494
+ */
438
495
  declare function emitModuleImportFacts(db: Database, filePath: string, module: ModuleBinding, resolverOptions: ModuleResolverOptions): void;
439
496
 
440
497
  interface ExtractPythonOptions {
@@ -456,4 +513,4 @@ declare function extractPythonProject(options: ExtractPythonOptions): Promise<Ex
456
513
  /** Every `.py` file under `root`, depth-first, skipping the usual non-source directories. */
457
514
  declare function findPythonFiles(root: string): string[];
458
515
 
459
- export { type AnnotationContext, type Binding, type BoundPythonFile, type DecoratedClassRoute, type DecoratedFunctionRoute, type DecoratorArg, type DecoratorClassification, type DiscoveryOptions, type ExtractPythonOptions, type ExtractPythonResult, type ModuleBinding, type ModuleResolution, type ModuleResolverOptions, type MountObjectCarrier, type MountObjectPrefix, type MountPrefixEffect, type NoValuePrefix, type PathRepeatedSlashes, type PrefixTrailingSlash, type PythonDiscoveryPattern, type PythonPack, type RelativeModuleSpec, type RouteConventions, type RoutePrefixResolution, type RouterComposition, type RouterIndex, type Scope, type ScopeKind, annotationToShape, bindModule, buildRouterIndex, classifyDecorator, discoverUnits, emitEntryFact, emitModuleImportFacts, extractPythonProject, findPythonFiles, parsePython, resolveAbsoluteModule, resolveModule, resolveName, resolveRelativeModule, shapeFromName, unitKey };
516
+ export { type AnnotationContext, type Binding, type BoundPythonFile, type DecoratedClassRoute, type DecoratedFunctionRoute, type DecoratorArg, type DecoratorClassification, type DiscoveryOptions, type ExtractPythonOptions, type ExtractPythonResult, type ModuleBinding, type ModuleResolution, type ModuleResolverOptions, type MountObjectCarrier, type MountObjectPrefix, type MountPrefixEffect, type NoValuePrefix, type PathRepeatedSlashes, type PrefixTrailingSlash, type PythonDiscoveryPattern, type PythonPack, type RelativeModuleSpec, type RouteConventions, type RoutePrefixResolution, type RouterComposition, type RouterIndex, type Scope, type ScopeKind, type StoragePattern, annotationToShape, bindModule, buildRouterIndex, classifyDecorator, containedValues, discoverUnits, emitEntryFact, emitModuleImportFacts, emitValueFacts, extractPythonProject, findPythonFiles, nodeId, objectReturnedBy, parsePython, resolveAbsoluteModule, resolveCalls, resolveModule, resolveName, resolveRelativeModule, shapeFromName, unitKey };