[Zope-Checkins] CVS: Zope3/lib/python/Zope/Publisher - IApplicationResponse.py:1.1.2.1 BaseRequest.py:1.1.2.23.4.2 BaseResponse.py:1.1.2.8.6.2 IApplicationRequest.py:1.1.2.1.2.1 IPublication.py:1.1.2.9.6.2 IPublicationRequest.py:1.1.2.1.2.1 IPublisherRequest.py:1.1.2.1.2.1 IPublisherResponse.py:1.1.2.1.2.1 Publish.py:1.1.2.13.6.2

Jim Fulton jim@zope.com
Wed, 20 Mar 2002 18:43:02 -0500


Update of /cvs-repository/Zope3/lib/python/Zope/Publisher
In directory cvs.zope.org:/tmp/cvs-serv21411/Zope/Publisher

Modified Files:
      Tag: Zope3-publisher-refactor-branch
	BaseRequest.py BaseResponse.py IApplicationRequest.py 
	IPublication.py IPublicationRequest.py IPublisherRequest.py 
	IPublisherResponse.py Publish.py 
Added Files:
      Tag: Zope3-publisher-refactor-branch
	IApplicationResponse.py 
Log Message:
Began significant refactoring of publication framework.

- Added accessor functions, getResponse and getPublication.

- Moved some methods between existing interfaces as seem best, and
  added new new interfaces.

- Getting rid of payloads.




