Commits

Anonymous committed 6ec4502

backporting changinges surrounding the test_callable_spec. r2512 r2084 r2085 r2086 and r2093. In reality this should bring the test_callable_spec and tests completely inline with trunk at this point

Comments (0)

Files changed (4)

cherrypy/_cpconfig.py

         'checker.on': False,
         'tools.log_headers.on': False,
         'request.show_tracebacks': False,
+        'request.show_mismatched_params': False,
         },
     "production": {
         'engine.autoreload_on': False,
         'checker.on': False,
         'tools.log_headers.on': False,
         'request.show_tracebacks': False,
+        'request.show_mismatched_params': False,
         'log.screen': False,
         },
     "embedded": {
         'checker.on': False,
         'tools.log_headers.on': False,
         'request.show_tracebacks': False,
+        'request.show_mismatched_params': False,
         'log.screen': False,
         'engine.SIGHUP': None,
         'engine.SIGTERM': None,
         'checker.on': False,
         'tools.log_headers.on': False,
         'request.show_tracebacks': True,
+        'request.show_mismatched_params': True,
         'log.screen': False,
         },
     }

cherrypy/_cpdispatch.py

         try:
             return self.callable(*self.args, **self.kwargs)
         except TypeError, x:
-            test_callable_spec(self.callable, self.args, self.kwargs)
+            try:
+                test_callable_spec(self.callable, self.args, self.kwargs)
+            except cherrypy.HTTPError, error:
+                raise error
+            except:
+                raise x
             raise
 
+
+
 def test_callable_spec(callable, callable_args, callable_kwargs):
     """
     Inspect callable and test to see if the given args are suitable for it.
     incorrect, then a 404 Not Found should be raised. Conversely the body
     parameters are part of the request; if they are invalid a 400 Bad Request.
     """
-    (args, varargs, varkw, defaults) = inspect.getargspec(callable)
+    show_mismatched_params = getattr(
+        cherrypy.serving.request, 'show_mismatched_params', False)
+    try:
+        (args, varargs, varkw, defaults) = inspect.getargspec(callable)
+    except TypeError:
+        if isinstance(callable, object) and hasattr(callable, '__call__'):
+            (args, varargs, varkw, defaults) = inspect.getargspec(callable.__call__)
+        else:
+            # If it wasn't one of our own types, re-raise 
+            # the original error
+            raise
 
     if args and args[0] == 'self':
         args = args[1:]
             varkw_usage += 1
             extra_kwargs.add(key)
 
+    # figure out which args have defaults.
+    args_with_defaults = args[-len(defaults or []):]
     for i, val in enumerate(defaults or []):
         # Defaults take effect only when the arg hasn't been used yet.
-        if arg_usage[args[i]] == 0:
-            arg_usage[args[i]] += 1
+        if arg_usage[args_with_defaults[i]] == 0:
+            arg_usage[args_with_defaults[i]] += 1
 
     missing_args = []
     multiple_args = []
-    for key, usage in arg_usage.iteritems():
+    for key, usage in arg_usage.items():
         if usage == 0:
             missing_args.append(key)
         elif usage > 1:
         # 
         # In the case where the method does not allow body
         # arguments it's definitely a 404.
-        raise cherrypy.HTTPError(404,
-                message="Missing parameters: %s" % ",".join(missing_args))
-
+        message = None
+        if show_mismatched_params:
+            message="Missing parameters: %s" % ",".join(missing_args)
+        raise cherrypy.HTTPError(404, message=message)
     # the extra positional arguments come from the path - 404 Not Found
     if not varargs and vararg_usage > 0:
         raise cherrypy.HTTPError(404)
 
-    body_params = cherrypy.request.body_params or {}
+    body_params = cherrypy.serving.request.body_params or {}
     body_params = set(body_params.keys())
     qs_params = set(callable_kwargs.keys()) - body_params
 
     if multiple_args:
-
         if qs_params.intersection(set(multiple_args)):
             # If any of the multiple parameters came from the query string then
             # it's a 404 Not Found
             # Otherwise it's a 400 Bad Request
             error = 400
 
