[Zope-Checkins] CVS: Zope/lib/python/ZPublisher - BaseRequest.py:1.39.20.2

Shane Hathaway shane@digicool.com
Wed, 3 Oct 2001 17:57:48 -0400


Update of /cvs-repository/Zope/lib/python/ZPublisher
In directory cvs.zope.org:/tmp/cvs-serv22204

Modified Files:
      Tag: ComponentArchitecture-branch
	BaseRequest.py 
Log Message:
Factored traverse() into several methods so I can read and modify it
without hurting my head. ;-)


=== Zope/lib/python/ZPublisher/BaseRequest.py 1.39.20.1 => 1.39.20.2 ===
             for k,v in kw.items(): self.__dict__[k]=v
 
+
+class StopTraversal (Exception):
+    """
+    """
+
+
 _marker=[]
 class BaseRequest:
     """Provide basic ZPublisher request management
@@ -120,6 +126,7 @@
     """
 
     maybe_webdav_client = 1
+    _no_acquire = 0
 
     # While the following assignment is not strictly necessary, it
     # prevents alot of unnecessary searches because, without it,
@@ -227,18 +234,7 @@
 
     __repr__=__str__
 
-
-    def traverse(self, path, response=None, validated_hook=None):
-        """Traverse the object space
-
-        The REQUEST must already have a PARENTS item with at least one
-        object in it.  This is typically the root object.
-        """
-        request=self
-        request_get=request.get
-        if response is None: response=self.response
-        debug_mode=response.debug_mode
-
+    def _splitPath(self, path):
         # Cleanup the path list
         if path[:1]=='/':  path=path[1:]
         if path[-1:]=='/': path=path[:-1]
@@ -247,51 +243,208 @@
             # Make sure that certain things that dont make sense
             # cannot be traversed.
             if item in ('REQUEST', 'aq_self', 'aq_base'):
-                return response.notFoundError(path) 
+                return self.response.notFoundError(path) 
             if not item or item=='.':
                 continue
             elif item == '..':
                 del clean[-1]
             else: clean.append(item)
-        path=clean
-    
-        # How did this request come in? (HTTP GET, PUT, POST, etc.)
-        method=req_method=upper(request_get('REQUEST_METHOD', 'GET'))
-        
-        no_acquire_flag=0
-        checked_default = 0
+        return clean
 
-        # Set the default method
-        if method=='GET' or method=='POST':
-            method='index_html'
-        else:
+    def _setRequestMethod(self):
+        # How did this request come in? (HTTP GET, PUT, POST, etc.)
+        self._request_method = m = upper(self.get('REQUEST_METHOD', 'GET'))
+        # If it's not GET or POST and might be a WebDAV request, don't acquire.
+        if m != 'GET' and m != 'POST':
             if self.maybe_webdav_client:
-                # Probably a WebDAV client.
-                no_acquire_flag=1
-        URL=request['URL']
-        
-        parents=request['PARENTS']
-        object=parents[-1]
-        del parents[:]
+                self._no_acquire = 1
 
-        roles = getattr(object, '__roles__', UNSPECIFIED_ROLES)
-        
+    def _initialTraverse(self, object):
         # if the top object has a __bobo_traverse__ method, then use it
         # to possibly traverse to an alternate top-level object.
+        roles = getattr(object, '__roles__', UNSPECIFIED_ROLES)
         if hasattr(object,'__bobo_traverse__'):
             try:
-                object=object.__bobo_traverse__(request)
-                roles =getattr(object, '__roles__', UNSPECIFIED_ROLES)
-            except: pass            
-
-        if not path and not method:
-            return response.forbiddenError(self['URL'])
-
+                object = object.__bobo_traverse__(self)
+                roles = getattr(object, '__roles__', UNSPECIFIED_ROLES)
+            except: pass
         # Traverse the URL to find the object:
-        if hasattr(object, '__of__'): 
+        if hasattr(object, '__of__'):
             # Try to bind the top-level object to the request
             # This is how you get 'self.REQUEST'
