@suss/adapter-python 0.22.4 → 0.23.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/README.md +31 -1
- package/dist/index.cjs +769 -193
- package/dist/index.d.cts +162 -2
- package/dist/index.d.ts +162 -2
- package/dist/index.js +710 -135
- package/package.json +8 -8
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { TypeShape, BehavioralSummary } from '@suss/behavioral-ir';
|
|
1
|
+
import { TypeShape, WrapperReference, 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
4
|
import { Database } from '@suss/datalog';
|
|
5
|
-
import {
|
|
5
|
+
import { RawCodeStructure, ExtractorOptions, TimingReport, ExtractionReport, CacheDiagnostic } from '@suss/extractor';
|
|
6
6
|
import { WhyExplained } from '@suss/resolution';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -323,8 +323,74 @@ interface RouteConventions {
|
|
|
323
323
|
* and one it does cover comes out as the response the library sends.
|
|
324
324
|
*/
|
|
325
325
|
responseStatusCalls?: PyStatusCall[];
|
|
326
|
+
/**
|
|
327
|
+
* The library's own classes whose instance, when a body returns it, is
|
|
328
|
+
* the response the library sends, and where each takes the status:
|
|
329
|
+
* Starlette's `JSONResponse(status_code=...)`. A return of one says its
|
|
330
|
+
* own status; a return of anything else keeps the declared one.
|
|
331
|
+
*/
|
|
332
|
+
responseConstructors?: PyStatusCall[];
|
|
326
333
|
/** Unset means the library has no router mounting, and a route's decorator path stands as written. */
|
|
327
334
|
routerComposition?: RouterComposition;
|
|
335
|
+
/** The ways the library runs a project's own function around a route. The README lists what each one covers. */
|
|
336
|
+
wrappers?: PyWrapperForm[];
|
|
337
|
+
}
|
|
338
|
+
type PyWrapperForm = PyDependencyForm | PyDecoratedWrapperForm;
|
|
339
|
+
/** Where a wrapper is registered, and which routes the registration reaches. */
|
|
340
|
+
interface PyWrapperRegistrar {
|
|
341
|
+
/** The constructor of the object the registration is written on, as the library exports it: `FastAPI`, `APIRouter`. */
|
|
342
|
+
constructorName: string;
|
|
343
|
+
/** Where the constructor is imported from, when that is not the pattern's own `importModule`: `flask` for the app a flask-restx API is served by. */
|
|
344
|
+
importModule?: string[];
|
|
345
|
+
/**
|
|
346
|
+
* `everyRoute` for the app, whose registration reaches every route of
|
|
347
|
+
* the pack in the run. `ownRoutes` for a router or a blueprint, whose
|
|
348
|
+
* registration reaches the routes decorated on that same object.
|
|
349
|
+
*/
|
|
350
|
+
covers: "everyRoute" | "ownRoutes";
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* The library calls a project function before the handler, given as an
|
|
354
|
+
* argument to one of its own callables: FastAPI's `Depends(get_user)`.
|
|
355
|
+
* One may be written as a parameter default or inside `Annotated[...]`
|
|
356
|
+
* on the route, or in a list under `keyword` on the route decorator or
|
|
357
|
+
* on one of the registrars. The function runs before the handler and
|
|
358
|
+
* ends the request by raising, so it is a wrapper whose every return
|
|
359
|
+
* hands on.
|
|
360
|
+
*/
|
|
361
|
+
interface PyDependencyForm {
|
|
362
|
+
type: "dependency";
|
|
363
|
+
/** The callables that take the function: `Depends`, `Security`. */
|
|
364
|
+
callees: string[];
|
|
365
|
+
/** The keyword a list of them is written under: `dependencies`. */
|
|
366
|
+
keyword: string;
|
|
367
|
+
registrars: PyWrapperRegistrar[];
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* A project function decorated with a method on the app or a router:
|
|
371
|
+
* `@app.middleware("http")`, `@app.exception_handler(ValueError)`,
|
|
372
|
+
* `@app.before_request`. What the decorated function's returns mean
|
|
373
|
+
* depends on which of the three fields below is set; with none set,
|
|
374
|
+
* every return hands the request on.
|
|
375
|
+
*/
|
|
376
|
+
interface PyDecoratedWrapperForm {
|
|
377
|
+
type: "decoratedWrapper";
|
|
378
|
+
/** The decorator's attribute name on the registrar: `middleware`. */
|
|
379
|
+
attribute: string;
|
|
380
|
+
registrars: PyWrapperRegistrar[];
|
|
381
|
+
/**
|
|
382
|
+
* The position of the parameter the wrapper calls to run what it
|
|
383
|
+
* wraps, `call_next` at 1 for Starlette middleware. A return before
|
|
384
|
+
* that call ends the request with what is returned.
|
|
385
|
+
*/
|
|
386
|
+
continuationParam?: number;
|
|
387
|
+
/**
|
|
388
|
+
* Set when a value returned ends the request and only a bare return
|
|
389
|
+
* hands on, which is what Flask does with `before_request`.
|
|
390
|
+
*/
|
|
391
|
+
returnedValueResponds?: boolean;
|
|
392
|
+
/** The position the library hands the raised exception at. Set on an error handler, which runs only when the handler raised. */
|
|
393
|
+
throwParam?: number;
|
|
328
394
|
}
|
|
329
395
|
/**
|
|
330
396
|
* One callable that ends the request, and where it takes the status.
|
|
@@ -577,6 +643,98 @@ interface RouterIndexOptions extends ModuleResolverOptions {
|
|
|
577
643
|
}
|
|
578
644
|
declare function buildRouterIndex(files: BoundPythonFile[], packs: PythonPack[], resolverOptions: RouterIndexOptions): RouterIndex;
|
|
579
645
|
|
|
646
|
+
/**
|
|
647
|
+
* What a call's callee is, read through the binder and the import
|
|
648
|
+
* facts: a function in this run the walk can step into, or a reason it
|
|
649
|
+
* cannot. A name is followed only through a binding that says where it
|
|
650
|
+
* came from, so a name that could be two definitions is a stop rather
|
|
651
|
+
* than a guess. The package README lists the spellings that are
|
|
652
|
+
* followed and the ones that stop.
|
|
653
|
+
*/
|
|
654
|
+
|
|
655
|
+
/** A function in this run, and the export path its summary gets. */
|
|
656
|
+
interface ReachedFunction {
|
|
657
|
+
readonly file: BoundPythonFile;
|
|
658
|
+
/** The `function_definition` node. */
|
|
659
|
+
readonly node: Node;
|
|
660
|
+
readonly name: string;
|
|
661
|
+
/** `[name]` for a module function, `[Class, name]` for a method. */
|
|
662
|
+
readonly exportPath: string[];
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* The project functions a library runs around a route, read from where
|
|
667
|
+
* they are registered: a dependency on a route's parameter or decorator,
|
|
668
|
+
* a dependency list on the app or a router, a function decorated with
|
|
669
|
+
* `@app.middleware(...)` or `@app.exception_handler(...)`. Each one
|
|
670
|
+
* becomes a unit of its own, and every route it reaches lists it, so the
|
|
671
|
+
* extractor can fold its outcomes into the route's. The README says which
|
|
672
|
+
* registrations are read and which are not.
|
|
673
|
+
*
|
|
674
|
+
* A registration on the app reaches every route of the pack in the run,
|
|
675
|
+
* since an app is one per run for every pack that has one. A registration
|
|
676
|
+
* on a router reaches the routes decorated on that same router object,
|
|
677
|
+
* and nothing mounted onto it.
|
|
678
|
+
*/
|
|
679
|
+
|
|
680
|
+
interface WrapperIndexOptions {
|
|
681
|
+
packs: readonly PythonPack[];
|
|
682
|
+
roots: string[];
|
|
683
|
+
facts: Database | undefined;
|
|
684
|
+
storageFor: (file: BoundPythonFile) => StorageLookup | undefined;
|
|
685
|
+
}
|
|
686
|
+
/** The route asking which wrappers reach it: the decorator it was found by, and where that decorator was read. */
|
|
687
|
+
interface RouteWrapperQuery {
|
|
688
|
+
pack: PythonPack;
|
|
689
|
+
pattern: PythonDiscoveryPattern;
|
|
690
|
+
/** The absolute path of the file the route is written in. */
|
|
691
|
+
file: string;
|
|
692
|
+
module: ModuleBinding;
|
|
693
|
+
classification: DecoratorClassification;
|
|
694
|
+
definitionNode: Node;
|
|
695
|
+
}
|
|
696
|
+
/** A wrapper the index has a unit for, and the form it was registered through. */
|
|
697
|
+
interface Registered {
|
|
698
|
+
reference: WrapperReference;
|
|
699
|
+
form: PyWrapperForm;
|
|
700
|
+
}
|
|
701
|
+
interface FormOf {
|
|
702
|
+
pack: PythonPack;
|
|
703
|
+
pattern: PythonDiscoveryPattern;
|
|
704
|
+
form: PyWrapperForm;
|
|
705
|
+
}
|
|
706
|
+
/** What every registration in the run reaches, asked once per route and then once per file for the wrapper units. */
|
|
707
|
+
declare class PythonWrapperIndex {
|
|
708
|
+
readonly options: WrapperIndexOptions;
|
|
709
|
+
readonly filesByPath: ReadonlyMap<string, BoundPythonFile>;
|
|
710
|
+
/** By pack name: what the app registered, in the order the forms are declared and then the order the files were read. */
|
|
711
|
+
private readonly everyRoute;
|
|
712
|
+
/** By construction key: what a router or a blueprint registered. */
|
|
713
|
+
private readonly ownRoutes;
|
|
714
|
+
private readonly unitsByKey;
|
|
715
|
+
private readonly unitsByFile;
|
|
716
|
+
constructor(options: WrapperIndexOptions, filesByPath: ReadonlyMap<string, BoundPythonFile>);
|
|
717
|
+
register(covers: {
|
|
718
|
+
kind: "everyRoute";
|
|
719
|
+
pack: string;
|
|
720
|
+
} | {
|
|
721
|
+
kind: "ownRoutes";
|
|
722
|
+
key: string;
|
|
723
|
+
}, target: ReachedFunction, declared: FormOf): void;
|
|
724
|
+
/** The unit for a function, built once however many registrations point at it. */
|
|
725
|
+
registered(target: ReachedFunction, declared: FormOf): Registered;
|
|
726
|
+
wrappersFor(query: RouteWrapperQuery): WrapperReference[];
|
|
727
|
+
private ownRoutesOf;
|
|
728
|
+
/**
|
|
729
|
+
* Where the decorator's object was built: the route's own file, the file
|
|
730
|
+
* a project wrapper function is written in, or the file an imported
|
|
731
|
+
* router comes from. The name is the one that file binds it under.
|
|
732
|
+
*/
|
|
733
|
+
private objectSiteOf;
|
|
734
|
+
private fileOfModule;
|
|
735
|
+
unitsIn(file: string): RawCodeStructure[];
|
|
736
|
+
}
|
|
737
|
+
|
|
580
738
|
/**
|
|
581
739
|
* Finds decorated routes and turns each one into a `RawCodeStructure`.
|
|
582
740
|
*
|
|
@@ -605,6 +763,8 @@ interface DiscoveryOptions {
|
|
|
605
763
|
absoluteFile?: string | undefined;
|
|
606
764
|
/** The project's facts, so the rules can say what an object no scope has a binding for was built by. */
|
|
607
765
|
facts?: Database | undefined;
|
|
766
|
+
/** What the project registered around its routes. Absent when no pack declares a wrapper form, or when a caller reads one file on its own. */
|
|
767
|
+
wrappers?: PythonWrapperIndex | undefined;
|
|
608
768
|
}
|
|
609
769
|
declare function discoverUnits(root: Node, module: ModuleBinding, options: DiscoveryOptions): RawCodeStructure[];
|
|
610
770
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { TypeShape, BehavioralSummary } from '@suss/behavioral-ir';
|
|
1
|
+
import { TypeShape, WrapperReference, 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
4
|
import { Database } from '@suss/datalog';
|
|
5
|
-
import {
|
|
5
|
+
import { RawCodeStructure, ExtractorOptions, TimingReport, ExtractionReport, CacheDiagnostic } from '@suss/extractor';
|
|
6
6
|
import { WhyExplained } from '@suss/resolution';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -323,8 +323,74 @@ interface RouteConventions {
|
|
|
323
323
|
* and one it does cover comes out as the response the library sends.
|
|
324
324
|
*/
|
|
325
325
|
responseStatusCalls?: PyStatusCall[];
|
|
326
|
+
/**
|
|
327
|
+
* The library's own classes whose instance, when a body returns it, is
|
|
328
|
+
* the response the library sends, and where each takes the status:
|
|
329
|
+
* Starlette's `JSONResponse(status_code=...)`. A return of one says its
|
|
330
|
+
* own status; a return of anything else keeps the declared one.
|
|
331
|
+
*/
|
|
332
|
+
responseConstructors?: PyStatusCall[];
|
|
326
333
|
/** Unset means the library has no router mounting, and a route's decorator path stands as written. */
|
|
327
334
|
routerComposition?: RouterComposition;
|
|
335
|
+
/** The ways the library runs a project's own function around a route. The README lists what each one covers. */
|
|
336
|
+
wrappers?: PyWrapperForm[];
|
|
337
|
+
}
|
|
338
|
+
type PyWrapperForm = PyDependencyForm | PyDecoratedWrapperForm;
|
|
339
|
+
/** Where a wrapper is registered, and which routes the registration reaches. */
|
|
340
|
+
interface PyWrapperRegistrar {
|
|
341
|
+
/** The constructor of the object the registration is written on, as the library exports it: `FastAPI`, `APIRouter`. */
|
|
342
|
+
constructorName: string;
|
|
343
|
+
/** Where the constructor is imported from, when that is not the pattern's own `importModule`: `flask` for the app a flask-restx API is served by. */
|
|
344
|
+
importModule?: string[];
|
|
345
|
+
/**
|
|
346
|
+
* `everyRoute` for the app, whose registration reaches every route of
|
|
347
|
+
* the pack in the run. `ownRoutes` for a router or a blueprint, whose
|
|
348
|
+
* registration reaches the routes decorated on that same object.
|
|
349
|
+
*/
|
|
350
|
+
covers: "everyRoute" | "ownRoutes";
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* The library calls a project function before the handler, given as an
|
|
354
|
+
* argument to one of its own callables: FastAPI's `Depends(get_user)`.
|
|
355
|
+
* One may be written as a parameter default or inside `Annotated[...]`
|
|
356
|
+
* on the route, or in a list under `keyword` on the route decorator or
|
|
357
|
+
* on one of the registrars. The function runs before the handler and
|
|
358
|
+
* ends the request by raising, so it is a wrapper whose every return
|
|
359
|
+
* hands on.
|
|
360
|
+
*/
|
|
361
|
+
interface PyDependencyForm {
|
|
362
|
+
type: "dependency";
|
|
363
|
+
/** The callables that take the function: `Depends`, `Security`. */
|
|
364
|
+
callees: string[];
|
|
365
|
+
/** The keyword a list of them is written under: `dependencies`. */
|
|
366
|
+
keyword: string;
|
|
367
|
+
registrars: PyWrapperRegistrar[];
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* A project function decorated with a method on the app or a router:
|
|
371
|
+
* `@app.middleware("http")`, `@app.exception_handler(ValueError)`,
|
|
372
|
+
* `@app.before_request`. What the decorated function's returns mean
|
|
373
|
+
* depends on which of the three fields below is set; with none set,
|
|
374
|
+
* every return hands the request on.
|
|
375
|
+
*/
|
|
376
|
+
interface PyDecoratedWrapperForm {
|
|
377
|
+
type: "decoratedWrapper";
|
|
378
|
+
/** The decorator's attribute name on the registrar: `middleware`. */
|
|
379
|
+
attribute: string;
|
|
380
|
+
registrars: PyWrapperRegistrar[];
|
|
381
|
+
/**
|
|
382
|
+
* The position of the parameter the wrapper calls to run what it
|
|
383
|
+
* wraps, `call_next` at 1 for Starlette middleware. A return before
|
|
384
|
+
* that call ends the request with what is returned.
|
|
385
|
+
*/
|
|
386
|
+
continuationParam?: number;
|
|
387
|
+
/**
|
|
388
|
+
* Set when a value returned ends the request and only a bare return
|
|
389
|
+
* hands on, which is what Flask does with `before_request`.
|
|
390
|
+
*/
|
|
391
|
+
returnedValueResponds?: boolean;
|
|
392
|
+
/** The position the library hands the raised exception at. Set on an error handler, which runs only when the handler raised. */
|
|
393
|
+
throwParam?: number;
|
|
328
394
|
}
|
|
329
395
|
/**
|
|
330
396
|
* One callable that ends the request, and where it takes the status.
|
|
@@ -577,6 +643,98 @@ interface RouterIndexOptions extends ModuleResolverOptions {
|
|
|
577
643
|
}
|
|
578
644
|
declare function buildRouterIndex(files: BoundPythonFile[], packs: PythonPack[], resolverOptions: RouterIndexOptions): RouterIndex;
|
|
579
645
|
|
|
646
|
+
/**
|
|
647
|
+
* What a call's callee is, read through the binder and the import
|
|
648
|
+
* facts: a function in this run the walk can step into, or a reason it
|
|
649
|
+
* cannot. A name is followed only through a binding that says where it
|
|
650
|
+
* came from, so a name that could be two definitions is a stop rather
|
|
651
|
+
* than a guess. The package README lists the spellings that are
|
|
652
|
+
* followed and the ones that stop.
|
|
653
|
+
*/
|
|
654
|
+
|
|
655
|
+
/** A function in this run, and the export path its summary gets. */
|
|
656
|
+
interface ReachedFunction {
|
|
657
|
+
readonly file: BoundPythonFile;
|
|
658
|
+
/** The `function_definition` node. */
|
|
659
|
+
readonly node: Node;
|
|
660
|
+
readonly name: string;
|
|
661
|
+
/** `[name]` for a module function, `[Class, name]` for a method. */
|
|
662
|
+
readonly exportPath: string[];
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* The project functions a library runs around a route, read from where
|
|
667
|
+
* they are registered: a dependency on a route's parameter or decorator,
|
|
668
|
+
* a dependency list on the app or a router, a function decorated with
|
|
669
|
+
* `@app.middleware(...)` or `@app.exception_handler(...)`. Each one
|
|
670
|
+
* becomes a unit of its own, and every route it reaches lists it, so the
|
|
671
|
+
* extractor can fold its outcomes into the route's. The README says which
|
|
672
|
+
* registrations are read and which are not.
|
|
673
|
+
*
|
|
674
|
+
* A registration on the app reaches every route of the pack in the run,
|
|
675
|
+
* since an app is one per run for every pack that has one. A registration
|
|
676
|
+
* on a router reaches the routes decorated on that same router object,
|
|
677
|
+
* and nothing mounted onto it.
|
|
678
|
+
*/
|
|
679
|
+
|
|
680
|
+
interface WrapperIndexOptions {
|
|
681
|
+
packs: readonly PythonPack[];
|
|
682
|
+
roots: string[];
|
|
683
|
+
facts: Database | undefined;
|
|
684
|
+
storageFor: (file: BoundPythonFile) => StorageLookup | undefined;
|
|
685
|
+
}
|
|
686
|
+
/** The route asking which wrappers reach it: the decorator it was found by, and where that decorator was read. */
|
|
687
|
+
interface RouteWrapperQuery {
|
|
688
|
+
pack: PythonPack;
|
|
689
|
+
pattern: PythonDiscoveryPattern;
|
|
690
|
+
/** The absolute path of the file the route is written in. */
|
|
691
|
+
file: string;
|
|
692
|
+
module: ModuleBinding;
|
|
693
|
+
classification: DecoratorClassification;
|
|
694
|
+
definitionNode: Node;
|
|
695
|
+
}
|
|
696
|
+
/** A wrapper the index has a unit for, and the form it was registered through. */
|
|
697
|
+
interface Registered {
|
|
698
|
+
reference: WrapperReference;
|
|
699
|
+
form: PyWrapperForm;
|
|
700
|
+
}
|
|
701
|
+
interface FormOf {
|
|
702
|
+
pack: PythonPack;
|
|
703
|
+
pattern: PythonDiscoveryPattern;
|
|
704
|
+
form: PyWrapperForm;
|
|
705
|
+
}
|
|
706
|
+
/** What every registration in the run reaches, asked once per route and then once per file for the wrapper units. */
|
|
707
|
+
declare class PythonWrapperIndex {
|
|
708
|
+
readonly options: WrapperIndexOptions;
|
|
709
|
+
readonly filesByPath: ReadonlyMap<string, BoundPythonFile>;
|
|
710
|
+
/** By pack name: what the app registered, in the order the forms are declared and then the order the files were read. */
|
|
711
|
+
private readonly everyRoute;
|
|
712
|
+
/** By construction key: what a router or a blueprint registered. */
|
|
713
|
+
private readonly ownRoutes;
|
|
714
|
+
private readonly unitsByKey;
|
|
715
|
+
private readonly unitsByFile;
|
|
716
|
+
constructor(options: WrapperIndexOptions, filesByPath: ReadonlyMap<string, BoundPythonFile>);
|
|
717
|
+
register(covers: {
|
|
718
|
+
kind: "everyRoute";
|
|
719
|
+
pack: string;
|
|
720
|
+
} | {
|
|
721
|
+
kind: "ownRoutes";
|
|
722
|
+
key: string;
|
|
723
|
+
}, target: ReachedFunction, declared: FormOf): void;
|
|
724
|
+
/** The unit for a function, built once however many registrations point at it. */
|
|
725
|
+
registered(target: ReachedFunction, declared: FormOf): Registered;
|
|
726
|
+
wrappersFor(query: RouteWrapperQuery): WrapperReference[];
|
|
727
|
+
private ownRoutesOf;
|
|
728
|
+
/**
|
|
729
|
+
* Where the decorator's object was built: the route's own file, the file
|
|
730
|
+
* a project wrapper function is written in, or the file an imported
|
|
731
|
+
* router comes from. The name is the one that file binds it under.
|
|
732
|
+
*/
|
|
733
|
+
private objectSiteOf;
|
|
734
|
+
private fileOfModule;
|
|
735
|
+
unitsIn(file: string): RawCodeStructure[];
|
|
736
|
+
}
|
|
737
|
+
|
|
580
738
|
/**
|
|
581
739
|
* Finds decorated routes and turns each one into a `RawCodeStructure`.
|
|
582
740
|
*
|
|
@@ -605,6 +763,8 @@ interface DiscoveryOptions {
|
|
|
605
763
|
absoluteFile?: string | undefined;
|
|
606
764
|
/** The project's facts, so the rules can say what an object no scope has a binding for was built by. */
|
|
607
765
|
facts?: Database | undefined;
|
|
766
|
+
/** What the project registered around its routes. Absent when no pack declares a wrapper form, or when a caller reads one file on its own. */
|
|
767
|
+
wrappers?: PythonWrapperIndex | undefined;
|
|
608
768
|
}
|
|
609
769
|
declare function discoverUnits(root: Node, module: ModuleBinding, options: DiscoveryOptions): RawCodeStructure[];
|
|
610
770
|
|