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 message based on that. In CherryPy webpages can be defined as functions, and URL parameters are passed as an argument to that function.
As explained in this tutorial URL parameters are passed as strings, so to calculate the sum I want to convert each argument to a float. I will have many URL parameters, so doing this one by one seems cumbersome.
How can I type convert (a large number of) arguments in place?
The "dumb" approach would be to simply take each argument and re-assign it as a float:
def dumb(a="0", b="0", c="0", d="0", e="0", f="0", g="0"):
a = float(a)
b = float(b)
c = float(c)
d = float(d)
e = float(e)
f = float(f)
g = float(g)
return print(sum([a, b, c, d, e, f, g]))
It's readable, but rather repetitive and not very "pythonic".
locals()
Another option I found is to re-assign the locals to a dictionary, then loop over it and call the values from the dict.
def looping_dict(a="0", b="0", c="0", d="0", e="0", f="0", g="0"):
args = locals()
for key in args:
if key in ["a", "b", "c", "d", "e", "f", "g"]:
args[key] = float(args[key])
return print(sum([args["a"], args["b"], args["c"], args["d"], args["e"], args["f"], args["g"]] ) )
This is a bit annoying as I have to reference the dictionary every time. So a simple reference d
becomes args["d"]
. Doesn't help code readability neither.
Here's a @convert
decorator I've used before for something similar (originally inspired by https://stackoverflow.com/a/28268292/4597523 ):
import functools, inspect
def convert(*to_convert, to):
def actual_convert(fn):
arg_names = inspect.signature(fn).parameters.keys()
@functools.wraps(fn)
def wrapper(*args, **kwargs):
args_converted = [to(arg) if arg_name in to_convert else arg
for arg, arg_name in zip(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)
def f(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.
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()
def your_http_handler(
a: float = 0, b: float = 0,
c: float = 0, d: float = 0,
e: float = 0, f: float = 0,
g: float = 0,
):
return str(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
def your_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 here
try:
return str(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),
)
PS 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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.