-            object=object.__of__(RequestContainer(REQUEST=request))
+            object=object.__of__(RequestContainer(REQUEST=self))
+        return object, roles
+
+    def _addDefaultView(self, object, path):
+        """
+        returns (object, entry_name)
+        """
+        psteps = None
+        default = getattr(object, '__browser_default__', None)
+        if default is not None:
+            o, add_steps = default(self)
+            if add_steps:
+                if len(add_steps) > 1:
+                    path.extend(add_steps[1:])
+                return o, add_steps[0]
+        default = self._request_method
+        if default == 'GET' or default == 'POST':
+            default = 'index_html'
+        if default and getattr(object, default, None) is not None:
+            return object, default
+        return object, None
+
+    def _traverseName(self, object, entry_name):
+        """
+        Returns object, roles.
+        """
+        got = 0
+        if entry_name[:1]=='_':
+            if self.response.debug_mode:
+                raise StopTraversal, self.response.debugError(
+                    "Object name begins with an underscore at: %s"
+                    % self['URL'])
+            else:
+                raise StopTraversal, self.response.forbiddenError(entry_name)
+
+        if hasattr(object,'__bobo_traverse__'):
+            subobject=object.__bobo_traverse__(self, entry_name)
+            if type(subobject) is type(()) and len(subobject) > 1:
+                # Add additional parents into the path
+                parents = self['PARENTS']
+                parents[-1:] = subobject[:-1]
+                object, subobject = subobject[-2:]
+        else:
+            try:
+
+                # Note - no_acquire_flag is necessary to support
+                # things like DAV.  We have to make sure
+                # that the target object is not acquired
+                # if the request_method is other than GET
+                # or POST. Otherwise, you could never use
+                # PUT to add a new object named 'test' if
+                # an object 'test' existed above it in the
+                # heirarchy -- you'd always get the
+                # existing object :(
+
+                if (self._no_acquire and len(self.path) == 0 and
+                    hasattr(object, 'aq_base')):
+                    if hasattr(object.aq_base, entry_name):
+                        subobject=getattr(object, entry_name)
+                    else: raise AttributeError, entry_name
+                else: subobject=getattr(object, entry_name)
+            except AttributeError:
+                got=1
+                try: subobject=object[entry_name]
+                except (KeyError, IndexError,
+                        TypeError, AttributeError):
+                    if self.response.debug_mode:
+                        raise StopTraversal, self.response.debugError(
+                            "Cannot locate object at: %s" % self['URL']) 
+                    else:
+                        raise StopTraversal, self.response.notFoundError(
+                            self['URL'])
+
+        try:
+            try: doc=subobject.__doc__
+            except: doc=getattr(object, entry_name+'__doc__')
+            if not doc: raise AttributeError, entry_name
+        except:
+            raise StopTraversal, self.response.debugError(
+                "Missing doc string at: %s" % self['URL'])
+
+        roles = getattr(subobject, '__roles__', UNSPECIFIED_ROLES)
+        if roles is UNSPECIFIED_ROLES and not got:
+            # We got the subobject as an attribute, not an item,
+            # so we should check "next to it" for __roles__.
+            roles = getattr(object, entry_name+'__roles__', UNSPECIFIED_ROLES)
+
+        return subobject, roles
+
+    def _checkAuthorization(self, object, parents, roles, validated_hook):
+        user=groups=None
+        i=0
+
+        last_parent_index=len(parents)
+        if hasattr(object, '__allow_groups__'):
+            groups=object.__allow_groups__
+            inext=0
+        else:
+            inext=None
+            for i in range(last_parent_index):
+                if hasattr(parents[i],'__allow_groups__'):
+                    groups=parents[i].__allow_groups__
+                    inext=i+1
+                    break
+
+        if inext is not None:
+            i=inext
+
+            if hasattr(groups, 'validate'): v=groups.validate
+            else: v=old_validation
+
+            auth=self._auth
+
+            if v is old_validation and roles is UNSPECIFIED_ROLES:
+                # No roles, so if we have a named group, get roles from
+                # group keys
+                if hasattr(groups,'keys'): roles=groups.keys()
+                else:
+                    try: groups=groups()
+                    except: pass
+                    try: roles=groups.keys()
+                    except: pass
+
+                if groups is None:
+                    # Public group, hack structures to get it to validate
+                    roles=None
+                    auth=''
+
+            if v is old_validation:
+                user=old_validation(groups, self, auth, roles)
+            elif roles is UNSPECIFIED_ROLES: user=v(self, auth)
+            else: user=v(self, auth, roles)
+
+            while user is None and i < last_parent_index:
+                parent=parents[i]
+                i=i+1
+                if hasattr(parent, '__allow_groups__'): 
+                    groups=parent.__allow_groups__
+                else: continue
+                if hasattr(groups,'validate'): v=groups.validate
+                else: v=old_validation
+                if v is old_validation:
+                    user=old_validation(groups, self, auth, roles)
+                elif roles is UNSPECIFIED_ROLES: user=v(self, auth)
+                else: user=v(self, auth, roles)
+
+        if user is None and roles != UNSPECIFIED_ROLES:
+            self.response.unauthorized()
+
+        if user is not None:
+            if validated_hook is not None: validated_hook(self, user)
+            self['AUTHENTICATED_USER']=user
+            self['AUTHENTICATION_PATH']=join(self.steps[:-i],'/')
+
+
+    def traverse(self, path_str, validated_hook=None):
+        """Traverse the object space
+
+        The REQUEST must already have a PARENTS item with at least one
+        object in it.  This is typically the root object.
+        """
+        request = self
+        checked_default = 0
+        path = self._splitPath(path_str)
+        self._setRequestMethod()
+        if not path and not self._request_method:
+            return self.response.forbiddenError(self['URL'])
+        
+        parents=request['PARENTS']
+        object=parents[-1]
+        del parents[:]
+
+        object, roles = self._initialTraverse(object)
         parents.append(object)
 
         steps=self.steps
