[Checkins] SVN: Sandbox/shane/republish/zope Sketching ideas for publisher refactoring
Shane Hathaway
shane at hathawaymix.org
Tue Feb 10 04:40:42 EST 2009
Log message for revision 96372:
Sketching ideas for publisher refactoring
Changed:
A Sandbox/shane/republish/zope/
A Sandbox/shane/republish/zope/pipeline/
A Sandbox/shane/republish/zope/pipeline/caller.py
A Sandbox/shane/republish/zope/pipeline/complextraverser.py
A Sandbox/shane/republish/zope/pipeline/dbopener.py
A Sandbox/shane/republish/zope/pipeline/errorhandler.py
A Sandbox/shane/republish/zope/pipeline/requestcreator.py
A Sandbox/shane/republish/zope/pipeline/simpletraverser.py
A Sandbox/shane/republish/zope/pipeline/standard.py
A Sandbox/shane/republish/zope/pipeline/txncontroller.py
A Sandbox/shane/republish/zope/publisher/
A Sandbox/shane/republish/zope/publisher/interfaces/
A Sandbox/shane/republish/zope/publisher/interfaces/base.py
A Sandbox/shane/republish/zope/publisher/interfaces/browser.py
A Sandbox/shane/republish/zope/publisher/interfaces/complextraversal.py
A Sandbox/shane/republish/zope/publisher/interfaces/exceptions.py
A Sandbox/shane/republish/zope/publisher/interfaces/http.py
A Sandbox/shane/republish/zope.app.http/
-=-
Added: Sandbox/shane/republish/zope/pipeline/caller.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/caller.py (rev 0)
+++ Sandbox/shane/republish/zope/pipeline/caller.py 2009-02-10 09:40:42 UTC (rev 96372)
@@ -0,0 +1,109 @@
+
+from zope.proxy import removeAllProxies
+
+class Caller(object):
+ """WSGI app that calls the traversed object.
+
+ Requires 'zope.request', which implements IRequest, in the environment.
+ """
+ def __call__(self, environ, start_response):
+ request = environ['zope.request']
+ name, ob = request.traversed[-1]
+ result = mapply(ob, request.getPositionalArguments(), request)
+ response = request.response
+ if result is not response:
+ response.setResult(result)
+ start_response(response.getStatusString(), response.getHeaders())
+ return response.consumeBodyIter()
+
+
+_marker = object() # Create a new marker object.
+
+def unwrapMethod(obj):
+ """obj -> (unwrapped, wrapperCount)
+
+ Unwrap 'obj' until we get to a real function, counting the number of
+ unwrappings.
+
+ Bail if we find a class or something we can't identify as callable.
+ """
+ wrapperCount = 0
+ unwrapped = obj
+
+ for i in range(10):
+ bases = getattr(unwrapped, '__bases__', None)
+ if bases is not None:
+ raise TypeError("mapply() can not call class constructors")
+
+ im_func = getattr(unwrapped, 'im_func', None)
+ if im_func is not None:
+ unwrapped = im_func
+ wrapperCount += 1
+ elif getattr(unwrapped, 'func_code', None) is not None:
+ break
+ else:
+ unwrapped = getattr(unwrapped, '__call__' , None)
+ if unwrapped is None:
+ raise TypeError("mapply() can not call %s" % repr(obj))
+ else:
+ raise TypeError("couldn't find callable metadata, mapply() error on %s"
+ % repr(obj))
+
+ return unwrapped, wrapperCount
+
+
+def mapply(obj, positional=(), request={}):
+ __traceback_info__ = obj
+
+ # we need deep access for introspection. Waaa.
+ unwrapped = removeAllProxies(obj)
+
+ unwrapped, wrapperCount = unwrapMethod(unwrapped)
+
+ code = unwrapped.func_code
+ defaults = unwrapped.func_defaults
+ names = code.co_varnames[wrapperCount:code.co_argcount]
+
+ nargs = len(names)
+ if not positional:
+ args = []
+ else:
+ args = list(positional)
+ if len(args) > nargs:
+ given = len(args)
+ if wrapperCount:
+ given += wrapperCount
+ raise TypeError('%s() takes at most %d argument%s(%d given)' % (
+ getattr(unwrapped, '__name__', repr(obj)),
+ code.co_argcount,
+ (code.co_argcount > 1 and 's ' or ' '),
+ given))
+
+ get = request.get
+ nrequired = len(names)
+ if defaults:
+ nrequired -= len(defaults)
+
+ for index in range(len(args), nargs):
+ name = names[index]
+ v = get(name, _marker)
+ if v is _marker:
+ if name == 'REQUEST':
+ v = request
+ elif index < nrequired:
+ raise TypeError('Missing argument to %s(): %s' % (
+ getattr(unwrapped, '__name__', repr(obj)), name))
+ else:
+ v = defaults[index - nrequired]
+ args.append(v)
+
+ args = tuple(args)
+
+ if __debug__:
+ return debug_call(obj, args)
+
+ return obj(*args)
+
+def debug_call(obj, args):
+ # The presence of this function allows us to set a pdb breakpoint
+ return obj(*args)
Property changes on: Sandbox/shane/republish/zope/pipeline/caller.py
___________________________________________________________________
Added: svn:mergeinfo
+
Property changes on: Sandbox/shane/republish/zope/pipeline/dbopener.py
___________________________________________________________________
Added: svn:mergeinfo
+
Property changes on: Sandbox/shane/republish/zope/pipeline/errorhandler.py
___________________________________________________________________
Added: svn:mergeinfo
+
Property changes on: Sandbox/shane/republish/zope/pipeline/requestcreator.py
___________________________________________________________________
Added: svn:mergeinfo
+
Added: Sandbox/shane/republish/zope/pipeline/simpletraverser.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/simpletraverser.py (rev 0)
+++ Sandbox/shane/republish/zope/pipeline/simpletraverser.py 2009-02-10 09:40:42 UTC (rev 96372)
@@ -0,0 +1,11 @@
+
+
+
+class SimpleTraverser(object):
+
+ def __init__(self, app):
+ self.app = app
+
+ def __call__(self, environ, start_response):
+ request = environ['zope.request']
+
\ No newline at end of file
Property changes on: Sandbox/shane/republish/zope/pipeline/simpletraverser.py
___________________________________________________________________
Added: svn:mergeinfo
+
Added: Sandbox/shane/republish/zope/pipeline/standard.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/standard.py (rev 0)
+++ Sandbox/shane/republish/zope/pipeline/standard.py 2009-02-10 09:40:42 UTC (rev 96372)
@@ -0,0 +1,29 @@
+
+"""Creates a standard Zope WSGI publishing pipeline."""
+
+from zope.pipeline.call import Caller
+
+standard_pipeline = (
+ # RequestProfiler,
+ # CodeFreshnessChecker,
+ # Multiprocessor,
+ # ComponentConfigurator,
+ # DetailedTracebackGenerator,
+ RequestLogger,
+ RequestCreator, # also sets locale (?)
+ DatabaseOpener,
+ TransactionController, # includes retry logic and annotation
+ choose_traverser, # complex or simple traversal based on zope.conf
+ AppErrorHandler,
+ Caller
+)
+
+def make_app(zope_conf, pipeline=standard_pipeline):
+ p = list(pipeline)
+ p.reverse()
+ factory = p.pop()
+ app = factory(zope_conf)
+ while p:
+ factory, kw = p.pop()
+ app = factory(app, zope_conf)
+ return app
Property changes on: Sandbox/shane/republish/zope/pipeline/txncontroller.py
___________________________________________________________________
Added: svn:mergeinfo
+
Added: Sandbox/shane/republish/zope/publisher/interfaces/base.py
===================================================================
--- Sandbox/shane/republish/zope/publisher/interfaces/base.py (rev 0)
+++ Sandbox/shane/republish/zope/publisher/interfaces/base.py 2009-02-10 09:40:42 UTC (rev 96372)
@@ -0,0 +1,242 @@
+
+"""The base interfaces for request and response objects."""
+
+from zope.interface import Attribute
+from zope.interface import Interface
+from zope.interface.common.mapping import IExtendedReadMapping
+
+
+class IRequest(IExtendedReadMapping):
+ """Basic request data.
+
+ The request object may be used as a mapping object, in which case
+ values will be looked up in the order: [TODO].
+ """
+
+ environment = Attribute(
+ """Request environment data.
+
+ This is a pointer to the WSGI or CGI environment.
+ """)
+
+ traversal_path = Attribute(
+ """Sequence of steps to traverse, where each step is a unicode.
+ """)
+
+ traversed = Attribute(
+ """List of (name, obj) steps that were traversed.
+
+ The first object is the application root and has an empty name.
+ The last object is the object to call.
+ """)
+
+ response = Attribute(
+ """The request's IResponse object
+ """)
+
+ positional_args = Attribute(
+ """The positional arguments given to the request.
+ """)
+
+ principal = Attribute("""Principal object associated with the request.
+
+ It should be an IPrincipal wrapped in its AuthenticationService's context.
+ """)
+
+ bodyStream = Attribute(
+ """The stream that provides the data of the request.
+
+ The data returned by the stream will not include any possible header
+ information, which should have been stripped by the server (or
+ previous layer) before.
+
+ Also, the body stream might already be read and not return any
+ data. This is commonly done when retrieving the data for the ``body``
+ attribute.
+
+ If you access this stream directly to retrieve data, it will not be
+ possible by other parts of the framework to access the data of the
+ request via the ``body`` attribute.""")
+
+ debug = Attribute("""Debug flags (see IDebugFlags).""")
+
+ annotations = Attribute(
+ """Stores arbitrary application data under package-unique keys.
+
+ By "package-unique keys", we mean keys that are are unique by
+ virtue of including the dotted name of a package as a prefex. A
+ package name is used to limit the authority for picking names for
+ a package to the people using that package.
+
+ For example, when implementing annotations for hypothetical
+ request-persistent adapters in a hypothetical zope.persistentadapter
+ package, the key would be (or at least begin with) the following::
+
+ "zope.persistentadapter"
+ """)
+
+ def hold(held):
+ """Hold a reference to an object until the request is closed.
+
+ The object should be an IHeld. If it is an IHeld, its
+ release method will be called when it is released.
+ """
+
+ def close():
+ """Release resources held by the request.
+ """
+
+ def retry():
+ """Returns a re-initialized request to be retried.
+
+ Returns a request suitable for repeating the publication attempt,
+ or raises RetryNotSupported if the response can not be retried.
+ """
+
+
+class IResponse(Interface):
+ """Holds a response result."""
+
+ def getStatus():
+ """Returns the current status code as an integer.
+ """
+
+ def getStatusString():
+ """Return the status followed by the reason."""
+
+ def setStatus(status, reason=None):
+ """Sets the status code of the response
+
+ The status parameter must be either an integer (preferred)
+ or a value that can be converted to an integer using the int()
+ function,
+
+ The reason parameter is a short message to be sent with the status
+ code to the client. If reason is not provided, a standard
+ reason will be supplied, falling back to "Unknown" for unregistered
+ status codes.
+ """
+
+ def setStandardStatus(name):
+ """Sets the status of the response using a standard status name.
+
+ The name will be converted to a code appropriate for the protocol.
+ For example, the name "NotFound" when applied to an HTTP
+ request will set the response status code to 404 and the name
+ "OK" will set the status to 200.
+
+ If the provided name is not recognized, the response status will
+ be set to a generic error status code (for example,
+ 500 for HTTP).
+ """
+
+ def getHeaders():
+ """Returns a sequence of header name, value tuples.
+
+ If the protocol does not support headers, such as FTP, this
+ method should return an empty sequence.
+ """
+
+ def setResult(result):
+ """Sets response result value based on input.
+
+ Input is usually a unicode string, a string, None, or an object
+ that can be adapted to IResult with the request. The end result
+ is an iterable such as WSGI prefers, determined by following the
+ process described below.
+
+ Try to adapt the given input, with the request, to IResult
+ (found elsewhere in this module). If this fails, and the original
+ value was a string, use the string as the result; or if was
+ None, use an empty string as the result; and if it was anything
+ else, raise a TypeError.
+
+ If the result of the above (the adaptation or the default
+ handling of string and None) is unicode, encode it (to the
+ preferred encoding found by adapting the request to
+ zope.i18n.interfaces.IUserPreferredCharsets, usually implemented
+ by looking at the HTTP Accept-Charset header in the request, and
+ defaulting to utf-8) and set the proper encoding information on
+ the Content-Type header, if present. Otherwise (the end result
+ was not unicode) application is responsible for setting
+ Content-Type header encoding value as necessary.
+
+ If the result of the above is a string, set the Content-Length
+ header, and make the string be the single member of an iterable
+ such as a tuple (to send large chunks over the wire; see
+ discussion in the IResult interface). Otherwise (the end result
+ was not a string) application is responsible for setting
+ Content-Length header as necessary.
+
+ Set the result of all of the above as the response's result. If
+ the status has not been set, set it to an OK status (200 for HTTP).
+ """
+
+ def consumeBodyIter():
+ """Returns the response body as an iterable.
+
+ Note that this function can be only requested once, since it is
+ constructed from the result.
+ """
+
+ def retry():
+ """Returns a re-initialized response to be retried.
+
+ Returns a response suitable for repeating the publication attempt,
+ or raises RetryNotSupported if the response can not be retried.
+ """
+
+
+class IResult(Interface):
+ """An iterable that provides the body data of the response.
+
+ For simplicity, an adapter to this interface may in fact return any
+ iterable, without needing to strictly have the iterable provide
+ IResult.
+
+ IMPORTANT: The result object may be held indefinitely by a server
+ and may be accessed by arbitrary threads. For that reason the result
+ should not hold on to any application resources (i.e., should not
+ have a connection to the database) and should be prepared to be
+ invoked from any thread.
+
+ This iterable should generally be appropriate for WSGI iteration.
+
+ Each element of the iteration should generally be much larger than a
+ character or line; concrete advice on chunk size is hard to come by,
+ but a single chunk of even 100 or 200 K is probably fine.
+
+ If the IResult is a string, then, the default iteration of
+ per-character is wildly too small. Because this is such a common
+ case, if a string is used as an IResult then this is special-cased
+ to simply convert to a tuple of one value, the string.
+
+ Adaptation to this interface provides the opportunity for efficient file
+ delivery, pipelining hooks, and more.
+ """
+
+ def __iter__():
+ """iterate over the values that should be returned as the result.
+
+ See IHTTPResponse.setResult.
+ """
+
+
+class IHeld(Interface):
+ """Object to be held and explicitly released by a request
+ """
+
+ def release():
+ """Release the held object
+
+ This is called by a request that holds the IHeld when the
+ request is closed
+
+ """
+
+class IDebugFlags(Interface):
+ """Features that support debugging."""
+
+ sourceAnnotations = Attribute("""Enable ZPT source annotations""")
+ showTAL = Attribute("""Leave TAL markup in rendered page templates""")
+
Added: Sandbox/shane/republish/zope/publisher/interfaces/browser.py
===================================================================
--- Sandbox/shane/republish/zope/publisher/interfaces/browser.py (rev 0)
+++ Sandbox/shane/republish/zope/publisher/interfaces/browser.py 2009-02-10 09:40:42 UTC (rev 96372)
@@ -0,0 +1,24 @@
+
+
+
+class IBrowserRequest(IHTTPRequest):
+ """Browser-specific Request functionality.
+
+ Note that the browser is special in many ways, since it exposes
+ the Request object to the end-developer.
+ """
+
+class IBrowserSkinType(IInterface):
+ """A skin is a set of layers."""
+
+class IDefaultSkin(Interface):
+ """Any component providing this interface must be a skin.
+
+ This is a marker interface, so that we can register the default skin as an
+ adapter from the presentation type to `IDefaultSkin`.
+ """
+
+class ISkinChangedEvent(Interface):
+ """Event that gets triggered when the skin of a request is changed."""
+
+ request = Attribute("The request for which the skin was changed.")
Added: Sandbox/shane/republish/zope/publisher/interfaces/complextraversal.py
===================================================================
--- Sandbox/shane/republish/zope/publisher/interfaces/complextraversal.py (rev 0)
+++ Sandbox/shane/republish/zope/publisher/interfaces/complextraversal.py 2009-02-10 09:40:42 UTC (rev 96372)
@@ -0,0 +1,56 @@
+
+
+class IPublishTraverse(Interface):
+
+ def publishTraverse(request, name):
+ """Lookup a name
+
+ The 'request' argument is the publisher request object. The
+ 'name' argument is the name that is to be looked up; it must
+ be an ASCII string or Unicode object.
+
+ If a lookup is not possible, raise a NotFound error.
+
+ This method should return an object having the specified name and
+ `self` as parent. The method can use the request to determine the
+ correct object.
+ """
+
+class IHTTPPublisher(IPublishTraverse):
+ """HTTP-specific publisher traversal"""
+
+class IBrowserPublisher(IPublishTraverse):
+
+ def browserDefault(request):
+ """Provide the default object
+
+ The default object is expressed as a (possibly different)
+ object and/or additional traversal steps.
+
+ Returns an object and a sequence of names. If the sequence of
+ names is not empty, then a traversal step is made for each name.
+ After the publisher gets to the end of the sequence, it will
+ call browserDefault on the last traversed object.
+
+ Normal usage is to return self for object and a default view name.
+
+ The publisher calls this method at the end of each traversal path. If
+ a non-empty sequence of names is returned, the publisher will traverse
+ those names and call browserDefault again at the end.
+
+ Note that if additional traversal steps are indicated (via a
+ nonempty sequence of names), then the publisher will try to adjust
+ the base href.
+ """
+
+class IBrowserPage(IBrowserPublisher):
+ """Browser page"""
+
+ def __call__(*args, **kw):
+ """Compute a response body"""
+
+class IBrowserView(IView):
+ """Browser View"""
+
+class IDefaultBrowserLayer(IBrowserRequest):
+ """The default layer."""
Added: Sandbox/shane/republish/zope/publisher/interfaces/exceptions.py
===================================================================
--- Sandbox/shane/republish/zope/publisher/interfaces/exceptions.py (rev 0)
+++ Sandbox/shane/republish/zope/publisher/interfaces/exceptions.py 2009-02-10 09:40:42 UTC (rev 96372)
@@ -0,0 +1,75 @@
+class IPublishingException(IException):
+ pass
+
+class PublishingException(Exception):
+ implements(IPublishingException)
+
+class ITraversalException(IPublishingException):
+ pass
+
+class TraversalException(PublishingException):
+ implements(ITraversalException)
+
+class INotFound(ILookupError, ITraversalException):
+ def getObject():
+ 'Returns the object that was being traversed.'
+
+ def getName():
+ 'Returns the name that was being traversed.'
+
+class NotFound(LookupError, TraversalException):
+ implements(INotFound)
+
+ def __init__(self, ob, name, request=None):
+ self.ob = ob
+ self.name = name
+
+ def getObject(self):
+ return self.ob
+
+ def getName(self):
+ return self.name
+
+ def __str__(self):
+ try:
+ ob = `self.ob`
+ except:
+ ob = 'unprintable object'
+ return 'Object: %s, name: %s' % (ob, `self.name`)
+
+class IDebugError(ITraversalException):
+ def getObject():
+ 'Returns the object being traversed.'
+
+ def getMessage():
+ 'Returns the debug message.'
+
+class DebugError(TraversalException):
+ implements(IDebugError)
+
+ def __init__(self, ob, message):
+ self.ob = ob
+ self.message = message
+
+ def getObject(self):
+ return self.ob
+
+ def getMessage(self):
+ return self.message
+
+ def __str__(self):
+ return self.message
+
+class IBadRequest(IPublishingException):
+ def __str__():
+ 'Returns the error message.'
+
+class BadRequest(PublishingException):
+
+ implements(IBadRequest)
+
+ def __init__(self, message):
+ self.message = message
+
+ def __str__(self):
+ return self.message
Added: Sandbox/shane/republish/zope/publisher/interfaces/http.py
===================================================================
--- Sandbox/shane/republish/zope/publisher/interfaces/http.py (rev 0)
+++ Sandbox/shane/republish/zope/publisher/interfaces/http.py 2009-02-10 09:40:42 UTC (rev 96372)
@@ -0,0 +1,207 @@
+
+
+
+class IRedirect(IPublishingException):
+ def getLocation():
+ 'Returns the location.'
+
+class Redirect(PublishingException):
+
+ implements(IRedirect)
+
+ def __init__(self, location):
+ self.location = location
+
+ def getLocation(self):
+ return self.location
+
+ def __str__(self):
+ return 'Location: %s' % self.location
+
+
+class IMethodNotAllowed(IPublishingException):
+ """An exception that signals the 405 Method Not Allowed HTTP error"""
+
+ object = Attribute("""The object on which the error occurred""")
+
+ request = Attribute("""The request in which the error occurred""")
+
+
+class MethodNotAllowed(PublishingException):
+ """An exception that signals the 405 Method Not Allowed HTTP error"""
+
+ implements(IMethodNotAllowed)
+
+ def __init__(self, object, request):
+ self.object = object
+ self.request = request
+
+ def __str__(self):
+ return "%r, %r" % (self.object, self.request)
+
+
+class IHTTPRequest(IRequest):
+
+ method = Attribute("Request method, normalized to upper case")
+
+ path_suffix = Attribute(
+ """Additional traversal steps to be taken after all other traversal
+
+ This is used to handle HTTP request methods (except for GET
+ and POST in the case of browser requests) and XML-RPC methods.
+ """)
+
+ locale = Attribute(
+ """The locale object associated with this request.""")
+
+ form = Attribute(
+ """Form data
+
+ This is a read-only mapping from name to form value for the name.
+ """)
+
+ def getCookies():
+ """Return the cookie data
+
+ Data are returned as a mapping object, mapping cookie name to value.
+ """
+
+ cookies = Attribute(
+ """Request cookie data
+
+ This is a read-only mapping from variable name to value.
+ """)
+
+ def getHeader(name, default=None, literal=False):
+ """Get a header value
+
+ Return the named HTTP header, or an optional default
+ argument or None if the header is not found. Note that
+ both original and CGI-ified header names are recognized,
+ e.g. 'Content-Type', 'CONTENT_TYPE' and 'HTTP_CONTENT_TYPE'
+ should all return the Content-Type header, if available.
+
+ If the literal argument is passed, the header is searched
+ 'as is', eg: only if the case matches.
+ """
+
+ headers = Attribute(
+ """Request header data
+
+ This is a read-only mapping from variable name to value.
+ It does *not* support iteration.
+ """)
+
+ URL = Attribute(
+ """Request URL data
+
+ When converted to a string, this gives the effective published URL.
+
+ This object can also be used as a mapping object. The key must
+ be an integer or a string that can be converted to an
+ integer. A non-negative integer returns a URL n steps from the
+ URL of the top-level application objects. A negative integer
+ gives a URL that is -n steps back from the effective URL.
+
+ For example, 'request.URL[-2]' is equivalent to the Zope 2
+ 'request["URL2"]'. The notion is that this would be used in
+ path expressions, like 'request/URL/-2'.
+ """)
+
+
+ def getURL(level=0, path_only=False):
+ """Return the published URL with level names removed from the end.
+
+ If path_only is true, then only a path will be returned.
+ """
+
+ def getApplicationURL(depth=0, path_only=False):
+ """Return the application URL plus depth steps
+
+ If path_only is true, then only a path will be returned.
+ """
+
+ def getBasicAuth():
+ """Return (login, password) if there are basic credentials.
+
+ Return None if the request does not contain basic credentials.
+ """
+
+ _authUserPw = getBasicAuth
+
+ def unauthorized(challenge):
+ """Issue a 401 Unauthorized error (asking for login/password).
+
+ Sets the WWW-Authenticate header to the value of the
+ challenge parameter.
+ """
+
+
+class IHTTPResponse(IResponse):
+ """An object representation of an HTTP response."""
+
+ def setHeader(name, value, literal=False):
+ """Sets an HTTP return header "name" with value "value"
+
+ The previous value is cleared. If the literal flag is true,
+ the case of the header name is preserved, otherwise
+ word-capitalization will be performed on the header name on
+ output.
+ """
+
+ def addHeader(name, value):
+ """Add an HTTP Header
+
+ Sets a new HTTP return header with the given value, while retaining
+ any previously set headers with the same name.
+ """
+
+ def getHeader(name, default=None):
+ """Gets a header value
+
+ Returns the value associated with a HTTP return header, or
+ 'default' if no such header has been set in the response
+ yet.
+ """
+
+ def appendToCookie(name, value):
+ """Append text to a cookie value
+
+ If a value for the cookie has previously been set, the new
+ value is appended to the old one separated by a colon.
+ """
+
+ def expireCookie(name, **kw):
+ """Causes an HTTP cookie to be removed from the browser
+
+ The response will include an HTTP header that will remove the cookie
+ corresponding to "name" on the client, if one exists. This is
+ accomplished by sending a new cookie with an expiration date
+ that has already passed. Note that some clients require a path
+ to be specified - this path must exactly match the path given
+ when creating the cookie. The path can be specified as a keyword
+ argument.
+ If the value of a keyword argument is None, it will be ignored.
+ """
+
+ def setCookie(name, value, **kw):
+ """Sets an HTTP cookie on the browser
+
+ The response will include an HTTP header that sets a cookie on
+ cookie-enabled browsers with a key "name" and value
+ "value". This overwrites any previously set value for the
+ cookie in the Response object.
+ If the value of a keyword argument is None, it will be ignored.
+ """
+
+ def getCookie(name, default=None):
+ """Gets HTTP cookie data as a dict
+
+ Returns the dict of values associated with an HTTP cookie set in the
+ response, or 'default' if no such cookie has been set in the response
+ yet.
+ """
+
+ def redirect(location, status=302):
+ """Causes a redirection without raising an error.
+ """
More information about the Checkins
mailing list