@suss/adapter-python 0.33.1 → 0.34.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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 { RawCodeStructure, ExtractorOptions, TimingReport, ExtractionReport, CacheDiagnostic } from '@suss/extractor';
6
- import { WhyExplained } from '@suss/resolution';
5
+ import { RedirectOption, RawCodeStructure, ExtractorOptions, TimingReport, ExtractionReport, CacheDiagnostic, DeclaredModule, KeptParses } from '@suss/extractor';
6
+ import { UnwrapsByName, WhyExplained } from '@suss/resolution';
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. We cannot see `sys.path` order, since that only exists at runtime. */
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 Python adapter's own pattern-pack contract.
76
+ * The contract a Python pack is written against.
68
77
  *
69
- * This is deliberately not the TypeScript adapter's `PatternPack`. That type
70
- * dispatches its discovery variants through ts-morph-specific handlers, and
71
- * Python's two route shapes have no exact match in its union anyway. Match
72
- * shapes stay per-language until a second implementation shows what is actually
73
- * shared, and this is the per-language one for Python.
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 still plain data, following the same rule the TypeScript packs
76
- * follow: it describes what a library defines, never anything a project chose.
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
- * Pack version stamp, which feeds the cache invalidation key. Bump on
82
- * any change that affects discovered units or extracted summaries.
83
- * The CLI folds a hash of the loaded pack file and its config into
84
- * this stamp on top, so a pack run through the CLI invalidates on an
85
- * edit whether or not it declares a version of its own.
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
- * Files under the project this pack reads that are not among the
90
- * `.py` files a run walks, given the files the run is about to walk.
91
- * Their content feeds the same cache key the pack's own config does,
92
- * so an edit to one of them re-extracts instead of handing back the
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
- * Modules the project itself supplies, the wrappers a person names
101
- * when configuring the pack. The library's own module is not one of
102
- * these: it lives outside the project and would never resolve. The
103
- * adapter checks each of these against the project's roots, because
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. The README says how one is matched. */
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. The README says what a chain of them composes into. */
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[];
178
+ }
179
+ /**
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
+ }[];
120
208
  }
121
209
  /**
122
- * A call talks to the database when the method behind it says it returns one
123
- * of the library's query types. Matching on the return rather than on the
124
- * import is what reads a project base class that wraps the library, which is
125
- * how the measured corpus writes every one of its queries.
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: one that runs
136
- * a statement built elsewhere, `session.execute(stmt)`, whose own chain
137
- * already records the work, and one that manages the session, `close`.
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 rather than pick rows,
143
- * `values` in `update(User).where(id=1).values(name="x")`. Their
144
- * keywords are reported as fields; every other call's keywords are the
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, for the
150
- * case where a call site imports one rather than reaching it through a
151
- * project class. `select(...)` in SQLAlchemy 2.0 is one.
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
- * What a library gives back when a call is passed one of a project's
159
- * model classes, so a method read off the result runs the one that
160
- * class declares. SQLAlchemy and SQLModel take the class as an argument,
161
- * `session.get(User, id)` and `select(User)`, rather than as the
162
- * receiver a Rails finder is called on.
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 ancestry arrives at: a base class the library
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 rather than read off a session. */
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
- * `items: list[Item] = Relationship(...)`: the callable a model's field
182
- * is given to say it reaches another model, and where it comes from. A
183
- * project function spelled the same way is somebody else's, so the
184
- * import is what settles a match.
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 comes from. SQLAlchemy exports
206
- * `text` from `sqlalchemy`. A local function of the same name is
207
- * somebody else's, so the import is what settles a match.
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
- * imported; this matches a method on a value whose class came out of
221
- * the library, which is how a client built in one module and called in
222
- * another still reads.
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 here is the library's own: the module, the class names,
225
- * the method names, and where each method takes what it is given.
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 say which table rather than writing SQL, and what each does to it. */
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 hand back one of another library class, so a chain off one reaches its calls. */
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 down to the statement when it travels inside a dictionary
256
- * rather than as the argument itself, the `["query", "query"]` of
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 says which table it works on, and what it does to it. */
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 hands back an object of another of the library's classes. */
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
- * A class whose `__enter__` gives back the object it was called on, so
280
- * `with X() as y` makes y the `X()` the block opened. Python
281
- * lets `__enter__` return anything, so only the library that wrote the
282
- * class can say this, and a pack declares it for its own classes alone.
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 is a client of the boundary that call
293
- * states, and gets a unit bound to its method and path.
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 caller that
320
- * tests one of them is saying which statuses it handles, and the
321
- * checker compares that against what the other side produces.
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`. */
@@ -329,63 +415,108 @@ interface PyClientResponse {
329
415
  body?: string[];
330
416
  /** Whether a refused request comes back as a response or raises where it was made. */
331
417
  failureDelivery?: "response" | "exception";
418
+ /** Whether the client follows a redirect itself or hands the 3xx back. */
419
+ redirectDelivery?: "followed" | "response";
420
+ /**
421
+ * The keyword argument one call passes to change `redirectDelivery`
422
+ * for itself: `allow_redirects` for requests, `follow_redirects` for
423
+ * httpx.
424
+ */
425
+ redirectOption?: RedirectOption;
426
+ }
427
+ /** An object a module exports that a route reads the request off. */
428
+ interface RequestObject {
429
+ module: string;
430
+ name: string;
332
431
  }
333
432
  /** Conventions both kinds of route share. Each one describes what the library does, never a project's choice. */
334
433
  interface RouteConventions {
335
- /** How the library spells a path parameter. The README lists the syntaxes we know how to read. */
434
+ /** How the library writes a path parameter. DESIGN.md lists the syntaxes the adapter reads. */
336
435
  pathParamSyntax?: string;
337
436
  /** Set it only when the library itself binds an annotated local class to the request body. */
338
437
  annotatedClassIsRequestBody?: boolean;
339
438
  /**
340
- * Callables the library uses to inject a parameter rather than read it
341
- * off the request. FastAPI's `Depends` and `Security` are these: the
342
- * server supplies the value and the client sends nothing, so a
343
- * parameter defaulted to one of them is no part of the request however
344
- * its annotation reads.
439
+ * Callables the library uses to inject a parameter instead of reading it
440
+ * off the request, such as FastAPI's `Depends` and `Security`. The server
441
+ * supplies the value and the client sends nothing, so a parameter
442
+ * defaulted to one of them is not part of the request, whatever its
443
+ * annotation says.
345
444
  */
346
445
  injectedParameterCallees?: string[];
347
446
  /**
348
- * What the library serves when a composed path ends up with repeated
349
- * slashes in it. Werkzeug serves the merged path and redirects
350
- * the written one, so "merged" is what Flask needs; "kept" is the
351
- * default and is what Starlette does.
447
+ * Classes the library supplies a parameter for when the parameter is
448
+ * annotated with one, by dotted path, such as FastAPI's
449
+ * `fastapi.Request` and `fastapi.BackgroundTasks`. The client sends
450
+ * nothing for such a parameter either.
451
+ */
452
+ injectedParameterTypes?: string[];
453
+ /**
454
+ * What the library serves when a composed path has repeated slashes.
455
+ * Werkzeug serves the merged path and redirects the written one, so Flask
456
+ * needs "merged". The default, "kept", matches Starlette.
352
457
  */
353
458
  pathRepeatedSlashes?: PathRepeatedSlashes;
354
- /** The status the library returns for a declared response when the route does not give one. Library-defined. */
459
+ /** The status the library returns for a declared response when the route does not set one. */
355
460
  defaultStatusCode?: number;
356
461
  /**
357
462
  * 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 applies whatever the body returns, and a route
360
- * that sets its own status would be reported at the default.
463
+ * returns, as Flask does with `return body, 201`. Without it, every return
464
+ * is reported at the library's default status, including a route that
465
+ * sets its own.
361
466
  */
362
467
  statusFromReturnedTuple?: boolean;
363
468
  /**
364
469
  * The library's own callables that end the request with a status, such
365
- * as FastAPI's `HTTPException` and Flask's `abort`. Declaring them is
366
- * also what makes a `raise` in a route body an outcome of its own: a
367
- * raise the list does not cover comes out as a throw with no status,
368
- * and one it does cover comes out as the response the library sends.
470
+ * as FastAPI's `HTTPException` and Flask's `abort`. A `raise` of one in a
471
+ * route body becomes the response the library sends. A raise of anything
472
+ * the list does not cover becomes a throw with no status.
369
473
  */
370
474
  responseStatusCalls?: PyStatusCall[];
371
475
  /**
372
- * The library's own classes whose instance, when a body returns it, is
373
- * the response the library sends, and where each takes the status:
374
- * Starlette's `JSONResponse(status_code=...)`. A return of one says its
375
- * own status; a return of anything else keeps the declared one.
476
+ * The library's own classes that are sent as the response when a body
477
+ * returns an instance, and where each takes the status, as in Starlette's
478
+ * `JSONResponse(status_code=...)`. Returning one gives that status, and
479
+ * returning anything else keeps the declared one.
376
480
  */
377
481
  responseConstructors?: PyStatusCall[];
378
482
  /** Unset means the library has no router mounting, and a route's decorator path is used as written. */
379
483
  routerComposition?: RouterComposition;
380
- /** The ways the library runs a project's own function around a route. The README lists what each one covers. */
484
+ /**
485
+ * The callables that say which part of the request a parameter is read
486
+ * from, by the callee's name, written as the default or inside
487
+ * `Annotated[...]`: FastAPI's `x_tenant_id: str = Header()` is a header.
488
+ */
489
+ parameterSources?: Record<string, ParameterSource>;
490
+ /** The keyword those callables take a field's name under when it differs from the parameter's, FastAPI's `alias`. */
491
+ parameterAliasKeyword?: string;
492
+ /** The ways the library runs a project's own function around a route. DESIGN.md lists what each one covers. */
381
493
  wrappers?: PyWrapperForm[];
494
+ /**
495
+ * The status the library itself responds with, before the handler runs,
496
+ * when a request input the route declares does not validate. FastAPI
497
+ * sends 422. Unset means the library checks nothing on its own.
498
+ */
499
+ validationFailureStatus?: number;
500
+ /**
501
+ * The number each of the library's status constants is, keyed by the
502
+ * module and name it is imported as (`fastapi.status.HTTP_404_NOT_FOUND`),
503
+ * for a project read without the library installed.
504
+ */
505
+ statusCodeConstants?: Record<string, number>;
506
+ }
507
+ /** The part of the request a parameter declared with one callable is read from. */
508
+ interface ParameterSource {
509
+ /** The role the parameter gets, `headers` for FastAPI's `Header`. */
510
+ role: string;
511
+ /** What the library writes in place of `_` in the field's name, `-` for a FastAPI header. */
512
+ underscoresAs?: string;
382
513
  }
383
514
  type PyWrapperForm = PyDependencyForm | PyDecoratedWrapperForm;
384
515
  /** Where a wrapper is registered, and which routes the registration reaches. */
385
516
  interface PyWrapperRegistrar {
386
517
  /** The constructor of the object the registration is written on, as the library exports it: `FastAPI`, `APIRouter`. */
387
518
  constructorName: string;
388
- /** 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. */
519
+ /** 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
520
  importModule?: string[];
390
521
  /**
391
522
  * `everyRoute` for the app, whose registration reaches every route of
@@ -395,13 +526,12 @@ interface PyWrapperRegistrar {
395
526
  covers: "everyRoute" | "ownRoutes";
396
527
  }
397
528
  /**
398
- * The library calls a project function before the handler, given as an
399
- * argument to one of its own callables: FastAPI's `Depends(get_user)`.
400
- * One may be written as a parameter default or inside `Annotated[...]`
401
- * on the route, or in a list under `keyword` on the route decorator or
402
- * on one of the registrars. The function runs before the handler and
403
- * ends the request by raising, so it is a wrapper whose every return
404
- * hands on.
529
+ * A project function the library calls before the handler, passed to one
530
+ * of the library's callables, as in FastAPI's `Depends(get_user)`. It can
531
+ * be written as a parameter default, inside `Annotated[...]` on the route,
532
+ * or in a list under `keyword` on the route decorator or a registrar. It
533
+ * ends a request only by raising, so every return passes control on to
534
+ * the route.
405
535
  */
406
536
  interface PyDependencyForm {
407
537
  type: "dependency";
@@ -412,11 +542,10 @@ interface PyDependencyForm {
412
542
  registrars: PyWrapperRegistrar[];
413
543
  }
414
544
  /**
415
- * A project function decorated with a method on the app or a router:
416
- * `@app.middleware("http")`, `@app.exception_handler(ValueError)`,
417
- * `@app.before_request`. What the decorated function's returns mean
418
- * depends on which of the three fields below is set; with none set,
419
- * every return hands the request on.
545
+ * A project function decorated with a method on the app or a router, such
546
+ * as `@app.middleware("http")` or `@app.before_request`. Which of the three
547
+ * optional fields below is set decides what the function's returns mean.
548
+ * With none set, every return passes the request on.
420
549
  */
421
550
  interface PyDecoratedWrapperForm {
422
551
  type: "decoratedWrapper";
@@ -430,17 +559,17 @@ interface PyDecoratedWrapperForm {
430
559
  */
431
560
  continuationParam?: number;
432
561
  /**
433
- * Set when a value returned ends the request and only a bare return
434
- * hands on. Flask's `before_request` works this way.
562
+ * Set when returning a value ends the request and only a bare return
563
+ * passes it on. Flask's `before_request` works this way.
435
564
  */
436
565
  returnedValueResponds?: boolean;
437
- /** The position the library hands the raised exception at. Set on an error handler, which runs only when the handler raised. */
566
+ /** The parameter position the raised exception is passed at. Set on an exception handler, which runs only when the route raised. */
438
567
  throwParam?: number;
439
568
  }
440
569
  /**
441
- * One callable that ends the request, and where it takes the status.
442
- * A call may take it either way, so a pattern may state both, and the
443
- * keyword wins where an argument is written both ways.
570
+ * One callable that ends the request, and where it takes the status. A
571
+ * call can pass the status by keyword or by position, so a pattern can
572
+ * declare both. When a call writes both, the keyword is used.
444
573
  */
445
574
  interface PyStatusCall {
446
575
  /** The callee as the file imports it, module and name together, `fastapi.HTTPException`. */
@@ -453,9 +582,9 @@ interface PyStatusCall {
453
582
  defaultStatusCode?: number;
454
583
  }
455
584
  /**
456
- * A class has a decorator whose first string-literal argument is the route path,
457
- * and each method in its body named after an HTTP verb becomes its own unit,
458
- * with the verb taken from the method name.
585
+ * A route written as a class whose decorator takes the route path as its
586
+ * first argument. Each method in the class named after an HTTP verb becomes
587
+ * its own unit, with the verb taken from the method name.
459
588
  */
460
589
  interface DecoratedClassRoute extends RouteConventions {
461
590
  type: "decoratedClassRoute";
@@ -467,10 +596,10 @@ interface DecoratedClassRoute extends RouteConventions {
467
596
  verbMethodNames: Record<string, string>;
468
597
  }
469
598
  /**
470
- * A function has a decorator whose attribute name is the HTTP verb and whose
471
- * first string-literal argument is the route path, the `@app.get(path)`
472
- * convention. The object it hangs on may be imported, or built one hop away by
473
- * a call to something imported.
599
+ * A route written as a function decorated with `@app.get(path)`: the
600
+ * decorator's attribute name is the HTTP verb and its first argument is the
601
+ * route path. The object the decorator is called on can be imported, or
602
+ * built by a call to something imported.
474
603
  */
475
604
  interface DecoratedFunctionRoute extends RouteConventions {
476
605
  type: "decoratedFunctionRoute";
@@ -480,14 +609,21 @@ interface DecoratedFunctionRoute extends RouteConventions {
480
609
  /** Unset leaves the return annotation as the only source for the response shape. */
481
610
  responseModelKeyword?: string;
482
611
  statusCodeKeyword?: string;
612
+ /**
613
+ * The keyword whose dictionary lists statuses a route may respond
614
+ * with, FastAPI's `responses`, on the route decorator, its router, a
615
+ * mount call above it or the app. Its keys become the route's declared
616
+ * statuses.
617
+ */
618
+ responsesKeyword?: string;
483
619
  }
484
620
  /**
485
- * What a library calls the pieces of router mounting, so a route's served path
486
- * can be built from the literal prefixes written along the way: the router
487
- * constructor's own, the one at the call that mounts it, and, where the pack
488
- * says so, the prefix on the object the mount is called on. What each spelling
489
- * of a prefix means, and what makes a composition abstain, is the grid in the
490
- * adapter's README.
621
+ * The names a library gives the parts of router mounting, so a route's
622
+ * served path can be built from the prefixes written along the way: the
623
+ * router constructor's, the one on the call that mounts it, and, where the
624
+ * pack declares it, the one on the object the mount is called on. DESIGN.md
625
+ * has the tables for what each way of writing a prefix means and when a
626
+ * route abstains.
491
627
  */
492
628
  interface RouterComposition {
493
629
  /** Constructor whose call builds a mountable router, FastAPI's `APIRouter`. */
@@ -495,37 +631,36 @@ interface RouterComposition {
495
631
  /** Method that mounts a router onto the app, FastAPI's `include_router`. */
496
632
  includeMethodName: string;
497
633
  /**
498
- * What the mount method calls its router parameter, FastAPI's `router`.
499
- * A call that passes the router by keyword rather than by position is
500
- * read through this. Unset reads the first argument only.
634
+ * The mount method's name for its router parameter, FastAPI's `router`,
635
+ * used to read a call that passes the router by keyword. Unset reads the
636
+ * first positional argument only.
501
637
  */
502
638
  routerKeyword?: string;
503
- /** One keyword serves the constructor and the mount alike. A library that spells them apart needs two fields here. */
639
+ /** The prefix keyword, the same at the constructor and the mount. A library that uses two different keywords would need two fields here. */
504
640
  prefixKeyword: string;
505
- /** Default "prefixes". */
641
+ /** Whether a prefix written at the mount goes in front of the constructor's ("prefixes") or replaces it ("replaces"). Default "prefixes". */
506
642
  mountPrefixEffect?: MountPrefixEffect;
507
- /** Set it when the library works out a path for a router that gives no prefix. We cannot work that path out, so such routes abstain. */
643
+ /** 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
644
  constructorPrefixRequired?: boolean;
509
- /** Default "unreadable". The same setting covers the constructor and the mount. */
645
+ /** 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
646
  noValuePrefix?: NoValuePrefix;
511
- /** Default "kept". */
647
+ /** Whether the library trims trailing slashes off a constructor's prefix. Default "kept". */
512
648
  constructorPrefixTrailingSlash?: PrefixTrailingSlash;
513
649
  /**
514
- * Where the object the mount is called on states a prefix of its
515
- * own, in front of everything the constructor and the mount state.
516
- * Unset means it states none, which is FastAPI's behavior: an app
517
- * serves a mounted router exactly where the two prefixes put it.
650
+ * Where the object the mount is called on states a prefix of its own,
651
+ * in front of everything the constructor and the mount state. Unset
652
+ * means it states none. That matches FastAPI, where an app serves a
653
+ * mounted router exactly where the two prefixes put it.
518
654
  */
519
655
  mountObjectPrefix?: MountObjectPrefix;
520
656
  }
521
657
  /**
522
- * The prefix the object a mount is called on states, and where it is
523
- * written. flask-restx needs both halves: `Api(prefix=...)` states one
524
- * on the object itself, and the Flask blueprint the `Api` was built
525
- * from states another with `Blueprint(name, __name__,
526
- * url_prefix=...)`. The library serves a route under the blueprint's
527
- * prefix, then the `Api`'s, then whatever the namespace and the route
528
- * say.
658
+ * Where the object a mount is called on states its prefix. flask-restx
659
+ * needs both fields: `Api(prefix=...)` states one on the object itself,
660
+ * and the Flask blueprint the `Api` was built from states another with
661
+ * `Blueprint(name, __name__, url_prefix=...)`. A route is served under
662
+ * the blueprint's prefix, then the `Api`'s, then the namespace's path and
663
+ * the route's own.
529
664
  */
530
665
  interface MountObjectPrefix {
531
666
  /** Keyword stating a prefix on the mount object's own construction (flask-restx's `Api(prefix=...)`). */
@@ -534,11 +669,10 @@ interface MountObjectPrefix {
534
669
  carrier?: MountObjectCarrier;
535
670
  }
536
671
  /**
537
- * An object handed to the mount object's constructor, one hop further
538
- * out, with a prefix of its own (the Flask blueprint behind an
539
- * `Api`). Naming it here is what lets the adapter tell that object
540
- * apart from the plain app that appears in the same argument position and
541
- * has no prefix at all.
672
+ * An object passed to the mount object's constructor that has a prefix of
673
+ * its own, such as the Flask blueprint an `Api` is built from. Declaring
674
+ * its constructor lets the adapter tell it apart from the plain app, which
675
+ * is passed in the same argument position and has no prefix.
542
676
  */
543
677
  interface MountObjectCarrier {
544
678
  /** Modules the carrier's constructor is imported from (Flask's `flask`). */
@@ -547,32 +681,28 @@ interface MountObjectCarrier {
547
681
  constructorName: string;
548
682
  /** Position of the carrier among the mount object's constructor arguments. */
549
683
  argumentIndex: number;
550
- /** Keyword stating the carrier's prefix, at its construction and at its registration alike (Flask's `url_prefix`). */
684
+ /** The keyword for the carrier's prefix, at its construction and its registration alike (Flask's `url_prefix`). */
551
685
  prefixKeyword: string;
552
686
  /**
553
- * Method handing the carrier to an already-built mount object
554
- * (flask-restx's `init_app`, the application-factory spelling of
555
- * `Api(blueprint)`). Unset means the constructor argument is the
556
- * only way in.
687
+ * Method that passes the carrier to a mount object already built, as
688
+ * flask-restx's `init_app` does in an application factory in place of
689
+ * `Api(blueprint)`. Unset means the constructor argument is the only
690
+ * way to pass one.
557
691
  */
558
692
  handoffMethodName?: string;
559
693
  /**
560
- * Method registering the carrier somewhere else (Flask's
561
- * `register_blueprint`). The adapter reads it only to abstain: a
562
- * registration restating the prefix, putting the carrier inside
563
- * another carrier, or happening twice moves the served path
564
- * somewhere the carrier's own construction no longer says.
694
+ * Method that registers the carrier somewhere else (Flask's
695
+ * `register_blueprint`). The adapter reads it only to decide when to
696
+ * abstain. A registration that restates the prefix, registers the
697
+ * carrier inside another carrier, or registers it twice serves the
698
+ * routes somewhere other than where the carrier's construction says.
565
699
  */
566
700
  registerMethodName: string;
567
701
  }
568
702
  type MountPrefixEffect = "prefixes" | "replaces";
569
703
  type PrefixTrailingSlash = "kept" | "trimmed";
570
704
  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
- */
705
+ /** Werkzeug merges repeated slashes, so Flask needs "merged". Starlette does not, so FastAPI keeps "kept". */
576
706
  type PathRepeatedSlashes = "kept" | "merged";
577
707
 
578
708
  /**
@@ -581,11 +711,11 @@ type PathRepeatedSlashes = "kept" | "merged";
581
711
  * whether that was an import, an assignment, a def, a parameter, or a `global`
582
712
  * or `nonlocal` redirect to another scope.
583
713
  *
584
- * It never has to be complete, because "I could not resolve this name" is a
585
- * legal answer everywhere a name is read. What it has to avoid is being wrong.
586
- * So it only binds names written directly in a body's own statement list. A
587
- * definition or import nested inside an `if`, `try`, or `with` block is not
588
- * found, and a read of it comes back unresolved rather than as a guess.
714
+ * Every reader of a name accepts an unresolved answer, so the binder can
715
+ * leave names out, and a wrong binding is what it avoids. It binds names
716
+ * written directly in a body's statement list, plus the statements of a
717
+ * `with` body. A definition or import nested inside an `if` or `try` is
718
+ * left out, and a read of it comes back unresolved instead of guessed.
589
719
  */
590
720
 
591
721
  type ScopeKind = "module" | "class" | "function";
@@ -625,7 +755,7 @@ type Binding = {
625
755
  } | {
626
756
  kind: "parameter";
627
757
  }
628
- /** The right-hand side, when there is one, so we can trace a decorator's base object one hop back to whatever constructed it. */
758
+ /** The right-hand side, when there is one, so a reader can follow a name one hop back to what constructed it. */
629
759
  | {
630
760
  kind: "assignment";
631
761
  value: Node | null;
@@ -642,7 +772,7 @@ interface ModuleBinding {
642
772
  moduleScope: Scope;
643
773
  /** The scope a class_definition, a function_definition, or the module node opens, keyed by that node's id. */
644
774
  scopeFor: Map<number, Scope>;
645
- /** The modules a `from X import *` pulls in. We cannot list what a wildcard brings in, so nothing expands them here. */
775
+ /** The modules a `from X import *` pulls in. The binder cannot list what a wildcard brings in, so nothing expands them here. */
646
776
  openImports: string[];
647
777
  }
648
778
  declare function bindModule(root: Node): ModuleBinding;
@@ -654,21 +784,35 @@ declare function bindModule(root: Node): ModuleBinding;
654
784
  */
655
785
  declare function resolveName(scope: Scope, name: string): Binding | null;
656
786
 
787
+ /**
788
+ * Works out the prefix a route inherits from the routers it is mounted
789
+ * through, ahead of discovery. With `router = APIRouter(prefix="/items")`
790
+ * and `app.include_router(router, prefix="/api")`, a route on `router` is
791
+ * served under `/api/items`, a path its own file never states. A chain of
792
+ * routers mounted on routers adds a pair of prefixes per hop.
793
+ *
794
+ * Every prefix goes through `readPrefixKeyword` and comes back stated,
795
+ * unstated or unreadable. What each spelling means differs by library, so
796
+ * the pack declares it, and DESIGN.md has the tables. When a path cannot be
797
+ * settled, the route abstains: discovery keeps its name and gives it no
798
+ * path, so it pairs with nothing instead of with a guessed path.
799
+ */
800
+
657
801
  /** One file, already parsed and bound. `buildRouterIndex` takes a project as a list of these. */
658
802
  interface BoundPythonFile {
659
- /** The absolute path, which is what module resolution joins on. */
803
+ /** The absolute path. Module resolution matches files on it. */
660
804
  file: string;
661
- /** The path a gap refers to this file by, which a reader has to be able to open. */
805
+ /** The path a gap reason prints for this file, so a reader can open it. */
662
806
  displayPath: string;
663
807
  root: Node;
664
808
  module: ModuleBinding;
665
809
  }
666
810
  /**
667
- * What the object a decorator hangs on turns out to be. `notRouter` covers the
668
- * app itself and anything the index never saw constructed, so the decorator's
669
- * own path stands as written. `composed` gives the prefix to put in front of
670
- * that path. An `abstain` reason is written to follow "the router this route is
671
- * declared on ...".
811
+ * What the object a route decorator is called on turns out to be. `notRouter`
812
+ * covers the app itself and anything the index never saw constructed, and the
813
+ * decorator's own path is used as written. `composed` gives the prefix to put
814
+ * in front of that path. An `abstain` reason is worded to follow "the router
815
+ * this route is declared on ...".
672
816
  */
673
817
  type RoutePrefixResolution = {
674
818
  kind: "notRouter";
@@ -695,6 +839,19 @@ interface RouterIndex {
695
839
  * built it, and every construction is already keyed by its call.
696
840
  */
697
841
  resolveConstruction(pattern: PythonDiscoveryPattern, module: ModuleBinding, constructorName: string, constructionKey: string): RoutePrefixResolution;
842
+ /**
843
+ * The mounts above the router built by this call, innermost first, up
844
+ * to the one on the app. Null unless every router on the way is mounted
845
+ * exactly once by a mount this reading followed, since a route reached
846
+ * two ways runs only the wrappers of the way a request took.
847
+ */
848
+ mountsAbove(pattern: PythonDiscoveryPattern, constructionKey: string): MountHop[] | null;
849
+ }
850
+ /** One mount above a router: the call that mounts it, and the router it mounts it on, null for the app. */
851
+ interface MountHop {
852
+ call: Node;
853
+ file: string;
854
+ parentKey: string | null;
698
855
  }
699
856
  interface RouterIndexOptions extends ModuleResolverOptions {
700
857
  /** The project's facts, so a loop over a call can be settled by the rules. */
@@ -716,12 +873,14 @@ declare function buildRouterIndex(files: BoundPythonFile[], packs: PythonPack[],
716
873
  interface ImportedDefinition {
717
874
  /** The class_definition, or the value an assignment gave the name. */
718
875
  node: Node;
876
+ /** The file the definition is written in. */
877
+ file: string;
719
878
  /** The module scope of the file the definition is written in. */
720
879
  moduleScope: Scope;
721
880
  /** The scopes of that file, keyed by the node that opens each one. */
722
881
  scopeFor: Map<number, Scope>;
723
882
  }
724
- /** `scope` is where the name is read, which is what keys the import in the facts. */
883
+ /** `scope` is where the name is read. The facts key an import on the file that reads it. */
725
884
  type ImportedDefinitionLookup = (scope: Scope, name: string) => ImportedDefinition | null;
726
885
 
727
886
  /**
@@ -733,11 +892,11 @@ type ImportedDefinitionLookup = (scope: Scope, name: string) => ImportedDefiniti
733
892
  */
734
893
 
735
894
  /**
736
- * `definitions` stores each converted class shape once, however many annotations
737
- * mention it. `scopeMaps` is here so a name written inside a referenced class's
738
- * body resolves too, not only one written at the annotation's use site; a class
739
- * read from another file brings that file's scopes along. `importedDefinition`
740
- * is absent when a caller reads one file on its own, and an imported name is
895
+ * `definitions` stores each converted class shape once, however many
896
+ * annotations mention it. `scopeMaps` lets a name written inside a
897
+ * referenced class's body resolve as well as one at the annotation, and a
898
+ * class read from another file adds that file's scopes. `importedDefinition`
899
+ * is null when a caller reads one file on its own, and an imported name is
741
900
  * then a ref by name and nothing more.
742
901
  */
743
902
  interface AnnotationContext {
@@ -747,7 +906,7 @@ interface AnnotationContext {
747
906
  /** The alias values being expanded, so `A = B` and `B = A` end rather than recurse. */
748
907
  expanding: Set<number>;
749
908
  }
750
- /** `scope` is where the annotation is written, which is how we tell a project-local class from an external name. */
909
+ /** `scope` is where the annotation is written. Resolving the name there tells a project class apart from an external name. */
751
910
  declare function annotationToShape(typeNode: Node, scope: Scope, ctx: AnnotationContext): TypeShape;
752
911
  /** Exported so a decorator keyword that gives a class name reads the same way as that name written in annotation position. */
753
912
  declare function shapeFromName(name: string, scope: Scope, ctx: AnnotationContext): TypeShape;
@@ -757,7 +916,20 @@ interface Range {
757
916
  end: number;
758
917
  }
759
918
 
760
- /** An argument as written, plus its node so a reader can evaluate what it comes down to. */
919
+ /**
920
+ * Works out which library a decorator comes from, for discovery.
921
+ *
922
+ * `@app.route("/x")` is two steps: the call that builds the decorator, and
923
+ * applying the result to the function. Discovery needs only the first, so
924
+ * `classifyDecorator` reads which name the callee resolved to and which
925
+ * module that name came from, and stops there.
926
+ *
927
+ * A decorator re-exported through a project wrapper is handled the same
928
+ * way the TypeScript packs handle one: the name is classified as an import
929
+ * of some module, and the pack lists every module it accepts.
930
+ */
931
+
932
+ /** An argument as written, plus its node so a reader can evaluate it. */
761
933
  type DecoratorArg = DecoratorArgShape & {
762
934
  readonly node: Node;
763
935
  };
@@ -790,15 +962,15 @@ type DecoratorArgShape = {
790
962
  kind: "other";
791
963
  };
792
964
  interface DecoratorClassification {
793
- /** The name as its source module exports it, not the local alias or attribute path it was written under. */
965
+ /** The name as its source module exports it, whatever alias or attribute path the file wrote it under. */
794
966
  importedName: string | null;
795
967
  /** The dotted module the name was imported from. It is null whenever `importedName` is null. */
796
968
  module: string | null;
797
- /** The local variable an attribute decorator hangs on, `app` in `@app.get(...)`. */
969
+ /** The local variable an attribute decorator is called on, `app` in `@app.get(...)`. */
798
970
  objectName: string | null;
799
971
  args: DecoratorArg[];
800
972
  keywordArgs: Record<string, DecoratorArg>;
801
- /** Where the decorator is written. Anything we read out of its arguments uses this as its provenance. */
973
+ /** Where the decorator is written. Anything read out of its arguments reports this as its location. */
802
974
  range: Range;
803
975
  /**
804
976
  * The module the decorator's object lives in, when the decorator was read
@@ -807,9 +979,9 @@ interface DecoratorClassification {
807
979
  */
808
980
  objectModule?: ModuleBinding;
809
981
  /**
810
- * The call the rules say built the object this decorator hangs on. A
811
- * router the index never saw under a name is looked up by this call
812
- * instead, and a decorator on `self.router` has no name to look up.
982
+ * The call the rules say built the object this decorator is called on.
983
+ * The router index looks a router up by this call when it never saw the
984
+ * router under a name, as with a decorator on `self.router`.
813
985
  */
814
986
  subjectConstruction?: {
815
987
  key: string;
@@ -818,6 +990,13 @@ interface DecoratorClassification {
818
990
  }
819
991
  declare function classifyDecorator(decoratorNode: Node, module: ModuleBinding, facts?: Database): DecoratorClassification;
820
992
 
993
+ /**
994
+ * Finds the calls in a body that talk to the database, and what each one
995
+ * does. A pack lists its library's query types, and a call chain matches
996
+ * when the method behind it declares that it returns one. DESIGN.md says
997
+ * why the match goes by return type.
998
+ */
999
+
821
1000
  /**
822
1001
  * What discovery uses for one file. `factsPath` is the path the facts were
823
1002
  * keyed under, which is the absolute one, while a summary shows the short one.
@@ -826,7 +1005,6 @@ interface StorageLookup {
826
1005
  readonly facts: Database;
827
1006
  readonly factsPath: string;
828
1007
  readonly patterns: readonly StoragePattern[];
829
- readonly definitionAt: (key: string) => Node | undefined;
830
1008
  readonly couldMatch: ReadonlySet<string>;
831
1009
  /** What a pack says about statements a project writes as SQL itself. */
832
1010
  readonly rawSql?: readonly RawSqlPattern[];
@@ -835,15 +1013,16 @@ interface StorageLookup {
835
1013
  }
836
1014
 
837
1015
  /**
838
- * What a call's callee is: a function in this run the walk can step
839
- * into, or a reason it cannot.
1016
+ * What a call's callee is: a function in this run that the walk can
1017
+ * follow, or the reason it cannot.
840
1018
  *
841
- * The rules in @suss/resolution decide it. Every language feature that
842
- * moves a value is a hop they already state, so nothing here reads a
843
- * name, an alias, an attribute, or an instance for itself.
1019
+ * The rules in `@suss/resolution` settle the callee. They already follow
1020
+ * a value through names, aliases, attributes and instances, so nothing
1021
+ * here reads any of those itself.
844
1022
  *
845
- * What is left is about files rather than values: a module before the
846
- * dot, and a name only a wildcard import could have brought in.
1023
+ * This module handles the two cases that depend on files: a module
1024
+ * written before the dot, and a name that only a wildcard import could
1025
+ * have brought in.
847
1026
  */
848
1027
 
849
1028
  /** A function in this run, and the export path its summary gets. */
@@ -859,16 +1038,16 @@ interface ReachedFunction {
859
1038
  /**
860
1039
  * The project functions a library runs around a route, read from where
861
1040
  * 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 one
864
- * becomes a unit of its own, and every route it reaches lists it, so the
865
- * extractor can fold its outcomes into the route's. The README says which
866
- * registrations are read and which are not.
1041
+ * a dependency list on the app or a router, or a function decorated with
1042
+ * `@app.middleware(...)` or `@app.exception_handler(...)`. Each becomes a
1043
+ * unit of its own, and every route it reaches lists it, so the extractor
1044
+ * can fold its outcomes into the route's. DESIGN.md, under "What runs
1045
+ * around a route", lists which registrations are read.
867
1046
  *
868
1047
  * A registration on the app reaches every route of the pack in the run,
869
- * since an app is one per run for every pack that has one. A registration
870
- * on a router reaches the routes decorated on that same router object,
871
- * and nothing mounted onto it.
1048
+ * because the run treats a pack's app as the only one. A registration on
1049
+ * a router reaches its own routes, and with a mount call's own list it
1050
+ * reaches those below it when each router on the way is mounted once.
872
1051
  */
873
1052
 
874
1053
  interface WrapperIndexOptions {
@@ -878,6 +1057,8 @@ interface WrapperIndexOptions {
878
1057
  /** The function each function key was read from. */
879
1058
  definitions: ReadonlyMap<string, Node>;
880
1059
  storageFor: (file: BoundPythonFile) => StorageLookup | undefined;
1060
+ /** Where each router is mounted, so a dependency registered on the way reaches the routes below it. */
1061
+ routers?: RouterIndex;
881
1062
  }
882
1063
  /** The route asking which wrappers reach it: the decorator it was found by, and where that decorator was read. */
883
1064
  interface RouteWrapperQuery {
@@ -894,6 +1075,7 @@ interface Registered {
894
1075
  reference: WrapperReference;
895
1076
  form: PyWrapperForm;
896
1077
  }
1078
+ /** A wrapper form with the pattern and pack that declare it. */
897
1079
  interface FormOf {
898
1080
  pack: PythonPack;
899
1081
  pattern: PythonDiscoveryPattern;
@@ -908,8 +1090,20 @@ declare class PythonWrapperIndex {
908
1090
  /** By construction key: what a router or a blueprint registered. */
909
1091
  private readonly ownRoutes;
910
1092
  private readonly unitsByKey;
1093
+ /** By pack name: the app constructions its routes run under. */
1094
+ private readonly appCalls;
1095
+ /** By wrapper reference: the parameters a dependency function declares, and its file. */
1096
+ private readonly dependencyParameterTable;
911
1097
  private readonly unitsByFile;
1098
+ /** Told about every registration, so a cache can repeat the ones a file's discovery made. */
1099
+ onRegistered: ((target: ReachedFunction, declared: FormOf) => void) | null;
912
1100
  constructor(options: WrapperIndexOptions, filesByPath: ReadonlyMap<string, BoundPythonFile>);
1101
+ /**
1102
+ * The same lookup `wrappersFor` noted under `id`, made again over this
1103
+ * index, or null when `id` is not one of these.
1104
+ */
1105
+ lookAgain(id: string): string | null;
1106
+ private noted;
913
1107
  /** The project function a module-level name in this file refers to, or null for anything else. */
914
1108
  functionCalled(file: string, name: string): ReachedFunction | null;
915
1109
  register(covers: {
@@ -921,8 +1115,35 @@ declare class PythonWrapperIndex {
921
1115
  }, target: ReachedFunction, declared: FormOf): void;
922
1116
  /** The unit for a function, built once however many registrations point at it. */
923
1117
  registered(target: ReachedFunction, declared: FormOf): Registered;
1118
+ /**
1119
+ * The parameters the dependencies among these wrappers declare, and
1120
+ * whether one is declared through a parameter source such as `Query()`.
1121
+ * The library fills those from the request and validates them, the same
1122
+ * as parameters the route declares.
1123
+ */
1124
+ dependencyParameters(references: readonly WrapperReference[]): {
1125
+ names: ReadonlySet<string>;
1126
+ declaresSource: boolean;
1127
+ };
924
1128
  wrappersFor(query: RouteWrapperQuery): WrapperReference[];
1129
+ /**
1130
+ * What the mounts above the route's router register on the way down:
1131
+ * the dependencies an `include_router` call lists, and those of each
1132
+ * router it is mounted onto, outermost first, the order they run in.
1133
+ */
1134
+ private mountedAbove;
1135
+ private registeredAbove;
925
1136
  private ownRoutesOf;
1137
+ /** The construction call of the router a route is decorated on, when a registrar reads it. */
1138
+ private ownRouterOf;
1139
+ /** Records the app a pack's routes run under, for the keywords written on its construction. */
1140
+ recordApp(pack: string, call: Node, file: string): void;
1141
+ /**
1142
+ * Every call that registers something for a route on the way to it:
1143
+ * the app's construction, the mount calls and the routers they mount
1144
+ * onto, and the route's own router. A caller reads a keyword off each.
1145
+ */
1146
+ callsAbove(query: RouteWrapperQuery): Node[];
926
1147
  /**
927
1148
  * Where the decorator's object was built: the route's own file, the file
928
1149
  * a project wrapper function is written in, or the file an imported
@@ -934,30 +1155,29 @@ declare class PythonWrapperIndex {
934
1155
  }
935
1156
 
936
1157
  /**
937
- * Finds decorated routes and turns each one into a `RawCodeStructure`.
1158
+ * Finds decorated routes and turns each one into a `RawCodeStructure`, and
1159
+ * collects the client units for outgoing requests alongside them.
938
1160
  *
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.
945
- *
946
- * That is enough to pair a route against a caller by method and path, and it
947
- * claims nothing about behavior nobody read.
1161
+ * Every decorated definition in the file is classified, wherever it is
1162
+ * written. One a pack's pattern accepts becomes a unit per HTTP method and
1163
+ * per mount. The path is the decorator's first argument with the router
1164
+ * prefixes in front of it. The status and body shape come from decorator
1165
+ * keywords, the return annotation, and each return or raise in the body.
1166
+ * A route that cannot be read keeps its name and gets no path, so it pairs
1167
+ * with nothing.
948
1168
  */
949
1169
 
950
1170
  interface DiscoveryOptions {
951
1171
  packs: PythonPack[];
952
1172
  /** Repo-relative or absolute path recorded on each summary's `location.file`. */
953
1173
  filePath: string;
954
- /** Without it, every route object looks like the app itself and paths stand as written. */
1174
+ /** Without it, every route object is treated as the app itself and paths are used as written. */
955
1175
  routerIndex?: RouterIndex;
956
1176
  /** Under "strict" a route whose unit cannot be built stops the run instead of abstaining. */
957
1177
  gapHandling?: ExtractorOptions["gapHandling"];
958
- /** What a pack needs to say a call talks to the database. Absent when no pack does. */
1178
+ /** What a pack needs to decide that a call talks to the database. Absent when no pack declares storage. */
959
1179
  storage?: StorageLookup | undefined;
960
- /** The file's absolute path, which module resolution wants; `filePath` may be shortened for display. */
1180
+ /** The file's absolute path, which module resolution needs. `filePath` may be shortened for display. */
961
1181
  absoluteFile?: string | undefined;
962
1182
  /** The project's facts, so the rules can say what an object no scope has a binding for was built by. */
963
1183
  facts?: Database | undefined;
@@ -968,27 +1188,33 @@ interface DiscoveryOptions {
968
1188
  }
969
1189
  declare function discoverUnits(root: Node, module: ModuleBinding, options: DiscoveryOptions): RawCodeStructure[];
970
1190
 
971
- /** Ask what these calls come down to, then derive. */
1191
+ /**
1192
+ * Runs the shared resolution rules over this project's facts. The rules in
1193
+ * `@suss/resolution` are the same ones the TypeScript adapter runs, so a
1194
+ * Python value is followed the same way a TypeScript one is.
1195
+ */
1196
+
1197
+ /** Asks the rules to resolve these keys, and derives what follows. */
972
1198
  declare function resolveCalls(db: Database, callKeys: readonly string[]): void;
973
1199
  /**
974
1200
  * The values an object contains under its own keys, in the order the source
975
1201
  * writes them. Empty when nothing said what the object contains.
976
1202
  */
977
1203
  declare function containedValues(db: Database, objectKey: string): string[];
978
- /** What a call comes down to, when the rules settled it on an object. */
1204
+ /** The object a call returns, when the rules settled it on one. */
979
1205
  declare function objectReturnedBy(db: Database, callKey: string): string | null;
980
1206
 
981
1207
  /**
982
- * values.ts: the facts @suss/resolution already joins, emitted for Python.
983
- * The relation names and shapes come from that package's own header, so a
984
- * Python value follows the same rules a TypeScript one does.
1208
+ * Emits the value facts that the rules in @suss/resolution join, read from
1209
+ * Python source. The relations are the ones that package defines, so a
1210
+ * Python value is followed by the same rules as a TypeScript one.
985
1211
  *
986
1212
  * 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
- * and a module's names take the file's key, which is what another file
989
- * imports back out. Every write to a name in one scope is collected in
990
- * source order and `valueLeftByWrites` says what the name comes down to,
991
- * so a reassigned name states one value or none rather than two.
1213
+ * the function's key, so two handlers that both write `query` stay apart.
1214
+ * A module's names take the file's key, and another file's import matches
1215
+ * on that key. Every write to a name in one scope is collected in source
1216
+ * order, and `valueLeftByWrites` decides what the name comes down to, so a
1217
+ * reassigned name gets one value or none, never two.
992
1218
  */
993
1219
 
994
1220
  /**
@@ -1015,21 +1241,27 @@ declare function nameKeyIn(filePath: string, enclosing: Node | null, name: strin
1015
1241
  declare function emitValueFacts(db: Database, filePath: string, root: Node): void;
1016
1242
 
1017
1243
  /**
1018
- * The session behind `suss ask why` on a Python project: a tree-sitter
1019
- * parse of the asked-about source, a way to point at the expression
1020
- * somebody spelled, and the witness proof of what it resolved to,
1021
- * rendered through `@suss/resolution`'s phrases.
1244
+ * The session behind `suss ask why` on a Python project. It finds the
1245
+ * expression a question points at by file, line and text, and renders the
1246
+ * witness proof of what that expression resolved to through the phrases
1247
+ * in `@suss/resolution`.
1022
1248
  *
1023
- * It parses every file under the root and emits the same value facts
1024
- * `extractPythonProject` does, keeping a location for every fact key so
1025
- * a proof's atoms can point back at source. A handle this session hands
1026
- * back pairs a tree-sitter node with the file it came from, since a
1027
- * node alone does not say which file parsed it.
1249
+ * It parses every file under the root and records the same value facts
1250
+ * and pack words `extractPythonProject` does, along with a location for
1251
+ * every fact key, so each step of a proof can point back at the source. A
1252
+ * handle pairs a tree-sitter node with its file, because a node alone
1253
+ * does not say which file it was parsed from.
1028
1254
  */
1029
1255
 
1030
1256
  interface PythonWhySessionOptions {
1031
1257
  /** The project root, which paths in every answer come out relative to. */
1032
1258
  dir: string;
1259
+ /** Directories an absolute import is resolved against. Read from `dir` when absent, the way `extractPythonProject` reads them. */
1260
+ roots?: string[];
1261
+ /** Roots the project directory does not show, such as a checked-out submodule. Added after the others. */
1262
+ additionalRoots?: string[];
1263
+ /** 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. */
1264
+ packs?: readonly PythonPack[];
1033
1265
  }
1034
1266
  /** A found node, paired with the file it was parsed from. */
1035
1267
  interface PythonValueHandle {
@@ -1054,8 +1286,8 @@ declare class PythonWhySession {
1054
1286
  findCallee(file: string, startLine: number, endLine: number, calleeText: string): PythonValueHandle | null;
1055
1287
  /**
1056
1288
  * 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
- * which the caller says in its own words.
1289
+ * to the chain and rendered. Null when the value does not resolve, and
1290
+ * the caller then writes its own message.
1059
1291
  */
1060
1292
  explain(value: PythonValueHandle, options?: {
1061
1293
  maxDepth?: number;
@@ -1063,23 +1295,18 @@ declare class PythonWhySession {
1063
1295
  private locate;
1064
1296
  private rootOf;
1065
1297
  private pathOf;
1066
- /** A file path said relative to the root, or as given when it is outside the root. */
1298
+ /** A file path relative to the root, or unchanged when it is outside the root. */
1067
1299
  private displayPath;
1068
1300
  }
1069
1301
 
1070
1302
  /**
1071
- * The name is part of the key because the range is measured in lines, two
1072
- * units can start on the same line, and `entry` is a set, so keying on the range
1073
- * alone would drop one of them.
1303
+ * The facts the module resolver adds to the shared store.
1304
+ *
1305
+ * `importsModule` records each module a file imports, as written, and
1306
+ * `importsFile` the file an import resolved to. An import the resolver
1307
+ * abstains on gets no file rather than a guessed one.
1074
1308
  */
1075
- declare function unitKey(filePath: string, range: {
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;
1309
+
1083
1310
  /**
1084
1311
  * Every import in the file, wherever it is written. Python code puts an import
1085
1312
  * inside a function to break a cycle between two modules, and a chain through
@@ -1087,17 +1314,74 @@ declare function emitEntryFact(db: Database, filePath: string, range: {
1087
1314
  */
1088
1315
  declare function emitModuleImportFacts(db: Database, filePath: string, module: ModuleBinding, resolverOptions: ModuleResolverOptions): void;
1089
1316
 
1317
+ /**
1318
+ * The directories a Python project's absolute imports resolve against:
1319
+ * the project directory, and the source directories it declares.
1320
+ *
1321
+ * A project that keeps its package under `src/` imports it as
1322
+ * `orders.routes`, because installing the project puts `src/` on
1323
+ * `sys.path`. suss cannot run the install, so it reads where
1324
+ * `pyproject.toml` tells the build backend to look, and uses `src/`
1325
+ * when that declares nothing and `src/` contains a package.
1326
+ *
1327
+ * An extra root cannot pick the wrong module, because the module
1328
+ * resolver abstains when two roots both have a file for one import.
1329
+ */
1330
+ interface UnreadManifest {
1331
+ /** The file, relative to the project root. */
1332
+ where: string;
1333
+ /** Why it could not be read, in one line. */
1334
+ reason: string;
1335
+ }
1336
+ interface PythonSourceRoots {
1337
+ /** Absolute: the project directory first, then each source directory declared or found under it. */
1338
+ roots: string[];
1339
+ /** A manifest that might have declared a source directory and could not be read. */
1340
+ unread: UnreadManifest[];
1341
+ }
1342
+ type TomlTable = Record<string, unknown>;
1343
+ type TomlFileRead = {
1344
+ kind: "parsed";
1345
+ value: unknown;
1346
+ } | {
1347
+ kind: "unreadable";
1348
+ reason: string;
1349
+ };
1350
+ declare function readTomlFile(file: string): TomlFileRead;
1351
+ declare function tableAt(value: unknown, ...keys: string[]): TomlTable | null;
1352
+ declare function pythonSourceRoots(projectRoot: string): PythonSourceRoots;
1353
+
1354
+ /**
1355
+ * The entry point for a Python run. It discovers units, assembles their
1356
+ * summaries in the shared IR, and emits the run's facts into one `Database`.
1357
+ *
1358
+ * Every file is parsed and bound first, and the value facts are emitted
1359
+ * across all of them, before discovery runs on any file, because a mount in
1360
+ * one file can refer to a router built in another. Each discovered unit
1361
+ * goes through `@suss/extractor`'s `assembleSummary`, the same assembly the
1362
+ * TypeScript adapter uses, so both languages share one gap detection. With
1363
+ * a cache, a run after an edit replays the files and functions whose
1364
+ * inputs did not change, through the records in `reuse.ts`.
1365
+ */
1366
+
1090
1367
  interface ExtractPythonOptions {
1091
1368
  /** Absolute paths of the files to parse and extract. */
1092
1369
  files: string[];
1093
1370
  packs: PythonPack[];
1094
- /** Directories an absolute import is resolved against. */
1095
- roots: string[];
1371
+ /**
1372
+ * Directories an absolute import is resolved against. When absent,
1373
+ * they are read from `projectRoot`: the directory itself and the
1374
+ * source directories its `pyproject.toml` declares, or `src/` when
1375
+ * it contains a package.
1376
+ */
1377
+ roots?: string[];
1378
+ /** Roots that cannot be found from the project directory, such as a checked-out submodule. Added after the others. */
1379
+ additionalRoots?: string[];
1096
1380
  /** When set, `location.file` on each summary is relativized against this. */
1097
1381
  workspaceRoot?: string;
1098
1382
  /** The directory a summary's id measures its file from, when that differs from `workspaceRoot`. */
1099
1383
  projectRoot?: string;
1100
- /** As well as deciding how much of what nobody could read reaches a summary, "strict" lets a route that cannot be built stop the run. */
1384
+ /** 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
1385
  gapHandling?: ExtractorOptions["gapHandling"];
1102
1386
  /** Called once with the run's per-phase wall time, for `suss extract --timing`. */
1103
1387
  onTiming?: (report: TimingReport) => void;
@@ -1107,10 +1391,31 @@ interface ExtractPythonOptions {
1107
1391
  onCacheDiagnostic?: (diagnostic: CacheDiagnostic) => void;
1108
1392
  /** Absolute. `<projectRoot>/.suss/cache` by default; `null` turns it off. */
1109
1393
  cacheDir?: string | null;
1394
+ /** The modules the project lists in `suss.json`, with absolute paths. */
1395
+ modules?: readonly DeclaredModule[];
1396
+ /** Parses an earlier run in this process left, for a file whose text is unchanged. */
1397
+ keptParses?: KeptPythonParses;
1110
1398
  }
1399
+ /** A file's tree and the scopes bound over it, as a later run can reuse them. */
1400
+ type KeptPythonParses = KeptParses<{
1401
+ root: Node;
1402
+ module: ModuleBinding;
1403
+ }>;
1404
+ /** A holder for parses between runs, which frees each tree it lets go of. */
1405
+ declare function keptPythonParses(): KeptPythonParses;
1406
+ /**
1407
+ * Parses these files into `kept` before any run needs them. A run served
1408
+ * whole from the cache parses nothing, so a process that keeps parses
1409
+ * calls this while nobody is waiting.
1410
+ */
1411
+ declare function parsePythonAhead(files: readonly string[], kept: KeptPythonParses): Promise<void>;
1111
1412
  interface ExtractPythonResult {
1112
1413
  summaries: BehavioralSummary[];
1113
1414
  facts: Database;
1415
+ /** The roots absolute imports were resolved against. */
1416
+ roots: string[];
1417
+ /** A manifest that might have declared a source directory and could not be read. */
1418
+ unreadManifests: UnreadManifest[];
1114
1419
  }
1115
1420
  /** One parsed file and the packs a run over it would load. */
1116
1421
  interface FileFactsOptions {
@@ -1133,19 +1438,19 @@ declare function extractPythonProject(options: ExtractPythonOptions): Promise<Ex
1133
1438
  declare function findPythonFiles(root: string): string[];
1134
1439
 
1135
1440
  /**
1136
- * Evidence for drafting a project-wrapper dependency stub: every
1137
- * import, anywhere in the project, of the asked module or one of its
1138
- * submodules. A Python decorator pattern matches a wrapper's import
1139
- * module exactly (see discovery.ts's `importModule.includes`), so a
1140
- * stub covers one imported module at a time rather than one package,
1141
- * and this groups by the exact module text a project writes.
1441
+ * Every import in the project of a module or one of its submodules, for
1442
+ * drafting a stub of a project's wrapper package.
1443
+ *
1444
+ * A discovery pattern matches a decorator's import module exactly, so a
1445
+ * stub covers one imported module at a time. The sites are grouped by
1446
+ * the module text as the project wrote it for that reason.
1142
1447
  *
1143
- * This walks every node rather than going through the binder in
1144
- * scope.ts, so an import inside an `if` or `try` still counts as
1145
- * evidence even though the binder would not resolve a name through it.
1448
+ * The walk visits every node instead of asking the binder, so an import
1449
+ * inside an `if` or a `try` still counts here, even though the binder
1450
+ * does not resolve a name through it.
1146
1451
  */
1147
1452
  interface PythonImportSite {
1148
- /** The name imported at this site, null for a bare `import module`, which binds the whole module rather than one name from it. */
1453
+ /** The name imported at this site. Null for a bare `import module`, which binds the whole module. */
1149
1454
  name: string | null;
1150
1455
  file: string;
1151
1456
  line: number;
@@ -1167,13 +1472,13 @@ interface PythonImportEvidenceOptions {
1167
1472
  declare function pythonImportEvidence(options: PythonImportEvidenceOptions): Promise<PythonImportEvidence[]>;
1168
1473
 
1169
1474
  /**
1170
- * version.ts: this adapter's own half of the cache key.
1475
+ * The Python adapter's part of the extraction cache key.
1171
1476
  *
1172
- * `ADAPTER_VERSION` is the hand-bumped semver. Bump it on any change
1173
- * that affects extraction output: IR shape, discovery semantics,
1174
- * terminal classification, anything that would invalidate previously
1175
- * cached summaries.
1477
+ * Bump `ADAPTER_VERSION` by hand on any change to what extraction
1478
+ * produces, such as the IR it writes, what discovery finds, or how a
1479
+ * terminal is classified. Summaries cached under the old version are
1480
+ * then read again instead of reused.
1176
1481
  */
1177
1482
  declare const ADAPTER_VERSION = "0.1.0";
1178
1483
 
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, emitEntryFact, emitModuleImportFacts, emitValueFacts, extractPythonProject, factsForFile, findPythonFiles, nameKeyIn, nodeId, objectReturnedBy, parsePython, parsePythonSync, preloadPythonGrammar, pythonImportEvidence, readKey, resolveAbsoluteModule, resolveCalls, resolveModule, resolveName, resolveRelativeModule, shapeFromName, unitKey };
1484
+ 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 };