@@ -305,108 +458,46 @@
             # We build parents in the wrong order, so we
             # need to make sure we reverse it when we're doe.
             while 1:
-                bpth = getattr(object, '__before_publishing_traverse__', None)
-                if bpth is not None:
-                    bpth(object, self)
+                hook = getattr(object, '__before_publishing_traverse__', None)
+                if hook is not None:
+                    hook(object, self)
 
-                path = request.path = request['TraversalRequestNameStack']
-                # Check for method:
+                path = self.path = self['TraversalRequestNameStack']
                 if path:
                     entry_name = path.pop()
                 else:
-                    # Finished the URL.  Now publish a default view.
-                    psteps = None
+                    entry_name = None
                     if not checked_default:
+                        object, entry_name = self._addDefaultView(object, path)
                         checked_default = 1
-                        default = getattr(object, '__browser_default__', None)
-                        if default is not None:
-                            object, psteps = default(request)
-                            if psteps:
-                                request._hacked_path=1
-                                entry_name = psteps[0]
-                                path.extend(psteps[1:])
-                                method = None  # Don't hack the path again.
-                    if not psteps:
-                        if (method and hasattr(object,method)
-                            and entry_name != method
-                            and getattr(object, method) is not None):
-                            request._hacked_path=1
-                            entry_name = method
-                        else:
-                            if (hasattr(object, '__call__') and
-                                hasattr(object.__call__,'__roles__')):
-                                roles=object.__call__.__roles__
-                            if request._hacked_path:
-                                i=rfind(URL,'/')
-                                if i > 0: response.setBase(URL[:i])
-                            break
-                if not entry_name: continue
+                        if entry_name:
+                            self._hacked_path = 1
+                    if not entry_name:
+                        # Finished traversal.
+                        if (hasattr(object, '__call__') and
+                            hasattr(object.__call__,'__roles__')):
+                            roles=object.__call__.__roles__
+                        if self._hacked_path:
+                            URL = self['URL']
+                            i=rfind(URL,'/')
+                            if i > 0: self.response.setBase(URL[:i])
+                        break
+                    
+                if object is None:
+                    # Finished traversal.
+                    break
+                if not entry_name:
+                    continue
                 step = quote(entry_name)
                 _steps.append(step)
-                request['URL'] = URL = '%s/%s' % (request['URL'], step)
-                got = 0
-                if entry_name[:1]=='_':
-                    if debug_mode:
-                        return response.debugError(
-                          "Object name begins with an underscore at: %s" % URL)
-                    else: return response.forbiddenError(entry_name)
-    
-                if hasattr(object,'__bobo_traverse__'):
-                    subobject=object.__bobo_traverse__(request,entry_name)
-                    if type(subobject) is type(()) and len(subobject) > 1:
-                        # Add additional parents into the path
-                        parents[-1:] = subobject[:-1]
-                        object, subobject = subobject[-2:]
-                else:
-                    try:
+                request['URL'] = '%s/%s' % (request['URL'], step)
 
