Skip to content

Commit 83e0c1a

Browse files
committed
Merge branch 'master' into hide-options
2 parents 203800d + ec30f52 commit 83e0c1a

14 files changed

+359
-70
lines changed

.travis.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ language: python
22
cache: pip
33
sudo: false
44
env:
5-
- APISPEC_VERSION="==0.39.0"
6-
- APISPEC_VERSION=""
75
- MARSHMALLOW_VERSION="==2.13.0"
86
- MARSHMALLOW_VERSION=""
97
python:
@@ -15,7 +13,6 @@ before_install:
1513
install:
1614
- travis_retry pip install -U .
1715
- travis_retry pip install -U -r dev-requirements.txt
18-
- travis_retry pip install -U --pre apispec"$APISPEC_VERSION"
1916
- travis_retry pip install -U --pre marshmallow"$MARSHMALLOW_VERSION"
2017
before_script:
2118
- flake8 flask_apispec
@@ -29,7 +26,7 @@ jobs:
2926
env: []
3027
# Override install, and script to no-ops
3128
before_install: true
32-
install:
29+
install:
3330
- travis_retry pip install -U .
3431
- travis_retry pip install -r dev-requirements.txt
3532
before_script: true

CHANGELOG.rst

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

4+
0.8.8 (2020-03-29)
5+
******************
6+
7+
Bug fixes:
8+
9+
* Fix behavior when view returns ``(obj, status_code, headers)``
10+
(regression in 0.8.7) (:issue:`181`).
11+
Thanks :user:`decaz` for reporting and thanks :user:`c-kruse`
12+
for the PR.
13+
14+
0.8.7 (2020-03-10)
15+
******************
16+
17+
Bug fixes:
18+
19+
* Fix serialisation problem with return codes when used with flask-restful (:issue:`98`, :issue:`93`).
20+
Thanks :user:`AdamLeyshon` for the PR.
21+
22+
0.8.6 (2020-03-01)
23+
******************
24+
25+
Bug fixes:
26+
27+
* Restrict webargs version to <6.0 (:issue:`176`).
28+
Thanks :user:`c-kruse` for reporting and thanks :user:`saydamir`
29+
for the PR.
30+
31+
0.8.5 (2020-01-05)
32+
******************
33+
34+
Bug fixes:
35+
36+
* Fix setting ``default_in`` for compatibility with newer versions of apispec (:pr:`173`).
37+
Thanks :user:`AbdealiJK` for the PR.
38+
39+
0.8.4 (2019-12-04)
40+
******************
41+
42+
Bug fixes:
43+
44+
* Fix passing ``default_in`` argument when generating parameters (:issue:`165`).
45+
Thanks :user:`d42` for reporting and thanks :user:`zzz4zzz` for the fix.
46+
47+
0.8.3 (2019-09-17)
48+
******************
49+
50+
Bug fixes:
51+
52+
* Fix compatibility with apispec>=3.0.0 (:issue:`163`).
53+
Thanks :user:`decaz`.
54+
55+
0.8.2 (2019-09-16)
56+
******************
57+
58+
Bug fixes:
59+
60+
* Handle multiple locations when using use_kwargs multiple times on the same view (:issue:`78`).
61+
Thanks :user:`norbert-sebok` for the PR and thanks :user:`shrsubra` for updating it.
62+
63+
0.8.1 (2019-06-22)
64+
******************
65+
66+
Bug fixes:
67+
68+
* Fix support for ``@post_load`` methods that return a non-dictionary object
69+
(:issue:`103`). Thanks :user:`erezatiya` for reporting and thanks :user:`elatomo`
70+
for the PR.
71+
* Restrict marshmallow version based on Python version (:pr:`150`).
72+
73+
0.8.0 (2019-02-13)
74+
******************
75+
76+
Features:
77+
78+
* Supports apispec>=1.0.0 (:issue:`130`). Older apispec versions are no longer supported.
79+
Thanks :user:`DStape` for the PR.
80+
* Upgrade swagger-ui to version 3.20.7.
81+
482
0.7.0 (2018-07-01)
583
++++++++++++++++++
684

README.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ Quickstart
3535
from flask import Flask
3636
from flask_apispec import use_kwargs, marshal_with
3737
38-
from marshmallow import fields, Schema
38+
from marshmallow import Schema
39+
from webargs import fields
3940
4041
from .models import Pet
4142

flask_apispec/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from flask_apispec.extension import FlaskApiSpec
55
from flask_apispec.utils import Ref
66

