[Zope3-checkins] CVS: Zope3/src/zope/publisher - __init__.py:1.2 base.py:1.2 browser.py:1.2 configure.zcml:1.2 http.py:1.2 maybe_lock.py:1.2 meta.zcml:1.2 normal.clb:1.2 publish.py:1.2 vfs.py:1.2 xmlrpc.py:1.2

Jim Fulton jim@zope.com
Wed, 25 Dec 2002 09:15:49 -0500


Update of /cvs-repository/Zope3/src/zope/publisher
In directory cvs.zope.org:/tmp/cvs-serv20790/src/zope/publisher

Added Files:
	__init__.py base.py browser.py configure.zcml http.py 
	maybe_lock.py meta.zcml normal.clb publish.py vfs.py xmlrpc.py 
Log Message:
Grand renaming:

- Renamed most files (especially python modules) to lower case.

- Moved views and interfaces into separate hierarchies within each
  project, where each top-level directory under the zope package
  is a separate project.

- Moved everything to src from lib/python.

  lib/python will eventually go away. I need access to the cvs
  repository to make this happen, however.

There are probably some bits that are broken. All tests pass
and zope runs, but I haven't tried everything. There are a number
of cleanups I'll work on tomorrow.



=== Zope3/src/zope/publisher/__init__.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:49 2002
+++ Zope3/src/zope/publisher/__init__.py	Wed Dec 25 09:15:18 2002
@@ -0,0 +1,2 @@
+#
+# This file is necessary to make this directory a package.