-        raise cherrypy.HTTPError(error,
-                message="Multiple values for parameters: "\
-                        "%s" % ",".join(multiple_args))
+        message = None
+        if show_mismatched_params:
+            message="Multiple values for parameters: "\
+                    "%s" % ",".join(multiple_args)
+        raise cherrypy.HTTPError(error, message=message)
 
     if not varkw and varkw_usage > 0:
 
         # If there were extra query string parameters, it's a 404 Not Found
         extra_qs_params = set(qs_params).intersection(extra_kwargs)
         if extra_qs_params:
-            raise cherrypy.HTTPError(404,
+            message = None
+            if show_mismatched_params:
                 message="Unexpected query string "\
-                        "parameters: %s" % ", ".join(extra_qs_params))
+                        "parameters: %s" % ", ".join(extra_qs_params)
+            raise cherrypy.HTTPError(404, message=message)
 
         # If there were any extra body parameters, it's a 400 Not Found
         extra_body_params = set(body_params).intersection(extra_kwargs)
         if extra_body_params:
-            raise cherrypy.HTTPError(400,
+            message = None
+            if show_mismatched_params:
                 message="Unexpected body parameters: "\
-                        "%s" % ", ".join(extra_body_params))
+                        "%s" % ", ".join(extra_body_params)
+            raise cherrypy.HTTPError(400, message=message)
 
 
 try:

cherrypy/_cprequest.py

     show_tracebacks__doc = """
     If True, unexpected errors encountered during request processing will
     include a traceback in the response body."""
+
+    show_mismatched_params = True
+    show_mismatched_params__doc = """
+    If True, mismatched parameters encountered during PageHandler invocation
+    processing will be included in the response body."""
     
     throws = (KeyboardInterrupt, SystemExit, cherrypy.InternalRedirect)
     throws__doc = \

cherrypy/test/test_core.py

         def default(self, *args, **kwargs):
             return "args: %s kwargs: %s" % (args, kwargs)
 
+
+    class ParamErrorsCallable(object):
+        exposed = True
+        def __call__(self):
+            return "data"
+
     class ParamErrors(Test):
 
         def one_positional(self, param1):
             return "data"
         no_positional_kwargs.exposed = True
 
+        callable_object = ParamErrorsCallable()
+
+        def raise_type_error(self, **kwargs):
+            raise TypeError("Client Error")
+        raise_type_error.exposed = True
+
+        def raise_type_error_with_default_param(self, x, y=None):
+            return '%d' % 'a' # throw an exception
+        raise_type_error_with_default_param.exposed = True
+
 
     class Status(Test):
         
         self.assertBody("['a', 'b', 'c']")
 
         # Test friendly error message when given params are not accepted.
+        cherrypy.config.update({"request.show_mismatched_params": True})
         self.getPage("/params/?notathing=meeting")
         self.assertInBody("Missing parameters: thing")
         self.getPage("/params/?thing=meeting&notathing=meeting")
         self.assertInBody("Unexpected query string parameters: notathing")
         
+        # Test ability to turn off friendly error messages
+        cherrypy.config.update({"request.show_mismatched_params": False})
+        self.getPage("/params/?notathing=meeting")
+        self.assertInBody("Not Found")
+        self.getPage("/params/?thing=meeting&notathing=meeting")
+        self.assertInBody("Not Found")
+
         # Test "% HEX HEX"-encoded URL, param keys, and values
         self.getPage("/params/%d4%20%e3/cheese?Gruy%E8re=Bulgn%e9ville")
         self.assertBody(r"args: ('\xd4 \xe3', 'cheese') "
         self.assertBody("Coordinates: 223, 114")
 
     def testParamErrors(self):
-
         # test that all of the handlers work when given 
         # the correct parameters in order to ensure that the
         # errors below aren't coming from some other source.
                 '/paramerrors/no_positional_args_kwargs/foo?param2=bar',
                 '/paramerrors/no_positional_args_kwargs/foo/bar/baz?param2=bar&param3=baz',
                 '/paramerrors/no_positional_kwargs?param1=foo&param2=bar',
+                '/paramerrors/callable_object',
             ):
             self.getPage(uri)
             self.assertStatus(200)
 
         # query string parameters are part of the URI, so if they are wrong
         # for a particular handler, the status MUST be a 404.