=== Added File Zope3/lib/python/Zope/Publisher/IApplicationResponse.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: IApplicationResponse.py,v 1.1.2.1 2002/03/20 23:42:31 jim Exp $
"""

from Interface import Interface

class IApplicationResponse(Interface):
    """Features that support application logic
    """

    def write(string):
        """Output a atring to the response body.
        """


=== Zope3/lib/python/Zope/Publisher/BaseRequest.py 1.1.2.23.4.1 => 1.1.2.23.4.2 ===
 from cgi import escape
 from types import StringType
-from Zope.ComponentArchitecture.IViewService import IViewRequest
 from BaseResponse import BaseResponse
 from IApplicationRequest import IApplicationRequest
 from IPublisherRequest import IPublisherRequest
 from IPublicationRequest import IPublicationRequest
 
-class IRequest(IPublisherRequest, IPublicationRequest, IApplicationRequest,
-               IViewRequest):
+class IRequest(IPublisherRequest, IPublicationRequest, IApplicationRequest):
     """The basic request contract
     """
 
@@ -56,32 +54,126 @@
         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 = ()  # Release held objects
-        self.__traversed = None
+    ############################################################
+    # Implementation methods for interface
+    # Zope.Publisher.BaseRequest.IRequest
 
-    def processInputs(self):
-        """Do any initialization that could raise errors
-        """
+    ######################################
+    # from: Zope.Publisher.IPublisherRequest.IPublisherRequest
 
     def getPublication(self):
+        'See Zope.Publisher.IPublisherRequest.IPublisherRequest'
         return self.__publication
 
+    def processInputs(self):
+        'See Zope.Publisher.IPublisherRequest.IPublisherRequest'
+        # Nothing to do here
+
+    def retry(self):
+        'See Zope.Publisher.IPublisherRequest.IPublisherRequest'
+        raise TypeError('Retry is not supported')
+
     def setPublication(self, pub):
+        'See Zope.Publisher.IPublisherRequest.IPublisherRequest'
         self.__publication = pub
 
-    def getResponse(self, default=None):
+    def supportsRetry(self):
+        'See Zope.Publisher.IPublisherRequest.IPublisherRequest'
+        return 0
+
+    def traverse(self, object):
+        'See Zope.Publisher.IPublisherRequest.IPublisherRequest'
+
+        publication = self.getPublication()
+
+        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.__traversal_stack
+            prev_object = object
+
+            if to_traverse:
+                # Traverse to the next step.
+                entry_name = to_traverse.pop()
+                subobject = publication.traverseName(
+                    self, object, entry_name)
+                object = subobject
+            else:
+                # Finished traversal.
+                break
+
+        return object
+
+    ######################################
+    # from: Zope.Publisher.IPublicationRequest.IPublicationRequest
+
+    def close(self):
+        'See Zope.Publisher.IPublicationRequest.IPublicationRequest'
+        self._other.clear()
+        self.__dict__.clear()
+
+    def getPositionalArguments(self):
+        'See Zope.Publisher.IPublicationRequest.IPublicationRequest'
+        return self.__args
+
+    def getResponse(self):
+        'See Zope.Publisher.IPublicationRequest.IPublicationRequest'
         return self.__response
 
-    def getBody(self, default=None):
+    def getTraversalStack(self):
+        'See Zope.Publisher.IPublicationRequest.IPublicationRequest'
+        return self.__traversal_stack
+
+    def hold(self, object):
+        'See Zope.Publisher.IPublicationRequest.IPublicationRequest'
+        self._held = self._held + (object,)
+
+    def setTraversalStack(self, stack):
+        'See Zope.Publisher.IPublicationRequest.IPublicationRequest'
+        self.__traversal_stack = stack
+
+    _viewskin = ''
+
+    def setViewSkin(self, skin):
+        'See Zope.Publisher.IPublicationRequest.IPublicationRequest'
+        self._viewskin = skin
+
+    ######################################
+    # from: Zope.ComponentArchitecture.IViewService.IViewRequest
+
+    def getViewSkin(self):
+        'See Zope.ComponentArchitecture.IViewService.IViewRequest'
+        return self._viewskin
+
+    _viewtype = None
+
+    def getViewType(self):
+        'See Zope.ComponentArchitecture.IViewService.IViewRequest'
+        return self._viewtype
+
+    # This is not part of the interface:
+    def setViewType(self, viewtype):
+        '''Set the view type.
+
+        This method will normally only be called in tests, which will allow
+        us to use a simpler Request set-up.'''
+
+        # XXX This will probably go away
+
+        self._viewtype = viewtype
+
+
+    ######################################
+    # from: Zope.Publisher.IApplicationRequest.IApplicationRequest
+
+    __body = None
+    def getBody(self):
+        'See Zope.Publisher.IApplicationRequest.IApplicationRequest'
         body = self.__body
         if body is None:
             s = self.__body_instream
@@ -91,145 +183,96 @@
             s.seek(0)
             body = s.read()
             s.seek(p)
-            self._body = body
+            self.__body = body
         return body
 
-    def getBodyFile(self, default=None):
-        return self.body_instream
-
-    def get(self, key, default=None):
-
-        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):
-        result = self.get(key, _marker)
-        if result is _marker:
-            raise KeyError, key
-        else:
-            return result
+    def getBodyFile(self):
+        'See Zope.Publisher.IApplicationRequest.IApplicationRequest'
+        return self.__body_instream
+
+    ######################################
+    # from: Interface.Common.Mapping.IEnumerableMapping
+
+    def __len__(self):
+        'See Interface.Common.Mapping.IEnumerableMapping'
+        return len(self._common) + len(self._other) + len(self._environ)
 
-    def has_key(self, key):
-        return self.get(key, _marker) is not _marker
+    def items(self):
+        'See Interface.Common.Mapping.IEnumerableMapping'
+        result = []
+        get = self.get
+        for k in self.keys():
+            result.append((k, get(k)))
+        return result
 
     def keys(self):
+        'See Interface.Common.Mapping.IEnumerableMapping'
         keys = {}
         keys.update(self._common)
         keys.update(self._other)
         keys.update(self._environ)
         return keys.keys()
 
-    def items(self):
-        result = []
-        get = self.get
-        for k in self.keys():
-            result.append((k, get(k)))
-        return result
-
     def values(self):
+        'See Interface.Common.Mapping.IEnumerableMapping'
         result = []
         get = self.get
         for k in self.keys():
             result.append(get(k))
         return result
 
-    def __str__(self):
-        L1 = self.items()
-        L1.sort()
-        return "\n".join(map(lambda item: "%s:\t%s" % item, L1))
-
-    def __repr__(self):
-        # Returns a *short* string.
-        return '<%s instance at 0x%x, URL=%s>' % (
-            str(self.__class__), id(self), `self.URL`)
-
-    def setPathSuffix(self, steps):
-        """
-        Adds the specified steps to the URL, overriding
-        publication.getDefaultTraversal().
-        """
-        self.__path_suffix = steps
-
-    def getTraversalStack(self):
-        return self.__traversal_stack
+    ######################################
+    # from: Interface.Common.Mapping.IReadMapping
 
-    def setTraversalStack(self, stack):
-        self.__traversal_stack = stack
+    def __getitem__(self, key):
+        'See Interface.Common.Mapping.IReadMapping'
+        result = self.get(key, _marker)
+        if result is _marker:
+            raise KeyError, key
+        else:
+            return result
 
+    def get(self, key, default=None):
+        'See Interface.Common.Mapping.IReadMapping'
+        result = self._other.get(key, self)
+        if result is not self: return result 
 
-    def traverse(self, publication, object):
-        """
-        Traverses to an object and returns it.
-        Private.
-        """
-        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.__traversal_stack
-            prev_object = object
+        result = self._common.get(key, self)
+        if result is not self: return result 
 
-            if to_traverse:
-                # Traverse to the next step.
-                entry_name = to_traverse.pop()
-                subobject = publication.traverseName(
-                    self, object, entry_name)
-                object = subobject
-            else:
-                # Finished traversal.
-                break
+        result = self._environ.get(key, self)
+        if result is not self: return result
 
-        return object
+        return default
 
-    def supports_retry(self):
-        return 0
+    #
+    ############################################################
 
-    def hold(self, object):
-        """
-        Holds a reference to an object to delay its destruction until mine
-        """
-        self._held = self._held + (object,)
+    def _createResponse(self, outstream):
+        # Should be overridden by subclasses
+        return BaseResponse(outstream)
 
+    def __nonzero__(self):
+        # This is here to avoid calling __len__ for boolean tests
+        return 1
 
-    # Implementation methods for interface
-    # Zope.ComponentArchitecture.IViewService.IViewRequest
+    def __str__(self):
+        L1 = self.items()
+        L1.sort()
+        return "\n".join(map(lambda item: "%s:\t%s" % item, L1))
 
-    # Base classes might provide different attributes for this
+    def __repr__(self):
+        # Returns a *short* string.
+        return '<%s instance at 0x%x, URL=%s>' % (
+            str(self.__class__), id(self), `self.URL`)
 
-    _viewskin = ''
-    _viewtype = None
-    
-    def getViewSkin(self):
-        '''See interface IViewRequest'''
 
-        return self._viewskin
 
 
-    def setViewSkin(self, skin):
-        ''' add the view skin '''
- 
-        self._viewskin = skin
 
 
-    def getViewType(self):
-        '''See interface IViewRequest'''
 
-        return self._viewtype
 
 
-    def setViewType(self, viewtype):
-        ''' set the view type '''
 
-        self._viewtype = viewtype
 


=== Zope3/lib/python/Zope/Publisher/BaseResponse.py 1.1.2.8.6.1 => 1.1.2.8.6.2 ===
 
 from Exceptions import Unauthorized
+import traceback
 
 class BaseResponse(object):
     """Base Response Class
