Catch situations where currentframe() returns None. See SF patch #1447410, this is...
[python.git] / Lib / _LWPCookieJar.py
blob6d5ce185e55b29a3f1341fe03a585fb02a2e8842
1 """Load / save to libwww-perl (LWP) format files.
3 Actually, the format is slightly extended from that used by LWP's
4 (libwww-perl's) HTTP::Cookies, to avoid losing some RFC 2965 information
5 not recorded by LWP.
7 It uses the version string "2.0", though really there isn't an LWP Cookies
8 2.0 format. This indicates that there is extra information in here
9 (domain_dot and # port_spec) while still being compatible with
10 libwww-perl, I hope.
12 """
14 import time, re, logging
15 from cookielib import (reraise_unmasked_exceptions, FileCookieJar, LoadError,
16 Cookie, MISSING_FILENAME_TEXT, join_header_words, split_header_words,
17 iso2time, time2isoz)
19 def lwp_cookie_str(cookie):
20 """Return string representation of Cookie in an the LWP cookie file format.
22 Actually, the format is extended a bit -- see module docstring.
24 """
25 h = [(cookie.name, cookie.value),
26 ("path", cookie.path),
27 ("domain", cookie.domain)]
28 if cookie.port is not None: h.append(("port", cookie.port))
29 if cookie.path_specified: h.append(("path_spec", None))
30 if cookie.port_specified: h.append(("port_spec", None))
31 if cookie.domain_initial_dot: h.append(("domain_dot", None))
32 if cookie.secure: h.append(("secure", None))
33 if cookie.expires: h.append(("expires",
34 time2isoz(float(cookie.expires))))
35 if cookie.discard: h.append(("discard", None))
36 if cookie.comment: h.append(("comment", cookie.comment))
37 if cookie.comment_url: h.append(("commenturl", cookie.comment_url))
39 keys = cookie._rest.keys()
40 keys.sort()
41 for k in keys:
42 h.append((k, str(cookie._rest[k])))
44 h.append(("version", str(cookie.version)))
46 return join_header_words([h])
48 class LWPCookieJar(FileCookieJar):
49 """
50 The LWPCookieJar saves a sequence of"Set-Cookie3" lines.
51 "Set-Cookie3" is the format used by the libwww-perl libary, not known
52 to be compatible with any browser, but which is easy to read and
53 doesn't lose information about RFC 2965 cookies.
55 Additional methods
57 as_lwp_str(ignore_discard=True, ignore_expired=True)
59 """
61 def as_lwp_str(self, ignore_discard=True, ignore_expires=True):
62 """Return cookies as a string of "\n"-separated "Set-Cookie3" headers.
64 ignore_discard and ignore_expires: see docstring for FileCookieJar.save
66 """
67 now = time.time()
68 r = []
69 for cookie in self:
70 if not ignore_discard and cookie.discard:
71 continue
72 if not ignore_expires and cookie.is_expired(now):
73 continue
74 r.append("Set-Cookie3: %s" % lwp_cookie_str(cookie))
75 return "\n".join(r+[""])
77 def save(self, filename=None, ignore_discard=False, ignore_expires=False):
78 if filename is None:
79 if self.filename is not None: filename = self.filename
80 else: raise ValueError(MISSING_FILENAME_TEXT)
82 f = open(filename, "w")
83 try:
84 # There really isn't an LWP Cookies 2.0 format, but this indicates
85 # that there is extra information in here (domain_dot and
86 # port_spec) while still being compatible with libwww-perl, I hope.
87 f.write("#LWP-Cookies-2.0\n")
88 f.write(self.as_lwp_str(ignore_discard, ignore_expires))
89 finally:
90 f.close()
92 def _really_load(self, f, filename, ignore_discard, ignore_expires):
93 magic = f.readline()
94 if not re.search(self.magic_re, magic):
95 msg = "%s does not seem to contain cookies" % filename
96 raise LoadError(msg)
98 now = time.time()
100 header = "Set-Cookie3:"
101 boolean_attrs = ("port_spec", "path_spec", "domain_dot",
102 "secure", "discard")
103 value_attrs = ("version",
104 "port", "path", "domain",
105 "expires",
106 "comment", "commenturl")
108 try:
109 while 1:
110 line = f.readline()
111 if line == "": break
112 if not line.startswith(header):
113 continue
114 line = line[len(header):].strip()
116 for data in split_header_words([line]):
117 name, value = data[0]
118 standard = {}
119 rest = {}
120 for k in boolean_attrs:
121 standard[k] = False
122 for k, v in data[1:]:
123 if k is not None:
124 lc = k.lower()
125 else:
126 lc = None
127 # don't lose case distinction for unknown fields
128 if (lc in value_attrs) or (lc in boolean_attrs):
129 k = lc
130 if k in boolean_attrs:
131 if v is None: v = True
132 standard[k] = v
133 elif k in value_attrs:
134 standard[k] = v
135 else:
136 rest[k] = v
138 h = standard.get
139 expires = h("expires")
140 discard = h("discard")
141 if expires is not None:
142 expires = iso2time(expires)
143 if expires is None:
144 discard = True
145 domain = h("domain")
146 domain_specified = domain.startswith(".")
147 c = Cookie(h("version"), name, value,
148 h("port"), h("port_spec"),
149 domain, domain_specified, h("domain_dot"),
150 h("path"), h("path_spec"),
151 h("secure"),
152 expires,
153 discard,
154 h("comment"),
155 h("commenturl"),
156 rest)
157 if not ignore_discard and c.discard:
158 continue
159 if not ignore_expires and c.is_expired(now):
160 continue
161 self.set_cookie(c)
162 except:
163 reraise_unmasked_exceptions((IOError,))
164 raise LoadError("invalid Set-Cookie3 format file %s" % filename)