-        for uri in (
-                '/paramerrors/one_positional',
-                '/paramerrors/one_positional?foo=foo',
-                '/paramerrors/one_positional/foo/bar/baz',
-                '/paramerrors/one_positional/foo?param1=foo',
-                '/paramerrors/one_positional/foo?param1=foo&param2=foo',
-                '/paramerrors/one_positional_args/foo?param1=foo&param2=foo',
-                '/paramerrors/one_positional_args/foo/bar/baz?param2=foo',
-                '/paramerrors/one_positional_args_kwargs/foo/bar/baz?param1=bar&param3=baz',
-                '/paramerrors/one_positional_kwargs/foo?param1=foo&param2=bar&param3=baz',
-                '/paramerrors/no_positional/boo',
-                '/paramerrors/no_positional?param1=foo',
-                '/paramerrors/no_positional_args/boo?param1=foo',
-                '/paramerrors/no_positional_kwargs/boo?param1=foo',
+        error_msgs = [
+                'Missing parameters',
+                'Nothing matches the given URI',
+                'Multiple values for parameters',
+                'Unexpected query string parameters',
+                'Unexpected body parameters',
+            ]
+        for uri, msg in (
+            ('/paramerrors/one_positional', error_msgs[0]),
+            ('/paramerrors/one_positional?foo=foo', error_msgs[0]),
+            ('/paramerrors/one_positional/foo/bar/baz', error_msgs[1]),
+            ('/paramerrors/one_positional/foo?param1=foo', error_msgs[2]),
+            ('/paramerrors/one_positional/foo?param1=foo&param2=foo', error_msgs[2]),
+            ('/paramerrors/one_positional_args/foo?param1=foo&param2=foo', error_msgs[2]),
+            ('/paramerrors/one_positional_args/foo/bar/baz?param2=foo', error_msgs[3]),
+            ('/paramerrors/one_positional_args_kwargs/foo/bar/baz?param1=bar&param3=baz', error_msgs[2]),
+            ('/paramerrors/one_positional_kwargs/foo?param1=foo&param2=bar&param3=baz', error_msgs[2]),
+            ('/paramerrors/no_positional/boo', error_msgs[1]),
+            ('/paramerrors/no_positional?param1=foo', error_msgs[3]),
+            ('/paramerrors/no_positional_args/boo?param1=foo', error_msgs[3]),
+            ('/paramerrors/no_positional_kwargs/boo?param1=foo', error_msgs[1]),
+            ('/paramerrors/callable_object?param1=foo', error_msgs[3]),
+            ('/paramerrors/callable_object/boo', error_msgs[1]),
             ):
-            self.getPage(uri)
-            self.assertStatus(404)
+            for show_mismatched_params in (True, False):
+                cherrypy.config.update({'request.show_mismatched_params': show_mismatched_params})
+                self.getPage(uri)
+                self.assertStatus(404)
+                if show_mismatched_params:
+                    self.assertInBody(msg)
+                else:
+                    self.assertInBody("Not Found")
 
         # if body parameters are wrong, a 400 must be returned.
-        for uri, body in (
-                ('/paramerrors/one_positional/foo', 'param1=foo',),
-                ('/paramerrors/one_positional/foo', 'param1=foo&param2=foo',),
-                ('/paramerrors/one_positional_args/foo', 'param1=foo&param2=foo',),
-                ('/paramerrors/one_positional_args/foo/bar/baz', 'param2=foo',),
-                ('/paramerrors/one_positional_args_kwargs/foo/bar/baz', 'param1=bar&param3=baz',),
-                ('/paramerrors/one_positional_kwargs/foo', 'param1=foo&param2=bar&param3=baz',),
-                ('/paramerrors/no_positional', 'param1=foo',),
-                ('/paramerrors/no_positional_args/boo', 'param1=foo',),
+        for uri, body, msg in (
+                ('/paramerrors/one_positional/foo', 'param1=foo', error_msgs[2]),
+                ('/paramerrors/one_positional/foo', 'param1=foo&param2=foo', error_msgs[2]),
+                ('/paramerrors/one_positional_args/foo', 'param1=foo&param2=foo', error_msgs[2]),
+                ('/paramerrors/one_positional_args/foo/bar/baz', 'param2=foo', error_msgs[4]),
+                ('/paramerrors/one_positional_args_kwargs/foo/bar/baz', 'param1=bar&param3=baz', error_msgs[2]),
+                ('/paramerrors/one_positional_kwargs/foo', 'param1=foo&param2=bar&param3=baz', error_msgs[2]),
+                ('/paramerrors/no_positional', 'param1=foo', error_msgs[4]),
+                ('/paramerrors/no_positional_args/boo', 'param1=foo', error_msgs[4]),
+                ('/paramerrors/callable_object', 'param1=foo', error_msgs[4]),
             ):
-            self.getPage(uri, method='POST', body=body)
-            self.assertStatus(400)
+            for show_mismatched_params in (True, False):
+                cherrypy.config.update({'request.show_mismatched_params': show_mismatched_params})
+                self.getPage(uri, method='POST', body=body)
+                self.assertStatus(400)
+                if show_mismatched_params:
+                    self.assertInBody(msg)
+                else:
+                    self.assertInBody("Bad Request")
 
 
         # even if body parameters are wrong, if we get the uri wrong, then 
         # it's a 404
-        for uri, body in (
-                ('/paramerrors/one_positional?param2=foo', 'param1=foo',),
-                ('/paramerrors/one_positional/foo/bar', 'param2=foo',),
-                ('/paramerrors/one_positional_args/foo/bar?param2=foo', 'param3=foo',),
-                ('/paramerrors/one_positional_kwargs/foo/bar', 'param2=bar&param3=baz',),
-                ('/paramerrors/no_positional?param1=foo', 'param2=foo',),
-                ('/paramerrors/no_positional_args/boo?param2=foo', 'param1=foo',),
+        for uri, body, msg in (
+                ('/paramerrors/one_positional?param2=foo', 'param1=foo', error_msgs[3]),
+                ('/paramerrors/one_positional/foo/bar', 'param2=foo', error_msgs[1]),
+                ('/paramerrors/one_positional_args/foo/bar?param2=foo', 'param3=foo', error_msgs[3]),
+                ('/paramerrors/one_positional_kwargs/foo/bar', 'param2=bar&param3=baz', error_msgs[1]),
+                ('/paramerrors/no_positional?param1=foo', 'param2=foo', error_msgs[3]),
+                ('/paramerrors/no_positional_args/boo?param2=foo', 'param1=foo', error_msgs[3]),
+                ('/paramerrors/callable_object?param2=bar', 'param1=foo', error_msgs[3]),
             ):
-            self.getPage(uri, method='POST', body=body)
-            self.assertStatus(404)
+            for show_mismatched_params in (True, False):
+                cherrypy.config.update({'request.show_mismatched_params': show_mismatched_params})
+                self.getPage(uri, method='POST', body=body)
+                self.assertStatus(404)
+                if show_mismatched_params:
+                    self.assertInBody(msg)
+                else:
+                    self.assertInBody("Not Found")
 
 
+        # In the case that a handler raises a TypeError we should
+        # let that type error through.
+        for uri in (
+                '/paramerrors/raise_type_error',
+                '/paramerrors/raise_type_error_with_default_param?x=0',
+                '/paramerrors/raise_type_error_with_default_param?x=0&y=0',
+            ):
+            self.getPage(uri, method='GET')
+            self.assertStatus(500)
+            self.assertTrue('Client Error', self.body)
+
     def testStatus(self):
         self.getPage("/status/")
         self.assertBody('normal')
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.