如何使装饰器对调试器透明
How to make a decorator transparent to debugger
我正在开发一个装饰器库,它正在修改一个函数的关键字参数,并且该库正在做我想要它做的事情,但是在调试时非常不方便,每当调用用户函数时,调试器必须通过装饰器库代码。
我将装饰器实现为 class(参见 https://github.com/mapa17/configfy/blob/master/configfy/decorator.py)
并且用户函数由以下库代码包装:
def __call__(self, *args, **kwargs):
if self.needs_wrapping:
self.needs_wrapping = False
self.func = args[0]
functools.update_wrapper(self, self.func)
self.kwargs = self.__get_kw_args(self.func)
# If config file is specified in decorator, new kwargs can be precalculated!
if self.config is not None:
self.new_kwargs = self._get_new_kwargs()
return self
# Use precalculated kwargs if available
if self.new_kwargs is None:
new_kwargs = self._get_new_kwargs()
else:
new_kwargs = self.new_kwargs
# Overwrite them with any passed arguments; passed arguments have priority!
new_kwargs.update(kwargs)
# Call target (aka user) function with altered kwargs
return self.func(*args, **new_kwargs)
那么在调试时是否可以以某种方式跳过这个库代码?
装饰器没什么神奇的。 @decorator
语法只是语法糖,所以当你写:
@decorate
def some_func(...):
# ...
技术上真正发生的是:
def some_func(...):
# ...
some_func = decorate(some_func)
IOW,不,没有办法 "make a decorator transparent to the debugger",因为 "decorator" 只是一个普通的普通可调用对象(通常但不一定)returns 另一个普通的普通可调用对象 -事实上,根本就没有 "decorator" 这样的东西,如果你这样使用,可调用对象就是装饰器,仅此而已。
如@bruno-desthuilliers 所述,装饰器是用户函数的包装器,无法以某种方式删除它。
可以做的是,使用 skip 选项让调试器跳过装饰器模块代码 see
因为我对使用 pudb 进行调试很感兴趣,所以我创建了一个拉取请求,为 pdb 启用了类似的功能 see
对于 pdb
import pdb
from configfy import configfy as cfy
@cfy
def fuu(kw_me=42):
print(kw_me)
if __name__ == '__main__':
pdb.Pdb(skip=['configfy.*']).set_trace()
fuu()
对于 pudb(如果拉取请求被接受)
import pudb
from configfy import configfy as cfy
# Prevent pudb from stepping into the decorator library code
pudb._get_debugger(skip=['configfy.*'])
@cfy
def fuu(kw_me=42):
print(kw_me)
if __name__ == '__main__':
pudb.set_trace()
fuu()
我正在开发一个装饰器库,它正在修改一个函数的关键字参数,并且该库正在做我想要它做的事情,但是在调试时非常不方便,每当调用用户函数时,调试器必须通过装饰器库代码。
我将装饰器实现为 class(参见 https://github.com/mapa17/configfy/blob/master/configfy/decorator.py)
并且用户函数由以下库代码包装:
def __call__(self, *args, **kwargs):
if self.needs_wrapping:
self.needs_wrapping = False
self.func = args[0]
functools.update_wrapper(self, self.func)
self.kwargs = self.__get_kw_args(self.func)
# If config file is specified in decorator, new kwargs can be precalculated!
if self.config is not None:
self.new_kwargs = self._get_new_kwargs()
return self
# Use precalculated kwargs if available
if self.new_kwargs is None:
new_kwargs = self._get_new_kwargs()
else:
new_kwargs = self.new_kwargs
# Overwrite them with any passed arguments; passed arguments have priority!
new_kwargs.update(kwargs)
# Call target (aka user) function with altered kwargs
return self.func(*args, **new_kwargs)
那么在调试时是否可以以某种方式跳过这个库代码?
装饰器没什么神奇的。 @decorator
语法只是语法糖,所以当你写:
@decorate
def some_func(...):
# ...
技术上真正发生的是:
def some_func(...):
# ...
some_func = decorate(some_func)
IOW,不,没有办法 "make a decorator transparent to the debugger",因为 "decorator" 只是一个普通的普通可调用对象(通常但不一定)returns 另一个普通的普通可调用对象 -事实上,根本就没有 "decorator" 这样的东西,如果你这样使用,可调用对象就是装饰器,仅此而已。
如@bruno-desthuilliers 所述,装饰器是用户函数的包装器,无法以某种方式删除它。
可以做的是,使用 skip 选项让调试器跳过装饰器模块代码 see
因为我对使用 pudb 进行调试很感兴趣,所以我创建了一个拉取请求,为 pdb 启用了类似的功能 see
对于 pdb
import pdb
from configfy import configfy as cfy
@cfy
def fuu(kw_me=42):
print(kw_me)
if __name__ == '__main__':
pdb.Pdb(skip=['configfy.*']).set_trace()
fuu()
对于 pudb(如果拉取请求被接受)
import pudb
from configfy import configfy as cfy
# Prevent pudb from stepping into the decorator library code
pudb._get_debugger(skip=['configfy.*'])
@cfy
def fuu(kw_me=42):
print(kw_me)
if __name__ == '__main__':
pudb.set_trace()
fuu()