"]
license = "BSD-3-Clause"
-readme = "README.rst"
+readme = "README.md"
repository = "https://github.com/python-openapi/openapi-core"
documentation = "https://openapi-core.readthedocs.io"
keywords = ["openapi", "swagger", "schema"]
@@ -47,7 +47,6 @@ classifiers = [
"Topic :: Software Development :: Libraries :: Python Modules",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
@@ -60,24 +59,27 @@ include = [
]
[tool.poetry.dependencies]
-python = "^3.8.0"
+python = "^3.9.0"
django = {version = ">=3.0", optional = true}
falcon = {version = ">=3.0", optional = true}
flask = {version = "*", optional = true}
aiohttp = {version = ">=3.0", optional = true}
-starlette = {version = ">=0.26.1,<0.39.0", optional = true}
+starlette = {version = ">=0.26.1,<0.47.0", optional = true}
isodate = "*"
more-itertools = "*"
parse = "*"
openapi-schema-validator = "^0.6.0"
openapi-spec-validator = "^0.7.1"
requests = {version = "*", optional = true}
-werkzeug = "*"
-jsonschema-path = "^0.3.1"
-jsonschema = "^4.18.0"
+# werkzeug 3.1.2 changed the definition of Headers
+# See https://github.com/python-openapi/openapi-core/issues/938
+werkzeug = "<3.1.2"
+jsonschema-path = "^0.3.4"
+jsonschema = "^4.23.0"
multidict = {version = "^6.0.4", optional = true}
-aioitertools = {version = "^0.11.0", optional = true}
-fastapi = {version = ">=0.111,<0.113", optional = true}
+aioitertools = {version = ">=0.11,<0.13", optional = true}
+fastapi = {version = ">=0.111,<0.116", optional = true}
+typing-extensions = "^4.8.0"
[tool.poetry.extras]
django = ["django"]
@@ -101,21 +103,23 @@ pytest-flake8 = "*"
pytest-cov = "*"
python-multipart = "*"
responses = "*"
-starlette = ">=0.26.1,<0.39.0"
+starlette = ">=0.26.1,<0.47.0"
strict-rfc3339 = "^0.7"
webob = "*"
mypy = "^1.2"
-httpx = ">=0.24,<0.28"
-deptry = ">=0.11,<0.20"
+httpx = ">=0.24,<0.29"
+deptry = ">=0.11,<0.21"
aiohttp = "^3.8.4"
pytest-aiohttp = "^1.0.4"
bump2version = "^1.0.1"
pyflakes = "^3.1.0"
-fastapi = ">=0.111,<0.113"
+fastapi = ">=0.111,<0.116"
[tool.poetry.group.docs.dependencies]
-sphinx = ">=5.3,<8.0"
-sphinx-immaterial = "^0.11.0"
+mkdocs = "^1.6.1"
+mkdocstrings = {extras = ["python"], version = "^0.26.1"}
+mkdocs-material = "^9.5.34"
+griffe-typingdoc = "^0.2.7"
[tool.pytest.ini_options]
addopts = """
diff --git a/tests/integration/contrib/django/data/v3.0/djangoproject/status/__init__.py b/tests/integration/contrib/django/data/v3.0/djangoproject/status/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/integration/contrib/django/data/v3.0/djangoproject/status/migrations/__init__.py b/tests/integration/contrib/django/data/v3.0/djangoproject/status/migrations/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/integration/contrib/django/data/v3.0/djangoproject/status/views.py b/tests/integration/contrib/django/data/v3.0/djangoproject/status/views.py
new file mode 100644
index 00000000..10d87749
--- /dev/null
+++ b/tests/integration/contrib/django/data/v3.0/djangoproject/status/views.py
@@ -0,0 +1,17 @@
+from pathlib import Path
+
+from django.http import HttpResponse
+from jsonschema_path import SchemaPath
+
+from openapi_core.contrib.django.decorators import DjangoOpenAPIViewDecorator
+
+check_minimal_spec = DjangoOpenAPIViewDecorator.from_spec(
+ SchemaPath.from_file_path(
+ Path("tests/integration/data/v3.0/minimal_with_servers.yaml")
+ )
+)
+
+
+@check_minimal_spec
+def get_status(request):
+ return HttpResponse("OK")
diff --git a/tests/integration/contrib/django/data/v3.0/djangoproject/urls.py b/tests/integration/contrib/django/data/v3.0/djangoproject/urls.py
index ff987972..be4e9781 100644
--- a/tests/integration/contrib/django/data/v3.0/djangoproject/urls.py
+++ b/tests/integration/contrib/django/data/v3.0/djangoproject/urls.py
@@ -20,6 +20,7 @@
from djangoproject.pets.views import PetDetailView
from djangoproject.pets.views import PetListView
from djangoproject.pets.views import PetPhotoView
+from djangoproject.status.views import get_status
from djangoproject.tags.views import TagListView
urlpatterns = [
@@ -48,4 +49,9 @@
TagListView.as_view(),
name="tag_list_view",
),
+ path(
+ "status",
+ get_status,
+ name="get_status_view",
+ ),
]
diff --git a/tests/integration/contrib/django/test_django_project.py b/tests/integration/contrib/django/test_django_project.py
index 6614eeaf..8a0697e1 100644
--- a/tests/integration/contrib/django/test_django_project.py
+++ b/tests/integration/contrib/django/test_django_project.py
@@ -184,7 +184,7 @@ def test_post_media_type_invalid(self, client):
"title": (
"Content for the following mimetype not found: "
"text/html. "
- "Valid mimetypes: ['application/json', 'application/x-www-form-urlencoded', 'text/plain']"
+ "Valid mimetypes: ['application/json', 'application/x-www-form-urlencoded', 'multipart/form-data', 'text/plain']"
),
}
]
@@ -422,3 +422,41 @@ def test_post_valid(self, client, data_gif):
assert response.status_code == 201
assert not response.content
+
+
+class TestStatusView(BaseTestDjangoProject):
+
+ def test_get_valid(self, client, data_gif):
+ headers = {
+ "HTTP_AUTHORIZATION": "Basic testuser",
+ "HTTP_HOST": "petstore.swagger.io",
+ }
+ from django.conf import settings
+
+ MIDDLEWARE = [
+ v for v in settings.MIDDLEWARE if "openapi_core" not in v
+ ]
+ with override_settings(MIDDLEWARE=MIDDLEWARE):
+ response = client.get("/status", **headers)
+
+ assert response.status_code == 200
+ assert response.content.decode() == "OK"
+
+ def test_post_valid(self, client):
+ data = {"key": "value"}
+ content_type = "application/json"
+ headers = {
+ "HTTP_AUTHORIZATION": "Basic testuser",
+ "HTTP_HOST": "petstore.swagger.io",
+ }
+ from django.conf import settings
+
+ MIDDLEWARE = [
+ v for v in settings.MIDDLEWARE if "openapi_core" not in v
+ ]
+ with override_settings(MIDDLEWARE=MIDDLEWARE):
+ response = client.post(
+ "/status", data=data, content_type=content_type, **headers
+ )
+
+ assert response.status_code == 405 # Method Not Allowed
diff --git a/tests/integration/contrib/falcon/test_falcon_project.py b/tests/integration/contrib/falcon/test_falcon_project.py
index 69e11974..252e0d6a 100644
--- a/tests/integration/contrib/falcon/test_falcon_project.py
+++ b/tests/integration/contrib/falcon/test_falcon_project.py
@@ -2,6 +2,7 @@
from json import dumps
import pytest
+from urllib3 import encode_multipart_formdata
class BaseTestFalconProject:
@@ -204,7 +205,7 @@ def test_post_media_type_invalid(self, client):
"title": (
"Content for the following mimetype not found: "
f"{content_type}. "
- "Valid mimetypes: ['application/json', 'application/x-www-form-urlencoded', 'text/plain']"
+ "Valid mimetypes: ['application/json', 'application/x-www-form-urlencoded', 'multipart/form-data', 'text/plain']"
),
}
]
@@ -292,6 +293,43 @@ def test_post_valid(self, client, data_json):
assert response.status_code == 201
assert not response.content
+ @pytest.mark.xfail(
+ reason="falcon multipart form serialization unsupported",
+ strict=True,
+ )
+ def test_post_multipart_valid(self, client, data_gif):
+ cookies = {"user": 1}
+ auth = "authuser"
+ fields = {
+ "name": "Cat",
+ "address": (
+ "aaddress.json",
+ dumps(dict(city="Warsaw")),
+ "application/json",
+ ),
+ "photo": (
+ "photo.jpg",
+ data_gif,
+ "image/jpeg",
+ ),
+ }
+ body, content_type_header = encode_multipart_formdata(fields)
+ headers = {
+ "Authorization": f"Basic {auth}",
+ "Content-Type": content_type_header,
+ }
+
+ response = client.simulate_post(
+ "/v1/pets",
+ host="staging.gigantic-server.com",
+ headers=headers,
+ body=body,
+ cookies=cookies,
+ protocol="https",
+ )
+
+ assert response.status_code == 200
+
class TestPetDetailResource:
def test_get_server_invalid(self, client):
diff --git a/tests/integration/contrib/fastapi/test_fastapi_project.py b/tests/integration/contrib/fastapi/test_fastapi_project.py
index e8d795c6..242613bc 100644
--- a/tests/integration/contrib/fastapi/test_fastapi_project.py
+++ b/tests/integration/contrib/fastapi/test_fastapi_project.py
@@ -183,7 +183,7 @@ def test_post_media_type_invalid(self, client):
"title": (
"Content for the following mimetype not found: "
"text/html. "
- "Valid mimetypes: ['application/json', 'application/x-www-form-urlencoded', 'text/plain']"
+ "Valid mimetypes: ['application/json', 'application/x-www-form-urlencoded', 'multipart/form-data', 'text/plain']"
),
}
]
diff --git a/tests/integration/contrib/starlette/test_starlette_project.py b/tests/integration/contrib/starlette/test_starlette_project.py
index fc799a30..d1e8ed54 100644
--- a/tests/integration/contrib/starlette/test_starlette_project.py
+++ b/tests/integration/contrib/starlette/test_starlette_project.py
@@ -183,7 +183,7 @@ def test_post_media_type_invalid(self, client):
"title": (
"Content for the following mimetype not found: "
"text/html. "
- "Valid mimetypes: ['application/json', 'application/x-www-form-urlencoded', 'text/plain']"
+ "Valid mimetypes: ['application/json', 'application/x-www-form-urlencoded', 'multipart/form-data', 'text/plain']"
),
}
]
diff --git a/tests/integration/data/v3.0/petstore.yaml b/tests/integration/data/v3.0/petstore.yaml
index d26816ac..735fd96c 100644
--- a/tests/integration/data/v3.0/petstore.yaml
+++ b/tests/integration/data/v3.0/petstore.yaml
@@ -150,6 +150,9 @@ paths:
application/x-www-form-urlencoded:
schema:
$ref: '#/components/schemas/PetCreate'
+ multipart/form-data:
+ schema:
+ $ref: '#/components/schemas/PetWithPhotoCreate'
text/plain: {}
responses:
'201':
@@ -375,6 +378,16 @@ components:
oneOf:
- $ref: "#/components/schemas/Cat"
- $ref: "#/components/schemas/Bird"
+ PetWithPhotoCreate:
+ type: object
+ x-model: PetWithPhotoCreate
+ allOf:
+ - $ref: "#/components/schemas/PetCreatePartOne"
+ - $ref: "#/components/schemas/PetCreatePartTwo"
+ - $ref: "#/components/schemas/PetCreatePartPhoto"
+ oneOf:
+ - $ref: "#/components/schemas/Cat"
+ - $ref: "#/components/schemas/Bird"
PetCreatePartOne:
type: object
x-model: PetCreatePartOne
@@ -395,6 +408,15 @@ components:
$ref: "#/components/schemas/Position"
healthy:
type: boolean
+ PetCreatePartPhoto:
+ type: object
+ x-model: PetCreatePartPhoto
+ properties:
+ photo:
+ $ref: "#/components/schemas/PetPhoto"
+ PetPhoto:
+ type: string
+ format: binary
Bird:
type: object
x-model: Bird
diff --git a/tests/integration/unmarshalling/test_request_unmarshaller.py b/tests/integration/unmarshalling/test_request_unmarshaller.py
index 2993275b..0eefa3f0 100644
--- a/tests/integration/unmarshalling/test_request_unmarshaller.py
+++ b/tests/integration/unmarshalling/test_request_unmarshaller.py
@@ -201,6 +201,7 @@ def test_invalid_content_type(self, request_unmarshaller):
availableMimetypes=[
"application/json",
"application/x-www-form-urlencoded",
+ "multipart/form-data",
"text/plain",
],
)
diff --git a/tests/integration/validation/test_request_validators.py b/tests/integration/validation/test_request_validators.py
index 14a7e6d6..eaac8dbf 100644
--- a/tests/integration/validation/test_request_validators.py
+++ b/tests/integration/validation/test_request_validators.py
@@ -106,6 +106,7 @@ def test_media_type_not_found(self, request_validator):
availableMimetypes=[
"application/json",
"application/x-www-form-urlencoded",
+ "multipart/form-data",
"text/plain",
],
)
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