Skip to content Skip to sidebar Skip to footer

How Can I Type Convert Many Arguments Of A Function In Place?

Context I use CherryPy to serve a simple webpage that shows different content based on the URL parameters. Specifically it takes the sum of the parameters and shows a different mes

Solution 1:

This is only documented in the changelog but since 2016 with cherrypy >= 6.2.0 there is a @cherrypy.tools.params tool doing exactly what you want (provided that you use a Python 3 version supporting type annotations):

import cherrypy


@cherrypy.tools.params()defyour_http_handler(
        a: float = 0, b: float = 0,
        c: float = 0, d: float = 0,
        e: float = 0, f: float = 0,
        g: float = 0,
):
    returnstr(a + b + c + d + e + f + g)

The PR that added it is PR #1442 — you can explore the usage by looking at the tests there.

If your Python is old for some reason, you could do:

import cherrypy


defyour_http_handler(**kwargs):
    # Validate that the right query args are present in the HTTP request:if kwargs.keys() ^ {'a', 'b', 'c', 'd', 'e', 'f', 'g'}:
        raise cherrypy.HTTPError(400, message='Got invalid args!')

    numbers = (float(num) for num in kwargs.values())  # generator expression won't raise conversion errors heretry:
        returnstr(sum(numbers))  # this will actually call those `float()` conversions so we need to catch a `ValueError`except ValueError as val_err:
        raise cherrypy.HTTPError(
            400,
            message='All args should be valid numbers: {exc!s}'.format(exc=val_err),
        )

P.S. In your initial post you use return print(...) which is wrong. print() always returns None so you'd be sending "None" back to the HTTP client while the argument of print(arg) would be just printed out in your terminal where you run the server.

Solution 2:

Here's a @convert decorator I've used before for something similar (originally inspired by https://stackoverflow.com/a/28268292/4597523):

import functools, inspect

defconvert(*to_convert, to):
    defactual_convert(fn):
        arg_names = inspect.signature(fn).parameters.keys()
        @functools.wraps(fn)defwrapper(*args, **kwargs):
            args_converted = [to(arg) if arg_name in to_convert else arg
                               for arg, arg_name inzip(args, arg_names)]
            kwargs_converted = {kw_name: to(val) if kw_name in to_convert else val
                                 for kw_name, val in kwargs.items()}
            return fn(*args_converted, **kwargs_converted)
        return wrapper
    return actual_convert

@convert('a', 'c', 'd', to=str)deff(a, b, c=5, *, d, e=0):
    return a, b, c, d, e

print(f(1, 2, d=7))

# Output: ('1', 2, 5, '7', 0)# Passed params `a` and `d` got changed to `str`,# however `c` used the default value without conversion

It uses inspect.signature to get the non-keyword arg names. I am not sure how CherryPy passes the params or how it gets the names, but this might be a solid start. Using functools.wraps is important - it makes sure the original signature function signature is preserved, which seems to be important for CherryPy.

Post a Comment for "How Can I Type Convert Many Arguments Of A Function In Place?"