7-
__version__ = '0.7.0'
7+
__version__ = '0.8.8'
88
__all__ = [
99
'doc',
1010
'wrap_with',

flask_apispec/apidoc.py

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import six
66

7+
import apispec
78
from apispec.core import VALID_METHODS
89
from apispec.ext.marshmallow import MarshmallowPlugin
910

@@ -13,6 +14,10 @@
1314
from flask_apispec.paths import rule_to_path, rule_to_params
1415
from flask_apispec.utils import resolve_resource, resolve_annotations, merge_recursive
1516

17+
APISPEC_VERSION_INFO = tuple(
18+
[int(part) for part in apispec.__version__.split('.') if part.isdigit()]
19+
)
20+
1621
class Converter(object):
1722

1823
def __init__(self, app, spec, document_options=True):
@@ -40,6 +45,7 @@ def convert(self, target, endpoint=None, blueprint=None, **kwargs):
4045
def get_path(self, rule, target, **kwargs):
4146
operations = self.get_operations(rule, target)
4247
parent = self.get_parent(target, **kwargs)
48+
valid_methods = VALID_METHODS[self.spec.openapi_version.major]
4349
excluded_methods = {'head'}
4450
if not self.document_options:
4551
excluded_methods.add('options')
@@ -49,7 +55,7 @@ def get_path(self, rule, target, **kwargs):
4955
'operations': {
5056
method.lower(): self.get_operation(rule, view, parent=parent)
5157
for method, view in six.iteritems(operations)
52-
if method.lower() in (set(VALID_METHODS) - excluded_methods)
58+
if method.lower() in (set(valid_methods) - excluded_methods)
5359
},
5460
}
5561

@@ -70,28 +76,33 @@ def get_parent(self, view):
7076
return None
7177

7278
def get_parameters(self, rule, view, docs, parent=None):
73-
openapi = self.marshmallow_plugin.openapi
79+
if APISPEC_VERSION_INFO[0] < 3:
80+
openapi = self.marshmallow_plugin.openapi
81+
else:
82+
openapi = self.marshmallow_plugin.converter
7483
annotation = resolve_annotations(view, 'args', parent)
75-
args = merge_recursive(annotation.options)
76-
schema = args.get('args', {})
77-
if is_instance_or_subclass(schema, Schema):
78-
converter = openapi.schema2parameters
79-
elif callable(schema):
80-
schema = schema(request=None)
84+
extra_params = []
85+
for args in annotation.options:
86+
schema = args.get('args', {})
8187
if is_instance_or_subclass(schema, Schema):
8288
converter = openapi.schema2parameters
89+
elif callable(schema):
90+
schema = schema(request=None)
91+
if is_instance_or_subclass(schema, Schema):
92+
converter = openapi.schema2parameters
93+
else:
94+
converter = openapi.fields2parameters
8395
else:
8496
converter = openapi.fields2parameters
85-
else:
86-
converter = openapi.fields2parameters
87-
options = copy.copy(args.get('kwargs', {}))
88-
locations = options.pop('locations', None)
89-
if locations:
90-
options['default_in'] = locations[0]
91-
options['spec'] = self.app.config.get('APISPEC_SPEC', None)
97+
options = copy.copy(args.get('kwargs', {}))
98+
locations = options.pop('locations', None)
99+
if locations:
100+
options['default_in'] = locations[0]
101+
elif 'default_in' not in options:
102+
options['default_in'] = 'body'
103+
extra_params += converter(schema, **options) if args else []
92104

93105
rule_params = rule_to_params(rule, docs.get('params')) or []
94-
extra_params = converter(schema, **options) if args else []
95106

96107
return extra_params + rule_params
97108

