Repository: tornadoweb/tornado
Pull request #2046 (2017-05-20 16:31:29)

web: Clarify cookie method docs
Be explicit that get_cookie cannot see the cookies set within the
current request, avoiding confusion as seen in #1993


Patch:
diff --git a/tornado/web.py b/tornado/web.py
index 132f42dd..64425b94 100644
--- a/tornado/web.py
+++ b/tornado/web.py
@@ -319,7 +319,10 @@ class RequestHandler(object):
         if reason is not None:
             self._reason = escape.native_str(reason)
         else:
-            self._reason = httputil.responses.get(status_code, "Unknown")
+            try:
+                self._reason = httputil.responses[status_code]
+            except KeyError:
+                raise ValueError("unknown status code %d" % status_code)
 
     def get_status(self):
         """Returns the status code for our response."""
@@ -518,18 +521,28 @@ class RequestHandler(object):
         return self.request.cookies
 
     def get_cookie(self, name, default=None):
-        """Gets the value of the cookie with the given name, else default."""
+        """Returns the value of the request cookie with the given name.
+
+        If the named cookie is not present, returns ``default``.
+
+        This method only returns cookies that were present in the request.
+        It does not see the outgoing cookies set by `set_cookie` in this
+        handler.
+        """
         if self.request.cookies is not None and name in self.request.cookies:
             return self.request.cookies[name].value
         return default
 
     def set_cookie(self, name, value, domain=None, expires=None, path="/",
                    expires_days=None, **kwargs):
-        """Sets the given cookie name/value with the given options.
+        """Sets an outgoing cookie name/value with the given options.
+
+        Newly-set cookies are not immediately visible via `get_cookie`;
+        they are not present until the next request.
 
-        Additional keyword arguments are set on the Cookie.Morsel
+        Additional keyword arguments are set on the cookies.Morsel
         directly.
-        See https://docs.python.org/2/library/cookie.html#Cookie.Morsel
+        See https://docs.python.org/3/library/http.cookies.html#http.cookies.Morsel
         for available attributes.
         """
         # The cookie library only accepts type str, in both python 2 and 3
@@ -571,6 +584,9 @@ class RequestHandler(object):
         path and domain to clear a cookie as were used when that cookie
         was set (but there is no way to find out on the server side
         which values were used for a given cookie).
+
+        Similar to `set_cookie`, the effect of this method will not be
+        seen until the following request.
         """
         expires = datetime.datetime.utcnow() - datetime.timedelta(days=365)
         self.set_cookie(name, value="", path=path, expires=expires,
@@ -582,6 +598,9 @@ class RequestHandler(object):
         See `clear_cookie` for more information on the path and domain
         parameters.
 
+        Similar to `set_cookie`, the effect of this method will not be
+        seen until the following request.
+
         .. versionchanged:: 3.2
 
            Added the ``path`` and ``domain`` parameters.
@@ -606,6 +625,9 @@ class RequestHandler(object):
         Secure cookies may contain arbitrary byte values, not just unicode
         strings (unlike regular cookies)
 
+        Similar to `set_cookie`, the effect of this method will not be
+        seen until the following request.
+
         .. versionchanged:: 3.2.1
 
            Added the ``version`` argument.  Introduced cookie version 2
@@ -645,6 +667,10 @@ class RequestHandler(object):
         The decoded cookie value is returned as a byte string (unlike
         `get_cookie`).
 
+        Similar to `get_cookie`, this method only returns cookies that
+        were present in the request. It does not see outgoing cookies set by
+        `set_secure_cookie` in this handler.
+
         .. versionchanged:: 3.2.1
 
            Added the ``min_version`` argument.  Introduced cookie version 2;
@@ -1558,7 +1584,11 @@ class RequestHandler(object):
             # send a response.
             return
         if isinstance(e, HTTPError):
-            self.send_error(e.status_code, exc_info=sys.exc_info())
+            if e.status_code not in httputil.responses and not e.reason:
+                gen_log.error("Bad HTTP status code: %d", e.status_code)
+                self.send_error(500, exc_info=sys.exc_info())
+            else:
+                self.send_error(e.status_code, exc_info=sys.exc_info())
         else:
             self.send_error(500, exc_info=sys.exc_info())
 


Test patch:
diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py
index de26c42e..d79ea52c 100644
--- a/tornado/test/web_test.py
+++ b/tornado/test/web_test.py
@@ -917,10 +917,6 @@ class ErrorResponseTest(WebTestCase):
             self.assertEqual(response.code, 503)
             self.assertTrue(b"503: Service Unavailable" in response.body)
 
-            response = self.fetch("/default?status=435")
-            self.assertEqual(response.code, 435)
-            self.assertTrue(b"435: Unknown" in response.body)
-
     def test_write_error(self):
         with ExpectLog(app_log, "Uncaught exception"):
             response = self.fetch("/write_error")
@@ -1493,9 +1489,9 @@ class StatusReasonTest(SimpleHandlerTestCase):
         response = self.fetch("/?code=682&reason=Bar")
         self.assertEqual(response.code, 682)
         self.assertEqual(response.reason, "Bar")
-        response = self.fetch("/?code=682")
-        self.assertEqual(response.code, 682)
-        self.assertEqual(response.reason, "Unknown")
+        with ExpectLog(app_log, 'Uncaught exception'):
+            response = self.fetch("/?code=682")
+        self.assertEqual(response.code, 500)
 
 
 @wsgi_safe