=== Zope3/src/zope/publisher/base.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:49 2002
+++ Zope3/src/zope/publisher/base.py	Wed Dec 25 09:15:18 2002
@@ -0,0 +1,495 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+'''Response Output formatter
+
+$Id$
+'''
+
+
+import traceback
+from cStringIO import StringIO
+
+from zope.interface.common.mapping import IReadMapping, IEnumerableMapping
+from zope.exceptions import NotFoundError
+
+from zope.publisher.interfaces import IPublication
+from zope.publisher.interfaces import NotFound, DebugError, Unauthorized
+from zope.publisher.interfaces import IApplicationResponse
+from zope.publisher.interfaces import IApplicationRequest
+from zope.publisher.interfaces import IPublicationRequest
+from zope.publisher.interfaces import IPublisherResponse
+from zope.publisher.interfaces import IPublisherRequest
+
+from zope.publisher.publish import mapply
+
+
+class IResponse(IPublisherResponse, IApplicationResponse):
+    """The basic response contract
+    """
+
+
+class BaseResponse(object):
+    """Base Response Class
+    """
+
+    __slots__ = (
+        '_body',      # The response body
+        '_outstream', # The output stream
+        )
+
+    __implements__ = IResponse
+
+
+    def __init__(self, outstream):
+        self._body = ''
+        self._outstream = outstream
+
+    def outputBody(self):
+        'See IPublisherResponse'
+        self._outstream.write(self._getBody())
+
+    def setBody(self, body):
+        'See IPublisherResponse'
+        self._body = body
+
+    # This method is not part of this interface
+    def _getBody(self):
+        'Returns a string representing the currently set body.'
+        return self._body
+
+    def handleException(self, exc_info):
+        'See IPublisherResponse'
+        traceback.print_exception(
+            exc_info[0], exc_info[1], exc_info[2], 100, self)
+
+    def internalError(self):
+        'See IPublisherResponse'
+        pass
+
+    def retry(self):
+        'See IPublisherResponse'
+        return self.__class__(self.outstream)
+
+    def write(self, string):
+        'See IApplicationResponse'
+        self._body += string
+
+class RequestDataGetter(object):
+
+    __implements__ = IReadMapping
+
+    def __init__(self, request):
+        self.__get = getattr(request, self._gettrname)
+
+    def __getitem__(self, name):
+        return self.__get(name)
+
+    def get(self, name, default=None):
+        return self.__get(name, default)
+
+    def __contains__(self, key):
+        lookup = self.get(key, self)
+        return lookup is not self
+
+    has_key = __contains__
+
+class RequestDataMapper(object):
+
+    __implements__ = IEnumerableMapping
+
+    def __init__(self, request):
+        self.__map = getattr(request, self._mapname)
+
+    def __getitem__(self, name):
+        return self.__map[name]
+
+    def get(self, name, default=None):
+        return self.__map.get(name, default)
+
+    def __contains__(self, key):
+        lookup = self.get(key, self)
+        return lookup is not self
+
+    has_key = __contains__
+
+    def keys(self): return self.__map.keys()
+    def items(self): return self.__map.items()
+    def values(self): return self.__map.values()
+    def __len__(self): return len(self.__map)
+
+class RequestDataProperty(object):
+
+    def __init__(self, gettr_class):
+        self.__gettr_class = gettr_class
+
+    def __get__(self, request, rclass=None):
+        if request is not None:
+            return self.__gettr_class(request)
+
+    def __set__(*args):
+        raise AttributeError, 'Unassignable attribute'
+
+
+
+class IRequest(IPublisherRequest, IPublicationRequest, IApplicationRequest):
+    """The basic request contract
+    """
+
+_marker = object()
+
+class RequestEnvironment(RequestDataMapper):
+    _mapname = '_environ'
+
+class BaseRequest(object):
+    """Represents a publishing request.
+
+    This object provides access to request data. Request data may
+    vary depending on the protocol used.
+
+    Request objects are created by the object publisher and will be
+    passed to published objects through the argument name, REQUEST.
+
+    The request object is a mapping object that represents a
+    collection of variable to value mappings.
+    """
+
+    __implements__ = IRequest
+
+    __slots__ = (
+        '_held',             # Objects held until the request is closed
+        '_traversed_names',  # The names that have been traversed
+        '_traversal_stack',  # Names to be traversed, in reverse order
+        '_environ',          # The request environment variables
+        '_response',         # The response
+        '_args',             # positional arguments
+        '_body_instream',    # input stream
+        '_body',             # The request body as a string
+        '_publication',      # publication object
+        '_presentation_skin', # View skin
+        'user'               # request user, set by publication
+        )
+
+    environment = RequestDataProperty(RequestEnvironment)
+
+    def __init__(self, body_instream, outstream, environ, response=None,
+                 positional=()):
+        self._traversal_stack = []
+        self._traversed_names = []
+        self._environ = environ
+
+        self._args = positional
+        if response is None:
+            self._response = self._createResponse(outstream)
+        else:
+            self._response = response
+        self._body_instream = body_instream
+        self._held = ()
+        self.user = None
+
+    def _getPublication(self):
+        'See IPublisherRequest'
+        return getattr(self, '_publication', None)
+
+    publication = property(_getPublication)
+
+
+    def processInputs(self):
+        'See IPublisherRequest'
+        # Nothing to do here
+
+    def retry(self):
+        'See IPublisherRequest'
+        raise TypeError('Retry is not supported')
+
+    def setPublication(self, pub):
+        'See IPublisherRequest'
+        self._publication = pub
+
+    def supportsRetry(self):
+        'See IPublisherRequest'
+        return 0
+
+    def traverse(self, object):
+        'See IPublisherRequest'
+
+        publication = self.publication
+
+        traversal_stack = self._traversal_stack
+        traversed_names = self._traversed_names
+
+        prev_object = None
+        while 1:
+            if object is not prev_object:
+                # Invoke hooks (but not more than once).
+                publication.callTraversalHooks(self, object)
+
+            prev_object = object
+
+            if traversal_stack:
+                # Traverse to the next step.
+                entry_name = traversal_stack.pop()
+                subobject = publication.traverseName(
+                    self, object, entry_name)
+                traversed_names.append(entry_name)
+                object = subobject
+            else:
+                # Finished traversal.
+                break
+
+        return object
+
+    def close(self):
+        'See IPublicationRequest'
+        self._held = None
+        self._response = None
+        self._body_instream = None
+        self._publication = None
+
+    def getPositionalArguments(self):
+        'See IPublicationRequest'
+        return self._args
+
+    def _getResponse(self):
+        return self._response
+
+    response = property(_getResponse)
+
+    def getTraversalStack(self):
+        'See IPublicationRequest'
+        return list(self._traversal_stack) # Return a copy
+
+    def hold(self, object):
+        'See IPublicationRequest'
+        self._held = self._held + (object,)
+
+    def setTraversalStack(self, stack):
+        'See IPublicationRequest'
+        self._traversal_stack[:] = list(stack)
+
+    def setViewSkin(self, skin):
+        'See IPublicationRequest'
+        self._presentation_skin = skin
+
+    def getPresentationSkin(self):
+        'See IPresentationRequest'
+        return getattr(self, '_presentation_skin', '')
+
+    def getPresentationType(self):
+        'See IPresentationRequest'
+        return getattr(self, '_presentation_type', None)
+
+    # 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._presentation_type = viewtype
+
+    def _getBody(self):
+        body = getattr(self, '_body', None)
+        if body is None:
+            s = self._body_instream
+            if s is None:
+                return None # XXX what should be returned here?
+            p = s.tell()
+            s.seek(0)
+            body = s.read()
+            s.seek(p)
+            self._body = body
+        return body
+
+    body = property(_getBody)
+
+    def _getBodyFile(self):
+        'See IApplicationRequest'
+        return self._body_instream
+
+    bodyFile = property(_getBodyFile)
+
+    def __len__(self):
+        'See Interface.Common.Mapping.IEnumerableMapping'
+        return len(self.keys())
+
+    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'
+        return self._environ.keys()
+
+    def values(self):
+        'See Interface.Common.Mapping.IEnumerableMapping'
+        result = []
+        get = self.get
+        for k in self.keys():
+            result.append(get(k))
+        return result
+
+    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._environ.get(key, self)
+        if result is not self: return result
+
+        return default
+
+    def __contains__(self, key):
+        'See Interface.Common.Mapping.IReadMapping'
+        lookup = self.get(key, self)
+        return lookup is not self
+
+    has_key = __contains__
+
+    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
+
+    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 _setupPath_helper(self, attr):
+        path = self.get(attr, "/").strip()
+        if path.endswith('/'):
+            path = path[:-1] # XXX Why? Not sure
+            self._endswithslash = 1
+        else:
+            self._endswithslash = 0
+
+        clean = []
+        for item in path.split('/'):
+            if not item or item == '.':
+                continue
+            elif item == '..':
+                try: del clean[-1]
+                except IndexError:
+                    raise NotFoundError('..')
+            else: clean.append(item)
+
+        clean.reverse()
+        self.setTraversalStack(clean)
+
+        self._path_suffix = None
+
+class TestRequest(BaseRequest):
+
+    __slots__ = ('_presentation_type', )
+
+    def __init__(self, path, body_instream=None, outstream=None, environ=None):
+        if environ is None:
+            environ = {}
+        environ['PATH_INFO'] = path
+        if body_instream is None:
+            body_instream = StringIO('')
+        if outstream is None:
+            outstream = StringIO()
+
+        super(TestRequest, self).__init__(body_instream, outstream, environ)
+
+
+class DefaultPublication:
+
+    __implements__ = IPublication
+
+    require_docstrings = 1
+
+    def __init__(self, app):
+        self.app = app
+
+    def beforeTraversal(self, request):
+        # 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
+
+    def callTraversalHooks(self, request, ob):
+        pass
+
+    def traverseName(self, request, ob, name, check_auth=1):
+        if name.startswith('_'):
+            raise Unauthorized("Name %s begins with an underscore" % `name`)
+        if hasattr(ob, name):
+            subob = getattr(ob, name)
+        else:
+            try:
+                subob = ob[name]
+            except (KeyError, IndexError,
+                    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')
+        return subob
+
+    def getDefaultTraversal(self, request, ob):
+        return ob, ()
+
+    def afterTraversal(self, request, ob):
+        pass
+
+    def callObject(self, request, ob):
+        return mapply(ob, request.getPositionalArguments(), request)
+
+    def afterCall(self, request):
+        pass
+
+    def handleException(self, object, request, exc_info, retry_allowed=1):
+        # Let the response handle it as best it can.
+        request.response.handleException(exc_info)
+
+
+class TestPublication(DefaultPublication):
+
+    def traverseName(self, request, ob, name, check_auth=1):
+        if hasattr(ob, name):
+            subob = getattr(ob, name)
+        else:
+            try:
+                subob = ob[name]
+            except (KeyError, IndexError,
+                    TypeError, AttributeError):
+                raise NotFound(ob, name, request)
+        return subob


=== Zope3/src/zope/publisher/browser.py 1.1 => 1.2 === (779/879 lines abridged)
--- /dev/null	Wed Dec 25 09:15:49 2002
+++ Zope3/src/zope/publisher/browser.py	Wed Dec 25 09:15:18 2002
@@ -0,0 +1,876 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+__version__='$Revision$'[11:-2]
+
+import re
+from types import ListType, TupleType
+
+__ArrayTypes = (ListType, TupleType)
+
+def field2string(v):
+    if hasattr(v,'read'): v=v.read()
+    else: v=str(v)
+    return v
+
+def field2text(v, nl=re.compile('\r\n|\n\r').search):
+    if hasattr(v,'read'): v=v.read()
+    else: v=str(v)
+    mo = nl(v)
+    if mo is None: return v
+    l = mo.start(0)
+    r=[]
+    s=0
+    while l >= s:
+        r.append(v[s:l])
+        s=l+2
+        mo=nl(v,s)
+        if mo is None: l=-1
+        else:          l=mo.start(0)
+
+    r.append(v[s:])
+
+    return '\n'.join(r)
+
+def field2required(v):
+    if hasattr(v,'read'): v=v.read()
+    else: v=str(v)

[-=- -=- -=- 779 lines omitted -=- -=- -=-]

+    def __wrapInHTML(self, title, content):
+        t = escape(title)
+        return (
+            "<html><head><title>%s</title></head>\n"
+            "<body><h2>%s</h2>\n"
+            "%s\n"
+            "</body></html>\n" %
+            (t, t, content)
+            )
+
+
+    def __insertBase(self, body):
+        # Only insert a base tag if content appears to be html.
+        content_type = self.getHeader('content-type', '')
+        if content_type and not is_text_html(content_type):
+            return body
+
+        if getattr(self, '_base', ''):
+            if body:
+                match = start_of_header_search(body)
+                if match is not None:
+                    index = match.start(0) + len(match.group(0))
+                    ibase = base_re_search(body)
+                    if ibase is None:
+                        body = ('%s\n<base href="%s" />\n%s' %
+                                (body[:index], self._base, body[index:]))
+        return body
+
+    def getBase(self):
+        return getattr(self, '_base', '')
+
+    def setBase(self, base):
+        self._base = base
+
+    def redirect(self, location, status=302):
+        base = getattr(self, '_base', '')
+        if base and isRelative(str(location)):
+            l = base.rfind('/')
+            if l >= 0:
+                base = base[:l+1]
+            else:
+                base += '/'
+            location = base + location
+
+        super(BrowserResponse, self).redirect(location, status)
+
+
+
+def is_text_html(content_type):
+    return content_type.startswith('text/html')


=== Zope3/src/zope/publisher/configure.zcml 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:49 2002
+++ Zope3/src/zope/publisher/configure.zcml	Wed Dec 25 09:15:18 2002
@@ -0,0 +1,24 @@
+<zopeConfigure
+   xmlns='http://namespaces.zope.org/zope'
+>
+  <include package=".HTTP" />
+
+</zopeConfigure>
+
+<zopeConfigure
+   xmlns='http://namespaces.zope.org/zope'
+>
+
+  <content class="zope.publisher.http.HTTPRequest">
+    <require
+        permission="zope.View"
+        interface="zope.publisher.interfaces.http.IHTTPApplicationRequest"/>
+  </content>
+
+  <content class="zope.publisher.http.URLGetter">
+    <require
+        permission="zope.View" 
+        attributes="get __getitem__ __str__" />
+  </content>
+
+</zopeConfigure>


=== Zope3/src/zope/publisher/http.py 1.1 => 1.2 === (931/1031 lines abridged)
--- /dev/null	Wed Dec 25 09:15:49 2002
+++ Zope3/src/zope/publisher/http.py	Wed Dec 25 09:15:18 2002
@@ -0,0 +1,1028 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+
+import re, time, random
+from urllib import quote, splitport
+from types import StringType
+
+from zope.publisher.base import BaseRequest
+from zope.exceptions import NotFoundError
+
+
+from zope.publisher.interfaces.http import IHTTPCredentials
+from zope.publisher.interfaces.http import IHTTPRequest
+from zope.publisher.interfaces.http import IHTTPApplicationRequest
+
+from zope.publisher.base \
+     import RequestDataProperty, RequestDataMapper, RequestDataGetter
+
+# Default Encoding
+ENCODING = 'UTF-8'
+
+class CookieMapper(RequestDataMapper):
+    _mapname = '_cookies'
+
+class HeaderGetter(RequestDataGetter):
+    _gettrname = 'getHeader'
+
+_marker = object()
+
+class URLGetter:
+
+    def __init__(self, request):

[-=- -=- -=- 931 lines omitted -=- -=- -=-]

+    if y[1] == 'utf-8':
+        return 1
+    if x[1] == 'utf-8':
+        return -1
+    return cmp(y, x)
+
+
+class HTTPCharsets:
+
+    __implements__ =  IUserPreferredCharsets
+
+    def __init__(self, request):
+        self.request = request
+
+    def getPreferredCharsets(self):
+        '''See interface IUserPreferredCharsets'''
+        charsets = []
+        sawstar = sawiso88591 = 0
+        for charset in self.request.get('HTTP_ACCEPT_CHARSET', '').split(','):
+            charset = charset.strip().lower()
+            if charset:
+                if ';' in charset:
+                    charset, quality = charset.split(';')
+                    if not quality.startswith('q='):
+                        # not a quality parameter
+                        quality = 1.0
+                    else:
+                        try:
+                            quality = float(quality[2:])
+                        except ValueError:
+                            continue
+                else:
+                    quality = 1.0
+                if quality == 0.0:
+                    continue
+                if charset == '*':
+                    sawstar = 1
+                if charset == 'iso-8859-1':
+                    sawiso88591 = 1
+                charsets.append((quality, charset))
+        # Quoting RFC 2616, $14.2: If no "*" is present in an Accept-Charset
+        # field, then all character sets not explicitly mentioned get a
+        # quality value of 0, except for ISO-8859-1, which gets a quality
+        # value of 1 if not explicitly mentioned.
+        if not sawstar and not sawiso88591:
+            charsets.append((1.0, 'iso-8859-1'))
+        # UTF-8 is **always** preferred over anything else.
+        # XXX Please give more details as to why!
+        charsets.sort(sort_charsets)
+        return [c[1] for c in charsets]


=== Zope3/src/zope/publisher/maybe_lock.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:49 2002
+++ Zope3/src/zope/publisher/maybe_lock.py	Wed Dec 25 09:15:18 2002
@@ -0,0 +1,21 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+__version__='$Revision$'[11:-2]
+
+# Waaaa, I wish I didn't have to work this hard.
+try: from thread import allocate_lock
+except:
+    class allocate_lock:
+        def acquire(*args): pass
+        def release(*args): pass


=== Zope3/src/zope/publisher/meta.zcml 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:49 2002
+++ Zope3/src/zope/publisher/meta.zcml	Wed Dec 25 09:15:18 2002
@@ -0,0 +1,8 @@
+<zopeConfigure
+   xmlns='http://namespaces.zope.org/zope'
+   xmlns:browser='http://namespaces.zope.org/browser'
+>
+
+  <include package=".VFS" file="meta.zcml" />
+
+</zopeConfigure>


=== Zope3/src/zope/publisher/normal.clb 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:49 2002
+++ Zope3/src/zope/publisher/normal.clb	Wed Dec 25 09:15:18 2002
@@ -0,0 +1,108 @@
+Publisher framework base collaboration 
+
+  Participants:
+
+    publisher:IPublisher
+
+    request:IPublisherRequest
+
+    response:IPublicationResponse = request.getResponse()
+
+    publication:IPublication = request.getPublication()
+
+
+  Values:
+
+    root 
+        "the top-level object"
+
+    foo
+        "an object obtaines by traversing the root with 'foo'"
+
+    bar
+        "an object obtaines by traversing the root with 'bar'"
+
+    result
+        "The result of calling bar"
+
+
+  Scenario: Normal, path = foo/bar
+     "Show normal publishing of a simple path without errors"
+
+    publisher.publish(request)
+
+        request.processInputs()
+
+        publication.beforeTraversal(request) 
+
+        publication.getApplication(request)
+           "Get the root object to be traversed"
+
+        request.traverse(root)
+
+            publication.callTraversalHooks(request, root)
+                '''Call any "before traversal step" hooks. These hooks
+                should be called before traversing an object for the
+                first time. If the same object is traversed more than
+                once, the hook will still only be called the first
+                time.
+
+                The last constraint is probably important to get
+                virtual host semantics rigfht. :)
+                ''' 
+            
+            publication.traverseName(request, root, 'foo')
+
+            publication.callTraversalHooks(request, foo)
+                       
+            publication.traverseName(request, foo, 'bar')
+            
+            return bar
+            
+        publication.afterTraversal(request, bar)
+
+        publication.callObject(request, bar)
+
+            return result
+
+        response.setBody(result)
+
+        publication.afterCall(request)
+
+        response.outputBody()
+
+        request.close()
+
+
+  Scenario: Error during application call, path = foo
+      "Show what heppens when the main application code raises an error"
+
+    publisher.publish(request)
+
+        request.processInputs()
+
+        publication.beforeTraversal(request) 
+
+        publication.getApplication(request)
+
+        request.traverse(root)
+
+            publication.callTraversalHooks(request, root)
+            
+            publication.traverseName(request, root, 'foo')
+            
+            return foo
+
+        publication.afterTraversal(request, foo)
+
+        publication.callObject(request, foo)
+
+            raise AttributeError, 'spam'
+
+
+        publication.handleException()
+
+        response.outputBody()
+
+        request.close()
+


=== Zope3/src/zope/publisher/publish.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:49 2002
+++ Zope3/src/zope/publisher/publish.py	Wed Dec 25 09:15:18 2002
@@ -0,0 +1,182 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Provide an apply-like facility that works with any mapping object
+"""
+
+from zope.proxy.introspection import removeAllProxies
+
+_marker = []  # Create a new marker object.
+
+
+def unwrapMethod(object):
+    """ object -> ( unwrapped, wrapperCount )
+
+        Unwrap 'object' until we get to a real function, counting the
+        number of unwrappings.
+
+        Bail if we find a class or something we can't
+        idendify as callable.
+    """
+    wrapperCount = 0
+    unwrapped = object
+    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
+            continue
+
+        func_code = getattr(unwrapped, 'func_code', None)
+        if func_code is not None:
+            break
+
+        __call__ = getattr(unwrapped, '__call__' , None)
+        if __call__ is not None:
+            unwrapped = unwrapped.__call__
+        else:
+            raise TypeError, "mapply() can not call %s" % `object`
+
+    else:
+        raise TypeError(
+            "couldn't find callable metadata, mapply() error on %s"%`object`
+        )
+
+    return unwrapped, wrapperCount
+
+def mapply(object, positional=(), request={}):
+    __traceback_info__ = object
+
+    # we need deep access for intrspection. Waaa.
+    unwrapped = removeAllProxies(object)
+
+    unwrapped, wrapperCount = unwrapMethod(unwrapped)
+
+    code = unwrapped.func_code
+    defaults = unwrapped.func_defaults
+    names = code.co_varnames[wrapperCount:code.co_argcount]
+
+    nargs = len(names)
+    if positional:
+        args = list(positional)
+        if len(args) > nargs:
+            given = len(args)
+            if wrapperCount:
+                given = given + wrapperCount
+            raise TypeError, (
+                '%s() takes at most %d argument%s(%d given)' % (
+                getattr(unwrapped, '__name__', repr(object)), code.co_argcount,
+                (code.co_argcount > 1 and 's ' or ' '), given))
+    else:
+        args = []
+
+    get = request.get
+    if defaults:
+        nrequired = len(names) - (len(defaults))
+    else:
+        nrequired = len(names)
+    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(object)), name)
+            else:
+                v = defaults[index-nrequired]
+        args.append(v)
+
+    args = tuple(args)
+    return object(*args)
+
+
+"""
+Python Object Publisher -- Publish Python objects on web servers
+
+$Id$
+"""
+
+
+import sys, os
+from zope.publisher.interfaces import Retry
+
+def publish(request, handle_errors=1):
+    try: # finally to clean up to_raise and close request
+        to_raise = None
+        while 1:
+            publication = request.publication
+            try:
+                try:
+                    object = None
+                    try:
+                        request.processInputs()
+                        publication.beforeTraversal(request)
+
+                        object = publication.getApplication(request)
+                        object = request.traverse(object)
+                        publication.afterTraversal(request, object)
+
+                        result = publication.callObject(request, object)
+                        response = request.response
+                        if result is not response:
+                            response.setBody(result)
+
+                        publication.afterCall(request)
+
+                    except:
+                        publication.handleException(object, request, sys.exc_info(), 1)
+
+                        if not handle_errors:
+                            raise
+
+                    break # Successful.
+
+                except Retry, retryException:
+                    if request.supportsRetry():
+                        # Create a copy of the request and use it.
+                        newrequest = request.retry()
+                        request.close()
+                        request = newrequest
+                    elif handle_errors:
+                        # Output the original exception.
+                        publication = request.publication
+                        publication.handleException(
+                            object, request, retryException.getOriginalException(), 0)
+                        break
+                    else:
+                        raise
+
+            except:
+                # Bad exception handler or retry method.
+                # Re-raise after outputting the response.
+                if handle_errors:
+                    request.response.internalError()
+                    to_raise = sys.exc_info()
+                    break
+                else:
+                    raise
+
+        response = request.response
+        response.outputBody()
+        if to_raise is not None:
+            raise to_raise[0], to_raise[1], to_raise[2]
+
+    finally:
+        to_raise = None  # Avoid circ. ref.
+        request.close()  # Close database connections, etc.


=== Zope3/src/zope/publisher/vfs.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:49 2002
+++ Zope3/src/zope/publisher/vfs.py	Wed Dec 25 09:15:18 2002
@@ -0,0 +1,229 @@
+##############################################################################
+#
+# 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
+#
+##############################################################################
+"""
+
+$Id$
+"""
+
+from zope.publisher.base import BaseResponse
+
+
+class VFSResponse(BaseResponse):
+    """VFS response
+    """
+    __slots__ = (
+        '_exc',
+        )
+
+    def setBody(self, body):
+        """Sets the body of the response
+
+           It is very important to note that in this case the body may
+           not be just a astring, but any Python object.
+        """
+
+        self._body = body
+
+
+    def outputBody(self):
+        'See IPublisherResponse'
+        pass
+
+
+    def getResult(self):
+        if getattr(self, '_exc', None) is not None:
+            raise self._exc[0], self._exc[1]
+        return self._getBody()
+
+
+    def handleException(self, exc_info):
+        self._exc = exc_info[:2]
+        # import traceback
+        # traceback.print_exc()
+
+
+"""
+
+$Id$
+"""
+__metaclass__ = type # All classes are new style when run with Python 2.2+
+
+from zope.publisher.interfaces.vfs import IVFSView
+
+class VFSView:
+
+    __implements__ = IVFSView
+
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+
+
+"""
+
+$Id$
+"""
+
+from zope.publisher.base import BaseRequest
+from zope.publisher.interfaces.vfs import IVFSView
+from zope.publisher.interfaces.vfs import IVFSCredentials
+
+
+
+class VFSRequest(BaseRequest):
+
+    __implements__ = BaseRequest.__implements__, IVFSCredentials
+
+    # _presentation_type is overridden from the BaseRequest
+    # to implement IVFSView
+    _presentation_type = IVFSView
+
+
+    def __init__(self, body_instream, outstream, environ, response=None):
+        """ """
+        super(VFSRequest, self).__init__(
+            body_instream, outstream, environ, response)
+
+        self._environ = environ
+        self.method = ''
+        self.__setupPath()
+
+
+    def _createResponse(self, outstream):
+        """Create a specific XML-RPC response object."""
+        return VFSResponse(outstream)
+
+    def _authUserPW(self):
+        'See IVFSCredentials'
+        # XXX This is wrong.  Instead of _authUserPW() there should
+        # be a method of all requests called getCredentials() which
+        # returns an ICredentials instance.
+        credentials = self._environ['credentials']
+        return credentials.getUserName(), credentials.getPassword()
+
+    def unauthorized(self, challenge):
+        'See IVFSCredentials'
+        pass
+
+    def processInputs(self):
+        'See IPublisherRequest'
+
+        if 'command' in self._environ:
+            self.method = self._environ['command']
+
+    def __setupPath(self):
+        self._setupPath_helper("path")
+
+    def __repr__(self):
+        # Returns a *short* string.
+        return '<%s instance at 0x%x, path=%s>' % (
+            str(self.__class__), id(self), '/'.join(self._traversal_stack))
+
+
+"""VFS-View for IFile
+
+VFS-view implementation for a generic file.
+
+$Id$
+"""
+import datetime
+zerotime = datetime.datetime.fromtimestamp(0)
+
+from zope.component import queryAdapter
+
+
+from zope.publisher.interfaces.vfs import IVFSFilePublisher
+
+from zope.app.interfaces.dublincore import IZopeDublinCore
+
+
+class VFSFileView(VFSView):
+    """Abstract class providing the infrastructure for a basic VFS view
+    for basic file-like content object."""
+
+    __implements__ = IVFSFilePublisher, VFSView.__implements__
+
+
+    # These methods need to be implmented by the real view class
+
+    def _setData(self, data):
+        raise NotImplemented
+
+    def _getData(self):
+        raise NotImplemented
+
+    def _getSize(self):
+        return len(self._getData())
+
+    def read(self, mode, outstream, start = 0, end = -1):
+        """See IVFSFilePublisher"""
+
+        data = self._getData()
+        try:
+            if end != -1: data = data[:end]
+            if start != 0: data = data[start:]
+        except TypeError:
+            pass
+        outstream.write(data)
+
+
+    def write(self, mode, instream, start = 0):
+        """See IVFSFilePublisher"""
+        try:
+            instream.seek(start)
+        except:
+            pass
+        self._setData(instream.read())
+
+
+    def check_writable(self, mode):
+        """See IVFSFilePublisher"""
+        return 1
+
+    def isdir(self):
+        """See IVFSObjectPublisher"""
+        return 0
+
+
+    def isfile(self):
+        """See IVFSObjectPublisher"""
+        return 1
+
+
+    def stat(self):
+        """See IVFSObjectPublisher"""
+        dc = queryAdapter(self, IZopeDublinCore)
+        if dc is not None:
+            modified = dc.modified
+            created = dc.created
+        else:
+            created = zerotime
+            modified = zerotime
+
+        if created is None:
+            created = zerotime
+
+        if modified is None:
+            modified = created
+
+        size = self._getSize()
+        uid = "nouser"
+        gid = "nogroup"
+        return (504, 0, 0, 0, uid, gid, size, modified, modified, created)
+
+
+    def publishTraverse(self, request, name):
+        """See IVFSPublisher"""
+        # Traversing always stops here.
+        return None


=== Zope3/src/zope/publisher/xmlrpc.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:49 2002
+++ Zope3/src/zope/publisher/xmlrpc.py	Wed Dec 25 09:15:18 2002
@@ -0,0 +1,199 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+from zope.publisher.interfaces.xmlrpc import IXMLRPCPublisher
+from zope.publisher.http import DefaultPublisher
+
+class MethodPublisher(DefaultPublisher):
+    """Simple XML-RPC publisher that is identical to the HTTP Default Publisher
+       except that it implements the IXMLRPCPublisher interface.
+    """
+
+    __implements__ = IXMLRPCPublisher
+
+
+"""
+
+$Id$
+"""
+
+import xmlrpclib
+from cgi import FieldStorage
+from zope.publisher.http import HTTPRequest
+from zope.publisher.interfaces.xmlrpc import IXMLRPCPublisher
+from zope.publisher.interfaces.xmlrpc import IXMLRPCPublication
+from zope.publisher.interfaces.xmlrpc import IXMLRPCPresentation
+
+
+
+class XMLRPCRequest(HTTPRequest):
+
+    __implements__ = HTTPRequest.__implements__, IXMLRPCPublication
+
+    # _presentation_type is overridden from the BaseRequest
+    # to implement IXMLRPCPublisher
+    _presentation_type = IXMLRPCPresentation
+
+
+    _args = ()
+
+
+    def _createResponse(self, outstream):
+        """Create a specific XML-RPC response object."""
+        return XMLRPCResponse(outstream)
+
+
+    def processInputs(self):
+        'See IPublisherRequest'
+
+        # Parse the request XML structure
+        self._args, function = xmlrpclib.loads(self._body_instream.read())
+        # Translate '.' to '/' in function to represent object traversal.
+        function = function.replace('.', '/')
+
+        if function:
+            self.setPathSuffix((function,))
+
+
+class TestRequest(XMLRPCRequest):
+
+    def __init__(self, body_instream=None, outstream=None, environ=None,
+                 response=None, **kw):
+
+        _testEnv =  {
+            'SERVER_URL':         'http://127.0.0.1',
+            'HTTP_HOST':          '127.0.0.1',
+            'CONTENT_LENGTH':     '0',
+            'GATEWAY_INTERFACE':  'TestFooInterface/1.0',
+            }
+
+        if environ:
+            _testEnv.update(environ)
+        if kw:
+            _testEnv.update(kw)
+        if body_instream is None:
+            from StringIO import StringIO
+            body_instream = StringIO('')
+
+        if outstream is None:
+            outstream = StringIO()
+
+        super(TestRequest, self).__init__(
+            body_instream, outstream, _testEnv, response)
+
+
+
+"""
+
+$Id$
+"""
+import xmlrpclib
+
+from zope.publisher.http import HTTPResponse
+from zope.proxy.introspection import removeAllProxies
+
+
+class XMLRPCResponse(HTTPResponse):
+    """XMLRPC response
+    """
+
+    __implements__ = HTTPResponse.__implements__
+
+
+    def setBody(self, body):
+        """Sets the body of the response
+
+        Sets the return body equal to the (string) argument "body". Also
+        updates the "content-length" return header.
+
+        If the body is a 2-element tuple, then it will be treated
+        as (title,body)
+
+        If is_error is true then the HTML will be formatted as a Zope error
+        message instead of a generic HTML page.
+        """
+        body = removeAllProxies(body)
+        if isinstance(body, xmlrpclib.Fault):
+            # Convert Fault object to XML-RPC response.
+            body = xmlrpclib.dumps(body, methodresponse=1)
+        else:
+            # Marshall our body as an XML-RPC response. Strings will be sent
+            # strings, integers as integers, etc. We do *not* convert
+            # everything to a string first.
+            if body is None:
+                body = xmlrpclib.False # Argh, XML-RPC doesn't handle null
+            try:
+                body = xmlrpclib.dumps((body,), methodresponse=1)
+            except Exception, e:
+                self.handleException(e)
+                return
+        # Set our body to the XML-RPC message, and fix our MIME type.
+        self.setHeader('content-type', 'text/xml')
+
+        self._body = body
+        self._updateContentLength()
+
+        if not self._status_set:
+            self.setStatus(200)
+
+
+    def handleException(self, exc_info):
+        """Handle Errors during publsihing and wrap it in XML-RPC XML"""
+        t, value = exc_info[:2]
+
+        import traceback
+        traceback.print_tb(exc_info[2])
+        print t
+        print value
+
+        # Create an appropriate Fault object. Unfortunately, we throw away
+        # most of the debugging information. More useful error reporting is
+        # left as an exercise for the reader.
+        Fault = xmlrpclib.Fault
+        fault_text = None
+        try:
+            if isinstance(value, Fault):
+                fault_text = value
+            elif isinstance(value, Exception):
+                fault_text = Fault(-1, "Unexpected Zope exception: " +
+                                   str(value))
+            else:
+                fault_text = Fault(-2, "Unexpected Zope error value: " +
+                                   str(value))
+        except:
+            fault_text = Fault(-3, "Unknown Zope fault type")
+
+        # Do the damage.
+        self.setBody(fault_text)
+        self.setStatus(200)
+
+
+"""
+
+$Id$
+"""
+__metaclass__ = type # All classes are new style when run with Python 2.2+
+
+from zope.publisher.interfaces.xmlrpc import IXMLRPCView
+
+class XMLRPCView:
+
+    __implements__ = IXMLRPCView
+
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request