summaryrefslogtreecommitdiff
path: root/Lib/http/cookies.py
diff options
context:
space:
mode:
authorGeorg Brandl <georg@python.org>2010-07-31 21:04:00 +0000
committerGeorg Brandl <georg@python.org>2010-07-31 21:04:00 +0000
commited171c8ede76434bd6d6cd6a27e4962adc7dad2e (patch)
tree31531de5160552b6369d8c6d50f233008a560749 /Lib/http/cookies.py
parent9ac1cb7c1ddea2923a533caa5f570d3d1bd5d6e1 (diff)
downloadcpython-ed171c8ede76434bd6d6cd6a27e4962adc7dad2e.tar.gz
#3788: more tests for http.cookies, now at 95% coverage. Also bring coding style in the module up to PEP 8, where it does not break backwards compatibility.
Diffstat (limited to 'Lib/http/cookies.py')
-rw-r--r--Lib/http/cookies.py213
1 files changed, 103 insertions, 110 deletions
diff --git a/Lib/http/cookies.py b/Lib/http/cookies.py
index da30e253cf..c36c494579 100644
--- a/Lib/http/cookies.py
+++ b/Lib/http/cookies.py
@@ -46,7 +46,7 @@ At the moment, this is the only documentation.
The Basics
----------
-Importing is easy..
+Importing is easy...
>>> from http import cookies
@@ -127,19 +127,14 @@ the value to a string, when the values are set dictionary-style.
'Set-Cookie: number=7\r\nSet-Cookie: string=seven'
Finis.
-""" #"
-# ^
-# |----helps out font-lock
+"""
#
# Import our required modules
#
+import re
import string
-from pickle import dumps, loads
-
-import re, warnings
-
__all__ = ["CookieError", "BaseCookie", "SimpleCookie"]
_nulljoin = ''.join
@@ -235,7 +230,7 @@ def _quote(str, LegalChars=_LegalChars):
if all(c in LegalChars for c in str):
return str
else:
- return '"' + _nulljoin( map(_Translator.get, str, str) ) + '"'
+ return '"' + _nulljoin(map(_Translator.get, str, str)) + '"'
_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
@@ -244,7 +239,7 @@ _QuotePatt = re.compile(r"[\\].")
def _unquote(str):
# If there aren't any doublequotes,
# then there can't be any special characters. See RFC 2109.
- if len(str) < 2:
+ if len(str) < 2:
return str
if str[0] != '"' or str[-1] != '"':
return str
@@ -263,31 +258,32 @@ def _unquote(str):
n = len(str)
res = []
while 0 <= i < n:
- Omatch = _OctalPatt.search(str, i)
- Qmatch = _QuotePatt.search(str, i)
- if not Omatch and not Qmatch: # Neither matched
+ o_match = _OctalPatt.search(str, i)
+ q_match = _QuotePatt.search(str, i)
+ if not o_match and not q_match: # Neither matched
res.append(str[i:])
break
# else:
j = k = -1
- if Omatch: j = Omatch.start(0)
- if Qmatch: k = Qmatch.start(0)
- if Qmatch and ( not Omatch or k < j ): # QuotePatt matched
+ if o_match:
+ j = o_match.start(0)
+ if q_match:
+ k = q_match.start(0)
+ if q_match and (not o_match or k < j): # QuotePatt matched
res.append(str[i:k])
res.append(str[k+1])
- i = k+2
+ i = k + 2
else: # OctalPatt matched
res.append(str[i:j])
- res.append( chr( int(str[j+1:j+4], 8) ) )
- i = j+4
+ res.append(chr(int(str[j+1:j+4], 8)))
+ i = j + 4
return _nulljoin(res)
-# The _getdate() routine is used to set the expiration time in
-# the cookie's HTTP header. By default, _getdate() returns the
-# current time in the appropriate "expires" format for a
-# Set-Cookie header. The one optional argument is an offset from
-# now, in seconds. For example, an offset of -3600 means "one hour ago".
-# The offset may be a floating point number.
+# The _getdate() routine is used to set the expiration time in the cookie's HTTP
+# header. By default, _getdate() returns the current time in the appropriate
+# "expires" format for a Set-Cookie header. The one optional argument is an
+# offset from now, in seconds. For example, an offset of -3600 means "one hour
+# ago". The offset may be a floating point number.
#
_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
@@ -305,7 +301,7 @@ def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname):
class Morsel(dict):
- """A class to hold ONE key,value pair.
+ """A class to hold ONE (key, value) pair.
In a cookie, each such pair may have several attributes, so this class is
used to keep the attributes associated with the appropriate key,value pair.
@@ -326,23 +322,24 @@ class Morsel(dict):
# This dictionary provides a mapping from the lowercase
# variant on the left to the appropriate traditional
# formatting on the right.
- _reserved = { "expires" : "expires",
- "path" : "Path",
- "comment" : "Comment",
- "domain" : "Domain",
- "max-age" : "Max-Age",
- "secure" : "secure",
- "httponly" : "httponly",
- "version" : "Version",
- }
+ _reserved = {
+ "expires" : "expires",
+ "path" : "Path",
+ "comment" : "Comment",
+ "domain" : "Domain",
+ "max-age" : "Max-Age",
+ "secure" : "secure",
+ "httponly" : "httponly",
+ "version" : "Version",
+ }
def __init__(self):
# Set defaults
self.key = self.value = self.coded_value = None
# Set default attributes
- for K in self._reserved:
- dict.__setitem__(self, K, "")
+ for key in self._reserved:
+ dict.__setitem__(self, key, "")
def __setitem__(self, K, V):
K = K.lower()
@@ -362,18 +359,18 @@ class Morsel(dict):
raise CookieError("Illegal key value: %s" % key)
# It's a good key, so save it.
- self.key = key
- self.value = val
- self.coded_value = coded_val
+ self.key = key
+ self.value = val
+ self.coded_value = coded_val
- def output(self, attrs=None, header = "Set-Cookie:"):
- return "%s %s" % ( header, self.OutputString(attrs) )
+ def output(self, attrs=None, header="Set-Cookie:"):
+ return "%s %s" % (header, self.OutputString(attrs))
__str__ = output
def __repr__(self):
return '<%s: %s=%s>' % (self.__class__.__name__,
- self.key, repr(self.value) )
+ self.key, repr(self.value))
def js_output(self, attrs=None):
# Print javascript
@@ -383,34 +380,36 @@ class Morsel(dict):
document.cookie = \"%s\";
// end hiding -->
</script>
- """ % ( self.OutputString(attrs).replace('"',r'\"'))
+ """ % (self.OutputString(attrs).replace('"', r'\"'))
def OutputString(self, attrs=None):
# Build up our result
#
result = []
- RA = result.append
+ append = result.append
# First, the key=value pair
- RA("%s=%s" % (self.key, self.coded_value))
+ append("%s=%s" % (self.key, self.coded_value))
# Now add any defined attributes
if attrs is None:
attrs = self._reserved
items = sorted(self.items())
- for K,V in items:
- 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)))
- elif K == "max-age" and type(V) == type(1):
- RA("%s=%d" % (self._reserved[K], V))
- elif K == "secure":
- RA(str(self._reserved[K]))
- elif K == "httponly":
- RA(str(self._reserved[K]))
+ for key, value in items:
+ if value == "":
+ continue
+ if key not in attrs:
+ continue
+ if key == "expires" and isinstance(value, int):
+ append("%s=%s" % (self._reserved[key], _getdate(value)))
+ elif key == "max-age" and isinstance(value, int):
+ append("%s=%d" % (self._reserved[key], value))
+ elif key == "secure":
+ append(str(self._reserved[key]))
+ elif key == "httponly":
+ append(str(self._reserved[key]))
else:
- RA("%s=%s" % (self._reserved[K], V))
+ append("%s=%s" % (self._reserved[key], value))
# Return the result
return _semispacejoin(result)
@@ -426,24 +425,23 @@ class Morsel(dict):
#
_LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]"
-_CookiePattern = re.compile(
- r"(?x)" # This is a Verbose pattern
- r"(?P<key>" # 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<val>" # 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
- , re.ASCII) # May be removed if safe.
-
-
-# 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.
+_CookiePattern = re.compile(r"""
+ (?x) # This is a verbose pattern
+ (?P<key> # Start of group 'key'
+ """ + _LegalCharsPatt + r"""+? # Any word of at least one letter
+ ) # End of group 'key'
+ \s*=\s* # Equal Sign
+ (?P<val> # Start of group 'val'
+ "(?:[^\\"]|\\.)*" # Any doublequoted string
+ | # or
+ """ + _LegalCharsPatt + r"""* # Any word or empty string
+ ) # End of group 'val'
+ \s*;? # Probably ending in a semi-colon
+ """, re.ASCII) # May be removed if safe.
+
+
+# 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."""
@@ -467,7 +465,8 @@ class BaseCookie(dict):
return strval, strval
def __init__(self, input=None):
- if input: self.load(input)
+ if input:
+ self.load(input)
def __set(self, key, real_value, coded_value):
"""Private method for setting a cookie's value"""
@@ -484,25 +483,25 @@ class BaseCookie(dict):
"""Return a string suitable for HTTP."""
result = []
items = sorted(self.items())
- for K,V in items:
- result.append( V.output(attrs, header) )
+ for key, value in items:
+ result.append(value.output(attrs, header))
return sep.join(result)
__str__ = output
def __repr__(self):
- L = []
+ l = []
items = sorted(self.items())
- for K,V in items:
- L.append( '%s=%s' % (K,repr(V.value) ) )
- return '<%s: %s>' % (self.__class__.__name__, _spacejoin(L))
+ for key, value in items:
+ l.append('%s=%s' % (key, repr(value.value)))
+ return '<%s: %s>' % (self.__class__.__name__, _spacejoin(l))
def js_output(self, attrs=None):
"""Return a string suitable for JavaScript."""
result = []
items = sorted(self.items())
- for K,V in items:
- result.append( V.js_output(attrs) )
+ for key, value in items:
+ result.append(value.js_output(attrs))
return _nulljoin(result)
def load(self, rawdata):
@@ -511,15 +510,15 @@ class BaseCookie(dict):
is equivalent to calling:
map(Cookie.__setitem__, d.keys(), d.values())
"""
- if type(rawdata) == type(""):
- self.__ParseString(rawdata)
+ if isinstance(rawdata, str):
+ self.__parse_string(rawdata)
else:
# self.update() wouldn't call our custom __setitem__
- for k, v in rawdata.items():
- self[k] = v
+ for key, value in rawdata.items():
+ self[key] = value
return
- def __ParseString(self, str, patt=_CookiePattern):
+ def __parse_string(self, str, patt=_CookiePattern):
i = 0 # Our starting point
n = len(str) # Length of string
M = None # current morsel
@@ -527,25 +526,27 @@ class BaseCookie(dict):
while 0 <= i < n:
# Start looking for a cookie
match = patt.search(str, i)
- if not match: break # No more cookies
+ if not match:
+ # No more cookies
+ break
- K,V = match.group("key"), match.group("val")
+ key, value = match.group("key"), match.group("val")
i = match.end(0)
# Parse the key, value in case it's metainfo
- if K[0] == "$":
+ if key[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:
+ M[key[1:]] = value
+ elif key.lower() in Morsel._reserved:
if M:
- M[ K ] = _unquote(V)
+ M[key] = _unquote(value)
else:
- rval, cval = self.value_decode(V)
- self.__set(K, rval, cval)
- M = self[K]
+ rval, cval = self.value_decode(value)
+ self.__set(key, rval, cval)
+ M = self[key]
class SimpleCookie(BaseCookie):
@@ -556,16 +557,8 @@ class SimpleCookie(BaseCookie):
received from HTTP are kept as strings.
"""
def value_decode(self, val):
- return _unquote( val ), val
+ return _unquote(val), val
+
def value_encode(self, val):
strval = str(val)
- return strval, _quote( strval )
-
-###########################################################
-
-def _test():
- import doctest, http.cookies
- return doctest.testmod(http.cookies)
-
-if __name__ == "__main__":
- _test()
+ return strval, _quote(strval)