[Checkins] SVN: bobo/branches/patricks-backtrack/ Change bobo's behavior to continue looking for matching resources

Patrick Strawderman patrick at zope.com
Sat Sep 24 22:19:13 EST 2011


Log message for revision 122932:
  Change bobo's behavior to continue looking for matching resources
  after a matching resource raises MethodNotAllowed.
  
  The allowed methods are accumulated, and only if no handler is
  ultimately found will a MethodNotAllowed exception be raised.
  

Changed:
  A   bobo/branches/patricks-backtrack/
  U   bobo/branches/patricks-backtrack/bobo/src/bobo.py
  U   bobo/branches/patricks-backtrack/bobodoctestumentation/src/bobodoctestumentation/more.txt
  U   bobo/branches/patricks-backtrack/buildout.cfg

-=-
Modified: bobo/branches/patricks-backtrack/bobo/src/bobo.py
===================================================================
--- bobo/trunk/bobo/src/bobo.py	2011-09-19 00:27:30 UTC (rev 122831)
+++ bobo/branches/patricks-backtrack/bobo/src/bobo.py	2011-09-25 03:19:12 UTC (rev 122932)
@@ -182,18 +182,23 @@
 
     def bobo_response(self, request, path, method):
         try:
+            allowed = set()
             for handler in self.handlers:
-                response = handler(request, path, method)
+                try:
+                    response = handler(request, path, method)
+                except MethodNotAllowed, exc:
+                    allowed.update(exc.allowed)
+                    continue
                 if response is not None:
                     return response
+            if allowed:
+                return self.method_not_allowed(request, method, allowed)
             return self.not_found(request, method)
         except BoboException, exc:
             return self.build_response(request, method, exc)
-        except MethodNotAllowed, v:
-            return self.method_not_allowed(request, method, v.allowed)
         except MissingFormVariable, v:
             return self.missing_form_variable(request, method, v.name)
-        except NotFound, v:
+        except NotFound:
             return self.not_found(request, method)
         except bbbbad_errors:
             raise
@@ -1235,10 +1240,17 @@
         handlers.append(_make_br_method_for_name(name))
 
     def bobo_response(self, request, path, method):
+        allowed = set()
         for handler in handlers:
-            found = handler(self, request, path, method)
+            try:
+                found = handler(self, request, path, method)
+            except MethodNotAllowed, exc:
+                allowed.update(exc.allowed)
+                continue
             if found is not None:
                 return found
+        if allowed:
+            raise MethodNotAllowed(allowed)
 
     old = class_.__dict__.get('bobo_response')
     if isinstance(old, _subroute_class_method):

Modified: bobo/branches/patricks-backtrack/bobodoctestumentation/src/bobodoctestumentation/more.txt
===================================================================
--- bobo/trunk/bobodoctestumentation/src/bobodoctestumentation/more.txt	2011-09-19 00:27:30 UTC (rev 122831)
+++ bobo/branches/patricks-backtrack/bobodoctestumentation/src/bobodoctestumentation/more.txt	2011-09-25 03:19:12 UTC (rev 122932)
@@ -549,7 +549,7 @@
    method, bobo generates a "405 Method Not Allowed" response.
 3. When a ``query`` or ``post`` decorated function requires a
    parameter and the parameter is isn't in the given form data, bobo
-   generates a "405 Forbidden" response with a body that indicates the
+   generates a "403 Forbidden" response with a body that indicates the
    missing parameter.
 
 For each of these responses, bobo generates a small HTML body.
@@ -810,3 +810,105 @@
    :term:`resource` implementations.  Custom resource implementations
    must implement the resource interface and will provide an order
    using the ``bobo_order`` attribute.  See :ref:`resourceinterface`.
+
+
+Backtracking
+------------
+
+When handling a request, if bobo finds a resource that matches
+the route but does not accept the request method, it will continue
+looking for matching resources; if it eventually finds none, it will
+then generate a "405 Method Not Allowed" response.
+
+::
+
+   import bobo
+
+   @bobo.post("/event/create")
+   def create(bobo_request):
+       return "created event"
+       
+   @bobo.resource("/event/:action?", method=("GET",))
+   def catch_all(bobo_request, action=None):
+       return "get request for %r" % (action,)
+   
+   class User(object):
+   
+       @bobo.resource("/:userid", method=("POST",))
+       def create(self, bobo_request, userid):
+           return "created user with id %r" % (userid,)
+      
+       @bobo.resource("/:identifier", method=("HEAD",))
+       def head(self, bobo_request, identifier):
+           return ""
+      
+       @bobo.resource("/:id", method=("GET",))
+       def catch_all(self, bobo_request, id):
+           return "get user with id %r" % (id,)
+           
+   bobo.scan_class(User)
+   
+   class Thing(object):
+
+       @bobo.resource("/:id", method=("PUT",))
+       def put(self, bobo_request, id):
+           return "put thing with id %r" % (id,)
+   bobo.scan_class(Thing)
+       
+   @bobo.subroute("/users")
+   def users(bobo_request):
+       return User()
+       
+   @bobo.subroute("/:thing")
+   def thing(bobo_request, thing):
+       return Thing()
+
+
+.. -> src
+
+    >>> update_module('backtrack', src)
+    >>> app = webtest.TestApp(bobo.Application(
+    ...    bobo_resources='backtrack'))
+
+We have a resource that matches the route "/event/create", but it is
+for POST requests.  If we make a GET request, the second resource
+with the matching route that can handle GET requests gets called.
+
+    >>> app.get('/event/create').body
+    "get request for 'create'"
+
+Of course POST requests go to the appropriate resource.
+
+    >>> app.post('/event/create').body
+    'created event'
+
+If we perform a HEAD request for "/event/create", we get a 405 response,
+as no resource is able to handle the method.  The "Allow" header indicates
+all of the request methods that are valid for the particular path.
+
+    >>> app.head('/event/create', status=405).headers["Allow"]
+    'GET, POST, PUT'
+
+The backtracking behavior works with subroutes.
+
+    >>> app.get('/users/1234').body
+    "get user with id '1234'"
+
+    >>> app.head('/users/1234').status
+    '200 OK'
+    
+    >>> app.post('/users/1234').body
+    "created user with id '1234'"
+
+If the first matching subroute returns a resource with no handlers for
+the request method, the next matching subroute is tried.
+
+    >>> app.put('/users/54321').body
+    "put thing with id '54321'"
+
+If no resource is able to handle the request method, we get a 405 response
+with an Allow header.
+
+    >>> app.request('/users/54321',
+    ...     method="OPTIONS", status=405).headers["Allow"]
+    'GET, HEAD, POST, PUT'

Modified: bobo/branches/patricks-backtrack/buildout.cfg
===================================================================
--- bobo/trunk/buildout.cfg	2011-09-19 00:27:30 UTC (rev 122831)
+++ bobo/branches/patricks-backtrack/buildout.cfg	2011-09-25 03:19:12 UTC (rev 122932)
@@ -54,10 +54,10 @@
        bobodoctestumentation
 
 [versions]
-webtest = 1.2
-zope.testing = 3.8.6
-zope.interface = 3.5.3
-zope.exceptions = 3.5.2
+webtest = 1.3.1
+zope.testing = 3.10.2
+zope.interface = 3.8.0
+zope.exceptions = 3.6.1
 WebOb = 0.9.7.1
 simplejson = 2.0.9
-manuel = 1.0.4
+manuel = 1.5.0



More information about the checkins mailing list