根据包装器签名传递给金字塔视图函数的参数不一致
Inconsistent arguments passed to pyramid view function depending on wrapper signature
我正在尝试理解传递给金字塔视图函数的参数。
以下示例演示了用两个不同的包装器包装的函数。两个包装器之间的唯一区别是签名。在第一个包装器中,第一个位置参数 (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 的逻辑。
我在 webargs issue where this came up 中分享了我的发现,但以防万一有人在这里遇到这个问题:
Pyramid 允许您使用这些签名之一编写视图函数
def view(request):
...
def view(context, request):
...
第二个调用约定是原来的,第一个是较新的。所以即使它在 pyramid docs 中被称为 "alternate",它也是默认值。
他们使用 inspect.getfullargspec
查看视图是否采用单个位置参数,如果是,则将其包装以匹配第二个约定。如果视图不符合第一个约定,则假定它符合第二个约定(在这种情况下为 false)。
我正在尝试理解传递给金字塔视图函数的参数。
以下示例演示了用两个不同的包装器包装的函数。两个包装器之间的唯一区别是签名。在第一个包装器中,第一个位置参数 (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 的逻辑。
我在 webargs issue where this came up 中分享了我的发现,但以防万一有人在这里遇到这个问题:
Pyramid 允许您使用这些签名之一编写视图函数
def view(request):
...
def view(context, request):
...
第二个调用约定是原来的,第一个是较新的。所以即使它在 pyramid docs 中被称为 "alternate",它也是默认值。
他们使用 inspect.getfullargspec
查看视图是否采用单个位置参数,如果是,则将其包装以匹配第二个约定。如果视图不符合第一个约定,则假定它符合第二个约定(在这种情况下为 false)。