Skip to content

Commit 251b972

Browse files
committed
Dataclasses refactor
1 parent e9e43eb commit 251b972

File tree

18 files changed

+106
-72
lines changed

18 files changed

+106
-72
lines changed

openapi_core/contrib/django/requests.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
"""OpenAPI core contrib django requests module"""
22
import re
3-
43
from urllib.parse import urljoin
54

5+
from werkzeug.datastructures import ImmutableMultiDict, Headers
6+
67
from openapi_core.contrib.django.compat import (
78
get_request_headers, get_current_scheme_host,
89
)
@@ -43,13 +44,16 @@ def create(cls, request):
4344
route = route[:-1]
4445
path_pattern = '/' + route
4546

47+
request_headers = get_request_headers(request)
4648
path = request.resolver_match and request.resolver_match.kwargs or {}
47-
headers = get_request_headers(request)
49+
query = ImmutableMultiDict(request.GET)
50+
header = Headers(request_headers.items())
51+
cookie = ImmutableMultiDict(dict(request.COOKIES))
4852
parameters = RequestParameters(
4953
path=path,
50-
query=request.GET,
51-
header=list(headers.items()),
52-
cookie=request.COOKIES,
54+
query=query,
55+
header=header,
56+
cookie=cookie,
5357
)
5458
current_scheme_host = get_current_scheme_host(request)
5559
full_url_pattern = urljoin(current_scheme_host, path_pattern)

openapi_core/contrib/django/responses.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"""OpenAPI core contrib django responses module"""
2+
from werkzeug.datastructures import Headers
3+
24
from openapi_core.contrib.django.compat import get_response_headers
35
from openapi_core.validation.response.datatypes import OpenAPIResponse
46

@@ -9,9 +11,10 @@ class DjangoOpenAPIResponseFactory:
911
def create(cls, response):
1012
mimetype = response["Content-Type"]
1113
headers = get_response_headers(response)
14+
header = Headers(headers.items())
1215
return OpenAPIResponse(
1316
data=response.content,
1417
status_code=response.status_code,
15-
headers=list(headers.items()),
18+
headers=header,
1619
mimetype=mimetype,
1720
)

openapi_core/contrib/falcon/requests.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""OpenAPI core contrib falcon responses module"""
22
from json import dumps
33

4-
from werkzeug.datastructures import ImmutableMultiDict
4+
from werkzeug.datastructures import ImmutableMultiDict, Headers
55

66
from openapi_core.contrib.falcon.compat import get_request_media
77
from openapi_core.validation.request.datatypes import (
@@ -29,11 +29,12 @@ def create(cls, request, default_when_empty={}):
2929
mimetype = request.content_type.partition(";")[0]
3030

3131
query = ImmutableMultiDict(list(request.params.items()))
32+
header = Headers(request.headers)
3233

3334
# Path gets deduced by path finder against spec
3435
parameters = RequestParameters(
3536
query=query,
36-
header=request.headers,
37+
header=header,
3738
cookie=request.cookies,
3839
)
3940
url_pattern = request.prefix + request.path

openapi_core/contrib/falcon/responses.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"""OpenAPI core contrib falcon responses module"""
2+
from werkzeug.datastructures import Headers
3+
24
from openapi_core.contrib.falcon.compat import get_response_text
35
from openapi_core.validation.response.datatypes import OpenAPIResponse
46

@@ -15,10 +17,11 @@ def create(cls, response):
1517
mimetype = response.options.default_media_type
1618

1719
data = get_response_text(response)
20+
headers = Headers(response.headers)
1821

1922
return OpenAPIResponse(
2023
data=data,
2124
status_code=status_code,
22-
headers=response.headers,
25+
headers=headers,
2326
mimetype=mimetype,
2427
)

openapi_core/contrib/flask/requests.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
"""OpenAPI core contrib flask requests module"""
22
import re
3-
43
from urllib.parse import urljoin
54

5+
from werkzeug.datastructures import Headers
6+
67
from openapi_core.validation.request.datatypes import (
78
RequestParameters, OpenAPIRequest,
89
)
@@ -24,10 +25,11 @@ def create(cls, request):
2425
else:
2526
path_pattern = cls.path_regex.sub(r'{\1}', request.url_rule.rule)
2627

28+
header = Headers(request.headers)
2729
parameters = RequestParameters(
2830
path=request.view_args,
2931
query=request.args,
30-
header=request.headers,
32+
header=header,
3133
cookie=request.cookies,
3234
)
3335
full_url_pattern = urljoin(request.host_url, path_pattern)
+4-1
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
"""OpenAPI core contrib flask responses module"""
2+
from werkzeug.datastructures import Headers
3+
24
from openapi_core.validation.response.datatypes import OpenAPIResponse
35

46

57
class FlaskOpenAPIResponseFactory:
68

79
@classmethod
810
def create(cls, response):
11+
header = Headers(response.headers)
912
return OpenAPIResponse(
1013
data=response.data,
1114
status_code=response._status_code,
12-
headers=response.headers,
15+
headers=header,
1316
mimetype=response.mimetype,
1417
)

openapi_core/contrib/requests/requests.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from urllib.parse import urlparse, parse_qs
44

5-
from werkzeug.datastructures import ImmutableMultiDict
5+
from werkzeug.datastructures import ImmutableMultiDict, Headers
66
from requests import Request
77

88
from openapi_core.validation.request.datatypes import (
@@ -43,9 +43,9 @@ def create(cls, request):
4343
mimetype = request.headers.get('Content-Type') or \
4444
request.headers.get('Accept')
4545

46-
# Headers - request.headers is not an instance of dict
46+
# Headers - request.headers is not an instance of Headers
4747
# which is expected
48-
header = dict(request.headers)
48+
header = Headers(dict(request.headers))
4949

5050
# Body
5151
# TODO: figure out if request._body_position is relevant

openapi_core/contrib/requests/responses.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"""OpenAPI core contrib requests responses module"""
2+
from werkzeug.datastructures import Headers
3+
24
from openapi_core.validation.response.datatypes import OpenAPIResponse
35

46

@@ -7,7 +9,7 @@ class RequestsOpenAPIResponseFactory:
79
@classmethod
810
def create(cls, response):
911
mimetype = response.headers.get('Content-Type')
10-
headers = dict(response.headers)
12+
headers = Headers(dict(response.headers))
1113
return OpenAPIResponse(
1214
data=response.content,
1315
status_code=response.status_code,

openapi_core/testing/requests.py

+11-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""OpenAPI core testing requests module"""
22
from urllib.parse import urljoin
33

