首页 > 解决方案 > 根据包装器签名传递给金字塔视图函数的参数不一致

问题描述

我试图理解传递给金字塔视图函数的参数。

下面的示例演示了一个使用两个不同包装器包装的函数。两个包装器之间的唯一区别是签名。在第一个包装器中,第一个位置参数 ( obj) 是显式的。在第二种情况下,它包含在*args.

import functools
from pyramid.config import Configurator
import webtest

def decorator_1(func):
    @functools.wraps(func)
    def wrapper(obj, *args, **kwargs):  # <- obj
        print('decorator_1')
        print(type(obj), obj)
        print(args)
        print(kwargs)
        return func(obj, *args, **kwargs)  # <- obj
    wrapper.__wrapped__ = func
    return wrapper

def decorator_2(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('decorator_2')
        print(args)
        print(kwargs)
        return func(*args, **kwargs)
    wrapper.__wrapped__ = func
    return wrapper

@decorator_1
def func_1(request):
    return {'func': 'func_1'}

@decorator_2
def func_2(request):
    return {'func': 'func_2'}

我希望两个 wrapepd 方法的行为相同。

decorator_1中,我希望obj成为一个请求对象,确实如此。

decorator_2中,我希望args[0]是同一个请求对象,但事实并非如此。似乎在请求对象之前传递了一个额外的第一个位置参数。

def add_route(config, route, view, renderer="json"):
    """Helper for adding a new route-view pair."""
    route_name = view.__name__
    config.add_route(route_name, route)
    config.add_view(view, route_name=route_name, renderer=renderer)

config = Configurator()
add_route(config, "/func_1", func_1)
add_route(config, "/func_2", func_2)

app = config.make_wsgi_app()

testapp = webtest.TestApp(app)

testapp.get("/func_1")
testapp.get("/func_2")

输出:

decorator_1
<class 'pyramid.request.Request'> GET /func_1 HTTP/1.0
Host: localhost:80
()
{}
decorator_2
(<pyramid.traversal.DefaultRootFactory object at 0x7f981da2ee48>, <Request at 0x7f981da2ea20 GET http://localhost/func_2>)
{}

因此,func_2崩溃是因为它收到了一个DefaultRootFactory它不期望的对象。

我想了解这种差异。包装器的签名如何改变金字塔传递给包装函数的内容?

有一个我不明白的机制处于危险之中,我怀疑它可能在 Pyramid 的逻辑中。

标签: pythonpyramid

解决方案


我在出现此问题的 webargs 问题中分享了我的发现,但以防万一有人在这里遇到此问题:

Pyramid 允许您使用这些签名中的任何一个编写视图函数

def view(request):
    ...
def view(context, request):
    ...

第二个调用约定是原始的,第一个是更新的。因此,即使它在金字塔文档中被称为“替代” ,它也是默认值。

他们inspect.getfullargspec用来查看视图是否采用单个位置参数,如果是,则将其包装以匹配第二个约定。如果视图与第一个约定不匹配,则假定它与第二个约定匹配(在这种情况下为假)。


推荐阅读