-                        # Note - no_acquire_flag is necessary to support
-                        # things like DAV.  We have to make sure
-                        # that the target object is not acquired
-                        # if the request_method is other than GET
-                        # or POST. Otherwise, you could never use
-                        # PUT to add a new object named 'test' if
-                        # an object 'test' existed above it in the
-                        # heirarchy -- you'd always get the
-                        # existing object :(
-                        
-                        if (no_acquire_flag and len(path) == 0 and
-                            hasattr(object, 'aq_base')):
-                            if hasattr(object.aq_base, entry_name):
-                                subobject=getattr(object, entry_name)
-                            else: raise AttributeError, entry_name
-                        else: subobject=getattr(object, entry_name)
-                    except AttributeError:
-                        got=1
-                        try: subobject=object[entry_name]
-                        except (KeyError, IndexError,
-                                TypeError, AttributeError):
-                            if debug_mode:
-                                return response.debugError(
-                                    "Cannot locate object at: %s" % URL) 
-                            else:
-                                return response.notFoundError(URL)
-    
                 try:
-                    try: doc=subobject.__doc__
-                    except: doc=getattr(object, entry_name+'__doc__')
-                    if not doc: raise AttributeError, entry_name
-                except:
-                    if debug_mode:
-                        return response.debugError(
-                            "Missing doc string at: %s" % URL)
-                    else: return response.notFoundError("%s" % URL)
-
-                r = getattr(subobject, '__roles__', UNSPECIFIED_ROLES)
-                if r is not UNSPECIFIED_ROLES:
-                    roles = r
-                elif not got:
-                    # We got the subobject as an attribute, not an item,
-                    # so we should check "next to it" for __roles__.
-                    roles = getattr(object, entry_name+'__roles__', roles)
-
-                # Promote subobject to object
-                object=subobject
+                    object, r = self._traverseName(object, entry_name)
+                    if r is not UNSPECIFIED_ROLES:
+                        roles = r
+                except StopTraversal, v:
+                    return v
                 parents.append(object)
 
                 steps.append(entry_name)
@@ -416,75 +507,8 @@
         request['PUBLISHED'] = parents.pop(0)
 
         # Do authorization checks
-        user=groups=None
-        i=0
+        self._checkAuthorization(object, parents, roles, validated_hook)
 
-        if 1:  # Always perform authentication.
-
-            last_parent_index=len(parents)
-            if hasattr(object, '__allow_groups__'):
-                groups=object.__allow_groups__
-                inext=0
-            else:
-                inext=None
-                for i in range(last_parent_index):
-                    if hasattr(parents[i],'__allow_groups__'):
-                        groups=parents[i].__allow_groups__
-                        inext=i+1
-                        break
-
-            if inext is not None:
-                i=inext
-
-                if hasattr(groups, 'validate'): v=groups.validate
-                else: v=old_validation
-
-                auth=request._auth
-
-                if v is old_validation and roles is UNSPECIFIED_ROLES:
-                    # No roles, so if we have a named group, get roles from
-                    # group keys
-                    if hasattr(groups,'keys'): roles=groups.keys()
-                    else:
-                        try: groups=groups()
-                        except: pass
-                        try: roles=groups.keys()
-                        except: pass
-
-                    if groups is None:
-                        # Public group, hack structures to get it to validate
-                        roles=None
-                        auth=''
-
-                if v is old_validation:
-                    user=old_validation(groups, request, auth, roles)
-                elif roles is UNSPECIFIED_ROLES: user=v(request, auth)
-                else: user=v(request, auth, roles)
-
-                while user is None and i < last_parent_index:
-                    parent=parents[i]
-                    i=i+1
-                    if hasattr(parent, '__allow_groups__'): 
-                        groups=parent.__allow_groups__
-                    else: continue
-                    if hasattr(groups,'validate'): v=groups.validate
-                    else: v=old_validation
-                    if v is old_validation:
-                        user=old_validation(groups, request, auth, roles)
-                    elif roles is UNSPECIFIED_ROLES: user=v(request, auth)
-                    else: user=v(request, auth, roles)
-
-            if user is None and roles != UNSPECIFIED_ROLES:
-                response.unauthorized()
-
-        if user is not None:
-            if validated_hook is not None: validated_hook(self, user)
-            request['AUTHENTICATED_USER']=user
-            request['AUTHENTICATION_PATH']=join(steps[:-i],'/')
-
-        # Remove http request method from the URL.
-        request['URL']=URL
-    
         return object
 
     retry_count=0