Compare commits

..

5 Commits

Author SHA1 Message Date
Yurii Motov
e5dbca92d5 Merge branch 'master' into fix-multiple-upload-file-in-swagger 2026-05-20 22:37:18 +02:00
Motov Yurii
be926b473c Merge branch 'master' into fix-multiple-upload-file-in-swagger 2026-03-16 09:14:12 +01:00
Yurii Motov
6aa2f98504 Fix typing issue 2026-03-06 18:54:37 +01:00
Yurii Motov
3405306469 Fix schema for byte fields inside JSON (do not add "format": "binary") 2026-03-06 18:19:31 +01:00
Yurii Motov
506d278db7 Update schema for UploadFile (add format: binary) 2026-03-06 11:08:12 +01:00
15 changed files with 391 additions and 306 deletions

View File

@@ -58,14 +58,25 @@ class GenerateJsonSchema(_GenerateJsonSchema):
# TODO: remove when this is merged (or equivalent): https://github.com/pydantic/pydantic/pull/12841
# and dropping support for any version of Pydantic before that one (so, in a very long time)
def bytes_schema(self, schema: CoreSchema) -> JsonSchemaValue:
json_schema = {"type": "string", "contentMediaType": "application/octet-stream"}
bytes_mode = (
self._config.ser_json_bytes
if self.mode == "serialization"
else self._config.val_json_bytes
)
if bytes_mode == "base64":
json_schema["contentEncoding"] = "base64"
is_file_upload = schema.get("metadata", {}).get("fastapi_file_upload", False)
if is_file_upload:
json_schema: JsonSchemaValue = {
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
}
else:
json_schema = {
"type": "string",
"contentMediaType": "application/octet-stream",
}
bytes_mode = (
self._config.ser_json_bytes
if self.mode == "serialization"
else self._config.val_json_bytes
)
if bytes_mode == "base64":
json_schema["contentEncoding"] = "base64"
self.update_with_validations(json_schema, schema, self.ValidationsMapping.bytes)
return json_schema

View File

@@ -139,7 +139,11 @@ class UploadFile(StarletteUploadFile):
def __get_pydantic_json_schema__(
cls, core_schema: Mapping[str, Any], handler: GetJsonSchemaHandler
) -> dict[str, Any]:
return {"type": "string", "contentMediaType": "application/octet-stream"}
return {
"type": "string",
"format": "binary", # For compatibility with OAS 3.0
"contentMediaType": "application/octet-stream",
}
@classmethod
def __get_pydantic_core_schema__(

View File

@@ -2,7 +2,7 @@ import warnings
from collections.abc import Callable, Sequence
from dataclasses import dataclass
from enum import Enum
from typing import Annotated, Any, Literal
from typing import Annotated, Any, Literal, cast
from fastapi.exceptions import FastAPIDeprecationWarning
from fastapi.openapi.models import Example
@@ -660,6 +660,32 @@ class Form(Body): # type: ignore[misc]
)
class _FileUploadMarker:
"Pydantic metadata marker to tag bytes CoreSchemas as file uploads."
@classmethod
def __get_pydantic_core_schema__(
cls, source: type[Any], handler: Any
) -> dict[str, Any]:
schema = cast(dict[str, Any], handler(source))
# Find the inner type schema (if nullable or list)
inner_type_schema = schema
if inner_type_schema.get("type") != "bytes":
if inner_type_schema.get("type") == "list":
inner_type_schema = inner_type_schema["items_schema"]
elif "schema" in inner_type_schema:
inner_type_schema = inner_type_schema["schema"]
if inner_type_schema.get("type") == "list":
inner_type_schema = inner_type_schema["items_schema"]
# If the inner type is bytes, add the file upload marker metadata
if inner_type_schema.get("type") == "bytes":
metadata: dict[str, Any] = inner_type_schema.setdefault("metadata", {})
metadata["fastapi_file_upload"] = True
return schema
class File(Form): # type: ignore[misc]
def __init__(
self,
@@ -740,6 +766,7 @@ class File(Form): # type: ignore[misc]
json_schema_extra=json_schema_extra,
**extra,
)
self.metadata.append(_FileUploadMarker())
@dataclass(frozen=True)

View File

