Skip to content

Commit 44ca23a

Browse files
authored
Merge pull request jmcarp#107 from jmcarp/apispec-compat
apispec>=0.39.0 compatibility
2 parents 40f5f8d + 2ff63c4 commit 44ca23a

File tree

7 files changed

+75
-33
lines changed

7 files changed

+75
-33
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,5 @@ _sandbox
5050

5151
# JetBrains
5252
.idea/
53+
54+
.pytest_cache/

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
language: python
22
sudo: false
33
env:
4-
- APISPEC_VERSION="==0.17.0"
4+
- APISPEC_VERSION="==0.39.0"
55
- APISPEC_VERSION=""
66
- MARSHMALLOW_VERSION="==2.13.0"
77
- MARSHMALLOW_VERSION=""

CHANGELOG.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
Changelog
22
---------
33

4+
0.7.0 (unreleased)
5+
++++++++++++++++++
6+
7+
Features:
8+
9+
* Supports apispec>=0.39.0. Older apispec versions are no longer supported.
10+
411
0.6.1 (2018-06-25)
512
++++++++++++++++++
613

README.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,14 @@ Quickstart
8787
.. code-block:: python
8888
8989
from apispec import APISpec
90+
from apispec.ext.marshmallow import MarshmallowPlugin
9091
from flask_apispec.extension import FlaskApiSpec
9192
9293
app.config.update({
9394
'APISPEC_SPEC': APISpec(
9495
title='pets',
9596
version='v1',
96-
plugins=['apispec.ext.marshmallow'],
97+
plugins=[MarshmallowPlugin()],
9798
),
9899
'APISPEC_SWAGGER_URL': '/swagger/',
99100
})

flask_apispec/apidoc.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@
33
import copy
44

55
import six
6-
from pkg_resources import parse_version
76

8-
import apispec
97
from apispec.core import VALID_METHODS
10-
from apispec.ext.marshmallow import swagger
8+
from apispec.ext.marshmallow import MarshmallowPlugin
119

1210
from marshmallow import Schema
1311
from marshmallow.utils import is_instance_or_subclass
@@ -17,8 +15,19 @@
1715

1816
class Converter(object):
1917

20-
def __init__(self, app):
18+
def __init__(self, app, spec):
2119
self.app = app
20+
self.spec = spec
21+
try:
22+
self.marshmallow_plugin = next(
23+
plugin for plugin in self.spec.plugins
24+
if isinstance(plugin, MarshmallowPlugin)
25+
)
26+
except StopIteration:
27+
raise RuntimeError(
28+
"Must have a MarshmallowPlugin instance in the spec's list "
29+
'of plugins.'
30+
)
2231

2332
def convert(self, target, endpoint=None, blueprint=None, **kwargs):
2433
endpoint = endpoint or target.__name__.lower()
@@ -57,26 +66,24 @@ def get_parent(self, view):
5766
return None
5867

5968
def get_parameters(self, rule, view, docs, parent=None):
69+
openapi = self.marshmallow_plugin.openapi
6070
annotation = resolve_annotations(view, 'args', parent)
6171
args = merge_recursive(annotation.options)
6272
schema = args.get('args', {})
6373
if is_instance_or_subclass(schema, Schema):
64-
converter = swagger.schema2parameters
74+
converter = openapi.schema2parameters
6575
elif callable(schema):
6676
schema = schema(request=None)
6777
if is_instance_or_subclass(schema, Schema):
68-
converter = swagger.schema2parameters
78+
converter = openapi.schema2parameters
6979
else:
70-
converter = swagger.fields2parameters
80+
converter = openapi.fields2parameters
7181
else:
72-
converter = swagger.fields2parameters
82+
converter = openapi.fields2parameters
7383
options = copy.copy(args.get('kwargs', {}))
7484
locations = options.pop('locations', None)
7585
if locations:
7686
options['default_in'] = locations[0]
77-
if parse_version(apispec.__version__) < parse_version('0.20.0'):
78-
options['dump'] = False
79-
8087
options['spec'] = self.app.config.get('APISPEC_SPEC', None)
8188

8289
rule_params = rule_to_params(rule, docs.get('params')) or []

flask_apispec/extension.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import functools
44
import types
55
from apispec import APISpec
6+
from apispec.ext.marshmallow import MarshmallowPlugin
67

78
from flask_apispec import ResourceMeta
89
from flask_apispec.apidoc import ViewConverter, ResourceConverter
@@ -20,7 +21,7 @@ class FlaskApiSpec(object):
2021
'APISPEC_SPEC': APISpec(
2122
title='pets',
2223
version='v1',
23-
plugins=['apispec.ext.marshmallow'],
24+
plugins=[MarshmallowPlugin()],
2425
),
2526
'APISPEC_SWAGGER_URL': '/swagger/',
2627
})
@@ -48,13 +49,12 @@ def __init__(self, app=None):
4849

4950
def init_app(self, app):
5051
self.app = app
51-
self.view_converter = ViewConverter(self.app)
52-
self.resource_converter = ResourceConverter(self.app)
5352
self.spec = self.app.config.get('APISPEC_SPEC') or \
5453
make_apispec(self.app.config.get('APISPEC_TITLE', 'flask-apispec'),
5554
self.app.config.get('APISPEC_VERSION', 'v1'))
56-
5755
self.add_swagger_routes()
56+
self.resource_converter = ResourceConverter(self.app, spec=self.spec)
57+
self.view_converter = ViewConverter(app=self.app, spec=self.spec)
5858

