[Zope-Checkins] CVS: Zope3/lib/python/Zope/Publisher - IApplicationRequest.py:1.1.2.1 IPublicationRequest.py:1.1.2.1 IPublisher.py:1.1.2.1 IPublisherRequest.py:1.1.2.1 IPublisherResponse.py:1.1.2.1 BaseRequest.py:1.1.2.23.4.1 BaseResponse.py:1.1.2.8.6.1 DefaultPublication.py:1.1.2.9.4.1 Exceptions.py:1.1.2.10.6.1 IPublication.py:1.1.2.9.6.1 Publish.py:1.1.2.13.6.1
Jim Fulton
jim@zope.com
Sat, 16 Mar 2002 09:44:31 -0500
Update of /cvs-repository/Zope3/lib/python/Zope/Publisher
In directory cvs.zope.org:/tmp/cvs-serv24057/python/Zope/Publisher
Modified Files:
Tag: Zope3-publisher-refactor-branch
BaseRequest.py BaseResponse.py DefaultPublication.py
Exceptions.py IPublication.py Publish.py
Added Files:
Tag: Zope-3x-branch
IApplicationRequest.py IPublicationRequest.py IPublisher.py
IPublisherRequest.py IPublisherResponse.py
Log Message:
Checking in partial publisher refactoring on the
Zope3-publisher-refactor-branch branch to facilitate collaboration
with Stephan.
=== Added File Zope3/lib/python/Zope/Publisher/IApplicationRequest.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""
Revision information:
$Id: IApplicationRequest.py,v 1.1.2.1 2002/03/16 14:44:00 jim Exp $
"""
from Interface import Interface
from Interface.Common.Mapping import IEnumerableMapping
class IApplicationRequest(IEnumerableMapping):
"""Features that support application logic
"""
def getBody():
"""Return the body of the request as a string
"""
def getBodyFile():
"""Return the body of the request as a file
"""
=== Added File Zope3/lib/python/Zope/Publisher/IPublicationRequest.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""
Revision information:
$Id: IPublicationRequest.py,v 1.1.2.1 2002/03/16 14:44:00 jim Exp $
"""
from Interface import Interface
class IPublicationRequest(Interface):
"""Interface provided by requests to IPublication objects
"""
def hold(object):
"""Hold a reference to an object until the request is closed
"""
def getTraversalStack():
"""Return the request traversal stack
This is a sequence of steps to traverse in reverse order. They
will be traversed from last to first.
"""
def setTraversalStack(stack):
"""Change the traversal stack.
See getTraversalStack.
"""
def getPositionalArguments():
"""Return the positional arguments given to the request.
"""
=== Added File Zope3/lib/python/Zope/Publisher/IPublisher.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""
Revision information:
$Id: IPublisher.py,v 1.1.2.1 2002/03/16 14:44:00 jim Exp $
"""
from Interface import Interface
def IPublisher(Interface):
def publish(request):
"""Publish a request
The request must be an IPublisherRequest.
"""
=== Added File Zope3/lib/python/Zope/Publisher/IPublisherRequest.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""
Revision information:
$Id: IPublisherRequest.py,v 1.1.2.1 2002/03/16 14:44:00 jim Exp $
"""
from Interface import Interface
class IPublisherRequest(Interface):
"""Interface used by the publsher
"""
def supports_retry():
"""Check whether the request supports retry
Return a boolean value indicating whether the request can be retried.
"""
def retry():
"""Return a retry request
Return a request suitable for repeating the publication attempt.
"""
def getPublication():
"""Return the requets's publication object
The publication object, an IRequestPublication provides
application-specific functionality hooks.
"""
def getResponse():
"""Return the request's response object
Return an IPublisherResponse for the request.
"""
def traverse(publication, object):
"""Traverse from the given object to the published object
The published object is returned.
The following hook methods on the publication will be called:
- callTraversalHooks is called before each step and after
the last step.
- traverseName to actually do a single traversal
"""
def close():
"""Release resources held by the request.
"""
def processInputs():
"""Do any input processing that needs to bve done before traversing
This is done after construction to allow the publisher to
handle errors that arise.
"""
=== Added File Zope3/lib/python/Zope/Publisher/IPublisherResponse.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""
Revision information:
$Id: IPublisherResponse.py,v 1.1.2.1 2002/03/16 14:44:00 jim Exp $
"""
from Interface import Interface
class IPublisherResponse:
"""Interface used by the publsher
"""
def setBody(result):
"""Set's the response result value.
"""
def handleException(exc_info):
"""Handle an otherwise unhandled exception.
"""
def outputBody():
"""Output the response to the client
"""
=== Zope3/lib/python/Zope/Publisher/BaseRequest.py 1.1.2.23 => 1.1.2.23.4.1 ===
from types import StringType
from Zope.ComponentArchitecture.IViewService import IViewRequest
-
-def pc_quote(s):
- """
- URL-quotes only the characters not allowed in the path
- component of a URL. See RFC 2396 Section 3.3.
+from BaseResponse import BaseResponse
+from IApplicationRequest import IApplicationRequest
+from IPublisherRequest import IPublisherRequest
+from IPublicationRequest import IPublicationRequest
+
+class IRequest(IPublisherRequest, IPublicationRequest, IApplicationRequest,
+ IViewRequest):
+ """The basic request contract
"""
- return quote(s, "/:@&=+$,;")
-
-_marker = []
+_marker = object()
class BaseRequest:
"""Represents a publishing request.
@@ -42,76 +43,48 @@
collection of variable to value mappings.
"""
- __implements__ = IViewRequest
- # XXX: maybe more to implement :-)
-
- body_instream = None # The body input stream
- common = {} # Data common to all requests
- args = () # Positional arguments
- publication = None # Publication object
-
- # _held contains objects kept until the request is closed,
- # such as the database connection closer.
- _held = ()
- _request_default = None # Overrides publication.getDefaultTraversal().
- _body = None # Cached body
-
- # URL is a string built up during traversal and url_quoted.
- # It does not include names built from publication.getDefaultTraversal().
- URL = ''
- effective_url = '' # URL plus the names built from getDefaultTraversal().
- to_traverse = () # A sequence containing the names to traverse, reversed.
- steps = () # A sequence of names built up during traversal.
- quoted_steps = () # Same as steps but quoted.
-
- # traversed contains the objects traversed in order.
- traversed = ()
-
- def __init__(self, response, body_instream=None, publication=None):
- """
- The constructor should not raise errors.
- processInputs() should perform the unsafe initialization.
- """
- self.response = response
- self.body_instream = body_instream
- self.publication = publication
- self.steps = []
- self.quoted_steps = []
- self.other = {}
+ __implements__ = IRequest
+ _common = {} # Variables common to all request instances
+ _held = () # Objects held until the request is closed
+
+ def __init__(self, body_instream, outstream, environ):
+ self.__traversal_stack = ()
+ self._other = {}
+ self._environ = environ
+ self.__args = ()
+ self.__response = self._createResponse(outstream)
+ self.__body_instream = body_instream
+ self.__held = ()
+
+ def getPositionalArguments(self):
+ return self.__args
+
+ def _createResponse(self, outstream):
+ # Should be overridden by subclasses
+ return BaseResponse(outstream)
def close(self):
- self.other.clear()
- self._held = None # Release held objects
- self.traversed = None
+ self._other.clear()
+ self.__held = () # Release held objects
+ self.__traversed = None
def processInputs(self):
"""Do any initialization that could raise errors
"""
def getPublication(self):
- return self.publication
-
- def __len__(self):
- return 1
+ return self.__publication
- _key_handlers = {}
-
- def getRequest(self, default=None):
- return self
- _key_handlers['REQUEST'] = getRequest
+ def setPublication(self, pub):
+ self.__publication = pub
def getResponse(self, default=None):
- return self.response
- _key_handlers['RESPONSE'] = getResponse
-
- def getURL(self, default=None):
- return self.URL
- _key_handlers['URL'] = getURL
+ return self.__response
def getBody(self, default=None):
- body = self._body
+ body = self.__body
if body is None:
- s = self.body_instream
+ s = self.__body_instream
if s is None:
return default
p = s.tell()
@@ -120,52 +93,38 @@
s.seek(p)
self._body = body
return body
- _key_handlers['BODY'] = getBody
def getBodyFile(self, default=None):
return self.body_instream
- _key_handlers['BODYFILE'] = getBodyFile
-
- def getEffectiveURL(self):
- return self.effective_url or self.URL
def get(self, key, default=None):
- """Get a variable value
- Return a value for the required variable name.
- The value will be looked up from one of the request data
- categories. The search order is environment variables,
- other variables, form data, and then cookies.
-
- """
- handler = self._key_handlers.get(key, None)
- if handler is not None:
- v = handler(self, _marker)
- if v is not _marker:
- return v
- v = self.other.get(key, _marker)
- if v is not _marker:
- return v
- v = self.common.get(key, _marker)
- if v is not _marker:
- return v
+ result = self._other.get(key, self)
+ if result is not self: return result
+
+ result = self._common.get(key, self)
+ if result is not self: return result
+
+ result = self._environ.get(key, self)
+ if result is not self: return result
return default
def __getitem__(self, key):
- res = self.get(key, _marker)
- if res is _marker:
+ result = self.get(key, _marker)
+ if result is _marker:
raise KeyError, key
else:
- return res
+ return result
def has_key(self, key):
return self.get(key, _marker) is not _marker
def keys(self):
- keys = {'URL':1}
- keys.update(self.common)
- keys.update(self.other)
+ keys = {}
+ keys.update(self._common)
+ keys.update(self._other)
+ keys.update(self._environ)
return keys.keys()
def items(self):
@@ -192,34 +151,18 @@
return '<%s instance at 0x%x, URL=%s>' % (
str(self.__class__), id(self), `self.URL`)
- def splitPath(self, path):
- # Split and clean up the path.
- if path.startswith('/'): path = path[1:]
- if path.endswith('/'): path = path[:-1]
- clean = []
- for item in path.split('/'):
- if not item or item == '.':
- continue
- elif item == '..':
- del clean[-1]
- else: clean.append(item)
- return clean
-
- def setRequestDefault(self, path):
+ def setPathSuffix(self, steps):
"""
Adds the specified steps to the URL, overriding
publication.getDefaultTraversal().
"""
- steps = path.split('/')
- steps.reverse()
- self._request_default = steps
+ self.__path_suffix = steps
- def changeTraversalStack(self, names):
- """
- Private. Can be called by a traversal hook.
- 'names' argument should be in reverse.
- """
- self.to_traverse = names
+ def getTraversalStack(self):
+ return self.__traversal_stack
+
+ def setTraversalStack(self, stack):
+ self.__traversal_stack = stack
def traverse(self, publication, object):
@@ -227,88 +170,32 @@
Traverses to an object and returns it.
Private.
"""
-
- # XXX This is too Browser specific. I mean, setBase?
-
- traversal_altered = 0 # flag for adding traversal steps
- add_steps = None
- path_str = self.get('PATH_INFO', '').strip()
- to_traverse = self.splitPath(path_str)
-
- self.traversed = traversed = []
- traversed.append(object)
- steps = self.steps
- self.quoted_steps = quoted_steps = map(pc_quote, steps)
- to_traverse.reverse()
- self.to_traverse = to_traverse
-
+ to_traverse = self.__traversal_stack
prev_object = None
while 1:
if object is not prev_object:
# Invoke hooks (but not more than once).
publication.callTraversalHooks(self, object)
# A hook may have called changeTraversalStack().
- to_traverse = self.to_traverse
+ to_traverse = self.__traversal_stack
prev_object = object
if to_traverse:
# Traverse to the next step.
entry_name = to_traverse.pop()
- if entry_name:
- qstep = pc_quote(entry_name)
- quoted_steps.append(qstep)
-
- if traversal_altered:
- # The effective URL includes the altered traversal.
- #import pdb; pdb.set_trace()
- e_url = self.effective_url or self.URL
- self.effective_url = '%s/%s' % (e_url, qstep)
- else:
- # Build up the URL to the object, but not
- # to the default traversal.
- self.URL = '%s/%s' % (self.URL, qstep)
- subobject = publication.traverseName(
- self, object, entry_name)
- object = subobject
- traversed.append(object)
- steps.append(entry_name)
-
+ subobject = publication.traverseName(
+ self, object, entry_name)
+ object = subobject
else:
- add_steps = self._request_default
-
- if add_steps:
- self._request_default = None
-
- if add_steps is None:
- object, add_steps = publication.getDefaultTraversal(
- self, object)
-
- if add_steps:
- traversal_altered = 1
- to_traverse.extend(add_steps)
- else:
- # Finished traversal.
- break
-
- if traversal_altered:
- eurl = self.effective_url
- l = eurl.rfind('/')
- if l >= 0: eurl = eurl[:l+1] # XXX Quick bug fix, need better impl
- self.response.setBase(eurl)
-
- self.traversed = tuple(traversed) # No more changes allowed
- parents = traversed[:]
- parents.pop()
- parents.reverse()
- self.other['PARENTS'] = tuple(parents)
- self.other['PUBLISHED'] = object
+ # Finished traversal.
+ break
return object
def supports_retry(self):
return 0
- def _hold(self, object):
+ def hold(self, object):
"""
Holds a reference to an object to delay its destruction until mine
"""
=== Zope3/lib/python/Zope/Publisher/BaseResponse.py 1.1.2.8 => 1.1.2.8.6.1 ===
from Exceptions import Unauthorized
-class BaseResponse:
+class BaseResponse(object):
"""Base Response Class
What should be here?
@@ -28,8 +28,6 @@
def __init__(self, outstream):
self.outstream = outstream
- self.headers = {}
- self.cookies = {}
def setDebugMode(self, d):
self.debug_mode = d
=== Zope3/lib/python/Zope/Publisher/DefaultPublication.py 1.1.2.9 => 1.1.2.9.4.1 ===
def beforeTraversal(self, request):
- pass
+ # Lop off leading and trailing empty names
+ stack = request.getTraversalStack()
+ while stack and not stack[-1]:
+ stack.pop() # toss a trailing empty name
+ while stack and not stack[0]:
+ stack.pop(0) # toss a leading empty name
+ request.setTraversalStack(stack)
def getApplication(self, request):
return self.app
@@ -41,8 +47,7 @@
TypeError, AttributeError):
raise NotFound(ob, name, request)
if self.require_docstrings and not getattr(subob, '__doc__', None):
- raise DebugError(subob, 'Missing or empty doc string at: %s' %
- request.getURL())
+ raise DebugError(subob, 'Missing or empty doc string')
return subob
def getDefaultTraversal(self, request, ob):
@@ -52,13 +57,13 @@
pass
def callObject(self, request, ob):
- return mapply(ob, request.args, request)
+ return mapply(ob, request.getPositionalArguments(), request)
def afterCall(self, request):
pass
def handleException(self, request, exc_info, retry_allowed=1):
# Let the response handle it as best it can.
- response = request.response
+ response = request.getResponse()
response.handleException(exc_info)
=== Zope3/lib/python/Zope/Publisher/Exceptions.py 1.1.2.10 => 1.1.2.10.6.1 ===
self.ob = ob
self.name = name
- if request is not None:
- url = request.getEffectiveURL()
- else:
- url = None
- self.url = url
def getObject(self):
return self.ob
@@ -41,10 +36,7 @@
return self.name
def __str__(self):
- if self.url:
- return self.url
- else:
- return 'Object: %s, name: %s' % (`self.ob`, `self.name`)
+ return 'Object: %s, name: %s' % (`self.ob`, `self.name`)
class DebugError (TraversalException):
=== Zope3/lib/python/Zope/Publisher/IPublication.py 1.1.2.9 => 1.1.2.9.6.1 ===
"""
Either:
- - sets the body of request.response,
+ - sets the body of request.getResponse(),
- raises a Retry exception, or
- throws another exception, which is a Bad Thing.
- Returns the response object.
"""
=== Zope3/lib/python/Zope/Publisher/Publish.py 1.1.2.13 => 1.1.2.13.6.1 ===
"""
publication = None
- response = request.response
+ response = request.getResponse()
try:
request.processInputs()
@@ -49,7 +49,7 @@
if publication is not None:
publication.handleException(request, exc_info, allow_retry)
else:
- request.response.handleException(exc_info)
+ request.getResponse().handleException(exc_info)
@@ -78,14 +78,12 @@
# Bad exception handler or retry method.
# Re-raise after outputting the response.
to_raise = sys.exc_info()
- request.response.setStatus(500) # Try to indicate an error.
break
- response = request.response
+ response = request.getResponse()
response.outputBody()
if to_raise is not None:
raise to_raise[0], to_raise[1], to_raise[2]
- return response.getStatus()
finally:
to_raise = None # Avoid circ. ref.