@suss/adapter-python 0.22.1 → 0.22.3
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 +73 -16
- package/dist/index.cjs +2243 -17518
- package/dist/index.d.cts +43 -2
- package/dist/index.d.ts +43 -2
- package/dist/index.js +2269 -17522
- package/package.json +8 -6
package/README.md
CHANGED
|
@@ -66,7 +66,8 @@ Every cell describes what the library itself does. We read that off the library'
|
|
|
66
66
|
| `"/"` | the route path, no prefix | the app does not start | stated, and trimming leaves nothing |
|
|
67
67
|
| `""` | `/` + the namespace's name | the route path, no prefix | unstated where the pack says a no-value prefix is unstated, otherwise stated |
|
|
68
68
|
| `None`, `False`, `0` | `/` + the namespace's name | the app does not start | unstated where the pack says so, otherwise unreadable |
|
|
69
|
-
| a name or
|
|
69
|
+
| a name, a call, or an f-string the evaluator settles | whatever it evaluates to | whatever it evaluates to | stated, with the string it settles on |
|
|
70
|
+
| anything the evaluator cannot settle | whatever it evaluates to | whatever it evaluates to | unreadable |
|
|
70
71
|
|
|
71
72
|
**At the mount** (`add_namespace(ns, path=...)`, `include_router(router, prefix=...)`):
|
|
72
73
|
|
|
@@ -77,7 +78,8 @@ Every cell describes what the library itself does. We read that off the library'
|
|
|
77
78
|
| `"/api/"` | `/api/` + the route path, kept as written | the app does not start | stated |
|
|
78
79
|
| `""` | where the constructor put it | the constructor's prefix + the route path | unstated where the pack says so, otherwise stated |
|
|
79
80
|
| `None`, `False`, `0` | where the constructor put it | the app does not start | unstated where the pack says so, otherwise unreadable |
|
|
80
|
-
| a name or
|
|
81
|
+
| a name, a call, or an f-string the evaluator settles | whatever it evaluates to | whatever it evaluates to | stated, with the string it settles on |
|
|
82
|
+
| anything the evaluator cannot settle | whatever it evaluates to | whatever it evaluates to | unreadable |
|
|
81
83
|
|
|
82
84
|
The two libraries differ on one property, and the pack says which way each one goes. flask-restx checks whether the path is truthy, so all four no-value spellings mean the same thing as writing nothing, at both places. FastAPI wants a string, so an empty string is an ordinary prefix that happens to add nothing, and the other three stop the app from starting.
|
|
83
85
|
|
|
@@ -101,7 +103,8 @@ FastAPI has no column here: its app has no prefix of its own, and the pack decla
|
|
|
101
103
|
| `"/"` | the rest of the path, once the merge collapses the doubled slash | stated, and merging leaves nothing |
|
|
102
104
|
| `""`, `None` | the rest of the path, no prefix | unstated |
|
|
103
105
|
| `False`, `0` | the app does not start: Flask hands the value straight to `rstrip` | unstated |
|
|
104
|
-
| a name
|
|
106
|
+
| a name, a call, or an f-string the evaluator settles | whatever it evaluates to | stated, with the string it settles on |
|
|
107
|
+
| anything the evaluator cannot settle | whatever it evaluates to | unreadable |
|
|
105
108
|
|
|
106
109
|
**At the `Api`** (`Api(bp, prefix=...)`):
|
|
107
110
|
|
|
@@ -111,7 +114,8 @@ FastAPI has no column here: its app has no prefix of its own, and the pack decla
|
|
|
111
114
|
| `"/extra"` | the blueprint's prefix + `/extra` + the rest | stated |
|
|
112
115
|
| `"/extra/"` | the same, concatenated as written, and Werkzeug answers the merged path | stated, and the composed path is merged |
|
|
113
116
|
| `""`, `None`, `False`, `0` | the blueprint's prefix + the rest | unstated |
|
|
114
|
-
| a name
|
|
117
|
+
| a name, a call, or an f-string the evaluator settles | whatever it evaluates to | stated, with the string it settles on |
|
|
118
|
+
| anything the evaluator cannot settle | whatever it evaluates to | unreadable |
|
|
115
119
|
|
|
116
120
|
### A call that spreads a dictionary
|
|
117
121
|
|
|
@@ -149,11 +153,12 @@ A prefix written with a trailing slash leaves the composed path with two. Werkze
|
|
|
149
153
|
|
|
150
154
|
## What a body lowers to
|
|
151
155
|
|
|
152
|
-
A route
|
|
153
|
-
|
|
154
|
-
in `@suss/extractor` walks.
|
|
155
|
-
|
|
156
|
-
of an earlier arm, and the budget
|
|
156
|
+
A route ends where it returns and where it raises, and each of those places is
|
|
157
|
+
what makes it produce more than one transition, so the adapter lowers a function
|
|
158
|
+
body into the statement form the shared path engine in `@suss/extractor` walks.
|
|
159
|
+
That engine is generic over the language's own condition handle and never looks
|
|
160
|
+
inside one, so the enumeration, the negation of an earlier arm, and the budget
|
|
161
|
+
are all shared with TypeScript.
|
|
157
162
|
|
|
158
163
|
| Python | Lowers to |
|
|
159
164
|
| --- | --- |
|
|
@@ -161,7 +166,7 @@ of an earlier arm, and the budget are all shared with TypeScript.
|
|
|
161
166
|
| `while`, `for` | `loop` |
|
|
162
167
|
| `try` / `except` / `finally` | `try`, with every except arm as the catch body |
|
|
163
168
|
| `match` / `case` | `switch`, one group per case, `case _` as the default group |
|
|
164
|
-
| `return`, `raise` | `exit`, which is what gives each
|
|
169
|
+
| `return`, `raise` | `exit`, which is what gives each of them its own transition |
|
|
165
170
|
| `break`, `continue` | `exit`, which the engine uses for reachability rather than as an outcome |
|
|
166
171
|
| anything else | `opaque` |
|
|
167
172
|
|
|
@@ -182,6 +187,45 @@ comes out as two transitions, the 404 gated on the opaque condition
|
|
|
182
187
|
`not found` and the 200 gated on its negation, rather than as one claim with a
|
|
183
188
|
guessed status.
|
|
184
189
|
|
|
190
|
+
### A branch that ends by raising
|
|
191
|
+
|
|
192
|
+
Most frameworks let a route end the request by raising rather than by
|
|
193
|
+
returning, and the status the client receives is then whatever the raised
|
|
194
|
+
call was given. A pack says which of its library's callables do that, under
|
|
195
|
+
`responseStatusCalls`, and where each of them takes its status:
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
responseStatusCalls: [
|
|
199
|
+
{
|
|
200
|
+
callee: "fastapi.HTTPException",
|
|
201
|
+
statusKeyword: "status_code",
|
|
202
|
+
statusArgument: 0,
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
`callee` is the name as the file imports it, module and name together, which
|
|
208
|
+
is what tells FastAPI's `HTTPException` apart from a project class of the same
|
|
209
|
+
name. A `raise` of one of these becomes a transition responding with that
|
|
210
|
+
status, and so does a bare call to one written as a statement of its own,
|
|
211
|
+
which is how Flask's `abort(404)` ends a request. So this handler:
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
@app.get("/items/{item_id}")
|
|
215
|
+
def show(item_id: int):
|
|
216
|
+
if item_id > 10:
|
|
217
|
+
raise HTTPException(status_code=404, detail="missing")
|
|
218
|
+
return {"id": item_id}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
comes out as a 404 under `item_id > 10` and a 200 under its negation.
|
|
222
|
+
|
|
223
|
+
A status the reading cannot resolve to a number is reported as the text it was
|
|
224
|
+
written as, so the transition says a status was set without claiming which. A
|
|
225
|
+
`raise` of anything the pack does not list is still a transition, with the
|
|
226
|
+
exception type on it and no status, since the framework is the only thing that
|
|
227
|
+
knows what it turns that exception into.
|
|
228
|
+
|
|
185
229
|
### Keying anything on a node
|
|
186
230
|
|
|
187
231
|
tree-sitter hands back a fresh wrapper object every time a child is read, so
|
|
@@ -232,6 +276,15 @@ call that starts the chain is the one resolved, and the method the chain ends
|
|
|
232
276
|
with tells a read from a write. A method a base and a subclass both declare
|
|
233
277
|
gives two, and nothing is claimed.
|
|
234
278
|
|
|
279
|
+
A chain also matches when the name it starts on is declared, where the chain
|
|
280
|
+
is written, as one of the query types: a parameter annotated `db: Session`,
|
|
281
|
+
a local bound by `session = Session()` or `with Session() as session:`, or a
|
|
282
|
+
project function whose return annotation says `Session`. The type has to be
|
|
283
|
+
imported from the pattern's module, so a project class that happens to share
|
|
284
|
+
the name does not match. `recordsNothing` lists the methods on such a type
|
|
285
|
+
that touch no rows of their own: `execute` runs a statement whose own chain is
|
|
286
|
+
the read or write, and `close` manages the session.
|
|
287
|
+
|
|
235
288
|
A query built from a function the library exports has no project method in
|
|
236
289
|
between for its return to be read off, so a pack lists those by name and a call
|
|
237
290
|
site importing one matches on that:
|
|
@@ -240,11 +293,15 @@ site importing one matches on that:
|
|
|
240
293
|
queryFunctions: ["select", "insert", "update", "delete"]
|
|
241
294
|
```
|
|
242
295
|
|
|
243
|
-
|
|
244
|
-
`
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
296
|
+
For one of these the function itself is the operation, so
|
|
297
|
+
`update(User).where(...).values(...)` is an update however it ends.
|
|
298
|
+
|
|
299
|
+
`fields` comes from the columns a query spells out, `User.id` in
|
|
300
|
+
`select(User.id, User.email)`, and from the keywords of a call the pack lists
|
|
301
|
+
under `valueMethods`, `name` in `.values(name="x")`. `selector` comes from
|
|
302
|
+
the keywords every other call in the chain picks rows by, `id` in
|
|
303
|
+
`filter_by(id=user_id)`. Raw SQL handed to `text` is read as its own effect,
|
|
304
|
+
with the kind and table taken from the statement.
|
|
248
305
|
|
|
249
306
|
## Where a mount is written
|
|
250
307
|
|
|
@@ -296,7 +353,7 @@ Taking the only mount it could follow would put the route at `/test`, where no r
|
|
|
296
353
|
|
|
297
354
|
Both of those qualifiers matter. Only counting loops keeps the ordinary case working: two factories that each register their own routers by name are not competing, because neither one could have registered the other's. Only counting a loop in a *different* place keeps a factory working when it mixes an explicit registration with a loop, since both of those run together whenever that function runs.
|
|
298
355
|
|
|
299
|
-
A module-level mount is never dropped this way. It runs whichever factory the app calls.
|
|
356
|
+
A module-level mount is never dropped this way. It runs whichever factory the app calls.
|
|
300
357
|
|
|
301
358
|
## What a file reads from the environment
|
|
302
359
|
|