@@ -29,100 +30,26 @@
     def __init__(self, outstream):
         self.outstream = outstream
 
-    def setDebugMode(self, d):
-        self.debug_mode = d
-    
-    def setStatus(self, status, reason=None):
-        self.status = status
-
-    def setHeader(self, name, value):
-        self.headers[name] = value
-
-    __setitem__ = setHeader
-
     def outputBody(self):
+        """Output the response body.
         """
-        Output the response body.
-        """
-        self.outstream.write(str(self))
+        self.outstream.write(self._getBody())
+
+    def write(self, string):
+        self.body += string
 
     def setBody(self, body):
         self.body = body
 
-    def setBase(self,base):
-        'Sets the base URL for the returned document.'
-        self.base=base
-
-    def getBase(self):
-        ' return the base url '
-        return self.base
-
-    def getStatus(self):
-        'Returns the current HTTP status code as an integer. '
-        return self.status
-
-    def setCookie(self,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.
-        '''
-        cookies=self.cookies
-        if cookies.has_key(name):
-            cookie=cookies[name]
-        else: cookie=cookies[name]={}
-        for k, v in kw.items():
-            cookie[k]=v
-        cookie['value']=value
-
-    def getHeader(self, name):
-         '''
-         Gets a header value
-         
-         Returns the value associated with a HTTP return header, or
-         "None" if no such header has been set in the response
-         yet.
-         '''
-         return self.headers.get(name, None)
-
-    def __getitem__(self, name):
-        'Gets the value of an output header'
-        return self.headers[name]
-
-    def getBody(self):
+    def _getBody(self):
         'Returns a string representing the currently set body.'
         return self.body
 
-    def __str__(self):
-        return str(self.body)
-
-    def __repr__(self):
-        return '%s(%s)' % (self.__class__.__name__, `self.body`)
-
-    def flush(self):
-        pass
-
-    def write(self,data):
-        """
-        Implements the stream output interface.
-
-        HTML data may be returned using a stream-oriented interface.
-        This allows the browser to display partial results while
-        computation of a response to proceed.
-
-        The published object should first set any output headers or
-        cookies on the response object.
-
-        Note that published objects must not generate any errors
-        after beginning stream-oriented output. 
-        """
-        self.body = self.body + data
-
     def handleException(self, exc_info):
-        import traceback
         traceback.print_exception(
             exc_info[0], exc_info[1], exc_info[2], 100, self)
 
+    def retry(self):
+        return self.__class__(self.outstream)
+
+    


=== Zope3/lib/python/Zope/Publisher/IApplicationRequest.py 1.1.2.1 => 1.1.2.1.2.1 ===
 """
 