4-
from werkzeug.datastructures import ImmutableMultiDict
4+
from werkzeug.datastructures import Headers, ImmutableMultiDict
55

66
from openapi_core.validation.request.datatypes import (
77
RequestParameters, OpenAPIRequest,
@@ -15,13 +15,18 @@ def create(
1515
cls, host_url, method, path, path_pattern=None, args=None,
1616
view_args=None, headers=None, cookies=None, data=None,
1717
mimetype='application/json'):
18+
path_pattern = path_pattern or path
19+
20+
path = view_args or {}
21+
query = ImmutableMultiDict(args or {})
22+
header = Headers(headers or {})
23+
cookie = ImmutableMultiDict(cookies or {})
1824
parameters = RequestParameters(
19-
path=view_args or {},
20-
query=ImmutableMultiDict(args or []),
21-
header=headers or {},
22-
cookie=cookies or {},
25+
path=path,
26+
query=query,
27+
header=header,
28+
cookie=cookie,
2329
)
24-
path_pattern = path_pattern or path
2530
method = method.lower()
2631
body = data or ''
2732
full_url_pattern = urljoin(host_url, path_pattern)

openapi_core/testing/responses.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"""OpenAPI core testing responses module"""
2+
from werkzeug.datastructures import Headers
3+
24
from openapi_core.validation.response.datatypes import OpenAPIResponse
35

46

@@ -8,9 +10,10 @@ class MockResponseFactory:
810
def create(
911
cls, data, status_code=200, headers=None,
1012
mimetype='application/json'):
13+
headers = Headers(headers or {})
1114
return OpenAPIResponse(
1215
data=data,
1316
status_code=status_code,
14-
headers=headers or {},
17+
headers=headers,
1518
mimetype=mimetype,
1619
)

openapi_core/validation/datatypes.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
"""OpenAPI core validation datatypes module"""
2-
import attr
2+
from typing import List
33

4+
from dataclasses import dataclass
45

5-
@attr.s
6+
7+
@dataclass
68
class BaseValidationResult:
7-
errors = attr.ib(factory=list)
9+
errors: List[Exception]
810

911
def raise_for_errors(self):
1012
for error in self.errors:
+19-20
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
"""OpenAPI core validation request datatypes module"""
2-
import attr
2+
from typing import Dict, Optional
3+
4+
from dataclasses import dataclass, field
35
from werkzeug.datastructures import ImmutableMultiDict, Headers
46

57
from openapi_core.validation.datatypes import BaseValidationResult
68

79

8-
@attr.s
10+
@dataclass
911
class RequestParameters:
1012
"""OpenAPI request parameters dataclass.
1113
@@ -15,20 +17,20 @@ class RequestParameters:
1517
header
1618
Request headers as Headers.
1719
cookie
18-
Request cookies as dict.
20+
Request cookies as MultiDict.
1921
path
2022
Path parameters as dict. Gets resolved against spec if empty.
2123
"""
22-
query = attr.ib(factory=ImmutableMultiDict)
23-
header = attr.ib(factory=Headers, converter=Headers)
24-
cookie = attr.ib(factory=dict)
25-
path = attr.ib(factory=dict)
24+
query: ImmutableMultiDict = field(default_factory=ImmutableMultiDict)
25+
header: Headers = field(default_factory=Headers)
26+
cookie: ImmutableMultiDict = field(default_factory=ImmutableMultiDict)
27+
path: Dict = field(default_factory=dict)
2628

2729
def __getitem__(self, location):
2830
return getattr(self, location)
2931

3032

31-
@attr.s
33+
@dataclass
3234
class OpenAPIRequest:
3335
"""OpenAPI request dataclass.
3436
@@ -51,18 +53,15 @@ class OpenAPIRequest:
5153
the mimetype would be "text/html".
5254
"""
5355

54-
full_url_pattern = attr.ib()
55-
method = attr.ib()
56-
body = attr.ib()
57-
mimetype = attr.ib()
58-
parameters = attr.ib(factory=RequestParameters)
56+
full_url_pattern: str
57+
method: str
58+
body: str
59+
mimetype: str
60+
parameters: RequestParameters = field(default_factory=RequestParameters)
5961

6062

61-
@attr.s
63+
@dataclass
6264
class RequestValidationResult(BaseValidationResult):
63-
body = attr.ib(default=None)
64-
parameters = attr.ib(factory=RequestParameters)
65-
security = attr.ib(default=None)
66-
server = attr.ib(default=None)
67-
path = attr.ib(default=None)
68-
operation = attr.ib(default=None)
65+
body: Optional[str] = None
66+
parameters: RequestParameters = field(default_factory=RequestParameters)
67+
security: Dict[str, str] = None
+10-9
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
"""OpenAPI core validation response datatypes module"""
2-
import attr
2+
from typing import Dict, Optional
3+
from dataclasses import dataclass, field
34
from werkzeug.datastructures import Headers
45

56
from openapi_core.validation.datatypes import BaseValidationResult
67

78

8-
@attr.s
9+
@dataclass
910
class OpenAPIResponse:
1011
"""OpenAPI request dataclass.
1112
@@ -19,13 +20,13 @@ class OpenAPIResponse:
1920
mimetype
2021
Lowercase content type without charset.
2122
"""
22-
data = attr.ib()
23-
status_code = attr.ib()
24-
mimetype = attr.ib()
25-
headers = attr.ib(factory=Headers, converter=Headers)
23+
data: str
24+
status_code: int
25+
mimetype: str
26+
headers: Headers = field(default_factory=Headers)
2627

2728

28-
@attr.s
29+
@dataclass
2930
class ResponseValidationResult(BaseValidationResult):
30-
data = attr.ib(default=None)
31-
headers = attr.ib(factory=dict)
31+
data: Optional[str] = None
32+
headers: Dict = field(default_factory=dict)

requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ openapi-schema-validator
55
attrs
66
parse==1.14.0
77
more-itertools>=5.0.0
8+
dataclasses==0.8; python_version=="3.6"

setup.cfg

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ install_requires =
3333
werkzeug
3434
parse
3535
more-itertools
36+
dataclasses; python_version=="3.6"
3637
tests_require =
3738
pytest>=5.0.0
3839
pytest-flake8

tests/integration/contrib/flask/test_flask_requests.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from urllib.parse import urljoin
22

3-
from werkzeug.datastructures import EnvironHeaders, ImmutableMultiDict
3+
from werkzeug.datastructures import Headers, ImmutableMultiDict
44

55
from openapi_core.contrib.flask import FlaskOpenAPIRequest
66
from openapi_core.validation.request.datatypes import RequestParameters
@@ -15,7 +15,7 @@ def test_simple(self, request_factory, request):
1515

1616
path = {}
1717
query = ImmutableMultiDict([])
18-
headers = EnvironHeaders(request.environ)
18+
headers = Headers(request.headers)
1919
cookies = {}
2020
assert openapi_request.parameters == RequestParameters(
2121
path=path,
@@ -39,7 +39,7 @@ def test_multiple_values(self, request_factory, request):
3939
query = ImmutableMultiDict([
4040
('a', 'b'), ('a', 'c'),
4141
])
42-
headers = EnvironHeaders(request.environ)
42+
headers = Headers(request.headers)
4343
cookies = {}
4444
assert openapi_request.parameters == RequestParameters(
4545
path=path,
@@ -60,7 +60,7 @@ def test_url_rule(self, request_factory, request):
6060

6161
path = {'id': 12}
6262
query = ImmutableMultiDict([])
63-
headers = EnvironHeaders(request.environ)
63+
headers = Headers(request.headers)
6464
cookies = {}
6565
assert openapi_request.parameters == RequestParameters(
6666
path=path,

0 commit comments

Comments
 (0)