@suss/adapter-python 0.33.1 → 0.34.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 +11 -11
- package/dist/index.cjs +6264 -2830
- package/dist/index.d.cts +509 -284
- package/dist/index.d.ts +509 -284
- package/dist/index.js +5829 -2358
- package/grammar/README.md +26 -23
- package/package.json +10 -9
package/dist/index.d.cts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
import { TypeShape, WrapperReference, BehavioralSummary } from '@suss/behavioral-ir';
|
|
1
|
+
import { RequestSpellingMetadata, 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 {
|
|
6
|
-
import {
|
|
5
|
+
import { UnwrapsByName, WhyExplained } from '@suss/resolution';
|
|
6
|
+
import { RawCodeStructure, ExtractorOptions, TimingReport, ExtractionReport, CacheDiagnostic, DeclaredModule, KeptParses } from '@suss/extractor';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Loads the Python grammar into web-tree-sitter and parses a file.
|
|
10
|
+
*
|
|
11
|
+
* tree-sitter runs as WASM, so the adapter needs no native build step.
|
|
12
|
+
* The rest of the adapter reaches the parser only through this module
|
|
13
|
+
* and the node helpers in the `ast` module, so a different Python grammar would
|
|
14
|
+
* be a change here alone.
|
|
15
|
+
*/
|
|
7
16
|
|
|
8
17
|
/**
|
|
9
18
|
* A new `Parser` for every call, so that parses running at the same time do
|
|
@@ -50,7 +59,7 @@ type ModuleResolution = {
|
|
|
50
59
|
reason: "external" | "ambiguous" | "outsideRoots";
|
|
51
60
|
};
|
|
52
61
|
interface ModuleResolverOptions {
|
|
53
|
-
/** The directories an absolute dotted import is resolved against.
|
|
62
|
+
/** The directories an absolute dotted import is resolved against. Their order does not matter, because the order of `sys.path` exists only at run time. */
|
|
54
63
|
roots: string[];
|
|
55
64
|
}
|
|
56
65
|
declare function resolveAbsoluteModule(dotted: string, options: ModuleResolverOptions): ModuleResolution;
|
|
@@ -64,52 +73,50 @@ declare function resolveRelativeModule(importingFile: string, spec: RelativeModu
|
|
|
64
73
|
declare function resolveModule(importingFile: string, spec: RelativeModuleSpec, options: ModuleResolverOptions): ModuleResolution;
|
|
65
74
|
|
|
66
75
|
/**
|
|
67
|
-
* The
|
|
76
|
+
* The contract a Python pack is written against.
|
|
68
77
|
*
|
|
69
|
-
*
|
|
70
|
-
* dispatches its discovery variants through ts-morph
|
|
71
|
-
* Python's two route
|
|
72
|
-
*
|
|
73
|
-
*
|
|
78
|
+
* It is separate from the TypeScript adapter's `PatternPack` on purpose. That
|
|
79
|
+
* type dispatches its discovery variants through handlers built on ts-morph,
|
|
80
|
+
* and Python's two kinds of route have no exact match in its union. Each
|
|
81
|
+
* language keeps its own match patterns until a second implementation shows
|
|
82
|
+
* which parts the two have in common.
|
|
74
83
|
*
|
|
75
|
-
* A pack is
|
|
76
|
-
*
|
|
84
|
+
* A pack is plain data under the same rule the TypeScript packs follow: it
|
|
85
|
+
* describes what a library defines, and nothing a project chose.
|
|
77
86
|
*/
|
|
87
|
+
|
|
78
88
|
interface PythonPack {
|
|
79
89
|
name: string;
|
|
80
90
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
91
|
+
* Part of the cache key. Bump it on any change that affects the units a
|
|
92
|
+
* pack discovers or the summaries it produces. The CLI also adds a hash
|
|
93
|
+
* of the loaded pack file and its config to the key, so a pack run
|
|
94
|
+
* through the CLI invalidates the cache on an edit whether or not it
|
|
95
|
+
* declares a version.
|
|
86
96
|
*/
|
|
87
97
|
version?: string;
|
|
88
98
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* previous answer.
|
|
99
|
+
* Given the `.py` files a run is about to walk, returns the other files
|
|
100
|
+
* in the project this pack reads. Their content goes into the cache key
|
|
101
|
+
* along with the pack's config, so editing one of them re-extracts
|
|
102
|
+
* instead of returning the cached summaries.
|
|
94
103
|
*/
|
|
95
104
|
discoveryInputs?: (files: readonly string[]) => string[];
|
|
96
105
|
/** Wire protocol for the produced boundary bindings, e.g. "http". */
|
|
97
106
|
protocol: string;
|
|
98
107
|
discovery: PythonDiscoveryPattern[];
|
|
99
108
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
* a wrapper that resolves to nothing does not match any decorator
|
|
105
|
-
* and never says why (#188).
|
|
109
|
+
* Wrapper modules around the library that a person lists when
|
|
110
|
+
* configuring the pack. The library's own module does not go here. The adapter
|
|
111
|
+
* reports each of these that no file in the run imports, since
|
|
112
|
+
* otherwise it would match no decorator and nothing would say why (#188).
|
|
106
113
|
*/
|
|
107
114
|
projectModules?: string[];
|
|
108
115
|
/** The callables the library gives a project for making a request. */
|
|
109
116
|
clients?: PyClientCall[];
|
|
110
|
-
/** What the library's own database queries look like.
|
|
117
|
+
/** What the library's own database queries look like. DESIGN.md says how one is matched. */
|
|
111
118
|
storage?: StoragePattern[];
|
|
112
|
-
/** Which of the library's calls give back one of a model class.
|
|
119
|
+
/** Which of the library's calls give back one of a model class. DESIGN.md says what a chain of them composes into. */
|
|
113
120
|
models?: PyModelQueries[];
|
|
114
121
|
/** Which of the library's classes return themselves from `__enter__`, so a `with` block gets the object itself. */
|
|
115
122
|
contextManagers?: PyContextManager[];
|
|
@@ -117,12 +124,93 @@ interface PythonPack {
|
|
|
117
124
|
rawSql?: RawSqlPattern[];
|
|
118
125
|
/** The client objects the library hands a project, and the calls on one that reach the database. */
|
|
119
126
|
sqlClients?: SqlClientPattern[];
|
|
127
|
+
/** Functions the library exports that hand back the argument at `argument`, such as a decorator that returns the function it was given. */
|
|
128
|
+
transparentWrappers?: UnwrapsByName[];
|
|
129
|
+
/**
|
|
130
|
+
* The objects the library puts the request on for a route to read,
|
|
131
|
+
* such as Flask's module-level `request`. A route is handed no
|
|
132
|
+
* parameter for them, so without this a value read off one is not
|
|
133
|
+
* known to come from the request.
|
|
134
|
+
*/
|
|
135
|
+
requestObjects?: RequestObject[];
|
|
136
|
+
/** Where a route reads each part of the request, recorded on every route the pack finds. */
|
|
137
|
+
requestSpelling?: RequestSpellingMetadata;
|
|
138
|
+
/** How the library's test runner collects tests, so a PRD scenario can say which test covers it. */
|
|
139
|
+
tests?: PyTestCases[];
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* How a test runner finds tests and what it runs around each one. Every
|
|
143
|
+
* name here is one the runner itself defines or documents; a project's
|
|
144
|
+
* own conventions come through `files`, which lists the files to read.
|
|
145
|
+
* DESIGN.md says how the adapter reads each field.
|
|
146
|
+
*/
|
|
147
|
+
interface PyTestCases {
|
|
148
|
+
/** Base-name patterns of the files the runner collects, `*` matching any run of characters: `test_*.py`. */
|
|
149
|
+
filePatterns: string[];
|
|
150
|
+
/** What a test function or method name starts with: `test`. */
|
|
151
|
+
functionPrefix: string;
|
|
152
|
+
/** What a class of tests is named starting with: `Test`. */
|
|
153
|
+
classPrefix: string;
|
|
154
|
+
/** Base classes whose subclasses contain tests whatever the class is called, as dotted names: `unittest.TestCase`. */
|
|
155
|
+
caseBaseClasses: string[];
|
|
156
|
+
/** The functions and methods the runner calls before a test, by where they are written. */
|
|
157
|
+
setUp: PyTestSetUp;
|
|
158
|
+
/** Markers whose string arguments are the fixtures every test under them runs through, as dotted names: `pytest.mark.usefixtures`. */
|
|
159
|
+
usesFixtureMarkers: string[];
|
|
160
|
+
/** Decorators that declare a fixture, as dotted names: `pytest.fixture`. */
|
|
161
|
+
fixtureDecorators: string[];
|
|
162
|
+
/** The keyword on a fixture decorator that renames the fixture: `name`. */
|
|
163
|
+
fixtureNameKeyword: string;
|
|
164
|
+
/** The keyword on a fixture decorator that runs it before every test in its scope: `autouse`. */
|
|
165
|
+
autouseKeyword: string;
|
|
166
|
+
/** Files whose fixtures apply to every test under their directory: `conftest.py`. */
|
|
167
|
+
sharedFixtureFiles: string[];
|
|
168
|
+
/** Decorators that mark a test so it does not run, or runs expecting to fail, as dotted names: `pytest.mark.skip`. */
|
|
169
|
+
skipDecorators: string[];
|
|
170
|
+
/** The module or class variable the runner reads markers from for every test under it: `pytestmark`. */
|
|
171
|
+
markerVariable?: string;
|
|
172
|
+
/** Parameters the runner fills with something other than a fixture, which are never looked up: `self`, `request`. */
|
|
173
|
+
reservedParameters: string[];
|
|
174
|
+
/** How a test replaces something before it runs. */
|
|
175
|
+
mocks: PyTestMocks;
|
|
176
|
+
/** Read only these files, matched on whole path segments from the end. Left out, every collected file is read. */
|
|
177
|
+
files?: string[];
|
|
120
178
|
}
|
|
121
179
|
/**
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
|
|
180
|
+
* The setup a runner calls before a test, listed in the order it calls
|
|
181
|
+
* them. A method is looked for on the class and then on the project
|
|
182
|
+
* classes it extends, since the setup is often written on a base class.
|
|
183
|
+
*/
|
|
184
|
+
interface PyTestSetUp {
|
|
185
|
+
/** Functions in the test's module that run before any test in it: `setup_module`. */
|
|
186
|
+
module: string[];
|
|
187
|
+
/** Functions in the test's module that run before each test written as a function: `setup_function`. */
|
|
188
|
+
function: string[];
|
|
189
|
+
/** Methods of a class of tests that run before each test in it: `setup_class`, `setup_method`. */
|
|
190
|
+
testClass: string[];
|
|
191
|
+
/** Methods of a `caseBaseClasses` subclass that run before each test in it: `setUpClass`, `setUp`. */
|
|
192
|
+
caseClass: string[];
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* The calls that replace a module or a member for the length of a test.
|
|
196
|
+
* A patcher called with a dotted string replaces the object at that path,
|
|
197
|
+
* `patch("orders.service.cancel")`. Called with an object and a member
|
|
198
|
+
* name, it replaces that member, `patch.object(OrderService, "cancel")`.
|
|
199
|
+
*/
|
|
200
|
+
interface PyTestMocks {
|
|
201
|
+
/** Patchers imported from a library, as dotted names: `unittest.mock.patch`, `unittest.mock.patch.object`. */
|
|
202
|
+
patchers: string[];
|
|
203
|
+
/** Patchers a fixture hands the test, by the fixture's parameter name and the methods called on it. */
|
|
204
|
+
fixturePatchers: {
|
|
205
|
+
fixture: string;
|
|
206
|
+
methods: string[];
|
|
207
|
+
}[];
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* A call talks to the database when the method behind it declares that it
|
|
211
|
+
* returns one of the library's query types. Matching on the return type finds
|
|
212
|
+
* queries made through a project base class that wraps the library, where a
|
|
213
|
+
* match on the import would find none.
|
|
126
214
|
*/
|
|
127
215
|
interface StoragePattern {
|
|
128
216
|
/** The module a query type is imported from, `sqlalchemy.orm` for a Session. */
|
|
@@ -132,39 +220,37 @@ interface StoragePattern {
|
|
|
132
220
|
/** Chain-ending methods that change what is stored. Anything else reads. */
|
|
133
221
|
writes: string[];
|
|
134
222
|
/**
|
|
135
|
-
* Methods on a query type that touch no rows of their own
|
|
136
|
-
*
|
|
137
|
-
* already records the work, and
|
|
138
|
-
* A call to one of these records nothing.
|
|
223
|
+
* Methods on a query type that touch no rows of their own, and a call to
|
|
224
|
+
* one records nothing. `session.execute(stmt)` runs a statement whose own
|
|
225
|
+
* chain already records the work, and `close` manages the session.
|
|
139
226
|
*/
|
|
140
227
|
recordsNothing?: string[];
|
|
141
228
|
/**
|
|
142
|
-
* Methods whose keywords supply column values
|
|
143
|
-
* `values` in `update(User).where(id=1).values(name="x")`. Their
|
|
144
|
-
*
|
|
145
|
-
* selector.
|
|
229
|
+
* Methods whose keywords supply column values instead of picking rows,
|
|
230
|
+
* `values` in `update(User).where(id=1).values(name="x")`. Their keywords
|
|
231
|
+
* are reported as fields, and every other call's keywords as the selector.
|
|
146
232
|
*/
|
|
147
233
|
valueMethods?: string[];
|
|
148
234
|
/**
|
|
149
|
-
* Functions the library exports that start a query on their own,
|
|
150
|
-
*
|
|
151
|
-
*
|
|
235
|
+
* Functions the library exports that start a query on their own, such as
|
|
236
|
+
* SQLAlchemy 2.0's `select(...)`. A call site imports one directly instead
|
|
237
|
+
* of reaching it through a project class.
|
|
152
238
|
*/
|
|
153
239
|
queryFunctions?: string[];
|
|
154
240
|
/** Which database the library is talking to, for the boundary binding. */
|
|
155
241
|
storageSystem: "postgresql" | "mysql" | "sqlite";
|
|
156
242
|
}
|
|
157
243
|
/**
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
* class declares. SQLAlchemy and SQLModel take the class as an
|
|
161
|
-
* `session.get(User, id)` and `select(User)`,
|
|
162
|
-
*
|
|
244
|
+
* Which of a library's calls give back an instance of the project model
|
|
245
|
+
* class they were passed, so a method called on the result resolves to the
|
|
246
|
+
* one that class declares. SQLAlchemy and SQLModel take the class as an
|
|
247
|
+
* argument, as in `session.get(User, id)` and `select(User)`, where a Rails
|
|
248
|
+
* finder is called on the class itself.
|
|
163
249
|
*/
|
|
164
250
|
interface PyModelQueries {
|
|
165
251
|
/**
|
|
166
|
-
* The names a model's
|
|
167
|
-
* exports, `DeclarativeBase`, or the function that builds one,
|
|
252
|
+
* The names a model's base classes lead back to: a base class the
|
|
253
|
+
* library exports, `DeclarativeBase`, or the function that builds one,
|
|
168
254
|
* `declarative_base`.
|
|
169
255
|
*/
|
|
170
256
|
baseNames: string[];
|
|
@@ -172,16 +258,16 @@ interface PyModelQueries {
|
|
|
172
258
|
givesBack: string[];
|
|
173
259
|
/** Methods that take the model class and give back one of it, with the position it is written at. */
|
|
174
260
|
entryMethods: PyModelEntryMethod[];
|
|
175
|
-
/** Functions the library exports that do the same, called on their own
|
|
261
|
+
/** Functions the library exports that do the same, called on their own instead of on a session. */
|
|
176
262
|
entryFunctions: PyModelEntryFunction[];
|
|
177
263
|
/** The library's own constructors for a field that reaches another model. */
|
|
178
264
|
relationships?: PyRelationshipConstructor[];
|
|
179
265
|
}
|
|
180
266
|
/**
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
267
|
+
* The callable a model field is assigned from to say it points at another
|
|
268
|
+
* model, as in `items: list[Item] = Relationship(...)`, and where it is
|
|
269
|
+
* imported from. A match needs the import, so a project function with the
|
|
270
|
+
* same name does not count.
|
|
185
271
|
*/
|
|
186
272
|
interface PyRelationshipConstructor {
|
|
187
273
|
/** The module it is imported from, `sqlmodel` or `sqlalchemy.orm`. */
|
|
@@ -202,9 +288,9 @@ interface PyModelEntryFunction {
|
|
|
202
288
|
}
|
|
203
289
|
/**
|
|
204
290
|
* The function a library gives a project for handing the database a
|
|
205
|
-
* statement written as SQL, and where it
|
|
206
|
-
* `text` from `sqlalchemy`. A
|
|
207
|
-
*
|
|
291
|
+
* statement written as SQL, and where it is imported from. SQLAlchemy
|
|
292
|
+
* exports `text` from `sqlalchemy`. A match needs the import, so a local
|
|
293
|
+
* function with the same name does not count.
|
|
208
294
|
*/
|
|
209
295
|
interface RawSqlPattern {
|
|
210
296
|
/** The module the function is imported from. */
|
|
@@ -216,13 +302,13 @@ interface RawSqlPattern {
|
|
|
216
302
|
}
|
|
217
303
|
/**
|
|
218
304
|
* A client object a library hands a project, and the calls on one that
|
|
219
|
-
* reach the database. `RawSqlPattern` matches a function the file
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
305
|
+
* reach the database. `RawSqlPattern` matches a function the file imported.
|
|
306
|
+
* This matches a method called on a value whose class comes from the
|
|
307
|
+
* library, so a client built in one module and called in another still
|
|
308
|
+
* matches.
|
|
223
309
|
*
|
|
224
|
-
* Every field
|
|
225
|
-
*
|
|
310
|
+
* Every field describes the library: the module, the class and method
|
|
311
|
+
* names, and where each method takes its input.
|
|
226
312
|
*/
|
|
227
313
|
interface SqlClientPattern {
|
|
228
314
|
/** The module the client class comes from, `google.cloud.bigquery`. */
|
|
@@ -231,9 +317,9 @@ interface SqlClientPattern {
|
|
|
231
317
|
clientTypes: string[];
|
|
232
318
|
/** The methods that hand the database a statement, and where each takes it. */
|
|
233
319
|
statements?: SqlStatementCall[];
|
|
234
|
-
/** The methods that
|
|
320
|
+
/** The methods that take a table name instead of SQL, and what each does to the table. */
|
|
235
321
|
tables?: SqlTableCall[];
|
|
236
|
-
/** The methods that
|
|
322
|
+
/** The methods that return an instance of another of the library's classes, so a chain can continue through it. */
|
|
237
323
|
handsBack?: SqlClientHandoff[];
|
|
238
324
|
/** Which database the client is talking to, for the boundary binding. */
|
|
239
325
|
storageSystem: string;
|
|
@@ -252,20 +338,20 @@ interface SqlStatementCall extends SqlCallArgument {
|
|
|
252
338
|
/** The method name, `query`. */
|
|
253
339
|
method: string;
|
|
254
340
|
/**
|
|
255
|
-
* The keys
|
|
256
|
-
*
|
|
341
|
+
* The keys to follow when the statement is passed inside a dictionary
|
|
342
|
+
* instead of as the argument itself: `["query", "query"]` for
|
|
257
343
|
* `insert_job(configuration={"query": {"query": sql}})`.
|
|
258
344
|
*/
|
|
259
345
|
path?: string[];
|
|
260
346
|
}
|
|
261
|
-
/** One method that
|
|
347
|
+
/** One method that takes the name of the table it works on, and what it does to that table. */
|
|
262
348
|
interface SqlTableCall extends SqlCallArgument {
|
|
263
349
|
/** The method name, `get_table`. */
|
|
264
350
|
method: string;
|
|
265
351
|
/** Whether the call changes what is stored. */
|
|
266
352
|
kind: "read" | "write";
|
|
267
353
|
}
|
|
268
|
-
/** One method that
|
|
354
|
+
/** One method that returns an instance of another of the library's classes. */
|
|
269
355
|
interface SqlClientHandoff {
|
|
270
356
|
/** The method name, Airflow's `get_client`. */
|
|
271
357
|
method: string;
|
|
@@ -276,10 +362,10 @@ interface SqlClientHandoff {
|
|
|
276
362
|
}
|
|
277
363
|
type PythonDiscoveryPattern = DecoratedClassRoute | DecoratedFunctionRoute;
|
|
278
364
|
/**
|
|
279
|
-
*
|
|
280
|
-
* `with X() as y`
|
|
281
|
-
*
|
|
282
|
-
*
|
|
365
|
+
* Classes whose `__enter__` returns the object it was called on, so
|
|
366
|
+
* `with X() as y` binds `y` to the `X()` the block opened. Python lets
|
|
367
|
+
* `__enter__` return anything, so only the library that wrote the class
|
|
368
|
+
* knows this, and a pack declares it only for its own library's classes.
|
|
283
369
|
*/
|
|
284
370
|
interface PyContextManager {
|
|
285
371
|
/** The module the classes come from, `httpx`. */
|
|
@@ -289,8 +375,8 @@ interface PyContextManager {
|
|
|
289
375
|
}
|
|
290
376
|
/**
|
|
291
377
|
* The callables a library gives a project for making a request. A
|
|
292
|
-
* function that calls one of them
|
|
293
|
-
*
|
|
378
|
+
* function that calls one of them becomes a client unit, bound to the
|
|
379
|
+
* method and path that call is made with.
|
|
294
380
|
*/
|
|
295
381
|
interface PyClientCall {
|
|
296
382
|
type: "clientCall";
|
|
@@ -316,9 +402,9 @@ interface PyClientCall {
|
|
|
316
402
|
response?: PyClientResponse;
|
|
317
403
|
}
|
|
318
404
|
/**
|
|
319
|
-
* The members a library's response object gives a caller. A
|
|
320
|
-
*
|
|
321
|
-
*
|
|
405
|
+
* The members a library's response object gives a caller. A test on one of
|
|
406
|
+
* them shows which statuses the caller handles, and the checker compares
|
|
407
|
+
* that against what the other side sends.
|
|
322
408
|
*/
|
|
323
409
|
interface PyClientResponse {
|
|
324
410
|
/** Members whose value is the status code: `status_code`. */
|
|
@@ -330,62 +416,80 @@ interface PyClientResponse {
|
|
|
330
416
|
/** Whether a refused request comes back as a response or raises where it was made. */
|
|
331
417
|
failureDelivery?: "response" | "exception";
|
|
332
418
|
}
|
|
419
|
+
/** An object a module exports that a route reads the request off. */
|
|
420
|
+
interface RequestObject {
|
|
421
|
+
module: string;
|
|
422
|
+
name: string;
|
|
423
|
+
}
|
|
333
424
|
/** Conventions both kinds of route share. Each one describes what the library does, never a project's choice. */
|
|
334
425
|
interface RouteConventions {
|
|
335
|
-
/** How the library
|
|
426
|
+
/** How the library writes a path parameter. DESIGN.md lists the syntaxes the adapter reads. */
|
|
336
427
|
pathParamSyntax?: string;
|
|
337
428
|
/** Set it only when the library itself binds an annotated local class to the request body. */
|
|
338
429
|
annotatedClassIsRequestBody?: boolean;
|
|
339
430
|
/**
|
|
340
|
-
* Callables the library uses to inject a parameter
|
|
341
|
-
* off the request
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
431
|
+
* Callables the library uses to inject a parameter instead of reading it
|
|
432
|
+
* off the request, such as FastAPI's `Depends` and `Security`. The server
|
|
433
|
+
* supplies the value and the client sends nothing, so a parameter
|
|
434
|
+
* defaulted to one of them is not part of the request, whatever its
|
|
435
|
+
* annotation says.
|
|
345
436
|
*/
|
|
346
437
|
injectedParameterCallees?: string[];
|
|
347
438
|
/**
|
|
348
|
-
* What the library serves when a composed path
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
-
* default and is what Starlette does.
|
|
439
|
+
* What the library serves when a composed path has repeated slashes.
|
|
440
|
+
* Werkzeug serves the merged path and redirects the written one, so Flask
|
|
441
|
+
* needs "merged". The default, "kept", matches Starlette.
|
|
352
442
|
*/
|
|
353
443
|
pathRepeatedSlashes?: PathRepeatedSlashes;
|
|
354
|
-
/** The status the library returns for a declared response when the route does not
|
|
444
|
+
/** The status the library returns for a declared response when the route does not set one. */
|
|
355
445
|
defaultStatusCode?: number;
|
|
356
446
|
/**
|
|
357
447
|
* Set it only when the library reads a status out of the tuple a handler
|
|
358
|
-
* returns, as Flask does with `return body, 201`. Without it
|
|
359
|
-
* the library default
|
|
360
|
-
*
|
|
448
|
+
* returns, as Flask does with `return body, 201`. Without it, every return
|
|
449
|
+
* is reported at the library's default status, including a route that
|
|
450
|
+
* sets its own.
|
|
361
451
|
*/
|
|
362
452
|
statusFromReturnedTuple?: boolean;
|
|
363
453
|
/**
|
|
364
454
|
* The library's own callables that end the request with a status, such
|
|
365
|
-
* as FastAPI's `HTTPException` and Flask's `abort`.
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
* and one it does cover comes out as the response the library sends.
|
|
455
|
+
* as FastAPI's `HTTPException` and Flask's `abort`. A `raise` of one in a
|
|
456
|
+
* route body becomes the response the library sends. A raise of anything
|
|
457
|
+
* the list does not cover becomes a throw with no status.
|
|
369
458
|
*/
|
|
370
459
|
responseStatusCalls?: PyStatusCall[];
|
|
371
460
|
/**
|
|
372
|
-
* The library's own classes
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
*
|
|
461
|
+
* The library's own classes that are sent as the response when a body
|
|
462
|
+
* returns an instance, and where each takes the status, as in Starlette's
|
|
463
|
+
* `JSONResponse(status_code=...)`. Returning one gives that status, and
|
|
464
|
+
* returning anything else keeps the declared one.
|
|
376
465
|
*/
|
|
377
466
|
responseConstructors?: PyStatusCall[];
|
|
378
467
|
/** Unset means the library has no router mounting, and a route's decorator path is used as written. */
|
|
379
468
|
routerComposition?: RouterComposition;
|
|
380
|
-
/**
|
|
469
|
+
/**
|
|
470
|
+
* The callables that say which part of the request a parameter is read
|
|
471
|
+
* from, by the callee's name, written as the default or inside
|
|
472
|
+
* `Annotated[...]`: FastAPI's `x_tenant_id: str = Header()` is a header.
|
|
473
|
+
*/
|
|
474
|
+
parameterSources?: Record<string, ParameterSource>;
|
|
475
|
+
/** The keyword those callables take a field's name under when it differs from the parameter's, FastAPI's `alias`. */
|
|
476
|
+
parameterAliasKeyword?: string;
|
|
477
|
+
/** The ways the library runs a project's own function around a route. DESIGN.md lists what each one covers. */
|
|
381
478
|
wrappers?: PyWrapperForm[];
|
|
382
479
|
}
|
|
480
|
+
/** The part of the request a parameter declared with one callable is read from. */
|
|
481
|
+
interface ParameterSource {
|
|
482
|
+
/** The role the parameter gets, `headers` for FastAPI's `Header`. */
|
|
483
|
+
role: string;
|
|
484
|
+
/** What the library writes in place of `_` in the field's name, `-` for a FastAPI header. */
|
|
485
|
+
underscoresAs?: string;
|
|
486
|
+
}
|
|
383
487
|
type PyWrapperForm = PyDependencyForm | PyDecoratedWrapperForm;
|
|
384
488
|
/** Where a wrapper is registered, and which routes the registration reaches. */
|
|
385
489
|
interface PyWrapperRegistrar {
|
|
386
490
|
/** The constructor of the object the registration is written on, as the library exports it: `FastAPI`, `APIRouter`. */
|
|
387
491
|
constructorName: string;
|
|
388
|
-
/** Where the constructor is imported from, when that
|
|
492
|
+
/** Where the constructor is imported from, when that differs from the pattern's own `importModule`: `flask` for the app that serves a flask-restx API. */
|
|
389
493
|
importModule?: string[];
|
|
390
494
|
/**
|
|
391
495
|
* `everyRoute` for the app, whose registration reaches every route of
|
|
@@ -395,13 +499,12 @@ interface PyWrapperRegistrar {
|
|
|
395
499
|
covers: "everyRoute" | "ownRoutes";
|
|
396
500
|
}
|
|
397
501
|
/**
|
|
398
|
-
*
|
|
399
|
-
*
|
|
400
|
-
*
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
*
|
|
404
|
-
* hands on.
|
|
502
|
+
* A project function the library calls before the handler, passed to one
|
|
503
|
+
* of the library's callables, as in FastAPI's `Depends(get_user)`. It can
|
|
504
|
+
* be written as a parameter default, inside `Annotated[...]` on the route,
|
|
505
|
+
* or in a list under `keyword` on the route decorator or a registrar. It
|
|
506
|
+
* ends a request only by raising, so every return passes control on to
|
|
507
|
+
* the route.
|
|
405
508
|
*/
|
|
406
509
|
interface PyDependencyForm {
|
|
407
510
|
type: "dependency";
|
|
@@ -412,11 +515,10 @@ interface PyDependencyForm {
|
|
|
412
515
|
registrars: PyWrapperRegistrar[];
|
|
413
516
|
}
|
|
414
517
|
/**
|
|
415
|
-
* A project function decorated with a method on the app or a router
|
|
416
|
-
* `@app.middleware("http")
|
|
417
|
-
*
|
|
418
|
-
*
|
|
419
|
-
* every return hands the request on.
|
|
518
|
+
* A project function decorated with a method on the app or a router, such
|
|
519
|
+
* as `@app.middleware("http")` or `@app.before_request`. Which of the three
|
|
520
|
+
* optional fields below is set decides what the function's returns mean.
|
|
521
|
+
* With none set, every return passes the request on.
|
|
420
522
|
*/
|
|
421
523
|
interface PyDecoratedWrapperForm {
|
|
422
524
|
type: "decoratedWrapper";
|
|
@@ -430,17 +532,17 @@ interface PyDecoratedWrapperForm {
|
|
|
430
532
|
*/
|
|
431
533
|
continuationParam?: number;
|
|
432
534
|
/**
|
|
433
|
-
* Set when a value
|
|
434
|
-
*
|
|
535
|
+
* Set when returning a value ends the request and only a bare return
|
|
536
|
+
* passes it on. Flask's `before_request` works this way.
|
|
435
537
|
*/
|
|
436
538
|
returnedValueResponds?: boolean;
|
|
437
|
-
/** The position the
|
|
539
|
+
/** The parameter position the raised exception is passed at. Set on an exception handler, which runs only when the route raised. */
|
|
438
540
|
throwParam?: number;
|
|
439
541
|
}
|
|
440
542
|
/**
|
|
441
|
-
* One callable that ends the request, and where it takes the status.
|
|
442
|
-
*
|
|
443
|
-
*
|
|
543
|
+
* One callable that ends the request, and where it takes the status. A
|
|
544
|
+
* call can pass the status by keyword or by position, so a pattern can
|
|
545
|
+
* declare both. When a call writes both, the keyword is used.
|
|
444
546
|
*/
|
|
445
547
|
interface PyStatusCall {
|
|
446
548
|
/** The callee as the file imports it, module and name together, `fastapi.HTTPException`. */
|
|
@@ -453,9 +555,9 @@ interface PyStatusCall {
|
|
|
453
555
|
defaultStatusCode?: number;
|
|
454
556
|
}
|
|
455
557
|
/**
|
|
456
|
-
* A
|
|
457
|
-
*
|
|
458
|
-
* with the verb taken from the method name.
|
|
558
|
+
* A route written as a class whose decorator takes the route path as its
|
|
559
|
+
* first argument. Each method in the class named after an HTTP verb becomes
|
|
560
|
+
* its own unit, with the verb taken from the method name.
|
|
459
561
|
*/
|
|
460
562
|
interface DecoratedClassRoute extends RouteConventions {
|
|
461
563
|
type: "decoratedClassRoute";
|
|
@@ -467,10 +569,10 @@ interface DecoratedClassRoute extends RouteConventions {
|
|
|
467
569
|
verbMethodNames: Record<string, string>;
|
|
468
570
|
}
|
|
469
571
|
/**
|
|
470
|
-
* A
|
|
471
|
-
*
|
|
472
|
-
*
|
|
473
|
-
* a call to something imported.
|
|
572
|
+
* A route written as a function decorated with `@app.get(path)`: the
|
|
573
|
+
* decorator's attribute name is the HTTP verb and its first argument is the
|
|
574
|
+
* route path. The object the decorator is called on can be imported, or
|
|
575
|
+
* built by a call to something imported.
|
|
474
576
|
*/
|
|
475
577
|
interface DecoratedFunctionRoute extends RouteConventions {
|
|
476
578
|
type: "decoratedFunctionRoute";
|
|
@@ -482,12 +584,12 @@ interface DecoratedFunctionRoute extends RouteConventions {
|
|
|
482
584
|
statusCodeKeyword?: string;
|
|
483
585
|
}
|
|
484
586
|
/**
|
|
485
|
-
*
|
|
486
|
-
* can be built from the
|
|
487
|
-
* constructor's
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
587
|
+
* The names a library gives the parts of router mounting, so a route's
|
|
588
|
+
* served path can be built from the prefixes written along the way: the
|
|
589
|
+
* router constructor's, the one on the call that mounts it, and, where the
|
|
590
|
+
* pack declares it, the one on the object the mount is called on. DESIGN.md
|
|
591
|
+
* has the tables for what each way of writing a prefix means and when a
|
|
592
|
+
* route abstains.
|
|
491
593
|
*/
|
|
492
594
|
interface RouterComposition {
|
|
493
595
|
/** Constructor whose call builds a mountable router, FastAPI's `APIRouter`. */
|
|
@@ -495,37 +597,36 @@ interface RouterComposition {
|
|
|
495
597
|
/** Method that mounts a router onto the app, FastAPI's `include_router`. */
|
|
496
598
|
includeMethodName: string;
|
|
497
599
|
/**
|
|
498
|
-
*
|
|
499
|
-
*
|
|
500
|
-
*
|
|
600
|
+
* The mount method's name for its router parameter, FastAPI's `router`,
|
|
601
|
+
* used to read a call that passes the router by keyword. Unset reads the
|
|
602
|
+
* first positional argument only.
|
|
501
603
|
*/
|
|
502
604
|
routerKeyword?: string;
|
|
503
|
-
/**
|
|
605
|
+
/** The prefix keyword, the same at the constructor and the mount. A library that uses two different keywords would need two fields here. */
|
|
504
606
|
prefixKeyword: string;
|
|
505
|
-
/** Default "prefixes". */
|
|
607
|
+
/** Whether a prefix written at the mount goes in front of the constructor's ("prefixes") or replaces it ("replaces"). Default "prefixes". */
|
|
506
608
|
mountPrefixEffect?: MountPrefixEffect;
|
|
507
|
-
/** Set it when the library
|
|
609
|
+
/** Set it when the library makes up a path of its own for a router constructed with no prefix. The adapter cannot work that path out, so such routes abstain. */
|
|
508
610
|
constructorPrefixRequired?: boolean;
|
|
509
|
-
/**
|
|
611
|
+
/** What a prefix written as `""`, `None`, `False` or `0` means: no prefix ("unstated"), or one the route has to abstain on ("unreadable"). Default "unreadable", at the constructor and the mount alike. */
|
|
510
612
|
noValuePrefix?: NoValuePrefix;
|
|
511
|
-
/** Default "kept". */
|
|
613
|
+
/** Whether the library trims trailing slashes off a constructor's prefix. Default "kept". */
|
|
512
614
|
constructorPrefixTrailingSlash?: PrefixTrailingSlash;
|
|
513
615
|
/**
|
|
514
|
-
* Where the object the mount is called on states a prefix of its
|
|
515
|
-
*
|
|
516
|
-
*
|
|
517
|
-
*
|
|
616
|
+
* Where the object the mount is called on states a prefix of its own,
|
|
617
|
+
* in front of everything the constructor and the mount state. Unset
|
|
618
|
+
* means it states none. That matches FastAPI, where an app serves a
|
|
619
|
+
* mounted router exactly where the two prefixes put it.
|
|
518
620
|
*/
|
|
519
621
|
mountObjectPrefix?: MountObjectPrefix;
|
|
520
622
|
}
|
|
521
623
|
/**
|
|
522
|
-
*
|
|
523
|
-
*
|
|
524
|
-
*
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
*
|
|
528
|
-
* say.
|
|
624
|
+
* Where the object a mount is called on states its prefix. flask-restx
|
|
625
|
+
* needs both fields: `Api(prefix=...)` states one on the object itself,
|
|
626
|
+
* and the Flask blueprint the `Api` was built from states another with
|
|
627
|
+
* `Blueprint(name, __name__, url_prefix=...)`. A route is served under
|
|
628
|
+
* the blueprint's prefix, then the `Api`'s, then the namespace's path and
|
|
629
|
+
* the route's own.
|
|
529
630
|
*/
|
|
530
631
|
interface MountObjectPrefix {
|
|
531
632
|
/** Keyword stating a prefix on the mount object's own construction (flask-restx's `Api(prefix=...)`). */
|
|
@@ -534,11 +635,10 @@ interface MountObjectPrefix {
|
|
|
534
635
|
carrier?: MountObjectCarrier;
|
|
535
636
|
}
|
|
536
637
|
/**
|
|
537
|
-
* An object
|
|
538
|
-
*
|
|
539
|
-
*
|
|
540
|
-
*
|
|
541
|
-
* has no prefix at all.
|
|
638
|
+
* An object passed to the mount object's constructor that has a prefix of
|
|
639
|
+
* its own, such as the Flask blueprint an `Api` is built from. Declaring
|
|
640
|
+
* its constructor lets the adapter tell it apart from the plain app, which
|
|
641
|
+
* is passed in the same argument position and has no prefix.
|
|
542
642
|
*/
|
|
543
643
|
interface MountObjectCarrier {
|
|
544
644
|
/** Modules the carrier's constructor is imported from (Flask's `flask`). */
|
|
@@ -547,32 +647,28 @@ interface MountObjectCarrier {
|
|
|
547
647
|
constructorName: string;
|
|
548
648
|
/** Position of the carrier among the mount object's constructor arguments. */
|
|
549
649
|
argumentIndex: number;
|
|
550
|
-
/**
|
|
650
|
+
/** The keyword for the carrier's prefix, at its construction and its registration alike (Flask's `url_prefix`). */
|
|
551
651
|
prefixKeyword: string;
|
|
552
652
|
/**
|
|
553
|
-
* Method
|
|
554
|
-
*
|
|
555
|
-
* `Api(blueprint)
|
|
556
|
-
*
|
|
653
|
+
* Method that passes the carrier to a mount object already built, as
|
|
654
|
+
* flask-restx's `init_app` does in an application factory in place of
|
|
655
|
+
* `Api(blueprint)`. Unset means the constructor argument is the only
|
|
656
|
+
* way to pass one.
|
|
557
657
|
*/
|
|
558
658
|
handoffMethodName?: string;
|
|
559
659
|
/**
|
|
560
|
-
* Method
|
|
561
|
-
* `register_blueprint`). The adapter reads it only to
|
|
562
|
-
* registration
|
|
563
|
-
* another carrier, or
|
|
564
|
-
* somewhere the carrier's
|
|
660
|
+
* Method that registers the carrier somewhere else (Flask's
|
|
661
|
+
* `register_blueprint`). The adapter reads it only to decide when to
|
|
662
|
+
* abstain. A registration that restates the prefix, registers the
|
|
663
|
+
* carrier inside another carrier, or registers it twice serves the
|
|
664
|
+
* routes somewhere other than where the carrier's construction says.
|
|
565
665
|
*/
|
|
566
666
|
registerMethodName: string;
|
|
567
667
|
}
|
|
568
668
|
type MountPrefixEffect = "prefixes" | "replaces";
|
|
569
669
|
type PrefixTrailingSlash = "kept" | "trimmed";
|
|
570
670
|
type NoValuePrefix = "unstated" | "unreadable";
|
|
571
|
-
/**
|
|
572
|
-
* Werkzeug serves the merged path and redirects the written one, so Flask
|
|
573
|
-
* needs "merged". Starlette leaves the path as composed, so FastAPI keeps
|
|
574
|
-
* the default.
|
|
575
|
-
*/
|
|
671
|
+
/** Werkzeug merges repeated slashes, so Flask needs "merged". Starlette does not, so FastAPI keeps "kept". */
|
|
576
672
|
type PathRepeatedSlashes = "kept" | "merged";
|
|
577
673
|
|
|
578
674
|
/**
|
|
@@ -581,11 +677,11 @@ type PathRepeatedSlashes = "kept" | "merged";
|
|
|
581
677
|
* whether that was an import, an assignment, a def, a parameter, or a `global`
|
|
582
678
|
* or `nonlocal` redirect to another scope.
|
|
583
679
|
*
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
* definition or import nested inside an `if
|
|
588
|
-
*
|
|
680
|
+
* Every reader of a name accepts an unresolved answer, so the binder can
|
|
681
|
+
* leave names out, and a wrong binding is what it avoids. It binds names
|
|
682
|
+
* written directly in a body's statement list, plus the statements of a
|
|
683
|
+
* `with` body. A definition or import nested inside an `if` or `try` is
|
|
684
|
+
* left out, and a read of it comes back unresolved instead of guessed.
|
|
589
685
|
*/
|
|
590
686
|
|
|
591
687
|
type ScopeKind = "module" | "class" | "function";
|
|
@@ -625,7 +721,7 @@ type Binding = {
|
|
|
625
721
|
} | {
|
|
626
722
|
kind: "parameter";
|
|
627
723
|
}
|
|
628
|
-
/** The right-hand side, when there is one, so
|
|
724
|
+
/** The right-hand side, when there is one, so a reader can follow a name one hop back to what constructed it. */
|
|
629
725
|
| {
|
|
630
726
|
kind: "assignment";
|
|
631
727
|
value: Node | null;
|
|
@@ -642,7 +738,7 @@ interface ModuleBinding {
|
|
|
642
738
|
moduleScope: Scope;
|
|
643
739
|
/** The scope a class_definition, a function_definition, or the module node opens, keyed by that node's id. */
|
|
644
740
|
scopeFor: Map<number, Scope>;
|
|
645
|
-
/** The modules a `from X import *` pulls in.
|
|
741
|
+
/** The modules a `from X import *` pulls in. The binder cannot list what a wildcard brings in, so nothing expands them here. */
|
|
646
742
|
openImports: string[];
|
|
647
743
|
}
|
|
648
744
|
declare function bindModule(root: Node): ModuleBinding;
|
|
@@ -654,21 +750,35 @@ declare function bindModule(root: Node): ModuleBinding;
|
|
|
654
750
|
*/
|
|
655
751
|
declare function resolveName(scope: Scope, name: string): Binding | null;
|
|
656
752
|
|
|
753
|
+
/**
|
|
754
|
+
* Works out the prefix a route inherits from the routers it is mounted
|
|
755
|
+
* through, ahead of discovery. With `router = APIRouter(prefix="/items")`
|
|
756
|
+
* and `app.include_router(router, prefix="/api")`, a route on `router` is
|
|
757
|
+
* served under `/api/items`, a path its own file never states. A chain of
|
|
758
|
+
* routers mounted on routers adds a pair of prefixes per hop.
|
|
759
|
+
*
|
|
760
|
+
* Every prefix goes through `readPrefixKeyword` and comes back stated,
|
|
761
|
+
* unstated or unreadable. What each spelling means differs by library, so
|
|
762
|
+
* the pack declares it, and DESIGN.md has the tables. When a path cannot be
|
|
763
|
+
* settled, the route abstains: discovery keeps its name and gives it no
|
|
764
|
+
* path, so it pairs with nothing instead of with a guessed path.
|
|
765
|
+
*/
|
|
766
|
+
|
|
657
767
|
/** One file, already parsed and bound. `buildRouterIndex` takes a project as a list of these. */
|
|
658
768
|
interface BoundPythonFile {
|
|
659
|
-
/** The absolute path
|
|
769
|
+
/** The absolute path. Module resolution matches files on it. */
|
|
660
770
|
file: string;
|
|
661
|
-
/** The path a gap
|
|
771
|
+
/** The path a gap reason prints for this file, so a reader can open it. */
|
|
662
772
|
displayPath: string;
|
|
663
773
|
root: Node;
|
|
664
774
|
module: ModuleBinding;
|
|
665
775
|
}
|
|
666
776
|
/**
|
|
667
|
-
* What the object a decorator
|
|
668
|
-
* app itself and anything the index never saw constructed,
|
|
669
|
-
* own path
|
|
670
|
-
* that path. An `abstain` reason is
|
|
671
|
-
* declared on ...".
|
|
777
|
+
* What the object a route decorator is called on turns out to be. `notRouter`
|
|
778
|
+
* covers the app itself and anything the index never saw constructed, and the
|
|
779
|
+
* decorator's own path is used as written. `composed` gives the prefix to put
|
|
780
|
+
* in front of that path. An `abstain` reason is worded to follow "the router
|
|
781
|
+
* this route is declared on ...".
|
|
672
782
|
*/
|
|
673
783
|
type RoutePrefixResolution = {
|
|
674
784
|
kind: "notRouter";
|
|
@@ -716,12 +826,14 @@ declare function buildRouterIndex(files: BoundPythonFile[], packs: PythonPack[],
|
|
|
716
826
|
interface ImportedDefinition {
|
|
717
827
|
/** The class_definition, or the value an assignment gave the name. */
|
|
718
828
|
node: Node;
|
|
829
|
+
/** The file the definition is written in. */
|
|
830
|
+
file: string;
|
|
719
831
|
/** The module scope of the file the definition is written in. */
|
|
720
832
|
moduleScope: Scope;
|
|
721
833
|
/** The scopes of that file, keyed by the node that opens each one. */
|
|
722
834
|
scopeFor: Map<number, Scope>;
|
|
723
835
|
}
|
|
724
|
-
/** `scope` is where the name is read
|
|
836
|
+
/** `scope` is where the name is read. The facts key an import on the file that reads it. */
|
|
725
837
|
type ImportedDefinitionLookup = (scope: Scope, name: string) => ImportedDefinition | null;
|
|
726
838
|
|
|
727
839
|
/**
|
|
@@ -733,11 +845,11 @@ type ImportedDefinitionLookup = (scope: Scope, name: string) => ImportedDefiniti
|
|
|
733
845
|
*/
|
|
734
846
|
|
|
735
847
|
/**
|
|
736
|
-
* `definitions` stores each converted class shape once, however many
|
|
737
|
-
* mention it. `scopeMaps`
|
|
738
|
-
* body
|
|
739
|
-
* read from another file
|
|
740
|
-
* is
|
|
848
|
+
* `definitions` stores each converted class shape once, however many
|
|
849
|
+
* annotations mention it. `scopeMaps` lets a name written inside a
|
|
850
|
+
* referenced class's body resolve as well as one at the annotation, and a
|
|
851
|
+
* class read from another file adds that file's scopes. `importedDefinition`
|
|
852
|
+
* is null when a caller reads one file on its own, and an imported name is
|
|
741
853
|
* then a ref by name and nothing more.
|
|
742
854
|
*/
|
|
743
855
|
interface AnnotationContext {
|
|
@@ -747,7 +859,7 @@ interface AnnotationContext {
|
|
|
747
859
|
/** The alias values being expanded, so `A = B` and `B = A` end rather than recurse. */
|
|
748
860
|
expanding: Set<number>;
|
|
749
861
|
}
|
|
750
|
-
/** `scope` is where the annotation is written
|
|
862
|
+
/** `scope` is where the annotation is written. Resolving the name there tells a project class apart from an external name. */
|
|
751
863
|
declare function annotationToShape(typeNode: Node, scope: Scope, ctx: AnnotationContext): TypeShape;
|
|
752
864
|
/** Exported so a decorator keyword that gives a class name reads the same way as that name written in annotation position. */
|
|
753
865
|
declare function shapeFromName(name: string, scope: Scope, ctx: AnnotationContext): TypeShape;
|
|
@@ -757,7 +869,20 @@ interface Range {
|
|
|
757
869
|
end: number;
|
|
758
870
|
}
|
|
759
871
|
|
|
760
|
-
/**
|
|
872
|
+
/**
|
|
873
|
+
* Works out which library a decorator comes from, for discovery.
|
|
874
|
+
*
|
|
875
|
+
* `@app.route("/x")` is two steps: the call that builds the decorator, and
|
|
876
|
+
* applying the result to the function. Discovery needs only the first, so
|
|
877
|
+
* `classifyDecorator` reads which name the callee resolved to and which
|
|
878
|
+
* module that name came from, and stops there.
|
|
879
|
+
*
|
|
880
|
+
* A decorator re-exported through a project wrapper is handled the same
|
|
881
|
+
* way the TypeScript packs handle one: the name is classified as an import
|
|
882
|
+
* of some module, and the pack lists every module it accepts.
|
|
883
|
+
*/
|
|
884
|
+
|
|
885
|
+
/** An argument as written, plus its node so a reader can evaluate it. */
|
|
761
886
|
type DecoratorArg = DecoratorArgShape & {
|
|
762
887
|
readonly node: Node;
|
|
763
888
|
};
|
|
@@ -790,15 +915,15 @@ type DecoratorArgShape = {
|
|
|
790
915
|
kind: "other";
|
|
791
916
|
};
|
|
792
917
|
interface DecoratorClassification {
|
|
793
|
-
/** The name as its source module exports it,
|
|
918
|
+
/** The name as its source module exports it, whatever alias or attribute path the file wrote it under. */
|
|
794
919
|
importedName: string | null;
|
|
795
920
|
/** The dotted module the name was imported from. It is null whenever `importedName` is null. */
|
|
796
921
|
module: string | null;
|
|
797
|
-
/** The local variable an attribute decorator
|
|
922
|
+
/** The local variable an attribute decorator is called on, `app` in `@app.get(...)`. */
|
|
798
923
|
objectName: string | null;
|
|
799
924
|
args: DecoratorArg[];
|
|
800
925
|
keywordArgs: Record<string, DecoratorArg>;
|
|
801
|
-
/** Where the decorator is written. Anything
|
|
926
|
+
/** Where the decorator is written. Anything read out of its arguments reports this as its location. */
|
|
802
927
|
range: Range;
|
|
803
928
|
/**
|
|
804
929
|
* The module the decorator's object lives in, when the decorator was read
|
|
@@ -807,9 +932,9 @@ interface DecoratorClassification {
|
|
|
807
932
|
*/
|
|
808
933
|
objectModule?: ModuleBinding;
|
|
809
934
|
/**
|
|
810
|
-
* The call the rules say built the object this decorator
|
|
811
|
-
* router
|
|
812
|
-
*
|
|
935
|
+
* The call the rules say built the object this decorator is called on.
|
|
936
|
+
* The router index looks a router up by this call when it never saw the
|
|
937
|
+
* router under a name, as with a decorator on `self.router`.
|
|
813
938
|
*/
|
|
814
939
|
subjectConstruction?: {
|
|
815
940
|
key: string;
|
|
@@ -818,6 +943,13 @@ interface DecoratorClassification {
|
|
|
818
943
|
}
|
|
819
944
|
declare function classifyDecorator(decoratorNode: Node, module: ModuleBinding, facts?: Database): DecoratorClassification;
|
|
820
945
|
|
|
946
|
+
/**
|
|
947
|
+
* Finds the calls in a body that talk to the database, and what each one
|
|
948
|
+
* does. A pack lists its library's query types, and a call chain matches
|
|
949
|
+
* when the method behind it declares that it returns one. DESIGN.md says
|
|
950
|
+
* why the match goes by return type.
|
|
951
|
+
*/
|
|
952
|
+
|
|
821
953
|
/**
|
|
822
954
|
* What discovery uses for one file. `factsPath` is the path the facts were
|
|
823
955
|
* keyed under, which is the absolute one, while a summary shows the short one.
|
|
@@ -826,7 +958,6 @@ interface StorageLookup {
|
|
|
826
958
|
readonly facts: Database;
|
|
827
959
|
readonly factsPath: string;
|
|
828
960
|
readonly patterns: readonly StoragePattern[];
|
|
829
|
-
readonly definitionAt: (key: string) => Node | undefined;
|
|
830
961
|
readonly couldMatch: ReadonlySet<string>;
|
|
831
962
|
/** What a pack says about statements a project writes as SQL itself. */
|
|
832
963
|
readonly rawSql?: readonly RawSqlPattern[];
|
|
@@ -835,15 +966,16 @@ interface StorageLookup {
|
|
|
835
966
|
}
|
|
836
967
|
|
|
837
968
|
/**
|
|
838
|
-
* What a call's callee is: a function in this run the walk can
|
|
839
|
-
*
|
|
969
|
+
* What a call's callee is: a function in this run that the walk can
|
|
970
|
+
* follow, or the reason it cannot.
|
|
840
971
|
*
|
|
841
|
-
* The rules in
|
|
842
|
-
*
|
|
843
|
-
*
|
|
972
|
+
* The rules in `@suss/resolution` settle the callee. They already follow
|
|
973
|
+
* a value through names, aliases, attributes and instances, so nothing
|
|
974
|
+
* here reads any of those itself.
|
|
844
975
|
*
|
|
845
|
-
*
|
|
846
|
-
* dot, and a name only a wildcard import could
|
|
976
|
+
* This module handles the two cases that depend on files: a module
|
|
977
|
+
* written before the dot, and a name that only a wildcard import could
|
|
978
|
+
* have brought in.
|
|
847
979
|
*/
|
|
848
980
|
|
|
849
981
|
/** A function in this run, and the export path its summary gets. */
|
|
@@ -859,16 +991,16 @@ interface ReachedFunction {
|
|
|
859
991
|
/**
|
|
860
992
|
* The project functions a library runs around a route, read from where
|
|
861
993
|
* they are registered: a dependency on a route's parameter or decorator,
|
|
862
|
-
* a dependency list on the app or a router, a function decorated with
|
|
863
|
-
* `@app.middleware(...)` or `@app.exception_handler(...)`. Each
|
|
864
|
-
*
|
|
865
|
-
*
|
|
866
|
-
*
|
|
994
|
+
* a dependency list on the app or a router, or a function decorated with
|
|
995
|
+
* `@app.middleware(...)` or `@app.exception_handler(...)`. Each becomes a
|
|
996
|
+
* unit of its own, and every route it reaches lists it, so the extractor
|
|
997
|
+
* can fold its outcomes into the route's. DESIGN.md, under "What runs
|
|
998
|
+
* around a route", lists which registrations are read.
|
|
867
999
|
*
|
|
868
1000
|
* A registration on the app reaches every route of the pack in the run,
|
|
869
|
-
*
|
|
870
|
-
*
|
|
871
|
-
*
|
|
1001
|
+
* because the run treats a pack's app as the only one. A registration on
|
|
1002
|
+
* a router reaches the routes decorated on that same router object, and
|
|
1003
|
+
* nothing mounted onto it.
|
|
872
1004
|
*/
|
|
873
1005
|
|
|
874
1006
|
interface WrapperIndexOptions {
|
|
@@ -894,6 +1026,7 @@ interface Registered {
|
|
|
894
1026
|
reference: WrapperReference;
|
|
895
1027
|
form: PyWrapperForm;
|
|
896
1028
|
}
|
|
1029
|
+
/** A wrapper form with the pattern and pack that declare it. */
|
|
897
1030
|
interface FormOf {
|
|
898
1031
|
pack: PythonPack;
|
|
899
1032
|
pattern: PythonDiscoveryPattern;
|
|
@@ -909,7 +1042,15 @@ declare class PythonWrapperIndex {
|
|
|
909
1042
|
private readonly ownRoutes;
|
|
910
1043
|
private readonly unitsByKey;
|
|
911
1044
|
private readonly unitsByFile;
|
|
1045
|
+
/** Told about every registration, so a cache can repeat the ones a file's discovery made. */
|
|
1046
|
+
onRegistered: ((target: ReachedFunction, declared: FormOf) => void) | null;
|
|
912
1047
|
constructor(options: WrapperIndexOptions, filesByPath: ReadonlyMap<string, BoundPythonFile>);
|
|
1048
|
+
/**
|
|
1049
|
+
* The same lookup `wrappersFor` noted under `id`, made again over this
|
|
1050
|
+
* index, or null when `id` is not one of these.
|
|
1051
|
+
*/
|
|
1052
|
+
lookAgain(id: string): string | null;
|
|
1053
|
+
private noted;
|
|
913
1054
|
/** The project function a module-level name in this file refers to, or null for anything else. */
|
|
914
1055
|
functionCalled(file: string, name: string): ReachedFunction | null;
|
|
915
1056
|
register(covers: {
|
|
@@ -934,30 +1075,29 @@ declare class PythonWrapperIndex {
|
|
|
934
1075
|
}
|
|
935
1076
|
|
|
936
1077
|
/**
|
|
937
|
-
* Finds decorated routes and turns each one into a `RawCodeStructure
|
|
938
|
-
*
|
|
939
|
-
* Only decorated module-level functions and classes that are declared
|
|
940
|
-
* unconditionally get discovered, the same boundary the binder in scope.ts
|
|
941
|
-
* draws. Nothing here reads into a unit's body, so a route's summary comes out
|
|
942
|
-
* with no branches at all, or with exactly one branch describing what an
|
|
943
|
-
* annotation or a decorator keyword already states, such as a FastAPI
|
|
944
|
-
* `response_model` or `status_code`, or a return annotation.
|
|
1078
|
+
* Finds decorated routes and turns each one into a `RawCodeStructure`, and
|
|
1079
|
+
* collects the client units for outgoing requests alongside them.
|
|
945
1080
|
*
|
|
946
|
-
*
|
|
947
|
-
*
|
|
1081
|
+
* Every decorated definition in the file is classified, wherever it is
|
|
1082
|
+
* written. One a pack's pattern accepts becomes a unit per HTTP method and
|
|
1083
|
+
* per mount. The path is the decorator's first argument with the router
|
|
1084
|
+
* prefixes in front of it. The status and body shape come from decorator
|
|
1085
|
+
* keywords, the return annotation, and each return or raise in the body.
|
|
1086
|
+
* A route that cannot be read keeps its name and gets no path, so it pairs
|
|
1087
|
+
* with nothing.
|
|
948
1088
|
*/
|
|
949
1089
|
|
|
950
1090
|
interface DiscoveryOptions {
|
|
951
1091
|
packs: PythonPack[];
|
|
952
1092
|
/** Repo-relative or absolute path recorded on each summary's `location.file`. */
|
|
953
1093
|
filePath: string;
|
|
954
|
-
/** Without it, every route object
|
|
1094
|
+
/** Without it, every route object is treated as the app itself and paths are used as written. */
|
|
955
1095
|
routerIndex?: RouterIndex;
|
|
956
1096
|
/** Under "strict" a route whose unit cannot be built stops the run instead of abstaining. */
|
|
957
1097
|
gapHandling?: ExtractorOptions["gapHandling"];
|
|
958
|
-
/** What a pack needs to
|
|
1098
|
+
/** What a pack needs to decide that a call talks to the database. Absent when no pack declares storage. */
|
|
959
1099
|
storage?: StorageLookup | undefined;
|
|
960
|
-
/** The file's absolute path, which module resolution
|
|
1100
|
+
/** The file's absolute path, which module resolution needs. `filePath` may be shortened for display. */
|
|
961
1101
|
absoluteFile?: string | undefined;
|
|
962
1102
|
/** The project's facts, so the rules can say what an object no scope has a binding for was built by. */
|
|
963
1103
|
facts?: Database | undefined;
|
|
@@ -968,27 +1108,33 @@ interface DiscoveryOptions {
|
|
|
968
1108
|
}
|
|
969
1109
|
declare function discoverUnits(root: Node, module: ModuleBinding, options: DiscoveryOptions): RawCodeStructure[];
|
|
970
1110
|
|
|
971
|
-
/**
|
|
1111
|
+
/**
|
|
1112
|
+
* Runs the shared resolution rules over this project's facts. The rules in
|
|
1113
|
+
* `@suss/resolution` are the same ones the TypeScript adapter runs, so a
|
|
1114
|
+
* Python value is followed the same way a TypeScript one is.
|
|
1115
|
+
*/
|
|
1116
|
+
|
|
1117
|
+
/** Asks the rules to resolve these keys, and derives what follows. */
|
|
972
1118
|
declare function resolveCalls(db: Database, callKeys: readonly string[]): void;
|
|
973
1119
|
/**
|
|
974
1120
|
* The values an object contains under its own keys, in the order the source
|
|
975
1121
|
* writes them. Empty when nothing said what the object contains.
|
|
976
1122
|
*/
|
|
977
1123
|
declare function containedValues(db: Database, objectKey: string): string[];
|
|
978
|
-
/**
|
|
1124
|
+
/** The object a call returns, when the rules settled it on one. */
|
|
979
1125
|
declare function objectReturnedBy(db: Database, callKey: string): string | null;
|
|
980
1126
|
|
|
981
1127
|
/**
|
|
982
|
-
*
|
|
983
|
-
*
|
|
984
|
-
* Python value
|
|
1128
|
+
* Emits the value facts that the rules in @suss/resolution join, read from
|
|
1129
|
+
* Python source. The relations are the ones that package defines, so a
|
|
1130
|
+
* Python value is followed by the same rules as a TypeScript one.
|
|
985
1131
|
*
|
|
986
1132
|
* A name is keyed by the scope that binds it. A function's own names take
|
|
987
|
-
* the function's key, so two handlers that both write `query` stay apart
|
|
988
|
-
*
|
|
989
|
-
*
|
|
990
|
-
*
|
|
991
|
-
*
|
|
1133
|
+
* the function's key, so two handlers that both write `query` stay apart.
|
|
1134
|
+
* A module's names take the file's key, and another file's import matches
|
|
1135
|
+
* on that key. Every write to a name in one scope is collected in source
|
|
1136
|
+
* order, and `valueLeftByWrites` decides what the name comes down to, so a
|
|
1137
|
+
* reassigned name gets one value or none, never two.
|
|
992
1138
|
*/
|
|
993
1139
|
|
|
994
1140
|
/**
|
|
@@ -1015,21 +1161,27 @@ declare function nameKeyIn(filePath: string, enclosing: Node | null, name: strin
|
|
|
1015
1161
|
declare function emitValueFacts(db: Database, filePath: string, root: Node): void;
|
|
1016
1162
|
|
|
1017
1163
|
/**
|
|
1018
|
-
* The session behind `suss ask why` on a Python project
|
|
1019
|
-
*
|
|
1020
|
-
*
|
|
1021
|
-
*
|
|
1164
|
+
* The session behind `suss ask why` on a Python project. It finds the
|
|
1165
|
+
* expression a question points at by file, line and text, and renders the
|
|
1166
|
+
* witness proof of what that expression resolved to through the phrases
|
|
1167
|
+
* in `@suss/resolution`.
|
|
1022
1168
|
*
|
|
1023
|
-
* It parses every file under the root and
|
|
1024
|
-
* `extractPythonProject` does,
|
|
1025
|
-
* a proof
|
|
1026
|
-
*
|
|
1027
|
-
*
|
|
1169
|
+
* It parses every file under the root and records the same value facts
|
|
1170
|
+
* and pack words `extractPythonProject` does, along with a location for
|
|
1171
|
+
* every fact key, so each step of a proof can point back at the source. A
|
|
1172
|
+
* handle pairs a tree-sitter node with its file, because a node alone
|
|
1173
|
+
* does not say which file it was parsed from.
|
|
1028
1174
|
*/
|
|
1029
1175
|
|
|
1030
1176
|
interface PythonWhySessionOptions {
|
|
1031
1177
|
/** The project root, which paths in every answer come out relative to. */
|
|
1032
1178
|
dir: string;
|
|
1179
|
+
/** Directories an absolute import is resolved against. Read from `dir` when absent, the way `extractPythonProject` reads them. */
|
|
1180
|
+
roots?: string[];
|
|
1181
|
+
/** Roots the project directory does not show, such as a checked-out submodule. Added after the others. */
|
|
1182
|
+
additionalRoots?: string[];
|
|
1183
|
+
/** The packs the extraction ran with. A hop only a pack declares, such as `session.get(User, id)` giving back a User, is explained only when that pack is here. */
|
|
1184
|
+
packs?: readonly PythonPack[];
|
|
1033
1185
|
}
|
|
1034
1186
|
/** A found node, paired with the file it was parsed from. */
|
|
1035
1187
|
interface PythonValueHandle {
|
|
@@ -1054,8 +1206,8 @@ declare class PythonWhySession {
|
|
|
1054
1206
|
findCallee(file: string, startLine: number, endLine: number, calleeText: string): PythonValueHandle | null;
|
|
1055
1207
|
/**
|
|
1056
1208
|
* Why `value` resolves to what it does: the witness proof, flattened
|
|
1057
|
-
* to the chain and rendered. Null when the value does not resolve,
|
|
1058
|
-
*
|
|
1209
|
+
* to the chain and rendered. Null when the value does not resolve, and
|
|
1210
|
+
* the caller then writes its own message.
|
|
1059
1211
|
*/
|
|
1060
1212
|
explain(value: PythonValueHandle, options?: {
|
|
1061
1213
|
maxDepth?: number;
|
|
@@ -1063,23 +1215,18 @@ declare class PythonWhySession {
|
|
|
1063
1215
|
private locate;
|
|
1064
1216
|
private rootOf;
|
|
1065
1217
|
private pathOf;
|
|
1066
|
-
/** A file path
|
|
1218
|
+
/** A file path relative to the root, or unchanged when it is outside the root. */
|
|
1067
1219
|
private displayPath;
|
|
1068
1220
|
}
|
|
1069
1221
|
|
|
1070
1222
|
/**
|
|
1071
|
-
* The
|
|
1072
|
-
*
|
|
1073
|
-
*
|
|
1223
|
+
* The facts the module resolver adds to the shared store.
|
|
1224
|
+
*
|
|
1225
|
+
* `importsModule` records each module a file imports, as written, and
|
|
1226
|
+
* `importsFile` the file an import resolved to. An import the resolver
|
|
1227
|
+
* abstains on gets no file rather than a guessed one.
|
|
1074
1228
|
*/
|
|
1075
|
-
|
|
1076
|
-
start: number;
|
|
1077
|
-
end: number;
|
|
1078
|
-
}, name: string): string;
|
|
1079
|
-
declare function emitEntryFact(db: Database, filePath: string, range: {
|
|
1080
|
-
start: number;
|
|
1081
|
-
end: number;
|
|
1082
|
-
}, name: string): void;
|
|
1229
|
+
|
|
1083
1230
|
/**
|
|
1084
1231
|
* Every import in the file, wherever it is written. Python code puts an import
|
|
1085
1232
|
* inside a function to break a cycle between two modules, and a chain through
|
|
@@ -1087,17 +1234,74 @@ declare function emitEntryFact(db: Database, filePath: string, range: {
|
|
|
1087
1234
|
*/
|
|
1088
1235
|
declare function emitModuleImportFacts(db: Database, filePath: string, module: ModuleBinding, resolverOptions: ModuleResolverOptions): void;
|
|
1089
1236
|
|
|
1237
|
+
/**
|
|
1238
|
+
* The directories a Python project's absolute imports resolve against:
|
|
1239
|
+
* the project directory, and the source directories it declares.
|
|
1240
|
+
*
|
|
1241
|
+
* A project that keeps its package under `src/` imports it as
|
|
1242
|
+
* `orders.routes`, because installing the project puts `src/` on
|
|
1243
|
+
* `sys.path`. suss cannot run the install, so it reads where
|
|
1244
|
+
* `pyproject.toml` tells the build backend to look, and uses `src/`
|
|
1245
|
+
* when that declares nothing and `src/` contains a package.
|
|
1246
|
+
*
|
|
1247
|
+
* An extra root cannot pick the wrong module, because the module
|
|
1248
|
+
* resolver abstains when two roots both have a file for one import.
|
|
1249
|
+
*/
|
|
1250
|
+
interface UnreadManifest {
|
|
1251
|
+
/** The file, relative to the project root. */
|
|
1252
|
+
where: string;
|
|
1253
|
+
/** Why it could not be read, in one line. */
|
|
1254
|
+
reason: string;
|
|
1255
|
+
}
|
|
1256
|
+
interface PythonSourceRoots {
|
|
1257
|
+
/** Absolute: the project directory first, then each source directory declared or found under it. */
|
|
1258
|
+
roots: string[];
|
|
1259
|
+
/** A manifest that might have declared a source directory and could not be read. */
|
|
1260
|
+
unread: UnreadManifest[];
|
|
1261
|
+
}
|
|
1262
|
+
type TomlTable = Record<string, unknown>;
|
|
1263
|
+
type TomlFileRead = {
|
|
1264
|
+
kind: "parsed";
|
|
1265
|
+
value: unknown;
|
|
1266
|
+
} | {
|
|
1267
|
+
kind: "unreadable";
|
|
1268
|
+
reason: string;
|
|
1269
|
+
};
|
|
1270
|
+
declare function readTomlFile(file: string): TomlFileRead;
|
|
1271
|
+
declare function tableAt(value: unknown, ...keys: string[]): TomlTable | null;
|
|
1272
|
+
declare function pythonSourceRoots(projectRoot: string): PythonSourceRoots;
|
|
1273
|
+
|
|
1274
|
+
/**
|
|
1275
|
+
* The entry point for a Python run. It discovers units, assembles their
|
|
1276
|
+
* summaries in the shared IR, and emits the run's facts into one `Database`.
|
|
1277
|
+
*
|
|
1278
|
+
* Every file is parsed and bound first, and the value facts are emitted
|
|
1279
|
+
* across all of them, before discovery runs on any file, because a mount in
|
|
1280
|
+
* one file can refer to a router built in another. Each discovered unit
|
|
1281
|
+
* goes through `@suss/extractor`'s `assembleSummary`, the same assembly the
|
|
1282
|
+
* TypeScript adapter uses, so both languages share one gap detection. With
|
|
1283
|
+
* a cache, a run after an edit replays the files and functions whose
|
|
1284
|
+
* inputs did not change, through the records in `reuse.ts`.
|
|
1285
|
+
*/
|
|
1286
|
+
|
|
1090
1287
|
interface ExtractPythonOptions {
|
|
1091
1288
|
/** Absolute paths of the files to parse and extract. */
|
|
1092
1289
|
files: string[];
|
|
1093
1290
|
packs: PythonPack[];
|
|
1094
|
-
/**
|
|
1095
|
-
|
|
1291
|
+
/**
|
|
1292
|
+
* Directories an absolute import is resolved against. When absent,
|
|
1293
|
+
* they are read from `projectRoot`: the directory itself and the
|
|
1294
|
+
* source directories its `pyproject.toml` declares, or `src/` when
|
|
1295
|
+
* it contains a package.
|
|
1296
|
+
*/
|
|
1297
|
+
roots?: string[];
|
|
1298
|
+
/** Roots that cannot be found from the project directory, such as a checked-out submodule. Added after the others. */
|
|
1299
|
+
additionalRoots?: string[];
|
|
1096
1300
|
/** When set, `location.file` on each summary is relativized against this. */
|
|
1097
1301
|
workspaceRoot?: string;
|
|
1098
1302
|
/** The directory a summary's id measures its file from, when that differs from `workspaceRoot`. */
|
|
1099
1303
|
projectRoot?: string;
|
|
1100
|
-
/**
|
|
1304
|
+
/** How much of what could not be read goes on a summary. "strict" also makes a route that cannot be built stop the run. */
|
|
1101
1305
|
gapHandling?: ExtractorOptions["gapHandling"];
|
|
1102
1306
|
/** Called once with the run's per-phase wall time, for `suss extract --timing`. */
|
|
1103
1307
|
onTiming?: (report: TimingReport) => void;
|
|
@@ -1107,10 +1311,31 @@ interface ExtractPythonOptions {
|
|
|
1107
1311
|
onCacheDiagnostic?: (diagnostic: CacheDiagnostic) => void;
|
|
1108
1312
|
/** Absolute. `<projectRoot>/.suss/cache` by default; `null` turns it off. */
|
|
1109
1313
|
cacheDir?: string | null;
|
|
1314
|
+
/** The modules the project lists in `suss.json`, with absolute paths. */
|
|
1315
|
+
modules?: readonly DeclaredModule[];
|
|
1316
|
+
/** Parses an earlier run in this process left, for a file whose text is unchanged. */
|
|
1317
|
+
keptParses?: KeptPythonParses;
|
|
1110
1318
|
}
|
|
1319
|
+
/** A file's tree and the scopes bound over it, as a later run can reuse them. */
|
|
1320
|
+
type KeptPythonParses = KeptParses<{
|
|
1321
|
+
root: Node;
|
|
1322
|
+
module: ModuleBinding;
|
|
1323
|
+
}>;
|
|
1324
|
+
/** A holder for parses between runs, which frees each tree it lets go of. */
|
|
1325
|
+
declare function keptPythonParses(): KeptPythonParses;
|
|
1326
|
+
/**
|
|
1327
|
+
* Parses these files into `kept` before any run needs them. A run served
|
|
1328
|
+
* whole from the cache parses nothing, so a process that keeps parses
|
|
1329
|
+
* calls this while nobody is waiting.
|
|
1330
|
+
*/
|
|
1331
|
+
declare function parsePythonAhead(files: readonly string[], kept: KeptPythonParses): Promise<void>;
|
|
1111
1332
|
interface ExtractPythonResult {
|
|
1112
1333
|
summaries: BehavioralSummary[];
|
|
1113
1334
|
facts: Database;
|
|
1335
|
+
/** The roots absolute imports were resolved against. */
|
|
1336
|
+
roots: string[];
|
|
1337
|
+
/** A manifest that might have declared a source directory and could not be read. */
|
|
1338
|
+
unreadManifests: UnreadManifest[];
|
|
1114
1339
|
}
|
|
1115
1340
|
/** One parsed file and the packs a run over it would load. */
|
|
1116
1341
|
interface FileFactsOptions {
|
|
@@ -1133,19 +1358,19 @@ declare function extractPythonProject(options: ExtractPythonOptions): Promise<Ex
|
|
|
1133
1358
|
declare function findPythonFiles(root: string): string[];
|
|
1134
1359
|
|
|
1135
1360
|
/**
|
|
1136
|
-
*
|
|
1137
|
-
*
|
|
1138
|
-
*
|
|
1139
|
-
*
|
|
1140
|
-
* stub covers one imported module at a time
|
|
1141
|
-
*
|
|
1361
|
+
* Every import in the project of a module or one of its submodules, for
|
|
1362
|
+
* drafting a stub of a project's wrapper package.
|
|
1363
|
+
*
|
|
1364
|
+
* A discovery pattern matches a decorator's import module exactly, so a
|
|
1365
|
+
* stub covers one imported module at a time. The sites are grouped by
|
|
1366
|
+
* the module text as the project wrote it for that reason.
|
|
1142
1367
|
*
|
|
1143
|
-
*
|
|
1144
|
-
*
|
|
1145
|
-
*
|
|
1368
|
+
* The walk visits every node instead of asking the binder, so an import
|
|
1369
|
+
* inside an `if` or a `try` still counts here, even though the binder
|
|
1370
|
+
* does not resolve a name through it.
|
|
1146
1371
|
*/
|
|
1147
1372
|
interface PythonImportSite {
|
|
1148
|
-
/** The name imported at this site
|
|
1373
|
+
/** The name imported at this site. Null for a bare `import module`, which binds the whole module. */
|
|
1149
1374
|
name: string | null;
|
|
1150
1375
|
file: string;
|
|
1151
1376
|
line: number;
|
|
@@ -1167,13 +1392,13 @@ interface PythonImportEvidenceOptions {
|
|
|
1167
1392
|
declare function pythonImportEvidence(options: PythonImportEvidenceOptions): Promise<PythonImportEvidence[]>;
|
|
1168
1393
|
|
|
1169
1394
|
/**
|
|
1170
|
-
*
|
|
1395
|
+
* The Python adapter's part of the extraction cache key.
|
|
1171
1396
|
*
|
|
1172
|
-
* `ADAPTER_VERSION`
|
|
1173
|
-
*
|
|
1174
|
-
* terminal
|
|
1175
|
-
*
|
|
1397
|
+
* Bump `ADAPTER_VERSION` by hand on any change to what extraction
|
|
1398
|
+
* produces, such as the IR it writes, what discovery finds, or how a
|
|
1399
|
+
* terminal is classified. Summaries cached under the old version are
|
|
1400
|
+
* then read again instead of reused.
|
|
1176
1401
|
*/
|
|
1177
1402
|
declare const ADAPTER_VERSION = "0.1.0";
|
|
1178
1403
|
|
|
1179
|
-
export { ADAPTER_VERSION, type AnnotationContext, type Binding, type BoundPythonFile, type DecoratedClassRoute, type DecoratedFunctionRoute, type DecoratorArg, type DecoratorClassification, type DiscoveryOptions, type ExtractPythonOptions, type ExtractPythonResult, type FileFactsOptions, type ModuleBinding, type ModuleResolution, type ModuleResolverOptions, type MountObjectCarrier, type MountObjectPrefix, type MountPrefixEffect, type NoValuePrefix, type PathRepeatedSlashes, type PrefixTrailingSlash, type PyModelEntryFunction, type PyModelEntryMethod, type PyModelQueries, type PyStatusCall, type PythonDiscoveryPattern, type PythonImportEvidence, type PythonImportEvidenceOptions, type PythonImportSite, type PythonPack, type PythonValueHandle, PythonWhySession, type PythonWhySessionOptions, type RawSqlPattern, type RelativeModuleSpec, type RouteConventions, type RoutePrefixResolution, type RouterComposition, type RouterIndex, type Scope, type ScopeKind, type SqlCallArgument, type SqlClientHandoff, type SqlClientPattern, type SqlStatementCall, type SqlTableCall, type StoragePattern, annotationToShape, bindModule, buildRouterIndex, classifyDecorator, containedValues, discoverUnits,
|
|
1404
|
+
export { ADAPTER_VERSION, type AnnotationContext, type Binding, type BoundPythonFile, type DecoratedClassRoute, type DecoratedFunctionRoute, type DecoratorArg, type DecoratorClassification, type DiscoveryOptions, type ExtractPythonOptions, type ExtractPythonResult, type FileFactsOptions, type KeptPythonParses, type ModuleBinding, type ModuleResolution, type ModuleResolverOptions, type MountObjectCarrier, type MountObjectPrefix, type MountPrefixEffect, type NoValuePrefix, type ParameterSource, type PathRepeatedSlashes, type PrefixTrailingSlash, type PyModelEntryFunction, type PyModelEntryMethod, type PyModelQueries, type PyStatusCall, type PyTestCases, type PyTestMocks, type PyTestSetUp, type PythonDiscoveryPattern, type PythonImportEvidence, type PythonImportEvidenceOptions, type PythonImportSite, type PythonPack, type PythonSourceRoots, type PythonValueHandle, PythonWhySession, type PythonWhySessionOptions, type RawSqlPattern, type RelativeModuleSpec, type RequestObject, type RouteConventions, type RoutePrefixResolution, type RouterComposition, type RouterIndex, type Scope, type ScopeKind, type SqlCallArgument, type SqlClientHandoff, type SqlClientPattern, type SqlStatementCall, type SqlTableCall, type StoragePattern, type TomlFileRead, type TomlTable, type UnreadManifest, annotationToShape, bindModule, buildRouterIndex, classifyDecorator, containedValues, discoverUnits, emitModuleImportFacts, emitValueFacts, extractPythonProject, factsForFile, findPythonFiles, keptPythonParses, nameKeyIn, nodeId, objectReturnedBy, parsePython, parsePythonAhead, parsePythonSync, preloadPythonGrammar, pythonImportEvidence, pythonSourceRoots, readKey, readTomlFile, resolveAbsoluteModule, resolveCalls, resolveModule, resolveName, resolveRelativeModule, shapeFromName, tableAt };
|