Index: Lib/Cookie.py =================================================================== --- Lib/Cookie.py (revision 69625) +++ Lib/Cookie.py (working copy) @@ -517,34 +517,47 @@ # # Pattern for finding cookie # # This used to be strict parsing based on the RFC2109 and RFC2068 # specifications. I have since discovered that MSIE 3.0x doesn't # follow the character rules outlined in those specs. As a # result, the parsing rules here are less strict. # -_LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]" -_CookiePattern = re.compile( - r"(?x)" # This is a Verbose pattern - r"(?P" # Start of group 'key' - ""+ _LegalCharsPatt +"+?" # Any word of at least one letter, nongreedy - r")" # End of group 'key' - r"\s*=\s*" # Equal Sign - r"(?P" # Start of group 'val' - r'"(?:[^\\"]|\\.)*"' # Any doublequoted string - r"|" # or - ""+ _LegalCharsPatt +"*" # Any word or empty string - r")" # End of group 'val' - r"\s*;?" # Probably ending in a semi-colon +_LegalCharsPatt = r"\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=" +_MorselAndNextKeyPattern = re.compile(r""" + ;?,?\s* # Possible separator from preceding morsel. + (?P + [%s]+? # Any word of at least one letter, nongreedy ) + \s*=\s* # Equal Sign + (?P + "(?:[^\\"]|\\.)*" # Any doublequoted string + | # or + [%s\ ]*? # Any word or empty string + ) + # Match against the start of the next morsel (or end of the string) to + # be able to distinguish between ' ' and ',' being part of this morsel's + # value or the separator for the next morsel. + \s* + (?P # The start of the next morsel + (;,|;|,) # ... a separator + \s*[%s]+? # ... the key + \s*=\s* # ... the equal sign + | # or + ;?,? + \Z # the end of the string. + ) + """ % (_LegalCharsPatt, _LegalCharsPatt, _LegalCharsPatt), + re.VERBOSE) + # At long last, here is the cookie class. # Using this class is almost just like using a dictionary. # See this module's docstring for example usage. # class BaseCookie(dict): # A container class for a set of Morsels # @@ -621,32 +634,32 @@ is equivalent to calling: map(Cookie.__setitem__, d.keys(), d.values()) """ if type(rawdata) == type(""): self.__ParseString(rawdata) else: self.update(rawdata) return # end load() - def __ParseString(self, str, patt=_CookiePattern): + def __ParseString(self, str, patt=_MorselAndNextKeyPattern): i = 0 # Our starting point n = len(str) # Length of string M = None # current morsel while 0 <= i < n: # Start looking for a cookie match = patt.search(str, i) if not match: break # No more cookies - K,V = match.group("key"), match.group("val") - i = match.end(0) + K,V = match.group("key").strip(), match.group("val").strip() + i = match.start("next") # Parse the key, value in case it's metainfo if K[0] == "$": # We ignore attributes which pertain to the cookie # mechanism as a whole. See RFC 2109. # (Does anyone care?) if M: M[ K[1:] ] = V elif K.lower() in Morsel._reserved: if M: Index: Lib/test/test_cookie.py =================================================================== --- Lib/test/test_cookie.py (revision 69625) +++ Lib/test/test_cookie.py (working copy) @@ -23,21 +23,28 @@ 'dict': {'keebler' : 'E=mc2; L="Loves"; fudge=\012;'}, 'repr': '''''', 'output': 'Set-Cookie: keebler="E=mc2; L=\\"Loves\\"; fudge=\\012;"', }, # Check illegal cookies that have an '=' char in an unquoted value { 'data': 'keebler=E=mc2', 'dict': {'keebler' : 'E=mc2'}, 'repr': "", 'output': 'Set-Cookie: keebler=E=mc2', - } + }, + + # Check handling of spaces in 'expires' attribute. + { 'data': 'test=expiry; expires=Thu, 10 Apr 1980 16:33:12 GMT', + 'dict': {'test': 'expiry'}, + 'repr': "", + 'output': 'Set-Cookie: test=expiry; expires=Thu, 10 Apr 1980 16:33:12 GMT', + }, ] for case in cases: C = Cookie.SimpleCookie() C.load(case['data']) self.assertEqual(repr(C), case['repr']) self.assertEqual(C.output(sep='\n'), case['output']) for k, v in sorted(case['dict'].iteritems()): self.assertEqual(C[k].value, v)