jmcarp / jmcarp/flask-apispec

MethodResource.decorators don't work as expected

Open
#91 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
652
Forks
151
PR merge metrics
No merged PRs in 30d

Description

Imagine situation when we want to add decorator to all `MethodResource` methods (e.g. for authorization). According to [docs](http://flask.pocoo.org/docs/0.12/views/) we can create decorator like this:
```python
def auth_decorator(f):
@auth_required
@wraps(f)
def wrapped(*args, **kwargs):
return f(*args, **kwargs)
return wrapped
```
and add it to resource like bellow:
```python
@doc(tags=["example"])
class ExampleResource(MethodResource):
decorators = [auth_decorator]

def get(self):
return {"example"}, 200
```
At this point everything is OK - authorization is working and same decorator is also working for function-based views, so let's add `@doc` inside our `auth_decorator`:
```python
def auth_decorator(f):
@doc(params={
'Authorization': {
'description': 'Auth token',
'default': '',
'in': 'header',
'type': 'string',
'required': True
}
})
@auth_required
@wraps(f)
def wrapped(*args, **kwargs):
return f(*args, **kwargs)
return wrapped
```
Done, authorization is still working, tags from `ExampleResource` have been added to our docs, but our defined header and params not. Huh, why? Let's test it in other cases:
- for function-based views it is working as expected, we have cool documentation
- if we add our decorator directly to all methods it's also working
- if we remove `@auth_required` from `auth_decorator` it won't change anything

Unfortunately I didn't find reason of this issue, but I created workaround. You need to define `BaseResource` as child of `MethodResource`, and redefine `__init__`, so it will apply decorators from list to all http-based methods. Note that you can't use property name `decorators`, cuz it is risk that decorators will be applied twice - once by internal code and once by your code, to be safe name it e.g. `c_decorators`.
```python
from flask_apispec import MethodResource
from inspect import ismethod, getmembers

class BaseResource(MethodResource):
def __init__(self):
super().__init__()
if hasattr(self, "c_decorators"):
methods = [
m for n, m in getmembers(self, predicate=ismethod)
if n in ['get', 'post', 'patch', 'put', 'delete']
]
for method in methods:
for decorator in self.c_decorators:
self.method = decorator(method)
```

```python
class ExampleResource(BaseResource):
c_decorators = [auth_required]
```

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.