Skip to content

Commit 13f31a4

Browse files
authored
Merge pull request #967 from python-openapi/dependabot/pip/falcon-4.0.2
Bump falcon from 3.1.3 to 4.0.2
2 parents 3143d4f + 02f05e7 commit 13f31a4

File tree

12 files changed

+135
-49
lines changed

12 files changed

+135
-49
lines changed

docs/integrations/falcon.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
This section describes the integration with the [Falcon](https://falconframework.org) web framework.
44
The integration supports Falcon version 3.0 and above.
55

6+
!!! warning
7+
8+
This integration does not support multipart form body requests.
9+
610
## Middleware
711

812
The Falcon API can be integrated using the `FalconOpenAPIMiddleware` middleware.

openapi_core/contrib/falcon/requests.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,7 @@ def body(self) -> Optional[bytes]:
6666
self.request.content_type, self.request.options.default_media_type
6767
)
6868
try:
69-
body = handler.serialize(
70-
media, content_type=self.request.content_type
71-
)
69+
body = handler.serialize(media, content_type=self.content_type)
7270
# multipart form serialization is not supported
7371
except NotImplementedError:
7472
warnings.warn(

openapi_core/contrib/falcon/responses.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""OpenAPI core contrib falcon responses module"""
22

3+
from io import BytesIO
34
from itertools import tee
5+
from typing import Iterable
46

57
from falcon.response import Response
68
from werkzeug.datastructures import Headers
@@ -17,16 +19,22 @@ def data(self) -> bytes:
1719
if self.response.text is None:
1820
if self.response.stream is None:
1921
return b""
20-
resp_iter1, resp_iter2 = tee(self.response.stream)
21-
self.response.stream = resp_iter1
22-
content = b"".join(resp_iter2)
23-
return content
22+
if isinstance(self.response.stream, Iterable):
23+
resp_iter1, resp_iter2 = tee(self.response.stream)
24+
self.response.stream = resp_iter1
25+
content = b"".join(resp_iter2)
26+
return content
27+
# checks ReadableIO protocol
28+
if hasattr(self.response.stream, "read"):
29+
data = self.response.stream.read()
30+
self.response.stream = BytesIO(data)
31+
return data
2432
assert isinstance(self.response.text, str)
2533
return self.response.text.encode("utf-8")
2634

2735
@property
2836
def status_code(self) -> int:
29-
return int(self.response.status[:3])
37+
return self.response.status_code
3038

3139
@property
3240
def content_type(self) -> str:

openapi_core/contrib/falcon/util.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
from typing import Any
2-
from typing import Dict
32
from typing import Generator
3+
from typing import Mapping
44
from typing import Tuple
55

66

77
def unpack_params(
8-
params: Dict[str, Any]
8+
params: Mapping[str, Any]
99
) -> Generator[Tuple[str, Any], None, None]:
1010
for k, v in params.items():
1111
if isinstance(v, list):

poetry.lock

Lines changed: 49 additions & 35 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/integration/contrib/django/test_django_project.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ def test_post_media_type_invalid(self, client):
184184
"title": (
185185
"Content for the following mimetype not found: "
186186
"text/html. "
187-
"Valid mimetypes: ['application/json', 'application/x-www-form-urlencoded', 'text/plain']"
187+
"Valid mimetypes: ['application/json', 'application/x-www-form-urlencoded', 'multipart/form-data', 'text/plain']"
188188
),
189189
}
190190
]

tests/integration/contrib/falcon/test_falcon_project.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from json import dumps
33

44
import pytest
5+
from urllib3 import encode_multipart_formdata
56

67

78
class BaseTestFalconProject:
@@ -204,7 +205,7 @@ def test_post_media_type_invalid(self, client):
204205
"title": (
205206
"Content for the following mimetype not found: "
206207
f"{content_type}. "
207-
"Valid mimetypes: ['application/json', 'application/x-www-form-urlencoded', 'text/plain']"
208+
"Valid mimetypes: ['application/json', 'application/x-www-form-urlencoded', 'multipart/form-data', 'text/plain']"
208209
),
209210
}
210211
]
@@ -292,6 +293,43 @@ def test_post_valid(self, client, data_json):
292293
assert response.status_code == 201
293294
assert not response.content
294295

296+
@pytest.mark.xfail(
297+
reason="falcon multipart form serialization unsupported",
298+
strict=True,
299+
)
300+
def test_post_multipart_valid(self, client, data_gif):
301+
cookies = {"user": 1}
302+
auth = "authuser"
303+
fields = {
304+
"name": "Cat",
305+
"address": (
306+
"aaddress.json",
307+
dumps(dict(city="Warsaw")),
308+
"application/json",
309+
),
310+
"photo": (
311+
"photo.jpg",
312+
data_gif,
313+
"image/jpeg",
314+
),
315+
}
316+
body, content_type_header = encode_multipart_formdata(fields)
317+
headers = {
318+
"Authorization": f"Basic {auth}",
319+
"Content-Type": content_type_header,
320+
}
321+
322+
response = client.simulate_post(
323+
"/v1/pets",
324+
host="staging.gigantic-server.com",
325+
headers=headers,
326+
body=body,
327+
cookies=cookies,
328+
protocol="https",
329+
)
330+
331+
assert response.status_code == 200
332+
295333

296334
class TestPetDetailResource:
297335
def test_get_server_invalid(self, client):

tests/integration/contrib/fastapi/test_fastapi_project.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ def test_post_media_type_invalid(self, client):
183183
"title": (
184184
"Content for the following mimetype not found: "
185185
"text/html. "
186-
"Valid mimetypes: ['application/json', 'application/x-www-form-urlencoded', 'text/plain']"
186+
"Valid mimetypes: ['application/json', 'application/x-www-form-urlencoded', 'multipart/form-data', 'text/plain']"
187187
),
188188
}
189189
]

tests/integration/contrib/starlette/test_starlette_project.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ def test_post_media_type_invalid(self, client):
183183
"title": (
184184
"Content for the following mimetype not found: "
185185
"text/html. "
186-
"Valid mimetypes: ['application/json', 'application/x-www-form-urlencoded', 'text/plain']"
186+
"Valid mimetypes: ['application/json', 'application/x-www-form-urlencoded', 'multipart/form-data', 'text/plain']"
187187
),
188188
}
189189
]

tests/integration/data/v3.0/petstore.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ paths:
150150
application/x-www-form-urlencoded:
151151
schema:
152152
$ref: '#/components/schemas/PetCreate'
153+
multipart/form-data:
154+
schema:
155+
$ref: '#/components/schemas/PetWithPhotoCreate'
153156
text/plain: {}
154157
responses:
155158
'201':
@@ -375,6 +378,16 @@ components:
375378
oneOf:
376379
- $ref: "#/components/schemas/Cat"
377380
- $ref: "#/components/schemas/Bird"
381+
PetWithPhotoCreate:
382+
type: object
383+
x-model: PetWithPhotoCreate
384+
allOf:
385+
- $ref: "#/components/schemas/PetCreatePartOne"
386+
- $ref: "#/components/schemas/PetCreatePartTwo"
387+
- $ref: "#/components/schemas/PetCreatePartPhoto"
388+
oneOf:
389+
- $ref: "#/components/schemas/Cat"
390+
- $ref: "#/components/schemas/Bird"
378391
PetCreatePartOne:
379392
type: object
380393
x-model: PetCreatePartOne
@@ -395,6 +408,15 @@ components:
395408
$ref: "#/components/schemas/Position"
396409
healthy:
397410
type: boolean
411+
PetCreatePartPhoto:
412+
type: object
413+
x-model: PetCreatePartPhoto
414+
properties:
415+
photo:
416+
$ref: "#/components/schemas/PetPhoto"
417+
PetPhoto:
418+
type: string
419+
format: binary
398420
Bird:
399421
type: object
400422
x-model: Bird

tests/integration/unmarshalling/test_request_unmarshaller.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ def test_invalid_content_type(self, request_unmarshaller):
201201
availableMimetypes=[
202202
"application/json",
203203
"application/x-www-form-urlencoded",
204+
"multipart/form-data",
204205
"text/plain",
205206
],
206207
)

tests/integration/validation/test_request_validators.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ def test_media_type_not_found(self, request_validator):
106106
availableMimetypes=[
107107
"application/json",
108108
"application/x-www-form-urlencoded",
109+
"multipart/form-data",
109110
"text/plain",
110111
],
111112
)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy