From 70f8b39c52527573e65e7066024a187867564c01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85hl=C3=A9n?= Date: Tue, 31 Dec 2013 22:16:13 +0100 Subject: [PATCH 1/2] added a new behaviour for responses that enable the tuple to be in the form of (response, headers) and continiue to support the (response, status, headers) format. --- docs/quickstart.rst | 6 +++--- flask/app.py | 23 ++++++++++++++--------- flask/testsuite/basic.py | 22 ++++++++++++++++++++++ 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 3cb9b2f765..c8a1140b47 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -676,9 +676,9 @@ converting return values into response objects is as follows: default parameters. 3. If a tuple is returned the items in the tuple can provide extra information. Such tuples have to be in the form ``(response, status, - headers)`` where at least one item has to be in the tuple. The - `status` value will override the status code and `headers` can be a - list or dictionary of additional header values. + headers)`` or ``(response, headers)`` where at least one item has + to be in the tuple. The `status` value will override the status code + and `headers` can be a list or dictionary of additional header values. 4. If none of that works, Flask will assume the return value is a valid WSGI application and convert that into a response object. diff --git a/flask/app.py b/flask/app.py index 42a1943dfc..ea31393fed 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1544,7 +1544,8 @@ def make_response(self, rv): a WSGI function the function is called as WSGI application and buffered as response object :class:`tuple` A tuple in the form ``(response, status, - headers)`` where `response` is any of the + headers)`` or ``(response, headers)`` + where `response` is any of the types defined here, `status` is a string or an integer and `headers` is a list or a dictionary with header values. @@ -1556,34 +1557,38 @@ def make_response(self, rv): Previously a tuple was interpreted as the arguments for the response object. """ - status = headers = None + status_or_headers = headers = None if isinstance(rv, tuple): - rv, status, headers = rv + (None,) * (3 - len(rv)) + rv, status_or_headers, headers = rv + (None,) * (3 - len(rv)) if rv is None: raise ValueError('View function did not return a response') + if isinstance(status_or_headers, (dict, list)): + headers, status_or_headers = status_or_headers, None + if not isinstance(rv, self.response_class): # When we create a response object directly, we let the constructor # set the headers and status. We do this because there can be # some extra logic involved when creating these objects with # specific values (like default content type selection). if isinstance(rv, (text_type, bytes, bytearray)): - rv = self.response_class(rv, headers=headers, status=status) - headers = status = None + rv = self.response_class(rv, headers=headers, status=status_or_headers) + headers = status_or_headers = None else: rv = self.response_class.force_type(rv, request.environ) - if status is not None: - if isinstance(status, string_types): - rv.status = status + if status_or_headers is not None: + if isinstance(status_or_headers, string_types): + rv.status = status_or_headers else: - rv.status_code = status + rv.status_code = status_or_headers if headers: rv.headers.extend(headers) return rv + def create_url_adapter(self, request): """Creates a URL adapter for the given request. The URL adapter is created at a point where the request context is not yet set up diff --git a/flask/testsuite/basic.py b/flask/testsuite/basic.py index 71a1f832f8..be11fa6ba5 100644 --- a/flask/testsuite/basic.py +++ b/flask/testsuite/basic.py @@ -735,7 +735,17 @@ def from_tuple(): return 'Meh', 400, { 'X-Foo': 'Testing', 'Content-Type': 'text/plain; charset=utf-8' + } + @app.route("/two_args") + def from_two_args_tuple(): + return "Hello", { + 'X-Foo': 'Test', + 'Content-Type': 'text/plain; charset=utf-8' } + @app.route("/args_status") + def from_status_tuple(): + return "Hi, status!", 400 + c = app.test_client() self.assert_equal(c.get('/unicode').data, u'Hällo Wörld'.encode('utf-8')) self.assert_equal(c.get('/string').data, u'Hällo Wörld'.encode('utf-8')) @@ -745,6 +755,18 @@ def from_tuple(): self.assert_equal(rv.status_code, 400) self.assert_equal(rv.mimetype, 'text/plain') + rv2 = c.get("/two_args") + self.assert_equal(rv2.data, b'Hello') + self.assert_equal(rv2.headers['X-Foo'], 'Test') + self.assert_equal(rv2.status_code, 200) + self.assert_equal(rv2.mimetype, 'text/plain') + + rv3 = c.get("/args_status") + self.assert_equal(rv3.data, b'Hi, status!') + self.assert_equal(rv3.status_code, 400) + self.assert_equal(rv3.mimetype, 'text/html') + + def test_make_response(self): app = flask.Flask(__name__) with app.test_request_context(): From dbc40961913381ecf1513fa2467aefeef6d4d41b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85hl=C3=A9n?= Date: Tue, 31 Dec 2013 22:59:00 +0100 Subject: [PATCH 2/2] added a new test case with a Response instance and cleaned up the code --- flask/testsuite/basic.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/flask/testsuite/basic.py b/flask/testsuite/basic.py index be11fa6ba5..902a23f34f 100644 --- a/flask/testsuite/basic.py +++ b/flask/testsuite/basic.py @@ -736,15 +736,21 @@ def from_tuple(): 'X-Foo': 'Testing', 'Content-Type': 'text/plain; charset=utf-8' } - @app.route("/two_args") + @app.route('/two_args') def from_two_args_tuple(): - return "Hello", { + return 'Hello', { 'X-Foo': 'Test', 'Content-Type': 'text/plain; charset=utf-8' } - @app.route("/args_status") + @app.route('/args_status') def from_status_tuple(): - return "Hi, status!", 400 + return 'Hi, status!', 400 + @app.route('/args_header') + def from_response_instance_status_tuple(): + return flask.Response('Hello world', 404), { + "X-Foo": "Bar", + "X-Bar": "Foo" + } c = app.test_client() self.assert_equal(c.get('/unicode').data, u'Hällo Wörld'.encode('utf-8')) @@ -754,18 +760,20 @@ def from_status_tuple(): self.assert_equal(rv.headers['X-Foo'], 'Testing') self.assert_equal(rv.status_code, 400) self.assert_equal(rv.mimetype, 'text/plain') - - rv2 = c.get("/two_args") + rv2 = c.get('/two_args') self.assert_equal(rv2.data, b'Hello') self.assert_equal(rv2.headers['X-Foo'], 'Test') self.assert_equal(rv2.status_code, 200) self.assert_equal(rv2.mimetype, 'text/plain') - - rv3 = c.get("/args_status") + rv3 = c.get('/args_status') self.assert_equal(rv3.data, b'Hi, status!') self.assert_equal(rv3.status_code, 400) self.assert_equal(rv3.mimetype, 'text/html') - + rv4 = c.get('/args_header') + self.assert_equal(rv4.data, b'Hello world') + self.assert_equal(rv4.headers['X-Foo'], 'Bar') + self.assert_equal(rv4.headers['X-Bar'], 'Foo') + self.assert_equal(rv4.status_code, 404) def test_make_response(self): app = flask.Flask(__name__)