📝 Leaner URL-encoded form parsing
Starlette 1.10.0 parses URL-encoded forms with less memory and CPU, no longer drops the last field when a stream ends early, and lets you compare FormData that holds several uploads under one key.
uv pip install starlette==1.10.0- Forms with many fields parse faster and use less memory (#3724).
FormParserbuilds each field in the parser callbacks instead of queueing every event and replaying it. On CodSpeed, 1000 fields went from 42.8 ms to 34.9 ms and from 63.5 KB to 54.5 KB, and themax_fieldslimit case went from 6,880 B to 864 B. - The last field is kept when the request stream ends without an empty chunk (#3599). The parser is now finalized once the stream is exhausted, so custom streams that never yield a final
b""no longer lose their last field. FormDatawith repeated uploads can be compared (#3619). Comparing twoFormDataobjects with severalUploadFilevalues under the same key used to raiseTypeError.
⚠️ Behaviour changes
- Multidict equality keeps the order of values under a repeated key (#3619). Keys can still appear in any order, but
QueryParams("a=1&a=2")andQueryParams("a=2&a=1")are no longer equal, matching whatgetlist()returns. starlette.formparsers.FormMessageis deprecated (#3724). The parser no longer uses it, andFormParser.messagesis gone. CallingFormMessage(...)emits aStarletteDeprecationWarning.
🧭 Routing and URL generation
- Path parameter values that look like a placeholder are kept as they are (#3725).
url_path_for("route", category="{id}", id="42")on/{category}/{id}now returns/{id}/42instead of/42/42. Substitution also got faster, from 549 ns to 520 ns with two parameters and from 1,078 ns to 779 ns with five. - Route matching does less work per request (#3665, #3722).
Matchmembers are compared by identity, cutting up to 4.9% of the instructions per dispatch in the routing benchmarks. WSGIMiddlewarestripsroot_pathonly at a path segment boundary (#3718). A request to/applicationunder aroot_pathof/appno longer becomes/lication.FloatConvertorreports NaN and infinity correctly (#3716). NaN was reported as a negative float.
🛡️ Middleware, responses and config
- Handled exceptions no longer keep requests alive (#3205). Starlette clears the traceback after an exception handler runs, so an exception instance reused across requests, such as a module-level
HTTPException, no longer pins per-request objects in memory. - Middlewares accept
http.response.startwithoutheaders(#3721).BaseHTTPMiddleware,GZipMiddlewareandSessionMiddlewaredefault the omitted field to an empty list instead of raisingKeyError. FileResponseignoresRangeon methods other thanGETandHEAD(#3679). APOSTendpoint that returns a file now sends the full file, as RFC 9110 requires.Configstrips one pair of matching outer quotes from env file values (#3720).KEY='say "hi"'now reads assay "hi"instead ofsay "hi.
Full changelog: 1.9.0...1.10.0