blob: 15c950a8cb70d09371d2c1aef4ef4158005af607 [file] [log] [blame]
Andrew M. Kuchling52ea8722000-08-19 13:01:19 +00001#!/usr/bin/env python
2#
3
4####
5# Copyright 2000 by Timothy O'Malley <timo@alum.mit.edu>
6#
7# All Rights Reserved
8#
9# Permission to use, copy, modify, and distribute this software
10# and its documentation for any purpose and without fee is hereby
11# granted, provided that the above copyright notice appear in all
12# copies and that both that copyright notice and this permission
13# notice appear in supporting documentation, and that the name of
14# Timothy O'Malley not be used in advertising or publicity
15# pertaining to distribution of the software without specific, written
16# prior permission.
17#
18# Timothy O'Malley DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
19# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
20# AND FITNESS, IN NO EVENT SHALL Timothy O'Malley BE LIABLE FOR
21# ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
23# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
24# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
25# PERFORMANCE OF THIS SOFTWARE.
26#
27####
28#
29# $Id$
30# by Timothy O'Malley <timo@alum.mit.edu>
31#
32# Cookie.py is a Python module for the handling of HTTP
33# cookies as a Python dictionary. See RFC 2109 for more
34# information on cookies.
35#
36# The original idea to treat Cookies as a dictionary came from
37# Dave Mitchel (davem@magnet.com) in 1995, when he released the
38# first version of nscookie.py.
39#
40####
41
42"""
43Here's a sample session to show how to use this module.
44At the moment, this is the only documentation.
45
46The Basics
47----------
48
49Importing is easy..
50
51 >>> import Cookie
52
53Most of the time you start by creating a cookie. Cookies come in
54three flavors, each with slighly different encoding semanitcs, but
55more on that later.
56
57 >>> C = Cookie.SimpleCookie()
58 >>> C = Cookie.SerialCookie()
59 >>> C = Cookie.SmartCookie()
60
61[Note: Long-time users of Cookie.py will remember using
62Cookie.Cookie() to create an Cookie object. Although deprecated, it
63is still supported by the code. See the Backward Compatibility notes
64for more information.]
65
66Once you've created your Cookie, you can add values just as if it were
67a dictionary.
68
69 >>> C = Cookie.SmartCookie()
70 >>> C["fig"] = "newton"
71 >>> C["sugar"] = "wafer"
72 >>> C
73 Set-Cookie: sugar=wafer;
74 Set-Cookie: fig=newton;
75
76Notice that the printable representation of a Cookie is the
77appropriate format for a Set-Cookie: header. This is the
78default behavior. You can change the header and printed
79attributes by using the the .output() function
80
81 >>> C = Cookie.SmartCookie()
82 >>> C["rocky"] = "road"
83 >>> C["rocky"]["path"] = "/cookie"
84 >>> print C.output(header="Cookie:")
85 Cookie: rocky=road; Path=/cookie;
86 >>> print C.output(attrs=[], header="Cookie:")
87 Cookie: rocky=road;
88
89The load() method of a Cookie extracts cookies from a string. In a
90CGI script, you would use this method to extract the cookies from the
91HTTP_COOKIE environment variable.
92
93 >>> C = Cookie.SmartCookie()
94 >>> C.load("chips=ahoy; vienna=finger")
95 >>> C
96 Set-Cookie: vienna=finger;
97 Set-Cookie: chips=ahoy;
98
99The load() method is darn-tootin smart about identifying cookies
100within a string. Escaped quotation marks, nested semicolons, and other
101such trickeries do not confuse it.
102
103 >>> C = Cookie.SmartCookie()
104 >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
105 >>> C
106 Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;";
107
108Each element of the Cookie also supports all of the RFC 2109
109Cookie attributes. Here's an example which sets the Path
110attribute.
111
112 >>> C = Cookie.SmartCookie()
113 >>> C["oreo"] = "doublestuff"
114 >>> C["oreo"]["path"] = "/"
115 >>> C
116 Set-Cookie: oreo="doublestuff"; Path=/;
117
118Each dictionary element has a 'value' attribute, which gives you
119back the value associated with the key.
120
121 >>> C = Cookie.SmartCookie()
122 >>> C["twix"] = "none for you"
123 >>> C["twix"].value
124 'none for you'
125
126
127A Bit More Advanced
128-------------------
129
130As mentioned before, there are three different flavors of Cookie
131objects, each with different encoding/decoding semantics. This
132section briefly discusses the differences.
133
134SimpleCookie
135
136The SimpleCookie expects that all values should be standard strings.
137Just to be sure, SimpleCookie invokes the str() builtin to convert
138the value to a string, when the values are set dictionary-style.
139
140 >>> C = Cookie.SimpleCookie()
141 >>> C["number"] = 7
142 >>> C["string"] = "seven"
143 >>> C["number"].value
144 '7'
145 >>> C["string"].value
146 'seven'
147 >>> C
148 Set-Cookie: number=7;
149 Set-Cookie: string=seven;
150
151
152SerialCookie
153
154The SerialCookie expects that all values should be serialized using
155cPickle (or pickle, if cPickle isn't available). As a result of
156serializing, SerialCookie can save almost any Python object to a
157value, and recover the exact same object when the cookie has been
158returned. (SerialCookie can yield some strange-looking cookie
159values, however.)
160
161 >>> C = Cookie.SerialCookie()
162 >>> C["number"] = 7
163 >>> C["string"] = "seven"
164 >>> C["number"].value
165 7
166 >>> C["string"].value
167 'seven'
168 >>> C
169 Set-Cookie: number="I7\012.";
170 Set-Cookie: string="S'seven'\012p1\012.";
171
172Be warned, however, if SerialCookie cannot de-serialize a value (because
173it isn't a valid pickle'd object), IT WILL RAISE AN EXCEPTION.
174
175
176SmartCookie
177
178The SmartCookie combines aspects of each of the other two flavors.
179When setting a value in a dictionary-fashion, the SmartCookie will
180serialize (ala cPickle) the value *if and only if* it isn't a
181Python string. String objects are *not* serialized. Similarly,
182when the load() method parses out values, it attempts to de-serialize
183the value. If it fails, then it fallsback to treating the value
184as a string.
185
186 >>> C = Cookie.SmartCookie()
187 >>> C["number"] = 7
188 >>> C["string"] = "seven"
189 >>> C["number"].value
190 7
191 >>> C["string"].value
192 'seven'
193 >>> C
194 Set-Cookie: number="I7\012.";
195 Set-Cookie: string=seven;
196
197
198Backwards Compatibility
199-----------------------
200
201In order to keep compatibilty with earlier versions of Cookie.py,
202it is still possible to use Cookie.Cookie() to create a Cookie. In
203fact, this simply returns a SmartCookie.
204
205 >>> C = Cookie.Cookie()
206 >>> C.__class__
207 <class Cookie.SmartCookie at 99f88>
208
209
210Finis.
211""" #"
212# ^
213# |----helps out font-lock
214
215#
216# Import our required modules
217#
218import string, sys
219from UserDict import UserDict
220
221try:
222 from cPickle import dumps, loads
223except ImportError:
224 from pickle import dumps, loads
225
226try:
227 import re
228except ImportError:
229 raise ImportError, "Cookie.py requires 're' from Python 1.5 or later"
230
231
232#
233# Define an exception visible to External modules
234#
235class CookieError(Exception):
236 pass
237
238
239# These quoting routines conform to the RFC2109 specification, which in
240# turn references the character definitions from RFC2068. They provide
241# a two-way quoting algorithm. Any non-text character is translated
242# into a 4 character sequence: a forward-slash followed by the
243# three-digit octal equivalent of the character. Any '\' or '"' is
244# quoted with a preceeding '\' slash.
245#
246# These are taken from RFC2068 and RFC2109.
247# _LegalChars is the list of chars which don't require "'s
248# _Translator hash-table for fast quoting
249#
250_LegalChars = string.letters + string.digits + "!#$%&'*+-.^_`|~"
251_Translator = {
252 '\000' : '\\000', '\001' : '\\001', '\002' : '\\002',
253 '\003' : '\\003', '\004' : '\\004', '\005' : '\\005',
254 '\006' : '\\006', '\007' : '\\007', '\010' : '\\010',
255 '\011' : '\\011', '\012' : '\\012', '\013' : '\\013',
256 '\014' : '\\014', '\015' : '\\015', '\016' : '\\016',
257 '\017' : '\\017', '\020' : '\\020', '\021' : '\\021',
258 '\022' : '\\022', '\023' : '\\023', '\024' : '\\024',
259 '\025' : '\\025', '\026' : '\\026', '\027' : '\\027',
260 '\030' : '\\030', '\031' : '\\031', '\032' : '\\032',
261 '\033' : '\\033', '\034' : '\\034', '\035' : '\\035',
262 '\036' : '\\036', '\037' : '\\037',
263
264 '"' : '\\"', '\\' : '\\\\',
265
266 '\177' : '\\177', '\200' : '\\200', '\201' : '\\201',
267 '\202' : '\\202', '\203' : '\\203', '\204' : '\\204',
268 '\205' : '\\205', '\206' : '\\206', '\207' : '\\207',
269 '\210' : '\\210', '\211' : '\\211', '\212' : '\\212',
270 '\213' : '\\213', '\214' : '\\214', '\215' : '\\215',
271 '\216' : '\\216', '\217' : '\\217', '\220' : '\\220',
272 '\221' : '\\221', '\222' : '\\222', '\223' : '\\223',
273 '\224' : '\\224', '\225' : '\\225', '\226' : '\\226',
274 '\227' : '\\227', '\230' : '\\230', '\231' : '\\231',
275 '\232' : '\\232', '\233' : '\\233', '\234' : '\\234',
276 '\235' : '\\235', '\236' : '\\236', '\237' : '\\237',
277 '\240' : '\\240', '\241' : '\\241', '\242' : '\\242',
278 '\243' : '\\243', '\244' : '\\244', '\245' : '\\245',
279 '\246' : '\\246', '\247' : '\\247', '\250' : '\\250',
280 '\251' : '\\251', '\252' : '\\252', '\253' : '\\253',
281 '\254' : '\\254', '\255' : '\\255', '\256' : '\\256',
282 '\257' : '\\257', '\260' : '\\260', '\261' : '\\261',
283 '\262' : '\\262', '\263' : '\\263', '\264' : '\\264',
284 '\265' : '\\265', '\266' : '\\266', '\267' : '\\267',
285 '\270' : '\\270', '\271' : '\\271', '\272' : '\\272',
286 '\273' : '\\273', '\274' : '\\274', '\275' : '\\275',
287 '\276' : '\\276', '\277' : '\\277', '\300' : '\\300',
288 '\301' : '\\301', '\302' : '\\302', '\303' : '\\303',
289 '\304' : '\\304', '\305' : '\\305', '\306' : '\\306',
290 '\307' : '\\307', '\310' : '\\310', '\311' : '\\311',
291 '\312' : '\\312', '\313' : '\\313', '\314' : '\\314',
292 '\315' : '\\315', '\316' : '\\316', '\317' : '\\317',
293 '\320' : '\\320', '\321' : '\\321', '\322' : '\\322',
294 '\323' : '\\323', '\324' : '\\324', '\325' : '\\325',
295 '\326' : '\\326', '\327' : '\\327', '\330' : '\\330',
296 '\331' : '\\331', '\332' : '\\332', '\333' : '\\333',
297 '\334' : '\\334', '\335' : '\\335', '\336' : '\\336',
298 '\337' : '\\337', '\340' : '\\340', '\341' : '\\341',
299 '\342' : '\\342', '\343' : '\\343', '\344' : '\\344',
300 '\345' : '\\345', '\346' : '\\346', '\347' : '\\347',
301 '\350' : '\\350', '\351' : '\\351', '\352' : '\\352',
302 '\353' : '\\353', '\354' : '\\354', '\355' : '\\355',
303 '\356' : '\\356', '\357' : '\\357', '\360' : '\\360',
304 '\361' : '\\361', '\362' : '\\362', '\363' : '\\363',
305 '\364' : '\\364', '\365' : '\\365', '\366' : '\\366',
306 '\367' : '\\367', '\370' : '\\370', '\371' : '\\371',
307 '\372' : '\\372', '\373' : '\\373', '\374' : '\\374',
308 '\375' : '\\375', '\376' : '\\376', '\377' : '\\377'
309 }
310
311def _quote(str, LegalChars=_LegalChars,
312 join=string.join, idmap=string._idmap, translate=string.translate):
313 #
314 # If the string does not need to be double-quoted,
315 # then just return the string. Otherwise, surround
316 # the string in doublequotes and precede quote (with a \)
317 # special characters.
318 #
319 if "" == translate(str, idmap, LegalChars):
320 return str
321 else:
322 return '"' + join( map(_Translator.get, str, str), "" ) + '"'
323# end _quote
324
325
326_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
327_QuotePatt = re.compile(r"[\\].")
328
329def _unquote(str, join=string.join, atoi=string.atoi):
330 # If there aren't any doublequotes,
331 # then there can't be any special characters. See RFC 2109.
332 if len(str) < 2:
333 return str
334 if str[0] != '"' or str[-1] != '"':
335 return str
336
337 # We have to assume that we must decode this string.
338 # Down to work.
339
340 # Remove the "s
341 str = str[1:-1]
342
343 # Check for special sequences. Examples:
344 # \012 --> \n
345 # \" --> "
346 #
347 i = 0
348 n = len(str)
349 res = []
350 while 0 <= i < n:
351 Omatch = _OctalPatt.search(str, i)
352 Qmatch = _QuotePatt.search(str, i)
353 if not Omatch and not Qmatch: # Neither matched
354 res.append(str[i:])
355 break
356 # else:
357 j = k = -1
358 if Omatch: j = Omatch.start(0)
359 if Qmatch: k = Qmatch.start(0)
360 if Qmatch and ( not Omatch or k < j ): # QuotePatt matched
361 res.append(str[i:k])
362 res.append(str[k+1])
363 i = k+2
364 else: # OctalPatt matched
365 res.append(str[i:j])
366 res.append( chr( atoi(str[j+1:j+4], 8) ) )
367 i = j+4
368 return join(res, "")
369# end _unquote
370
371# The _getdate() routine is used to set the expiration time in
372# the cookie's HTTP header. By default, _getdate() returns the
373# current time in the appropriate "expires" format for a
374# Set-Cookie header. The one optional argument is an offset from
375# now, in seconds. For example, an offset of -3600 means "one hour ago".
376# The offset may be a floating point number.
377#
378
379_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
380
381_monthname = [None,
382 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
383 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
384
385def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname):
386 from time import gmtime, time
387 now = time()
388 year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future)
389 return "%s, %02d-%3s-%4d %02d:%02d:%02d GMT" % \
390 (weekdayname[wd], day, monthname[month], year, hh, mm, ss)
391
392
393#
394# A class to hold ONE key,value pair.
395# In a cookie, each such pair may have several attributes.
396# so this class is used to keep the attributes associated
397# with the appropriate key,value pair.
398# This class also includes a coded_value attribute, which
399# is used to hold the network representation of the
400# value. This is most useful when Python objects are
401# pickled for network transit.
402#
403
404class Morsel(UserDict):
405 # RFC 2109 lists these attributes as reserved:
406 # path comment domain
407 # max-age secure version
408 #
409 # For historical reasons, these attributes are also reserved:
410 # expires
411 #
412 # This dictionary provides a mapping from the lowercase
413 # variant on the left to the appropriate traditional
414 # formatting on the right.
415 _reserved = { "expires" : "expires",
416 "path" : "Path",
417 "comment" : "Comment",
418 "domain" : "Domain",
419 "max-age" : "Max-Age",
420 "secure" : "secure",
421 "version" : "Version",
422 }
423 _reserved_keys = _reserved.keys()
424
425 def __init__(self):
426 # Set defaults
427 self.key = self.value = self.coded_value = None
428 UserDict.__init__(self)
429
430 # Set default attributes
431 for K in self._reserved_keys:
432 UserDict.__setitem__(self, K, "")
433 # end __init__
434
435 def __setitem__(self, K, V):
436 K = string.lower(K)
437 if not K in self._reserved_keys:
438 raise CookieError("Invalid Attribute %s" % K)
439 UserDict.__setitem__(self, K, V)
440 # end __setitem__
441
442 def isReservedKey(self, K):
443 return string.lower(K) in self._reserved_keys
444 # end isReservedKey
445
446 def set(self, key, val, coded_val,
447 LegalChars=_LegalChars,
448 idmap=string._idmap, translate=string.translate ):
449 # First we verify that the key isn't a reserved word
450 # Second we make sure it only contains legal characters
451 if string.lower(key) in self._reserved_keys:
452 raise CookieError("Attempt to set a reserved key: %s" % key)
453 if "" != translate(key, idmap, LegalChars):
454 raise CookieError("Illegal key value: %s" % key)
455
456 # It's a good key, so save it.
457 self.key = key
458 self.value = val
459 self.coded_value = coded_val
460 # end set
461
462 def output(self, attrs=None, header = "Set-Cookie:"):
463 return "%s %s" % ( header, self.OutputString(attrs) )
464
465 __repr__ = output
466
467 def js_output(self, attrs=None):
468 # Print javascript
469 return """
470 <SCRIPT LANGUAGE="JavaScript">
471 <!-- begin hiding
472 document.cookie = \"%s\"
473 // end hiding -->
474 </script>
475 """ % ( self.OutputString(attrs), )
476 # end js_output()
477
478 def OutputString(self, attrs=None):
479 # Build up our result
480 #
481 result = []
482 RA = result.append
483
484 # First, the key=value pair
485 RA("%s=%s;" % (self.key, self.coded_value))
486
487 # Now add any defined attributes
488 if attrs == None:
489 attrs = self._reserved_keys
490 for K,V in self.items():
491 if not V: continue
492 if K not in attrs: continue
493 if K == "expires" and type(V) == type(1):
494 RA("%s=%s;" % (self._reserved[K], _getdate(V)))
495 elif K == "max-age" and type(V) == type(1):
496 RA("%s=%d;" % (self._reserved[K], V))
497 elif K == "secure":
498 RA("%s;" % self._reserved[K])
499 else:
500 RA("%s=%s;" % (self._reserved[K], V))
501
502 # Return the result
503 return string.join(result, " ")
504 # end OutputString
505# end Morsel class
506
507
508
509#
510# Pattern for finding cookie
511#
512# This used to be strict parsing based on the RFC2109 and RFC2068
513# specifications. I have since discovered that MSIE 3.0x doesn't
514# follow the character rules outlined in those specs. As a
515# result, the parsing rules here are less strict.
516#
517
518_LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{]"
519_CookiePattern = re.compile(
520 r"(?x)" # This is a Verbose pattern
521 r"(?P<key>" # Start of group 'key'
522 ""+ _LegalCharsPatt +"+" # Any word of at least one letter
523 r")" # End of group 'key'
524 r"\s*=\s*" # Equal Sign
525 r"(?P<val>" # Start of group 'val'
526 r'"(?:[^\\"]|\\.)*"' # Any doublequoted string
527 r"|" # or
528 ""+ _LegalCharsPatt +"*" # Any word or empty string
529 r")" # End of group 'val'
530 r"\s*;?" # Probably ending in a semi-colon
531 )
532
533
534# At long last, here is the cookie class.
535# Using this class is almost just like using a dictionary.
536# See this module's docstring for example usage.
537#
538class BaseCookie(UserDict):
539 # A container class for a set of Morsels
540 #
541
542 def value_decode(self, val):
543 """real_value, coded_value = value_decode(STRING)
544 Called prior to setting a cookie's value from the network
545 representation. The VALUE is the value read from HTTP
546 header.
547 Override this function to modify the behavior of cookies.
548 """
549 return val, val
550 # end value_encode
551
552 def value_encode(self, val):
553 """real_value, coded_value = value_encode(VALUE)
554 Called prior to setting a cookie's value from the dictionary
555 representation. The VALUE is the value being assigned.
556 Override this function to modify the behavior of cookies.
557 """
558 strval = str(val)
559 return strval, strval
560 # end value_encode
561
562 def __init__(self, input=None):
563 UserDict.__init__(self)
564 if input: self.load(input)
565 # end __init__
566
567 def __set(self, key, real_value, coded_value):
568 """Private method for setting a cookie's value"""
569 M = self.get(key, Morsel())
570 M.set(key, real_value, coded_value)
571 UserDict.__setitem__(self, key, M)
572 # end __set
573
574 def __setitem__(self, key, value):
575 """Dictionary style assignment."""
576 rval, cval = self.value_encode(value)
577 self.__set(key, rval, cval)
578 # end __setitem__
579
580 def output(self, attrs=None, header="Set-Cookie:", sep="\n"):
581 """Return a string suitable for HTTP."""
582 result = []
583 for K,V in self.items():
584 result.append( V.output(attrs, header) )
585 return string.join(result, sep)
586 # end output
587
588 __repr__ = output
589
590 def js_output(self, attrs=None):
591 """Return a string suitable for JavaScript."""
592 result = []
593 for K,V in self.items():
594 result.append( V.js_output(attrs) )
595 return string.join(result, "")
596 # end js_output
597
598 def load(self, rawdata):
599 """Load cookies from a string (presumably HTTP_COOKIE) or
600 from a dictionary. Loading cookies from a dictionary 'd'
601 is equivalent to calling:
602 map(Cookie.__setitem__, d.keys(), d.values())
603 """
604 if type(rawdata) == type(""):
605 self.__ParseString(rawdata)
606 else:
607 self.update(rawdata)
608 return
609 # end load()
610
611 def __ParseString(self, str, patt=_CookiePattern):
612 i = 0 # Our starting point
613 n = len(str) # Length of string
614 M = None # current morsel
615
616 while 0 <= i < n:
617 # Start looking for a cookie
618 match = patt.search(str, i)
619 if not match: break # No more cookies
620
621 K,V = match.group("key"), match.group("val")
622 i = match.end(0)
623
624 # Parse the key, value in case it's metainfo
625 if K[0] == "$":
626 # We ignore attributes which pertain to the cookie
627 # mechanism as a whole. See RFC 2109.
628 # (Does anyone care?)
629 if M:
630 M[ K[1:] ] = V
631 elif string.lower(K) in Morsel._reserved_keys:
632 if M:
633 M[ K ] = V
634 else:
635 rval, cval = self.value_decode(V)
636 self.__set(K, rval, cval)
637 M = self[K]
638
639 return
640 # end __ParseString
641# end BaseCookie class
642
643class SimpleCookie(BaseCookie):
644 """SimpleCookie
645 SimpleCookie supports strings as cookie values. When setting
646 the value using the dictionary assignment notation, SimpleCookie
647 calls the builtin str() to convert the value to a string. Values
648 received from HTTP are kept as strings.
649 """
650 def value_decode(self, val):
651 return _unquote( val ), val
652 def value_encode(self, val):
653 strval = str(val)
654 return strval, _quote( strval )
655# end SimpleCookie
656
657class SerialCookie(BaseCookie):
658 """SerialCookie
659 SerialCookie supports arbitrary objects as cookie values. All
660 values are serialized (using cPickle) before being sent to the
661 client. All incoming values are assumed to be valid Pickle
662 representations. IF AN INCOMING VALUE IS NOT IN A VALID PICKLE
663 FORMAT, THEN AN EXCEPTION WILL BE RAISED.
664
665 Note: Large cookie values add overhead because they must be
666 retransmitted on every HTTP transaction.
667
668 Note: HTTP has a 2k limit on the size of a cookie. This class
669 does not check for this limit, so be careful!!!
670 """
671 def value_decode(self, val):
672 # This could raise an exception!
673 return loads( _unquote(val) ), val
674 def value_encode(self, val):
675 return val, _quote( dumps(val) )
676# end SerialCookie
677
678class SmartCookie(BaseCookie):
679 """SmartCookie
680 SmartCookie supports arbitrary objects as cookie values. If the
681 object is a string, then it is quoted. If the object is not a
682 string, however, then SmartCookie will use cPickle to serialize
683 the object into a string representation.
684
685 Note: Large cookie values add overhead because they must be
686 retransmitted on every HTTP transaction.
687
688 Note: HTTP has a 2k limit on the size of a cookie. This class
689 does not check for this limit, so be careful!!!
690 """
691 def value_decode(self, val):
692 strval = _unquote(val)
693 try:
694 return loads(strval), val
695 except:
696 return strval, val
697 def value_encode(self, val):
698 if type(val) == type(""):
699 return val, _quote(val)
700 else:
701 return val, _quote( dumps(val) )
702# end SmartCookie
703
704
705###########################################################
706# Backwards Compatibility: Don't break any existing code!
707
708# We provide Cookie() as an alias for SmartCookie()
709Cookie = SmartCookie
710
711#
712###########################################################
713
714
715
716#
717# should add a test routine?
718#