Repository navigation
Multi-body endpoints with required requestBody emit | Unset without importing Unset #1451
Description
Activity
we're running into the same problem as well
We're hitting the same underlying defect, but triggered by an optional multi-content-type request body rather than a required one — worth flagging since PR #1478 (as currently written) only fixes the
body_requireduninitialized-variable bug inendpoint_macros.py.jinja, which controls whether| Unset = UNSETgets appended to the annotation. It doesn't touch the import-generation side, so an endpoint where| Unsetis correctly appended (because the body actually is optional) still emits the same NameError today, and looks like it still will after #1478 merges.Minimal repro (differs from the original only in
requestBody.required: false):openapi: 3.0.3 info: title: Unset Import Bug (optional body) version: 1.0.0 paths: /items/{id}: put: operationId: updateItem parameters: - name: id in: path required: true schema: type: string requestBody: required: false # <-- optional, not required content: application/json: schema: $ref: '#/components/schemas/Item' multipart/form-data: schema: $ref: '#/components/schemas/Item' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/Item' components: schemas: Item: type: object properties: name: type: string
Generated
update_item.py(confirmed on 0.29.0 and 0.29.1, the latest release as of this comment):from ...types import UNSET, Response def _get_kwargs( *, body: Item | Item | Unset = UNSET, ) -> dict[str, Any]: ...
Unsetis referenced in the annotation but never imported — same symptom as the original report.ruff --select F821flags it, and it raisesNameErroron import under Python versions with eager annotation evaluation (e.g. 3.12; PEP 649's lazy annotations on 3.14+ mask it until the annotation is first accessed).Since #1478 is scoped to the required-body path, would it make sense to also cover the import side (the static
from ...types import Response, UNSETline, or whereverUnset's import gets decided for the multi-body branch) in that same fix/test suite? Happy to open a separate issue if that's cleaner for tracking — just wanted to flag the gap here first since it's the same class of bug.We stumbled upon this bug too and, as a workaround, ended up removing all
multipart/form-datablocks that are identical toapplication/jsonblocks:
Describe the bug
When an endpoint has a required
requestBodywith multiple content types, the generator appends| Unset = UNSETto the type annotation - even though the body is required.Furthermore,
Unsetis not added to the imports, so the generated file references an undefined name - this causesruffto report multiple instances ofF821 Undefined name 'Unset'.To Reproduce
Create
openapi.yamlwith the following spec:Generate the client and lint:
# Tested on CachyOS with uv 0.11.21 and the following: uvx --python 3.14 openapi-python-client==0.29.0 generate --path openapi.yaml --output-path out uvx ruff==0.15.17 check outGenerated code snippet
In
out/unset_bug_demo_client/api/default/update_item.py:Potential root cause (unverified)
The following was generated by an LLM and has not been verified!
I'm not really familiar with the codebase, but I might take a deeper look in the coming weeks if I find time and turn this into a proper fix/PR...