如何用装饰器包裹 func.__code__.co_filename?
How to get wrapped func.__code__.co_filename with decorator?
下面是一个简单的文件夹结构,test.py中的所有函数都有关键字装饰器。
lib
|--- keyword.py
main
|--- test.py
Keyword.py
from functools import wraps
def keyword(name=None, tags=(), types=()):
def _method_wrapper(func):
@wraps(func)
def _passargs(self, *args, **kwargs):
print(func.__code__.co_filename) # --> '..\main\test.py'
return func(self, *args, **kwargs)
print(_passargs.__code__.co_filename) # --> '..\lib\keyword.py'
return _passargs
return _method_wrapper
注意:print(..)
只是一个例子,我需要 _passargs
和 func
都有相同的代码对象,而不是打印另一个变量:)
如您所见,_passargs
得到了错误的 co_filename
。
本文来自robotframework
keyword.py,我根据自己的需要对其进行了修改。但是我没有弄清楚如何使 _passargs
和 func
都具有正确的源文件位置,以便 robot.libdoc
可以正确生成 doc.libspec。
有人能帮忙吗?
期望值
func.__code__.co_filename = '..\main\test.py'
_passargs.__code__.co_filename = '..\main\test.py'
Python版本=3.8.10
在这里回答我自己的问题,以帮助可能遇到同样问题的人。
原始装饰器函数:
from functools import wraps
def keyword(name=None, tags=(), types=()):
def _method_wrapper(func):
@wraps(func)
def _passargs(self, *args, **kwargs):
print(func.__code__.co_filename) # --> '..\main\test.py'
return func(self, *args, **kwargs)
print(_passargs.__code__.co_filename) # --> '..\lib\keyword.py'
return _passargs
return _method_wrapper
大多数时候,我们只需要最后一层装饰器(在本例中为return func(self, *args, **kwargs)
)来return包装函数的适当属性。
然而,在我的例子中,有一个函数将在初始化 class 期间使用此 keyword
装饰器扫描所有函数。它不会进入 def _passargs
级别,因为我们没有提供任何参数,只是试图获取包装的 func (return _passargs
) 相关属性。
要解决此问题,我们只能在运行时覆盖 co_filename
。
这是在装饰器中覆盖 __code__
对象的解决方案。
from types import CodeType
from functools import wraps
def keyword(name=None, tags=(), types=()):
def _method_wrapper(func):
@wraps(func)
def _passargs(self, *args, **kwargs):
print(func.__code__.co_filename) # --> '..\main\test.py'
return func(self, *args, **kwargs)
fix_co_filename(_passargs, func.__code__.co_filename)
print(_passargs.__code__.co_filename) # --> '..\main\test.py'
return _passargs
return _method_wrapper
def fix_co_filename(func, co_filename):
fn_code = func.__code__
func.__code__ = CodeType(
fn_code.co_argcount,
fn_code.co_posonlyargcount,
fn_code.co_kwonlyargcount,
fn_code.co_nlocals,
fn_code.co_stacksize,
fn_code.co_flags,
fn_code.co_code,
fn_code.co_consts,
fn_code.co_names,
fn_code.co_varnames,
co_filename,
fn_code.co_name,
fn_code.co_firstlineno,
fn_code.co_lnotab,
fn_code.co_freevars,
fn_code.co_cellvars)
下面是一个简单的文件夹结构,test.py中的所有函数都有关键字装饰器。
lib
|--- keyword.py
main
|--- test.py
Keyword.py
from functools import wraps
def keyword(name=None, tags=(), types=()):
def _method_wrapper(func):
@wraps(func)
def _passargs(self, *args, **kwargs):
print(func.__code__.co_filename) # --> '..\main\test.py'
return func(self, *args, **kwargs)
print(_passargs.__code__.co_filename) # --> '..\lib\keyword.py'
return _passargs
return _method_wrapper
注意:print(..)
只是一个例子,我需要 _passargs
和 func
都有相同的代码对象,而不是打印另一个变量:)
如您所见,_passargs
得到了错误的 co_filename
。
本文来自robotframework
keyword.py,我根据自己的需要对其进行了修改。但是我没有弄清楚如何使 _passargs
和 func
都具有正确的源文件位置,以便 robot.libdoc
可以正确生成 doc.libspec。
有人能帮忙吗?
期望值
func.__code__.co_filename = '..\main\test.py'
_passargs.__code__.co_filename = '..\main\test.py'
Python版本=3.8.10
在这里回答我自己的问题,以帮助可能遇到同样问题的人。
原始装饰器函数:
from functools import wraps
def keyword(name=None, tags=(), types=()):
def _method_wrapper(func):
@wraps(func)
def _passargs(self, *args, **kwargs):
print(func.__code__.co_filename) # --> '..\main\test.py'
return func(self, *args, **kwargs)
print(_passargs.__code__.co_filename) # --> '..\lib\keyword.py'
return _passargs
return _method_wrapper
大多数时候,我们只需要最后一层装饰器(在本例中为return func(self, *args, **kwargs)
)来return包装函数的适当属性。
然而,在我的例子中,有一个函数将在初始化 class 期间使用此 keyword
装饰器扫描所有函数。它不会进入 def _passargs
级别,因为我们没有提供任何参数,只是试图获取包装的 func (return _passargs
) 相关属性。
要解决此问题,我们只能在运行时覆盖 co_filename
。
这是在装饰器中覆盖 __code__
对象的解决方案。
from types import CodeType
from functools import wraps
def keyword(name=None, tags=(), types=()):
def _method_wrapper(func):
@wraps(func)
def _passargs(self, *args, **kwargs):
print(func.__code__.co_filename) # --> '..\main\test.py'
return func(self, *args, **kwargs)
fix_co_filename(_passargs, func.__code__.co_filename)
print(_passargs.__code__.co_filename) # --> '..\main\test.py'
return _passargs
return _method_wrapper
def fix_co_filename(func, co_filename):
fn_code = func.__code__
func.__code__ = CodeType(
fn_code.co_argcount,
fn_code.co_posonlyargcount,
fn_code.co_kwonlyargcount,
fn_code.co_nlocals,
fn_code.co_stacksize,
fn_code.co_flags,
fn_code.co_code,
fn_code.co_consts,
fn_code.co_names,
fn_code.co_varnames,
co_filename,
fn_code.co_name,
fn_code.co_firstlineno,
fn_code.co_lnotab,
fn_code.co_freevars,
fn_code.co_cellvars)