diff options
author | Aric Coady <aric.coady@gmail.com> | 2016-06-05 14:12:55 -0700 |
---|---|---|
committer | Aric Coady <aric.coady@gmail.com> | 2016-07-17 11:05:45 -0700 |
commit | c060b46b0512b638bd32805bcae9f44af340bfba (patch) | |
tree | 7caf50f5ef6c585a9560406653e146d0b4592536 | |
parent | adb7ff5aa1f48506e2838a22176c43c6f3aa4fb5 (diff) | |
download | cherrypy-git-c060b46b0512b638bd32805bcae9f44af340bfba.tar.gz |
Convert request params based on function annotations. Fixes #1441.
-rw-r--r-- | CHANGES.txt | 1 | ||||
-rw-r--r-- | cherrypy/_cptools.py | 1 | ||||
-rw-r--r-- | cherrypy/lib/cptools.py | 18 | ||||
-rw-r--r-- | cherrypy/test/test_params.py | 55 |
4 files changed, 75 insertions, 0 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 20877f63..b06461ad 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,7 @@ * Issue #1411: Fix issue where autoreload fails when the host interpreter for CherryPy was launched using ``python -m``. +* #1441: Added tool to automatically convert request params. 6.1.0 ----- diff --git a/cherrypy/_cptools.py b/cherrypy/_cptools.py index 54c63734..060ca76f 100644 --- a/cherrypy/_cptools.py +++ b/cherrypy/_cptools.py @@ -533,5 +533,6 @@ _d.json_in = Tool('before_request_body', jsontools.json_in, priority=30) _d.json_out = Tool('before_handler', jsontools.json_out, priority=30) _d.auth_basic = Tool('before_handler', auth_basic.basic_auth, priority=1) _d.auth_digest = Tool('before_handler', auth_digest.digest_auth, priority=1) +_d.params = Tool('before_handler', cptools.convert_params) del _d, cptools, encoding, auth, static diff --git a/cherrypy/lib/cptools.py b/cherrypy/lib/cptools.py index fbed0e23..073216e0 100644 --- a/cherrypy/lib/cptools.py +++ b/cherrypy/lib/cptools.py @@ -630,3 +630,21 @@ def autovary(ignore=None, debug=False): v.sort() resp_h['Vary'] = ', '.join(v) request.hooks.attach('before_finalize', set_response_header, 95) + + +def convert_params(exception=ValueError, error=400): + """Convert request params based on function annotations, with error handling. + + exception + Exception class to catch. + + status + The HTTP error code to return to the client on failure. + """ + request = cherrypy.serving.request + types = request.handler.callable.__annotations__ + try: + for key in set(types).intersection(request.params): + request.params[key] = types[key](request.params[key]) + except exception as exc: + raise cherrypy.HTTPError(error, str(exc)) diff --git a/cherrypy/test/test_params.py b/cherrypy/test/test_params.py new file mode 100644 index 00000000..2d57c279 --- /dev/null +++ b/cherrypy/test/test_params.py @@ -0,0 +1,55 @@ +import sys +import cherrypy +from cherrypy.test import helper + + +class ParamsTest(helper.CPWebCase): + @staticmethod + def setup_server(): + class Root: + @cherrypy.expose + @cherrypy.tools.params() + def resource(self, limit=None, sort=None): + return type(limit).__name__ + # for testing on Py 2 + resource.__annotations__ = {'limit': int} + conf = {'/': {'tools.params.on': True}} + cherrypy.tree.mount(Root(), config=conf) + + def test_pass(self): + self.getPage('/resource') + self.assertStatus(200) + self.assertBody('NoneType') + + self.getPage('/resource?limit=0') + self.assertStatus(200) + self.assertBody('int') + + def test_error(self): + self.getPage('/resource?limit=') + self.assertStatus(400) + self.assertInBody('invalid literal for int') + + cherrypy.config['tools.params.error'] = 422 + self.getPage('/resource?limit=') + self.assertStatus(422) + self.assertInBody('invalid literal for int') + + cherrypy.config['tools.params.exception'] = TypeError + self.getPage('/resource?limit=') + self.assertStatus(500) + + def test_syntax(self): + if sys.version_info < (3,): + return self.skip("skipped (Python 3 only)") + exec("""class Root: + @cherrypy.expose + @cherrypy.tools.params() + def resource(self, limit: int): + return type(limit).__name__ +conf = {'/': {'tools.params.on': True}} +cherrypy.tree.mount(Root(), config=conf)""") + + self.getPage('/resource?limit=0') + self.assertStatus(200) + self.assertBody('int') |