@@ -844,6 +844,28 @@ class APIRoute(routing.Route):
self.path = path
self.endpoint = endpoint
self.stream_item_type: Any | None = None
if isinstance(response_model, DefaultPlaceholder):
return_annotation = get_typed_return_annotation(endpoint)
if lenient_issubclass(return_annotation, Response):
response_model = None
else:
stream_item = get_stream_item_type(return_annotation)
if stream_item is not None:
# Extract item type for JSONL or SSE streaming when
# response_class is DefaultPlaceholder (JSONL) or
# EventSourceResponse (SSE).
# ServerSentEvent is excluded: it's a transport
# wrapper, not a data model, so it shouldn't feed
# into validation or OpenAPI schema generation.
if (
isinstance(response_class, DefaultPlaceholder)
or lenient_issubclass(response_class, EventSourceResponse)
) and not lenient_issubclass(stream_item, ServerSentEvent):
self.stream_item_type = stream_item
response_model = None
else:
response_model = return_annotation
self.response_model = response_model
self.summary = summary
self.response_description = response_description
self.deprecated = deprecated
@@ -879,6 +901,27 @@ class APIRoute(routing.Route):
if isinstance(status_code, IntEnum):
status_code = int(status_code)
self.status_code = status_code
if self.response_model:
assert is_body_allowed_for_status_code(status_code), (
f"Status code {status_code} must not have a response body"
)
response_name = "Response_" + self.unique_id
self.response_field = create_model_field(
name=response_name,
type_=self.response_model,
mode="serialization",
)
else:
self.response_field = None # type: ignore[assignment]
if self.stream_item_type:
stream_item_name = "StreamItem_" + self.unique_id
self.stream_item_field: ModelField | None = create_model_field(
name=stream_item_name,
type_=self.stream_item_type,
mode="serialization",
)
else:
self.stream_item_field = None
self.dependencies = list(dependencies or [])
self.description = description or inspect.cleandoc(self.endpoint.__doc__ or "")
# if a "form feed" character (page break) is found in the description text,
@@ -930,50 +973,6 @@ class APIRoute(routing.Route):
self.is_json_stream = is_generator and isinstance(
response_class, DefaultPlaceholder
)
if isinstance(response_model, DefaultPlaceholder):
return_annotation = get_typed_return_annotation(endpoint)
if lenient_issubclass(return_annotation, Response):
response_model = None
else:
stream_item = get_stream_item_type(return_annotation)
if stream_item is not None and is_generator:
# Extract item type for JSONL or SSE streaming for
# generator endpoints when response_class is
# DefaultPlaceholder (JSONL) or EventSourceResponse
# (SSE).
# ServerSentEvent is excluded: it's a transport
# wrapper, not a data model, so it shouldn't feed
# into validation or OpenAPI schema generation.
if (
isinstance(response_class, DefaultPlaceholder)
or lenient_issubclass(response_class, EventSourceResponse)
) and not lenient_issubclass(stream_item, ServerSentEvent):
self.stream_item_type = stream_item
response_model = None
else:
response_model = return_annotation
self.response_model = response_model
if self.response_model:
assert is_body_allowed_for_status_code(status_code), (
f"Status code {status_code} must not have a response body"
)
response_name = "Response_" + self.unique_id
self.response_field = create_model_field(
name=response_name,
type_=self.response_model,
mode="serialization",
)
else:
self.response_field = None # type: ignore[assignment]
if self.stream_item_type:
stream_item_name = "StreamItem_" + self.unique_id
self.stream_item_field: ModelField | None = create_model_field(
name=stream_item_name,
type_=self.stream_item_type,
mode="serialization",
)
else:
self.stream_item_field = None
self.app = request_response(self.get_route_handler())
def get_route_handler(self) -> Callable[[Request], Coroutine[Any, Any, Response]]:

View File