5959
for deferred in self._deferred:
6060
deferred()
@@ -150,5 +150,5 @@ def make_apispec(title='flask-apispec', version='v1'):
150150
return APISpec(
151151
title=title,
152152
version=version,
153-
plugins=['apispec.ext.marshmallow'],
153+
plugins=[MarshmallowPlugin()],
154154
)

tests/test_swagger.py renamed to tests/test_openapi.py

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import pytest
44
from apispec import APISpec
5-
from apispec.ext.marshmallow import swagger
5+
from apispec.ext.marshmallow import MarshmallowPlugin
66
from marshmallow import fields, Schema
77
from flask import make_response
88

@@ -11,13 +11,34 @@
1111
from flask_apispec import doc, use_kwargs, marshal_with
1212
from flask_apispec.apidoc import ViewConverter, ResourceConverter
1313

14+
@pytest.fixture()
15+
def marshmallow_plugin():
16+
return MarshmallowPlugin()
17+
1418
@pytest.fixture
15-
def spec():
19+
def spec(marshmallow_plugin):
1620
return APISpec(
1721
title='title',
1822
version='v1',
19-
plugins=['apispec.ext.marshmallow'],
23+
plugins=[marshmallow_plugin],
24+
)
25+
26+
@pytest.fixture()
27+
def openapi(marshmallow_plugin):
28+
return marshmallow_plugin.openapi
29+
30+
31+
def test_error_if_spec_does_not_have_marshmallow_plugin(app):
32+
bad_spec = APISpec(
33+
title='title',
34+
version='v1',
35+
plugins=[], # oh no! no MarshmallowPlugin
2036
)
37+
with pytest.raises(RuntimeError):
38+
ViewConverter(app=app, spec=bad_spec)
39+
with pytest.raises(RuntimeError):
40+
ResourceConverter(app=app, spec=bad_spec)
41+
2142

2243
class TestFunctionView:
2344

@@ -33,7 +54,7 @@ def get_band(band_id):
3354

3455
@pytest.fixture
3556
def path(self, app, spec, function_view):
36-
converter = ViewConverter(app)
57+
converter = ViewConverter(app=app, spec=spec)
3758
paths = converter.convert(function_view)
3859
for path in paths:
3960
spec.add_path(**path)
@@ -53,10 +74,11 @@ def test_params(self, app, path):
5374
)
5475
assert params == expected
5576

56-
def test_responses(self, schemas, path):
77+
def test_responses(self, schemas, path, openapi):
5778
response = path['get']['responses']['default']
5879
assert response['description'] == 'a band'
59-
assert response['schema'] == swagger.schema2jsonschema(schemas.BandSchema)
80+
expected = openapi.schema2jsonschema(schemas.BandSchema)
81+
assert response['schema'] == expected
6082

6183
def test_tags(self, path):
6284
assert path['get']['tags'] == ['band']
@@ -76,17 +98,18 @@ def get_band(**kwargs):
7698

7799
@pytest.fixture
78100
def path(self, app, spec, function_view):
79-
converter = ViewConverter(app)
101+
converter = ViewConverter(app=app, spec=spec)
80102
paths = converter.convert(function_view)
81103
for path in paths:
82104
spec.add_path(**path)
83105
return spec._paths['/bands/{band_id}/']
84106

85-
def test_params(self, app, path):
107+
def test_params(self, app, path, openapi):
86108
params = path['get']['parameters']
87109
rule = app.url_map._rules_by_endpoint['get_band'][0]
88110
expected = (
89-
swagger.fields2parameters({'name': fields.Str()}, default_in='query') +
111+
openapi.fields2parameters(
112+
{'name': fields.Str()}, default_in='query') +
90113
rule_to_params(rule)
91114
)
92115
assert params == expected
@@ -119,7 +142,7 @@ def delete_band(band_id):
119142

120143
@pytest.fixture
121144
def path(self, app, spec, function_view):
122-
converter = ViewConverter(app)
145+
converter = ViewConverter(app=app, spec=spec)
123146
paths = converter.convert(function_view)
124147
for path in paths:
125148
spec.add_path(**path)
@@ -146,25 +169,27 @@ def get(self, **kwargs):
146169

147170
@pytest.fixture
148171
def path(self, app, spec, resource_view):
149-
converter = ResourceConverter(app)
172+
converter = ResourceConverter(app=app, spec=spec)
150173
paths = converter.convert(resource_view, endpoint='band')
151174
for path in paths:
152175
spec.add_path(**path)
153176
return spec._paths['/bands/{band_id}/']
154177

155-
def test_params(self, app, path):
178+
def test_params(self, app, path, openapi):
156179
params = path['get']['parameters']
157180
rule = app.url_map._rules_by_endpoint['band'][0]
158181
expected = (
159-
swagger.fields2parameters({'name': fields.Str()}, default_in='query') +
182+
openapi.fields2parameters(
183+
{'name': fields.Str()}, default_in='query') +
160184
rule_to_params(rule)
161185
)
162186
assert params == expected
163187

164-
def test_responses(self, schemas, path):
188+
def test_responses(self, schemas, path, openapi):
165189
response = path['get']['responses']['default']
166190
assert response['description'] == 'a band'
167-
assert response['schema'] == swagger.schema2jsonschema(schemas.BandSchema)
191+
expected = openapi.schema2jsonschema(schemas.BandSchema)
192+
assert response['schema'] == expected
168193

169194
def test_tags(self, path):
170195
assert path['get']['tags'] == ['band']

0 commit comments

Comments
 (0)