aiohttp:装饰器序列链

aiohttp: chain of decorators sequence

考虑以下代码:

from aiohttp_mako import template

def authorize():
    def wrapper(func):
        @asyncio.coroutine
        @functools.wraps(func)
        def wrapped(*args):
            allowed = # Some auth stuff
            if not allowed:
                return HTTPUnauthorized()
            return func()
        return wrapped
    return wrapper


@authorize()
@template('admin.mak')
async def admin(request):
    return dict(ok=True)

我希望 authorize()' wrapper 得到 template 装饰器作为它的 func 所以我可以 return 它在我的 authorize 装饰器中生成的 Response。但是 authorize()' wrapperadmin() 协程作为 func 并以

结束
File "/Users/etemin/virtualenvs/onlinelux/lib/python3.5/site-packages/aiohttp/web.py", line 306, in _handle
    resp = yield from handler(request)
File "/Users/etemin/virtualenvs/onlinelux/lib/python3.5/site-packages/aiohttp_session/__init__.py", line 134, in middleware
    raise RuntimeError("Expect response, not {!r}", type(response))
RuntimeError: ('Expect response, not {!r}', <class 'generator'>)

因为它试图 return 协程。我应该如何到达 return template 装饰器?

你已经包装了一个协程,所以你需要等待那个协程(从中产生):

def authorize():
    def wrapper(func):
        @asyncio.coroutine
        @functools.wraps(func)
        def wrapped(*args):
            allowed = # Some auth stuff
            if not allowed:
                return HTTPUnauthorized()
            return yield from func()
        return wrapped
    return wrapper

因为你已经在使用 async/await 语法,我也只是在这里使用它而不是使用 @asyncio.coroutine:

def authorize():
    async def wrapper(func):
        @functools.wraps(func)
        async def wrapped(*args):
            allowed = # Some auth stuff
            if not allowed:
                return HTTPUnauthorized()
            return await func()
        return wrapped
    return wrapper

请注意,我在 func() 那里等待,并返回了结果。

这对我有用

灵感来自 @aiohttp_csrf.csrf_protect.

from functools import wraps

from aiohttp import web


def is_authorized(request):
    # Write logic to validate request
    return True


def authorize(handler=None):  # When python module having view was imported, it expected to have this.

    def wrapper(handler):
        @wraps(handler)
        async def wrapped(*args, **kwargs):
            request = args[-1]

            if isinstance(request, web.View):
                # Should also work for Class-Based Views
                request = request.request

            try:
                is_authorized(request)
            except Exception as ex:
                # request not authorized. Write logic to handle it accordingly
                print('Request not authorized: ', repr(ex))
                raise ex

            response = await handler(*args, **kwargs)
            return response

        return wrapped

    if handler is None:
        return wrapper

    return wrapper(handler)

在您看来的某处:

@authorize()
@template('admin.mak')
async def admin(request):
    return dict(ok=True)