@@ -3,6 +3,7 @@ from typing import Annotated
import pytest
from fastapi import FastAPI, File, UploadFile
from fastapi.testclient import TestClient
from inline_snapshot import Is, snapshot
from .utils import get_body_model_name
@@ -33,21 +34,24 @@ def test_list_schema(path: str):
openapi = app.openapi()
body_model_name = get_body_model_name(openapi, path)
assert app.openapi()["components"]["schemas"][body_model_name] == {
"properties": {
"p": {
"type": "array",
"items": {
"type": "string",
"contentMediaType": "application/octet-stream",
assert app.openapi()["components"]["schemas"][body_model_name] == snapshot(
{
"properties": {
"p": {
"type": "array",
"items": {
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
"title": "P",
},
"title": "P",
},
},
"required": ["p"],
"title": body_model_name,
"type": "object",
}
"required": ["p"],
"title": Is(body_model_name),
"type": "object",
}
)
@pytest.mark.parametrize(
@@ -114,21 +118,24 @@ def test_list_alias_schema(path: str):
openapi = app.openapi()
body_model_name = get_body_model_name(openapi, path)
assert app.openapi()["components"]["schemas"][body_model_name] == {
"properties": {
"p_alias": {
"type": "array",
"items": {
"type": "string",
"contentMediaType": "application/octet-stream",
assert app.openapi()["components"]["schemas"][body_model_name] == snapshot(
{
"properties": {
"p_alias": {
"type": "array",
"items": {
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
"title": "P Alias",
},
"title": "P Alias",
},
},
"required": ["p_alias"],
"title": body_model_name,
"type": "object",
}
"required": ["p_alias"],
"title": Is(body_model_name),
"type": "object",
}
)
@pytest.mark.parametrize(
@@ -223,21 +230,24 @@ def test_list_validation_alias_schema(path: str):
openapi = app.openapi()
body_model_name = get_body_model_name(openapi, path)
assert app.openapi()["components"]["schemas"][body_model_name] == {
"properties": {
"p_val_alias": {
"type": "array",
"items": {
"type": "string",
"contentMediaType": "application/octet-stream",
assert app.openapi()["components"]["schemas"][body_model_name] == snapshot(
{
"properties": {
"p_val_alias": {
"type": "array",
"items": {
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
"title": "P Val Alias",
},
"title": "P Val Alias",
},
},
"required": ["p_val_alias"],
"title": body_model_name,
"type": "object",
}
"required": ["p_val_alias"],
"title": Is(body_model_name),
"type": "object",
}
)
@pytest.mark.parametrize(
@@ -343,21 +353,24 @@ def test_list_alias_and_validation_alias_schema(path: str):
openapi = app.openapi()
body_model_name = get_body_model_name(openapi, path)
assert app.openapi()["components"]["schemas"][body_model_name] == {
"properties": {
"p_val_alias": {
"type": "array",
"items": {
"type": "string",
"contentMediaType": "application/octet-stream",
assert app.openapi()["components"]["schemas"][body_model_name] == snapshot(
{
"properties": {
"p_val_alias": {
"type": "array",
"items": {
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
"title": "P Val Alias",
},
"title": "P Val Alias",
},
},
"required": ["p_val_alias"],
"title": body_model_name,
"type": "object",
}
"required": ["p_val_alias"],
"title": Is(body_model_name),
"type": "object",
}
)
@pytest.mark.parametrize(

View File

@@ -3,6 +3,7 @@ from typing import Annotated
import pytest
from fastapi import FastAPI, File, UploadFile
from fastapi.testclient import TestClient
from inline_snapshot import Is, snapshot
from .utils import get_body_model_name
@@ -33,19 +34,25 @@ def test_optional_schema(path: str):
openapi = app.openapi()
body_model_name = get_body_model_name(openapi, path)
assert app.openapi()["components"]["schemas"][body_model_name] == {
"properties": {
"p": {
"anyOf": [
{"type": "string", "contentMediaType": "application/octet-stream"},
{"type": "null"},
],
"title": "P",
}
},
"title": body_model_name,
"type": "object",
}
assert app.openapi()["components"]["schemas"][body_model_name] == snapshot(
{
"properties": {
"p": {
"anyOf": [
{
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
{"type": "null"},
],
"title": "P",
}
},
"title": Is(body_model_name),
"type": "object",
}
)
@pytest.mark.parametrize(
@@ -105,19 +112,25 @@ def test_optional_alias_schema(path: str):
openapi = app.openapi()
body_model_name = get_body_model_name(openapi, path)
assert app.openapi()["components"]["schemas"][body_model_name] == {
"properties": {
"p_alias": {
"anyOf": [
{"type": "string", "contentMediaType": "application/octet-stream"},
{"type": "null"},
],
"title": "P Alias",
}
},
"title": body_model_name,
"type": "object",
}
assert app.openapi()["components"]["schemas"][body_model_name] == snapshot(
{
"properties": {
"p_alias": {
"anyOf": [
{
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
{"type": "null"},
],
"title": "P Alias",
}
},
"title": Is(body_model_name),
"type": "object",
}
)
@pytest.mark.parametrize(
@@ -196,19 +209,25 @@ def test_optional_validation_alias_schema(path: str):
openapi = app.openapi()
body_model_name = get_body_model_name(openapi, path)
assert app.openapi()["components"]["schemas"][body_model_name] == {
"properties": {
"p_val_alias": {
"anyOf": [
{"type": "string", "contentMediaType": "application/octet-stream"},
{"type": "null"},
],
"title": "P Val Alias",
}
},
"title": body_model_name,
"type": "object",
}
assert app.openapi()["components"]["schemas"][body_model_name] == snapshot(
{
"properties": {
"p_val_alias": {
"anyOf": [
{
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
{"type": "null"},
],
"title": "P Val Alias",
}
},
"title": Is(body_model_name),
"type": "object",
}
)
@pytest.mark.parametrize(
@@ -292,19 +311,25 @@ def test_optional_alias_and_validation_alias_schema(path: str):
openapi = app.openapi()
body_model_name = get_body_model_name(openapi, path)
assert app.openapi()["components"]["schemas"][body_model_name] == {
"properties": {
"p_val_alias": {
"anyOf": [
{"type": "string", "contentMediaType": "application/octet-stream"},
{"type": "null"},
],
"title": "P Val Alias",
}
},
"title": body_model_name,
"type": "object",
}
assert app.openapi()["components"]["schemas"][body_model_name] == snapshot(
{
"properties": {
"p_val_alias": {
"anyOf": [
{
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
{"type": "null"},
],
"title": "P Val Alias",
}
},
"title": Is(body_model_name),
"type": "object",
}
)
@pytest.mark.parametrize(

View File

@@ -3,6 +3,7 @@ from typing import Annotated
import pytest
from fastapi import FastAPI, File, UploadFile
from fastapi.testclient import TestClient
from inline_snapshot import Is, snapshot
from .utils import get_body_model_name
@@ -35,25 +36,28 @@ def test_optional_list_schema(path: str):
openapi = app.openapi()
body_model_name = get_body_model_name(openapi, path)
assert app.openapi()["components"]["schemas"][body_model_name] == {
"properties": {
"p": {
"anyOf": [
{
"type": "array",
"items": {
"type": "string",
"contentMediaType": "application/octet-stream",
assert app.openapi()["components"]["schemas"][body_model_name] == snapshot(
{
"properties": {
"p": {
"anyOf": [
{
"type": "array",
"items": {
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
},
},
{"type": "null"},
],
"title": "P",
}
},
"title": body_model_name,
"type": "object",
}
{"type": "null"},
],
"title": "P",
}
},
"title": Is(body_model_name),
"type": "object",
}
)
@pytest.mark.parametrize(
@@ -113,25 +117,28 @@ def test_optional_list_alias_schema(path: str):
openapi = app.openapi()
body_model_name = get_body_model_name(openapi, path)
assert app.openapi()["components"]["schemas"][body_model_name] == {
"properties": {
"p_alias": {
"anyOf": [
{
"type": "array",
"items": {
"type": "string",
"contentMediaType": "application/octet-stream",
assert app.openapi()["components"]["schemas"][body_model_name] == snapshot(
{
"properties": {
"p_alias": {
"anyOf": [
{
"type": "array",
"items": {
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
},
},
{"type": "null"},
],
"title": "P Alias",
}
},
"title": body_model_name,
"type": "object",
}
{"type": "null"},
],
"title": "P Alias",
}
},
"title": Is(body_model_name),
"type": "object",
}
)
@pytest.mark.parametrize(
@@ -205,25 +212,28 @@ def test_optional_validation_alias_schema(path: str):
openapi = app.openapi()
body_model_name = get_body_model_name(openapi, path)
assert app.openapi()["components"]["schemas"][body_model_name] == {
"properties": {
"p_val_alias": {
"anyOf": [
{
"type": "array",
"items": {
"type": "string",
"contentMediaType": "application/octet-stream",
assert app.openapi()["components"]["schemas"][body_model_name] == snapshot(
{
"properties": {
"p_val_alias": {
"anyOf": [
{
"type": "array",
"items": {
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
},
},
{"type": "null"},
],
"title": "P Val Alias",
}
},
"title": body_model_name,
"type": "object",
}
{"type": "null"},
],
"title": "P Val Alias",
}
},
"title": Is(body_model_name),
"type": "object",
}
)
@pytest.mark.parametrize(
@@ -304,25 +314,28 @@ def test_optional_list_alias_and_validation_alias_schema(path: str):
openapi = app.openapi()
body_model_name = get_body_model_name(openapi, path)
assert app.openapi()["components"]["schemas"][body_model_name] == {
"properties": {
"p_val_alias": {
"anyOf": [
{
"type": "array",
"items": {
"type": "string",
"contentMediaType": "application/octet-stream",
assert app.openapi()["components"]["schemas"][body_model_name] == snapshot(
{
"properties": {
"p_val_alias": {
"anyOf": [
{
"type": "array",
"items": {
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
},
},
{"type": "null"},
],
"title": "P Val Alias",
}
},
"title": body_model_name,
"type": "object",
}
{"type": "null"},
],
"title": "P Val Alias",
}
},
"title": Is(body_model_name),
"type": "object",
}
)
@pytest.mark.parametrize(

View File

@@ -3,6 +3,7 @@ from typing import Annotated
import pytest
from fastapi import FastAPI, File, UploadFile
from fastapi.testclient import TestClient
from inline_snapshot import Is, snapshot
from .utils import get_body_model_name
@@ -33,18 +34,21 @@ def test_required_schema(path: str):
openapi = app.openapi()
body_model_name = get_body_model_name(openapi, path)
assert app.openapi()["components"]["schemas"][body_model_name] == {
"properties": {
"p": {
"title": "P",
"type": "string",
"contentMediaType": "application/octet-stream",
assert app.openapi()["components"]["schemas"][body_model_name] == snapshot(
{
"properties": {
"p": {
"title": "P",
"format": "binary",
"type": "string",
"contentMediaType": "application/octet-stream",
},
},
},
"required": ["p"],
"title": body_model_name,
"type": "object",
}
"required": ["p"],
"title": Is(body_model_name),
"type": "object",
}
)
@pytest.mark.parametrize(
@@ -111,18 +115,21 @@ def test_required_alias_schema(path: str):
openapi = app.openapi()
body_model_name = get_body_model_name(openapi, path)
assert app.openapi()["components"]["schemas"][body_model_name] == {
"properties": {
"p_alias": {
"title": "P Alias",
"type": "string",
"contentMediaType": "application/octet-stream",
assert app.openapi()["components"]["schemas"][body_model_name] == snapshot(
{
"properties": {
"p_alias": {
"title": "P Alias",
"format": "binary",
"type": "string",
"contentMediaType": "application/octet-stream",
},
},
},
"required": ["p_alias"],
"title": body_model_name,
"type": "object",
}
"required": ["p_alias"],
"title": Is(body_model_name),
"type": "object",
}
)
@pytest.mark.parametrize(
@@ -219,18 +226,21 @@ def test_required_validation_alias_schema(path: str):
openapi = app.openapi()
body_model_name = get_body_model_name(openapi, path)
assert app.openapi()["components"]["schemas"][body_model_name] == {
"properties": {
"p_val_alias": {
"title": "P Val Alias",
"type": "string",
"contentMediaType": "application/octet-stream",
assert app.openapi()["components"]["schemas"][body_model_name] == snapshot(
{
"properties": {
"p_val_alias": {
"title": "P Val Alias",
"format": "binary",
"type": "string",
"contentMediaType": "application/octet-stream",
},
},
},
"required": ["p_val_alias"],
"title": body_model_name,
"type": "object",
}
"required": ["p_val_alias"],
"title": Is(body_model_name),
"type": "object",
}
)
@pytest.mark.parametrize(
@@ -332,18 +342,21 @@ def test_required_alias_and_validation_alias_schema(path: str):
openapi = app.openapi()
body_model_name = get_body_model_name(openapi, path)
assert app.openapi()["components"]["schemas"][body_model_name] == {
"properties": {
"p_val_alias": {
"title": "P Val Alias",
"type": "string",
"contentMediaType": "application/octet-stream",
assert app.openapi()["components"]["schemas"][body_model_name] == snapshot(
{
"properties": {
"p_val_alias": {
"title": "P Val Alias",
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
},
},
"required": ["p_val_alias"],
"title": body_model_name,
"type": "object",
}
"required": ["p_val_alias"],
"title": Is(body_model_name),
"type": "object",
}
)
@pytest.mark.parametrize(

View File

@@ -1,5 +1,3 @@
from collections.abc import Iterable
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel
@@ -67,21 +65,6 @@ def get_exclude_unset_none() -> ModelDefaults:
return ModelDefaults(x=None, y="y")
@app.get("/iterable_exclude_unset", response_model_exclude_unset=True)
def get_iterable_exclude_unset() -> Iterable[ModelDefaults]:
return [ModelDefaults(x=None, y="y")]
@app.get("/iterable_exclude_defaults", response_model_exclude_defaults=True)
def get_iterable_exclude_defaults() -> Iterable[ModelDefaults]:
return [ModelDefaults(x=None, y="y")]
@app.get("/iterable_exclude_none", response_model_exclude_none=True)
def get_iterable_exclude_none() -> Iterable[ModelDefaults]:
return [ModelDefaults(x=None, y="y")]
client = TestClient(app)
@@ -108,18 +91,3 @@ def test_return_exclude_none():
def test_return_exclude_unset_none():
response = client.get("/exclude_unset_none")
assert response.json() == {"y": "y"}
def test_return_iterable_exclude_unset():
response = client.get("/iterable_exclude_unset")
assert response.json() == [{"x": None, "y": "y"}]
def test_return_iterable_exclude_defaults():
response = client.get("/iterable_exclude_defaults")
assert response.json() == [{}]
def test_return_iterable_exclude_none():
response = client.get("/iterable_exclude_none")
assert response.json() == [{"y": "y", "z": "z"}]

View File

@@ -162,6 +162,7 @@ def test_openapi_schema(client: TestClient):
"properties": {
"file": {
"title": "File",
"format": "binary",
"contentMediaType": "application/octet-stream",
"type": "string",
}
@@ -174,6 +175,7 @@ def test_openapi_schema(client: TestClient):
"properties": {
"file": {
"title": "File",
"format": "binary",
"type": "string",
"contentMediaType": "application/octet-stream",
}

View File

@@ -136,6 +136,7 @@ def test_openapi_schema(client: TestClient):
"anyOf": [
{
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
{"type": "null"},
@@ -152,6 +153,7 @@ def test_openapi_schema(client: TestClient):
"anyOf": [
{
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
{"type": "null"},

View File

@@ -121,6 +121,7 @@ def test_openapi_schema(client: TestClient):
"properties": {
"file": {
"title": "File",
"format": "binary",
"type": "string",
"description": "A file read as bytes",
"contentMediaType": "application/octet-stream",
@@ -134,6 +135,7 @@ def test_openapi_schema(client: TestClient):
"properties": {
"file": {
"title": "File",
"format": "binary",
"contentMediaType": "application/octet-stream",
"type": "string",
"description": "A file read as UploadFile",

View File

@@ -197,6 +197,7 @@ def test_openapi_schema(client: TestClient):
"type": "array",
"items": {
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
}
@@ -212,6 +213,7 @@ def test_openapi_schema(client: TestClient):
"type": "array",
"items": {
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
}

View File

@@ -167,6 +167,7 @@ def test_openapi_schema(client: TestClient):
"type": "array",
"items": {
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
"description": "Multiple files as bytes",
@@ -183,6 +184,7 @@ def test_openapi_schema(client: TestClient):
"type": "array",
"items": {
"type": "string",
"format": "binary",
"contentMediaType": "application/octet-stream",
},
"description": "Multiple files as UploadFile",

View File

@@ -197,11 +197,13 @@ def test_openapi_schema(client: TestClient):
"properties": {
"file": {
"title": "File",
"format": "binary",
"type": "string",
"contentMediaType": "application/octet-stream",
},
"fileb": {
"title": "Fileb",
"format": "binary",
"contentMediaType": "application/octet-stream",
"type": "string",
},