fix(i18n): read refusals along a closed list of shapes, report the rest
The scan followed many shapes and called the rest unread, but each shape it learned opened a silent branch: a parameter rebound in the body, a second visit of a followed helper, an `import app.x`, a name bound twice or under an `if`, a spread or a store that replaces the code, a detail rebuilt from its variable, a None reason. It now reads only what the app writes and reports everything else: a branch the app never takes is gone (an `or`, a default, a base class's attribute, string and union annotations, a bare module constant), a spread is clean only from an untouched `**kwargs` no call puts a `code` in, and the detail's variable may only grow by `|=` or `["reason"]`. The real tree reads exactly as before; every exit and every kept branch has a self-test that a mutant of it fails. The detail test also fails a dict without a `code`, a JSONResponse sentence and a detail-less HTTPException; the two that are no refusal (the setup gate's 503, an unmatched api path's 404) are listed with why. The contract's assertion that compared the reason map with itself is gone.
This commit is contained in:
@@ -7,10 +7,11 @@ whatever sentence the route happened to hand-write. `refused` is the generic fam
|
||||
(`errors.refused.<reason>`, the params interpolated into the sentence); the older specific codes
|
||||
(`not_found`, `youtube`, `playlist`, the admin refusals, …) stay, since each is already translated.
|
||||
|
||||
A test (`test_http_details.py`) reads every `HTTPException` in the app and fails on a string detail.
|
||||
`tests/refusal_sites.py` reads every `{"code": …}` detail from the syntax tree and follows each reason
|
||||
to its values: a literal, a `Literal` type on the parameter or attribute it travels in, every call of
|
||||
a helper like `refused` below, a map's values. The frontend's contract test
|
||||
A test (`test_http_details.py`) reads every `HTTPException` and `JSONResponse` detail in the app and
|
||||
fails on a sentence, a dict without a `code`, or none at all. `tests/refusal_sites.py` reads every
|
||||
`{"code": …}` detail from the syntax tree and follows each reason along a closed list of shapes: a
|
||||
literal, a `Literal` type on the parameter or attribute it travels in, every call of a helper like
|
||||
`refused` below, a map's values; anything else is reported, never guessed. The frontend's contract test
|
||||
(`i18n/backendIds.contract.test.ts`) runs it and requires each code in one record of
|
||||
`lib/api/refusalCodes.ts`, and each reason's sentence in both languages; a code or reason it cannot
|
||||
follow fails it, unless no screen shows that code. So the rule is enforced, not described.
|
||||
@@ -23,7 +24,8 @@ from app.sysconfig import InvalidSetting, SettingReason
|
||||
|
||||
def refused(status: int, reason: str, **params: object) -> HTTPException:
|
||||
"""The refusal to raise: `raise refused(409, "view_name_taken", name=name)`. `params` travel in
|
||||
the detail for the sentence to interpolate (`{{name}}`); keep them to values the reader may see."""
|
||||
the detail for the sentence to interpolate (`{{name}}`); keep them to values the reader may see,
|
||||
and never a `code`: it would replace this one (the refusal scan reads such a call as an unread code)."""
|
||||
return HTTPException(status_code=status, detail={"code": "refused", "reason": reason, **params})
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user