diff --git a/Doc/lib/libcookie.tex b/Doc/lib/libcookie.tex index bba9c79ed93..39b9cb4ebce 100644 --- a/Doc/lib/libcookie.tex +++ b/Doc/lib/libcookie.tex @@ -98,7 +98,9 @@ In general, it should be the case that \method{value_encode()} and Return a string representation suitable to be sent as HTTP headers. \var{attrs} and \var{header} are sent to each \class{Morsel}'s \method{output()} method. \var{sep} is used to join the headers -together, and is by default a newline. +together, and is by default the combination '\r\n' (CRLF). +\versionchanged[The default separator has been changed from '\n' to match the cookie +specification]{2.5} \end{methoddesc} \begin{methoddesc}[BaseCookie]{js_output}{\optional{attrs}} @@ -195,32 +197,32 @@ The following example demonstrates how to use the \module{Cookie} module. >>> C["fig"] = "newton" >>> C["sugar"] = "wafer" >>> print C # generate HTTP headers -Set-Cookie: sugar=wafer; -Set-Cookie: fig=newton; +Set-Cookie: sugar=wafer +Set-Cookie: fig=newton >>> print C.output() # same thing -Set-Cookie: sugar=wafer; -Set-Cookie: fig=newton; +Set-Cookie: sugar=wafer +Set-Cookie: fig=newton >>> C = Cookie.SmartCookie() >>> C["rocky"] = "road" >>> C["rocky"]["path"] = "/cookie" >>> print C.output(header="Cookie:") -Cookie: rocky=road; Path=/cookie; +Cookie: rocky=road; Path=/cookie >>> print C.output(attrs=[], header="Cookie:") -Cookie: rocky=road; +Cookie: rocky=road >>> C = Cookie.SmartCookie() >>> C.load("chips=ahoy; vienna=finger") # load from a string (HTTP header) >>> print C -Set-Cookie: vienna=finger; -Set-Cookie: chips=ahoy; +Set-Cookie: vienna=finger +Set-Cookie: chips=ahoy >>> C = Cookie.SmartCookie() >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";') >>> print C -Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"; +Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;" >>> C = Cookie.SmartCookie() >>> C["oreo"] = "doublestuff" >>> C["oreo"]["path"] = "/" >>> print C -Set-Cookie: oreo=doublestuff; Path=/; +Set-Cookie: oreo=doublestuff; Path=/ >>> C = Cookie.SmartCookie() >>> C["twix"] = "none for you" >>> C["twix"].value @@ -233,8 +235,8 @@ Set-Cookie: oreo=doublestuff; Path=/; >>> C["string"].value 'seven' >>> print C -Set-Cookie: number=7; -Set-Cookie: string=seven; +Set-Cookie: number=7 +Set-Cookie: string=seven >>> C = Cookie.SerialCookie() >>> C["number"] = 7 >>> C["string"] = "seven" @@ -243,8 +245,8 @@ Set-Cookie: string=seven; >>> C["string"].value 'seven' >>> print C -Set-Cookie: number="I7\012."; -Set-Cookie: string="S'seven'\012p1\012."; +Set-Cookie: number="I7\012." +Set-Cookie: string="S'seven'\012p1\012." >>> C = Cookie.SmartCookie() >>> C["number"] = 7 >>> C["string"] = "seven" @@ -253,6 +255,6 @@ Set-Cookie: string="S'seven'\012p1\012."; >>> C["string"].value 'seven' >>> print C -Set-Cookie: number="I7\012."; -Set-Cookie: string=seven; +Set-Cookie: number="I7\012." +Set-Cookie: string=seven \end{verbatim} diff --git a/Lib/Cookie.py b/Lib/Cookie.py index 4661d7c3ffa..fa2e05ec572 100644 --- a/Lib/Cookie.py +++ b/Lib/Cookie.py @@ -69,9 +69,8 @@ a dictionary. >>> C = Cookie.SmartCookie() >>> C["fig"] = "newton" >>> C["sugar"] = "wafer" - >>> print C - Set-Cookie: fig=newton; - Set-Cookie: sugar=wafer; + >>> C.output() + 'Set-Cookie: fig=newton\r\nSet-Cookie: sugar=wafer' Notice that the printable representation of a Cookie is the appropriate format for a Set-Cookie: header. This is the @@ -82,9 +81,9 @@ attributes by using the .output() function >>> C["rocky"] = "road" >>> C["rocky"]["path"] = "/cookie" >>> print C.output(header="Cookie:") - Cookie: rocky=road; Path=/cookie; + Cookie: rocky=road; Path=/cookie >>> print C.output(attrs=[], header="Cookie:") - Cookie: rocky=road; + Cookie: rocky=road The load() method of a Cookie extracts cookies from a string. In a CGI script, you would use this method to extract the cookies from the @@ -92,9 +91,8 @@ HTTP_COOKIE environment variable. >>> C = Cookie.SmartCookie() >>> C.load("chips=ahoy; vienna=finger") - >>> print C - Set-Cookie: chips=ahoy; - Set-Cookie: vienna=finger; + >>> C.output() + 'Set-Cookie: chips=ahoy\r\nSet-Cookie: vienna=finger' The load() method is darn-tootin smart about identifying cookies within a string. Escaped quotation marks, nested semicolons, and other @@ -103,7 +101,7 @@ such trickeries do not confuse it. >>> C = Cookie.SmartCookie() >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";') >>> print C - Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"; + Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;" Each element of the Cookie also supports all of the RFC 2109 Cookie attributes. Here's an example which sets the Path @@ -113,7 +111,7 @@ attribute. >>> C["oreo"] = "doublestuff" >>> C["oreo"]["path"] = "/" >>> print C - Set-Cookie: oreo=doublestuff; Path=/; + Set-Cookie: oreo=doublestuff; Path=/ Each dictionary element has a 'value' attribute, which gives you back the value associated with the key. @@ -144,9 +142,8 @@ the value to a string, when the values are set dictionary-style. '7' >>> C["string"].value 'seven' - >>> print C - Set-Cookie: number=7; - Set-Cookie: string=seven; + >>> C.output() + 'Set-Cookie: number=7\r\nSet-Cookie: string=seven' SerialCookie @@ -165,9 +162,8 @@ values, however.) 7 >>> C["string"].value 'seven' - >>> print C - Set-Cookie: number="I7\012."; - Set-Cookie: string="S'seven'\012p1\012."; + >>> C.output() + 'Set-Cookie: number="I7\\012."\r\nSet-Cookie: string="S\'seven\'\\012p1\\012."' Be warned, however, if SerialCookie cannot de-serialize a value (because it isn't a valid pickle'd object), IT WILL RAISE AN EXCEPTION. @@ -190,9 +186,8 @@ as a string. 7 >>> C["string"].value 'seven' - >>> print C - Set-Cookie: number="I7\012."; - Set-Cookie: string=seven; + >>> C.output() + 'Set-Cookie: number="I7\\012."\r\nSet-Cookie: string=seven' Backwards Compatibility @@ -228,7 +223,7 @@ __all__ = ["CookieError","BaseCookie","SimpleCookie","SerialCookie", "SmartCookie","Cookie"] _nulljoin = ''.join -_spacejoin = ' '.join +_semispacejoin = '; '.join # # Define an exception visible to External modules @@ -485,7 +480,7 @@ class Morsel(dict): RA = result.append # First, the key=value pair - RA("%s=%s;" % (self.key, self.coded_value)) + RA("%s=%s" % (self.key, self.coded_value)) # Now add any defined attributes if attrs is None: @@ -496,16 +491,16 @@ class Morsel(dict): if V == "": continue if K not in attrs: continue if K == "expires" and type(V) == type(1): - RA("%s=%s;" % (self._reserved[K], _getdate(V))) + RA("%s=%s" % (self._reserved[K], _getdate(V))) elif K == "max-age" and type(V) == type(1): - RA("%s=%d;" % (self._reserved[K], V)) + RA("%s=%d" % (self._reserved[K], V)) elif K == "secure": - RA("%s;" % self._reserved[K]) + RA(str(self._reserved[K])) else: - RA("%s=%s;" % (self._reserved[K], V)) + RA("%s=%s" % (self._reserved[K], V)) # Return the result - return _spacejoin(result) + return _semispacejoin(result) # end OutputString # end Morsel class @@ -581,7 +576,7 @@ class BaseCookie(dict): self.__set(key, rval, cval) # end __setitem__ - def output(self, attrs=None, header="Set-Cookie:", sep="\n"): + def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"): """Return a string suitable for HTTP.""" result = [] items = self.items() @@ -599,7 +594,7 @@ class BaseCookie(dict): items.sort() for K,V in items: L.append( '%s=%s' % (K,repr(V.value) ) ) - return '<%s: %s>' % (self.__class__.__name__, _spacejoin(L)) + return '<%s: %s>' % (self.__class__.__name__, _semispacejoin(L)) def js_output(self, attrs=None): """Return a string suitable for JavaScript.""" diff --git a/Misc/NEWS b/Misc/NEWS index fab6163a58a..20e1f8938d6 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -193,6 +193,9 @@ Extension Modules Library ------- +- Patch #848017: Make Cookie more RFC-compliant. Use CRLF as default output + separator and do not output trailing semicola. + - Patch #1062060: urllib.urlretrieve() now raises a new exception, named ContentTooShortException, when the actually downloaded size does not match the Content-Length header.