-from Interface import Interface
 from Interface.Common.Mapping import IEnumerableMapping
 
 class IApplicationRequest(IEnumerableMapping):


=== Zope3/lib/python/Zope/Publisher/IPublication.py 1.1.2.9.6.1 => 1.1.2.9.6.2 ===
 
 class IPublication (Interface):
-    """
-    Object publication framework.
+    """Object publication framework.
+
+    The responsibility of publication objects is to provide
+    application hooks for the publishing process. This allows
+    application-specific tasks, such as connecting to databases,
+    managing transactions, and setting security contexts to be invoked
+    during the publishing process.
+
     """
     # The order of the hooks mostly corresponds with the order in which
     # they are invoked.
@@ -39,35 +45,31 @@
         Returns the subobject.
         """
 
-    def getDefaultTraversal(request, ob):
-        """
-        Allows a default view to be added to traversal.
-        Returns (ob, steps_reversed).
-        """
-
     def afterTraversal(request, ob):
-        """
-        Post-traversal hook.
+        """Post-traversal hook.
         """
 
     def callObject(request, ob):
-        """
-        Calls the object, returning the result as a string.
+        """Call the object, returning the result.
+        
         For GET/POST this means calling it, but for other methods
         (including those of WebDAV and FTP) this might mean invoking
         a method of an adapter.
         """
 
     def afterCall(request):
-        """
-        Post-callObject hook (if it was successful).
+        """Post-callObject hook (if it was successful).
         """
 
     def handleException(request, exc_info, retry_allowed=1):
-        """
+        """Handle an exception
+        
         Either:
-        - sets the body of request.getResponse(),
+        - sets the body of the response, request.getResponse(), or
         - raises a Retry exception, or
         - throws another exception, which is a Bad Thing.
+
+        Note that this method should not leak, which means that
+        exc_info must be set to some other value before exiting the method.
         """
 