flask_apispec/extension.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class FlaskApiSpec(object):
2121
'APISPEC_SPEC': APISpec(
2222
title='pets',
2323
version='v1',
24+
openapi_version='2.0',
2425
plugins=[MarshmallowPlugin()],
2526
),
2627
'APISPEC_SWAGGER_URL': '/swagger/',
@@ -54,7 +55,8 @@ def init_app(self, app):
5455
self.app = app
5556
self.spec = self.app.config.get('APISPEC_SPEC') or \
5657
make_apispec(self.app.config.get('APISPEC_TITLE', 'flask-apispec'),
57-
self.app.config.get('APISPEC_VERSION', 'v1'))
58+
self.app.config.get('APISPEC_VERSION', 'v1'),
59+
self.app.config.get('APISPEC_OAS_VERSION', '2.0'))
5860
self.add_swagger_routes()
5961
self.resource_converter = ResourceConverter(self.app,
6062
self.spec,
@@ -148,12 +150,13 @@ def _register(self, target, endpoint=None, blueprint=None,
148150
else:
149151
raise TypeError()
150152
for path in paths:
151-
self.spec.add_path(**path)
153+
self.spec.path(**path)
152154

153155

154-
def make_apispec(title='flask-apispec', version='v1'):
156+
def make_apispec(title='flask-apispec', version='v1', openapi_version='2.0'):
155157
return APISpec(
156158
title=title,
157159
version=version,
160+
openapi_version=openapi_version,
158161
plugins=[MarshmallowPlugin()],
159162
)

flask_apispec/wrapper.py

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,30 @@
11
# -*- coding: utf-8 -*-
2+
from flask import Response
23

3-
from six.moves import http_client as http
4+
try:
5+
from collections.abc import Mapping
6+
except ImportError: # Python 2
7+
from collections import Mapping
48

59
import flask
6-
10+
import marshmallow as ma
711
import werkzeug
812
from webargs import flaskparser
913

1014
from flask_apispec import utils
1115

12-
import marshmallow as ma
13-
1416
MARSHMALLOW_VERSION_INFO = tuple(
1517
[int(part) for part in ma.__version__.split('.') if part.isdigit()]
1618
)
1719

20+
1821
class Wrapper(object):
1922
"""Apply annotations to a view function.
2023
2124
:param func: View function to wrap
2225
:param instance: Optional instance or parent
2326
"""
27+
2428
def __init__(self, func, instance=None):
2529
self.func = func
2630
self.instance = instance
@@ -29,9 +33,10 @@ def __call__(self, *args, **kwargs):
2933
response = self.call_view(*args, **kwargs)
3034
if isinstance(response, werkzeug.Response):
3135
return response
32-
unpacked = unpack(response)
33-
status_code = unpacked[1] or http.OK
34-
return self.marshal_result(unpacked, status_code)
36+
rv, status_code, headers = unpack(response)
37+
mv = self.marshal_result(rv, status_code)
38+
response = packed(mv, status_code, headers)
39+
return flask.current_app.make_response(response)
3540

3641
def call_view(self, *args, **kwargs):
3742
config = flask.current_app.config
@@ -43,32 +48,55 @@ def call_view(self, *args, **kwargs):
4348
parsed = parser.parse(schema, locations=option['kwargs']['locations'])
4449
if getattr(schema, 'many', False):
4550
args += tuple(parsed)
46-
else:
51+
elif isinstance(parsed, Mapping):
4752
kwargs.update(parsed)
53+
else:
54+
args += (parsed,)
55+
4856
return self.func(*args, **kwargs)
4957

50-
def marshal_result(self, unpacked, status_code):
58+
def marshal_result(self, result, status_code):
5159
config = flask.current_app.config
5260
format_response = config.get('APISPEC_FORMAT_RESPONSE', flask.jsonify) or identity
5361
annotation = utils.resolve_annotations(self.func, 'schemas', self.instance)
5462
schemas = utils.merge_recursive(annotation.options)
5563
schema = schemas.get(status_code, schemas.get('default'))
5664
if schema and annotation.apply is not False:
5765
schema = utils.resolve_schema(schema['schema'], request=flask.request)
58-
dumped = schema.dump(unpacked[0])
66+
dumped = schema.dump(result)
5967
output = dumped.data if MARSHMALLOW_VERSION_INFO[0] < 3 else dumped
6068
else:
61-
output = unpacked[0]
62-
return format_output((format_response(output), ) + unpacked[1:])
69+
output = result
70+
71+
return format_response(output) # type: Response
72+
6373

6474
def identity(value):
6575
return value
6676

77+
6778
def unpack(resp):
68-
resp = resp if isinstance(resp, tuple) else (resp, )
69-
return resp + (None, ) * (3 - len(resp))
79+
data = headers = None
80+
status_code = 200
81+
if isinstance(resp, tuple):
82+
data = resp[0]
83+
len_resp = len(resp)
84+
if len_resp == 3:
85+
status_code, headers = resp[1:]
86+
elif len_resp == 2:
87+
if isinstance(resp[1], (werkzeug.datastructures.Headers, dict, tuple, list)):
88+
headers = resp[1]
89+
else:
90+
status_code = resp[1]
91+
else:
92+
data = resp
93+
return data, status_code, headers
94+
7095

71-
def format_output(values):
72-
while values[-1] is None:
73-
values = values[:-1]
74-
return values if len(values) > 1 else values[0]
96+
def packed(data, status_code, headers):
97+
resp = (data, )
98+
if status_code:
99+
resp += (status_code, )
100+
if headers:
101+
resp += (headers, )
102+
return resp

package-lock.json

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

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "flask-apispec",
33
"version": "0.7.0",
44
"dependencies": {
5-
"swagger-ui-dist": "3.17.2"
5+
"swagger-ui-dist": "3.20.7"
66
},
77
"repository": {
88
"type": "git",

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ universal=1
1010
# E301: expected 1 blank line, found 1
1111
# E302: expected 2 blank lines, found 0
1212
[flake8]
13-
ignore = E127,E128,E265,E301,E302
13+
ignore = E127,E128,E265,E301,E302,W504
1414
max-line-length = 90

0 commit comments

Comments
 (0)