Skip to content

Commit 8520dc3

Browse files
Merge pull request #115 from kiforchuk/bump_apispec
Bump apispec to the latest version (5.1.1) Closes #110, #105, #74
2 parents cfa1964 + 4359a45 commit 8520dc3

File tree

8 files changed

+59
-41
lines changed

8 files changed

+59
-41
lines changed

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ dist: xenial
33
language: python
44

55
python:
6-
- 3.5
76
- 3.6
87
- 3.7
98
- 3.8

aiohttp_apispec/aiohttp_apispec.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
VALID_RESPONSE_FIELDS = {"description", "headers", "examples"}
1919

20+
DEFAULT_RESPONSE_LOCATION = "json"
21+
2022
NAME_SWAGGER_SPEC = "swagger.spec"
2123
NAME_SWAGGER_DOCS = "swagger.docs"
2224
NAME_SWAGGER_STATIC = "swagger.static"
@@ -160,7 +162,7 @@ def _update_paths(self, data: dict, method: str, url_path: str):
160162
return None
161163
for schema in data.pop("schemas", []):
162164
parameters = self.plugin.converter.schema2parameters(
163-
schema["schema"], **schema["options"]
165+
schema["schema"], location=schema["location"], **schema["options"]
164166
)
165167
self._add_examples(schema["schema"], parameters, schema["example"])
166168
data["parameters"].extend(parameters)
@@ -178,6 +180,7 @@ def _update_paths(self, data: dict, method: str, url_path: str):
178180
if "schema" in actual_params:
179181
raw_parameters = self.plugin.converter.schema2parameters(
180182
actual_params["schema"],
183+
location=DEFAULT_RESPONSE_LOCATION,
181184
required=actual_params.get("required", False),
182185
)[0]
183186
updated_params = {
@@ -200,7 +203,7 @@ def _update_paths(self, data: dict, method: str, url_path: str):
200203
def _add_examples(self, ref_schema, endpoint_schema, example):
201204
def add_to_endpoint_or_ref():
202205
if add_to_refs:
203-
self.spec.components._schemas[name]["example"] = example
206+
self.spec.components.schemas[name]["example"] = example
204207
else:
205208
endpoint_schema[0]['schema']['allOf'] = [endpoint_schema[0]['schema'].pop('$ref')]
206209
endpoint_schema[0]['schema']["example"] = example
@@ -210,7 +213,7 @@ def add_to_endpoint_or_ref():
210213
name = self.plugin.converter.schema_name_resolver(schema_instance)
211214
add_to_refs = example.pop('add_to_refs')
212215
if self.spec.components.openapi_version.major < 3:
213-
if name and name in self.spec.components._schemas:
216+
if name and name in self.spec.components.schemas:
214217
add_to_endpoint_or_ref()
215218
else:
216219
add_to_endpoint_or_ref()

aiohttp_apispec/decorators/request.py

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,21 @@
22
import copy
33

44

5-
def request_schema(schema, location=None, put_into=None, example=None, add_to_refs=False, **kwargs):
5+
# locations supported by both openapi and webargs.aiohttpparser
6+
VALID_SCHEMA_LOCATIONS = (
7+
"cookies",
8+
"files",
9+
"form",
10+
"headers",
11+
"json",
12+
"match_info",
13+
"path",
14+
"query",
15+
"querystring",
16+
)
17+
18+
19+
def request_schema(schema, location="json", put_into=None, example=None, add_to_refs=False, **kwargs):
620
"""
721
Add request info into the swagger spec and
822
prepare injection keyword arguments from the specified
@@ -29,7 +43,7 @@ async def index(request):
2943
'id': data['id']})
3044
3145
:param schema: :class:`Schema <marshmallow.Schema>` class or instance
32-
:param locations: Default request locations to parse
46+
:param location: Default request locations to parse
3347
:param put_into: name of the key in Request object
3448
where validated data will be placed.
3549
If None (by default) default key will be used
@@ -39,19 +53,14 @@ async def index(request):
3953
Otherwise add example to endpoint.
4054
Default False
4155
"""
56+
57+
if location not in VALID_SCHEMA_LOCATIONS:
58+
raise ValueError(f"Invalid location argument: {location}")
59+
4260
if callable(schema):
4361
schema = schema()
44-
45-
# Compatability with old versions should be dropped,
46-
# multiple locations are no longer supported by a single call
47-
# so therefore **locations should never be used
4862

4963
options = {"required": kwargs.pop("required", False)}
50-
# to support apispec >=4 need to rename default_in
51-
if location:
52-
options["default_in"] = location
53-
elif "default_in" not in options:
54-
options["default_in"] = "body"
5564

5665
def wrapper(func):
5766
if not hasattr(func, "__apispec__"):
@@ -61,14 +70,17 @@ def wrapper(func):
6170
_example = copy.copy(example) or {}
6271
if _example:
6372
_example['add_to_refs'] = add_to_refs
64-
func.__apispec__["schemas"].append({"schema": schema, "options": options, "example": _example})
73+
func.__apispec__["schemas"].append(
74+
{"schema": schema, "location": location, "options": options, "example": _example}
75+
)
76+
6577
# TODO: Remove this block?
66-
if location and "body" in location:
67-
body_schema_exists = (
68-
"body" in func_schema["location"] for func_schema in func.__schemas__
69-
)
70-
if any(body_schema_exists):
71-
raise RuntimeError("Multiple body parameters are not allowed")
78+
# "body" location was replaced by "json" location
79+
if (
80+
location == "json" and
81+
any(func_schema["location"] == "json" for func_schema in func.__schemas__)
82+
):
83+
raise RuntimeError("Multiple json locations are not allowed")
7284

7385
func.__schemas__.append({"schema": schema, "location": location, "put_into": put_into})
7486

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
aiohttp>=3.0.1,<4.0
2-
apispec>=3.0.0,<4.0
2+
apispec>=5.1.1
33
webargs>=8.0.1
44
jinja2<3.0

setup.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,12 @@ def read(file_name):
2323
url='https://github.com/maximdanilchenko/aiohttp-apispec',
2424
zip_safe=False,
2525
keywords='aiohttp marshmallow apispec swagger',
26-
python_requires='>=3.5',
26+
python_requires='>=3.6',
2727
classifiers=[
2828
'Development Status :: 5 - Production/Stable',
2929
'Intended Audience :: Developers',
3030
'License :: OSI Approved :: MIT License',
3131
'Natural Language :: English',
32-
'Programming Language :: Python :: 3.5',
3332
'Programming Language :: Python :: 3.6',
3433
'Programming Language :: Python :: 3.7',
3534
'Programming Language :: Python :: 3.8',

tests/conftest.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,24 +40,29 @@ def pytest_report_header(config):
4040
' ' '
4141
"""
4242

43+
4344
class MyNestedSchema(Schema):
4445
i = fields.Int()
4546

47+
4648
class RequestSchema(Schema):
4749
id = fields.Int()
48-
name = fields.Str(description="name")
50+
name = fields.Str(metadata={"description": "name"})
4951
bool_field = fields.Bool()
5052
list_field = fields.List(fields.Int())
5153
nested_field = fields.Nested(MyNestedSchema)
5254

55+
5356
class ResponseSchema(Schema):
5457
msg = fields.Str()
5558
data = fields.Dict()
5659

60+
5761
class MyException(Exception):
5862
def __init__(self, message):
5963
self.message = message
6064

65+
6166
@pytest.fixture
6267
def example_for_request_schema():
6368
return {
@@ -68,6 +73,7 @@ def example_for_request_schema():
6873
'nested_field': {'i': 12}
6974
}
7075

76+
7177
@pytest.fixture(
7278
# since multiple locations are no longer supported
7379
# in a single call, location should always expect string

tests/test_decorators.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
class RequestSchema(Schema):
99
id = fields.Int()
10-
name = fields.Str(description="name")
10+
name = fields.Str(metadata={"description": "name"})
1111
bool_field = fields.Bool()
1212
list_field = fields.List(fields.Int())
1313

@@ -25,7 +25,7 @@ def aiohttp_view_all(self):
2525
summary="Test method summary",
2626
description="Test method description",
2727
)
28-
@request_schema(RequestSchema, location=["querystring"])
28+
@request_schema(RequestSchema, location="querystring")
2929
@response_schema(ResponseSchema, 200)
3030
async def index(request, **data):
3131
return web.json_response({"msg": "done", "data": {}})
@@ -46,7 +46,7 @@ async def index(request, **data):
4646

4747
@pytest.fixture
4848
def aiohttp_view_kwargs(self):
49-
@request_schema(RequestSchema, location=["querystring"])
49+
@request_schema(RequestSchema, location="querystring")
5050
async def index(request, **data):
5151
return web.json_response({"msg": "done", "data": {}})
5252

@@ -91,7 +91,7 @@ def test_request_schema_view(self, aiohttp_view_kwargs):
9191
aiohttp_view_kwargs.__schemas__[0].pop("schema"), RequestSchema
9292
)
9393
assert aiohttp_view_kwargs.__schemas__ == [
94-
{"location": ["querystring"], 'put_into': None}
94+
{"location": "querystring", 'put_into': None}
9595
]
9696
for param in ("parameters", "responses"):
9797
assert param in aiohttp_view_kwargs.__apispec__
@@ -161,10 +161,10 @@ def test_all(self, aiohttp_view_all):
161161
def test_view_multiple_body_parameters(self):
162162
with pytest.raises(RuntimeError) as ex:
163163

164-
@request_schema(RequestSchema, location=["body"])
165-
@request_schema(RequestSchema, location=["body"])
164+
@request_schema(RequestSchema)
165+
@request_schema(RequestSchema, location="json")
166166
async def index(request, **data):
167167
return web.json_response({"msg": "done", "data": {}})
168168

169169
assert isinstance(ex.value, RuntimeError)
170-
assert str(ex.value) == "Multiple body parameters are not allowed"
170+
assert str(ex.value) == "Multiple json locations are not allowed"

tests/test_documentation.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ async def test_app_swagger_json(aiohttp_app, example_for_request_schema):
3838
"type": "boolean",
3939
},
4040
{
41-
"format": "int32",
4241
"in": "query",
4342
"name": "id",
4443
"required": False,
@@ -47,7 +46,7 @@ async def test_app_swagger_json(aiohttp_app, example_for_request_schema):
4746
{
4847
"collectionFormat": "multi",
4948
"in": "query",
50-
"items": {"format": "int32", "type": "integer"},
49+
"items": {"type": "integer"},
5150
"name": "list_field",
5251
"required": False,
5352
"type": "array",
@@ -97,7 +96,6 @@ async def test_app_swagger_json(aiohttp_app, example_for_request_schema):
9796
"type": "boolean",
9897
},
9998
{
100-
"format": "int32",
10199
"in": "query",
102100
"name": "id",
103101
"required": False,
@@ -106,7 +104,7 @@ async def test_app_swagger_json(aiohttp_app, example_for_request_schema):
106104
{
107105
"collectionFormat": "multi",
108106
"in": "query",
109-
"items": {"format": "int32", "type": "integer"},
107+
"items": {"type": "integer"},
110108
"name": "list_field",
111109
"required": False,
112110
"type": "array",
@@ -140,7 +138,7 @@ async def test_app_swagger_json(aiohttp_app, example_for_request_schema):
140138
'name': 'body',
141139
'schema': {
142140
'allOf': [
143-
'#/definitions/Request'
141+
{'$ref': '#/definitions/#/definitions/Request'}
144142
],
145143
'example': example_for_request_schema
146144
}
@@ -150,9 +148,9 @@ async def test_app_swagger_json(aiohttp_app, example_for_request_schema):
150148
_request_properties = {
151149
"properties": {
152150
"bool_field": {"type": "boolean"},
153-
"id": {"format": "int32", "type": "integer"},
151+
"id": {"type": "integer"},
154152
"list_field": {
155-
"items": {"format": "int32", "type": "integer"},
153+
"items": {"type": "integer"},
156154
"type": "array",
157155
},
158156
"name": {"description": "name", "type": "string"},
@@ -163,7 +161,7 @@ async def test_app_swagger_json(aiohttp_app, example_for_request_schema):
163161
assert json.dumps(docs["definitions"], sort_keys=True) == json.dumps(
164162
{
165163
"MyNested": {
166-
"properties": {"i": {"format": "int32", "type": "integer"}},
164+
"properties": {"i": {"type": "integer"}},
167165
"type": "object",
168166
},
169167
"Request": {**_request_properties, 'example': example_for_request_schema},
@@ -176,6 +174,7 @@ async def test_app_swagger_json(aiohttp_app, example_for_request_schema):
176174
sort_keys=True,
177175
)
178176

177+
179178
async def test_not_register_route_for_none_url():
180179
app = web.Application()
181180
routes_count = len(app.router.routes())

0 commit comments

Comments
 (0)