=== Zope3/lib/python/Zope/Publisher/IPublicationRequest.py 1.1.2.1 => 1.1.2.1.2.1 ===
 """
 
-from Interface import Interface
+from Zope.ComponentArchitecture.IViewService import IViewRequest
 
-class IPublicationRequest(Interface):
+class IPublicationRequest(IViewRequest):
     """Interface provided by requests to IPublication objects
     """
 
+    def getResponse():
+        """Return the request's response object
+
+        Return an IPublisherResponse for the request.
+        """
+
+    def close():
+        """Release resources held by the request.
+        """
+
     def hold(object):
         """Hold a reference to an object until the request is closed
         """
@@ -44,3 +54,10 @@
         """Return the positional arguments given to the request.
         """
         
+    def setViewSkin(skin):
+        """Set the skin to be used for the request.
+
+        It's up to the publication object to decide this.
+        """
+
+


=== Zope3/lib/python/Zope/Publisher/IPublisherRequest.py 1.1.2.1 => 1.1.2.1.2.1 ===
 """
 
-from Interface import Interface
+from IPublicationRequest import IPublicationRequest
 
-class IPublisherRequest(Interface):
-    """Interface used by the publsher
+class IPublisherRequest(IPublicationRequest):
+    """Request interface use by the publisher
+
+    The responsability of requests is to encapsulate protocol
+    specific details, especially wrt request inputs.
+
+    Request objects also serve as "context" objects. providing
+    construction of and access to responses and storage of publication
+    objects. 
+    
     """
 
-    def supports_retry():
+    def supportsRetry():
         """Check whether the request supports retry
 
         Return a boolean value indicating whether the request can be retried.
@@ -42,13 +50,11 @@
         application-specific functionality hooks.
         """
 
-    def getResponse():
-        """Return the request's response object
-
-        Return an IPublisherResponse for the request.
+    def setPublication(publication):
+        """Set the requets's publication object
         """
     
-    def traverse(publication, object):
+    def traverse(object):
         """Traverse from the given object to the published object
 
         The published object is returned.
@@ -60,10 +66,6 @@
 
           - traverseName to actually do a single traversal
           
-        """
-
-    def close():
-        """Release resources held by the request.
         """
 
     def processInputs():


=== Zope3/lib/python/Zope/Publisher/IPublisherResponse.py 1.1.2.1 => 1.1.2.1.2.1 ===
     def handleException(exc_info):
         """Handle an otherwise unhandled exception.
+
+        The handling of the exception is expected to effect the reponse body.
         """
+        # XXX ZopePublication seems to call this, so maybe this should be
+        # in an IPublicationResponse interface, but maybe this will change,
+        # so we'll apply YAGNI for now.
 
     def outputBody():
         """Output the response to the client
         """
         
+    def retry():
+        """Return a retry response
+
+        Return a response suitable for repeating the publication attempt.
+        """


=== Zope3/lib/python/Zope/Publisher/Publish.py 1.1.2.13.6.1 => 1.1.2.13.6.2 ===
     - Some other exception if handleException() raised an exception.
     """
-    publication = None
+    
+    publication = request.getPublication()
     response = request.getResponse()
 
     try:
         request.processInputs()
-        publication = request.getPublication()
         publication.beforeTraversal(request)
 
         root_object = publication.getApplication(request)
-        object = request.traverse(publication, root_object)
+        object = request.traverse(root_object)
         publication.afterTraversal(request, object)
 
         result = publication.callObject(request, object)
@@ -41,16 +41,7 @@
 
         publication.afterCall(request)
     except:
-        handleException(request, publication, sys.exc_info(), 1)
-
-
-
-def handleException(request, publication, exc_info, allow_retry=1):
-    if publication is not None:
-        publication.handleException(request, exc_info, allow_retry)
-    else:
-        request.getResponse().handleException(exc_info)
-
+        publication.handleException(request, sys.exc_info(), 1)
 
 
 def publish(request):
@@ -62,8 +53,8 @@
                     executeRequest(request)
                     # Successful.
                     break
-                except Retry, v:
-                    if request.supports_retry():
+                except Retry, retryException:
+                    if request.supportsRetry():
                         # Create a copy of the request and use it.
                         newrequest = request.retry()
                         request.close()
@@ -71,8 +62,8 @@
                     else:
                         # Output the original exception.
                         publication = request.getPublication()
-                        handleException(request, publication,
-                                        v.getOriginalException(), 0)
+                        publication.handleException(
+                            request, retryException.getOriginalException(), 0)
                         break
             except:
                 # Bad exception handler or retry method.