bottlepy / bottle-sqlite

MIT License
24 stars 13 forks source link

Plugin fails with decorated views ... #21

Open oz123 opened 6 years ago

oz123 commented 6 years ago

If you implement a decorator like:

@app.route('/secret/:item')
@valid_user()
def secret(item, db):
      ...

The valid_user() plugin will throw an error like

Traceback (most recent call last):
  File "/home/oznt/Software/bottlesession/bottle.py", line 998, in _handle
    out = route.call(**args)
  File "/home/oznt/Software/bottlesession/bottle.py", line 1999, in wrapper
    rv = callback(*a, **ka)
  File "/home/oznt/Software/bottlesession/bottlesession.py", line 48, in check_auth
    return handler(*a, **ka)
TypeError: secret() missing 1 required positional argument: 'db'

Which hides the fact that the check for the keyword db is failing, although, the callable does have the keyword db.

The reason the check fails, is because _callback is no longer the original function. this is solved in python3.3 with

inspect.signature(callable, *, follow_wrapped=True)

In earlier versions of python you would have to use functools.wraps your original function and check if the callback has the attribute __wrapped__. This will only work for 1 level, if you do :

@app.route('/secret/:item')
@valid_user()
@has_permission()
def secret(item,db):
       ...

you would have to check for callback.__wrapped__.__wrapped__, or for each level of decoration add one __wrapped__.

The solution is to replace the check from:

argspec = inspect.getargspec(_callback)

To

params = inspect.signature(_callback).parameters

if keyword not in params:
    return callback
oz123 commented 6 years ago

You can find a demo application and a fixed version of the plugin in the following repository:

https://github.com/oz123/demo-bottle-sqlite-bug