summaryrefslogtreecommitdiff
path: root/pecan/rest.py
diff options
context:
space:
mode:
Diffstat (limited to 'pecan/rest.py')
-rw-r--r--pecan/rest.py106
1 files changed, 61 insertions, 45 deletions
diff --git a/pecan/rest.py b/pecan/rest.py
index db955c5..9cc8b35 100644
--- a/pecan/rest.py
+++ b/pecan/rest.py
@@ -1,9 +1,10 @@
from inspect import getargspec, ismethod
+import warnings
from webob import exc
import six
-from .core import abort, request
+from .core import abort
from .decorators import expose
from .routing import lookup_controller, handle_lookup_traversal
from .util import iscontroller
@@ -26,31 +27,41 @@ class RestController(object):
'''
_custom_actions = {}
+ def _get_args_for_controller(self, controller):
+ """
+ Retrieve the arguments we actually care about. For Pecan applications
+ that utilize thread locals, we should truncate the first argument,
+ `self`. For applications that explicitly pass request/response
+ references as the first controller arguments, we should truncate the
+ first three arguments, `self, req, resp`.
+ """
+ argspec = getargspec(controller)
+ from pecan import request
+ try:
+ request.path
+ except AttributeError:
+ return argspec.args[3:]
+ return argspec.args[1:]
+
@expose()
- def _route(self, args):
+ def _route(self, args, request=None):
'''
Routes a request to the appropriate controller and returns its result.
Performs a bit of validation - refuses to route delete and put actions
via a GET request).
'''
+ if request is None:
+ from pecan import request
# convention uses "_method" to handle browser-unsupported methods
- if request.environ.get('pecan.validation_redirected', False) is True:
- #
- # If the request has been internally redirected due to a validation
- # exception, we want the request method to be enforced as GET, not
- # the `_method` param which may have been passed for REST support.
- #
- method = request.method.lower()
- else:
- method = request.params.get('_method', request.method).lower()
+ method = request.params.get('_method', request.method).lower()
# make sure DELETE/PUT requests don't use GET
if request.method == 'GET' and method in ('delete', 'put'):
abort(405)
# check for nested controllers
- result = self._find_sub_controllers(args)
+ result = self._find_sub_controllers(args, request)
if result:
return result
@@ -62,17 +73,17 @@ class RestController(object):
)
try:
- result = handler(method, args)
+ result = handler(method, args, request)
#
# If the signature of the handler does not match the number
# of remaining positional arguments, attempt to handle
# a _lookup method (if it exists)
#
- argspec = getargspec(result[0])
- num_args = len(argspec[0][1:])
+ argspec = self._get_args_for_controller(result[0])
+ num_args = len(argspec)
if num_args < len(args):
- _lookup_result = self._handle_lookup(args)
+ _lookup_result = self._handle_lookup(args, request)
if _lookup_result:
return _lookup_result
except exc.HTTPNotFound:
@@ -80,7 +91,7 @@ class RestController(object):
# If the matching handler results in a 404, attempt to handle
# a _lookup method (if it exists)
#
- _lookup_result = self._handle_lookup(args)
+ _lookup_result = self._handle_lookup(args, request)
if _lookup_result:
return _lookup_result
raise
@@ -88,7 +99,7 @@ class RestController(object):
# return the result
return result
- def _handle_lookup(self, args):
+ def _handle_lookup(self, args, request):
# filter empty strings from the arg list
args = list(six.moves.filter(bool, args))
@@ -97,7 +108,8 @@ class RestController(object):
if args and iscontroller(lookup):
result = handle_lookup_traversal(lookup, args)
if result:
- return lookup_controller(*result)
+ obj, remainder = result
+ return lookup_controller(obj, remainder, request)
def _find_controller(self, *args):
'''
@@ -109,7 +121,7 @@ class RestController(object):
return obj
return None
- def _find_sub_controllers(self, remainder):
+ def _find_sub_controllers(self, remainder, request):
'''
Identifies the correct controller to route to by analyzing the
request URI.
@@ -124,31 +136,33 @@ class RestController(object):
return
# get the args to figure out how much to chop off
- args = getargspec(getattr(self, method))
- fixed_args = len(args[0][1:]) - len(
+ args = self._get_args_for_controller(getattr(self, method))
+ fixed_args = len(args) - len(
request.pecan.get('routing_args', [])
)
- var_args = args[1]
+ var_args = getargspec(getattr(self, method)).varargs
# attempt to locate a sub-controller
if var_args:
for i, item in enumerate(remainder):
controller = getattr(self, item, None)
if controller and not ismethod(controller):
- self._set_routing_args(remainder[:i])
- return lookup_controller(controller, remainder[i + 1:])
+ self._set_routing_args(request, remainder[:i])
+ return lookup_controller(controller, remainder[i + 1:],
+ request)
elif fixed_args < len(remainder) and hasattr(
self, remainder[fixed_args]
):
controller = getattr(self, remainder[fixed_args])
if not ismethod(controller):
- self._set_routing_args(remainder[:fixed_args])
+ self._set_routing_args(request, remainder[:fixed_args])
return lookup_controller(
controller,
- remainder[fixed_args + 1:]
+ remainder[fixed_args + 1:],
+ request
)
- def _handle_unknown_method(self, method, remainder):
+ def _handle_unknown_method(self, method, remainder, request):
'''
Routes undefined actions (like RESET) to the appropriate controller.
'''
@@ -164,11 +178,12 @@ class RestController(object):
abort(405)
sub_controller = getattr(self, remainder[0], None)
if sub_controller:
- return lookup_controller(sub_controller, remainder[1:])
+ return lookup_controller(sub_controller, remainder[1:],
+ request)
abort(404)
- def _handle_get(self, method, remainder):
+ def _handle_get(self, method, remainder, request):
'''
Routes ``GET`` actions to the appropriate controller.
'''
@@ -176,8 +191,8 @@ class RestController(object):
if not remainder or remainder == ['']:
controller = self._find_controller('get_all', 'get')
if controller:
- argspec = getargspec(controller)
- fixed_args = len(argspec.args[1:]) - len(
+ argspec = self._get_args_for_controller(controller)
+ fixed_args = len(argspec) - len(
request.pecan.get('routing_args', [])
)
if len(remainder) < fixed_args:
@@ -194,13 +209,13 @@ class RestController(object):
if controller:
return controller, remainder[:-1]
- match = self._handle_custom_action(method, remainder)
+ match = self._handle_custom_action(method, remainder, request)
if match:
return match
controller = getattr(self, remainder[0], None)
if controller and not ismethod(controller):
- return lookup_controller(controller, remainder[1:])
+ return lookup_controller(controller, remainder[1:], request)
# finally, check for the regular get_one/get requests
controller = self._find_controller('get_one', 'get')
@@ -209,18 +224,18 @@ class RestController(object):
abort(404)
- def _handle_delete(self, method, remainder):
+ def _handle_delete(self, method, remainder, request):
'''
Routes ``DELETE`` actions to the appropriate controller.
'''
if remainder:
- match = self._handle_custom_action(method, remainder)
+ match = self._handle_custom_action(method, remainder, request)
if match:
return match
controller = getattr(self, remainder[0], None)
if controller and not ismethod(controller):
- return lookup_controller(controller, remainder[1:])
+ return lookup_controller(controller, remainder[1:], request)
# check for post_delete/delete requests first
controller = self._find_controller('post_delete', 'delete')
@@ -234,23 +249,24 @@ class RestController(object):
abort(405)
sub_controller = getattr(self, remainder[0], None)
if sub_controller:
- return lookup_controller(sub_controller, remainder[1:])
+ return lookup_controller(sub_controller, remainder[1:],
+ request)
abort(404)
- def _handle_post(self, method, remainder):
+ def _handle_post(self, method, remainder, request):
'''
Routes ``POST`` requests.
'''
# check for custom POST/PUT requests
if remainder:
- match = self._handle_custom_action(method, remainder)
+ match = self._handle_custom_action(method, remainder, request)
if match:
return match
controller = getattr(self, remainder[0], None)
if controller and not ismethod(controller):
- return lookup_controller(controller, remainder[1:])
+ return lookup_controller(controller, remainder[1:], request)
# check for regular POST/PUT requests
controller = self._find_controller(method)
@@ -259,10 +275,10 @@ class RestController(object):
abort(404)
- def _handle_put(self, method, remainder):
- return self._handle_post(method, remainder)
+ def _handle_put(self, method, remainder, request):
+ return self._handle_post(method, remainder, request)
- def _handle_custom_action(self, method, remainder):
+ def _handle_custom_action(self, method, remainder, request):
remainder = [r for r in remainder if r]
if remainder:
if method in ('put', 'delete'):
@@ -281,7 +297,7 @@ class RestController(object):
if controller:
return controller, remainder
- def _set_routing_args(self, args):
+ def _set_routing_args(self, request, args):
'''
Sets default routing arguments.
'''