Skip to content

Commit aa87289

Browse files
authored
Merge pull request #184 from c-kruse/feature/marshal_with_returns_response
Return Response Object from View Functions
2 parents 57ef362 + 11337cf commit aa87289

File tree

3 files changed

+73
-17
lines changed

3 files changed

+73
-17
lines changed

CHANGELOG.rst

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

4+
0.8.8 (unreleased)
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+
414
0.8.7 (2020-03-10)
515
******************
616

flask_apispec/wrapper.py

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ def __call__(self, *args, **kwargs):
3434
response = self.call_view(*args, **kwargs)
3535
if isinstance(response, werkzeug.Response):
3636
return response
37-
unpacked = unpack(response)
38-
status_code = unpacked[1] or http.OK
39-
return self.marshal_result(unpacked, status_code)
37+
rv, status_code, headers = unpack(response)
38+
mv = self.marshal_result(rv, status_code)
39+
response = packed(mv, status_code, headers)
40+
return flask.current_app.make_response(response)
4041

4142
def call_view(self, *args, **kwargs):
4243
config = flask.current_app.config
@@ -55,34 +56,48 @@ def call_view(self, *args, **kwargs):
5556

5657
return self.func(*args, **kwargs)
5758

58-
def marshal_result(self, unpacked, status_code):
59+
def marshal_result(self, result, status_code):
5960
config = flask.current_app.config
6061
format_response = config.get('APISPEC_FORMAT_RESPONSE', flask.jsonify) or identity
6162
annotation = utils.resolve_annotations(self.func, 'schemas', self.instance)
6263
schemas = utils.merge_recursive(annotation.options)
6364
schema = schemas.get(status_code, schemas.get('default'))
6465
if schema and annotation.apply is not False:
6566
schema = utils.resolve_schema(schema['schema'], request=flask.request)
66-
dumped = schema.dump(unpacked[0])
67+
dumped = schema.dump(result)
6768
output = dumped.data if MARSHMALLOW_VERSION_INFO[0] < 3 else dumped
6869
else:
69-
output = unpacked[0]
70+
output = result
7071

71-
response_object = format_response(output) # type: Response
72-
response_object.status_code = unpacked[1] or 200
73-
return format_output((response_object,))
72+
return format_response(output) # type: Response
7473

7574

7675
def identity(value):
7776
return value
7877

7978

8079
def unpack(resp):
81-
resp = resp if isinstance(resp, tuple) else (resp,)
82-
return resp + (None,) * (3 - len(resp))
83-
84-
85-
def format_output(values):
86-
while values[-1] is None:
87-
values = values[:-1]
88-
return values if len(values) > 1 else values[0]
80+
data = headers = None
81+
status_code = 200
82+
if isinstance(resp, tuple):
83+
data = resp[0]
84+
len_resp = len(resp)
85+
if len_resp == 3:
86+
status_code, headers = resp[1:]
87+
elif len_resp == 2:
88+
if isinstance(resp[1], (werkzeug.datastructures.Headers, dict, tuple, list)):
89+
headers = resp[1]
90+
else:
91+
status_code = resp[1]
92+
else:
93+
data = resp
94+
return data, status_code, headers
95+
96+
97+
def packed(data, status_code, headers):
98+
resp = (data, )
99+
if status_code:
100+
resp += (status_code, )
101+
if headers:
102+
resp += (headers, )
103+
return resp

tests/test_views.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,37 @@ def view(**kwargs):
1919
res = client.get('/', {'name': 'freddie'})
2020
assert res.json == {'name': 'freddie'}
2121

22+
def test_view_returning_tuple(self, app, client):
23+
@app.route('/all')
24+
@use_kwargs({'name': fields.Str()})
25+
def all(**kwargs):
26+
return kwargs, 202, {'x-msg': 'test'}
27+
28+
@app.route('/headers')
29+
@use_kwargs({'name': fields.Str()})
30+
def view_headers(**kwargs):
31+
return kwargs, {'x-msg': 'test'}
32+
33+
@app.route('/code')
34+
@use_kwargs({'name': fields.Str()})
35+
def view_code(**kwargs):
36+
return kwargs, 202
37+
38+
res_all = client.get('/all', {'name': 'freddie'})
39+
assert res_all.json == {'name': 'freddie'}
40+
assert res_all.status_code == 202
41+
assert res_all.headers.get('x-msg') == 'test'
42+
43+
res_headers = client.get('/headers', {'name': 'freddie'})
44+
assert res_headers.json == {'name': 'freddie'}
45+
assert res_headers.status_code == 200
46+
assert res_headers.headers.get('x-msg') == 'test'
47+
48+
res_code = client.get('/code', {'name': 'freddie'})
49+
assert res_code.json == {'name': 'freddie'}
50+
assert res_code.status_code == 202
51+
assert 'x-msg' not in res_code.headers
52+
2253
def test_use_kwargs_schema(self, app, client):
2354
class ArgSchema(Schema):
2455
name = fields.Str()

0 commit comments

Comments
 (0)