我想避免依赖标准库之外的太多垃圾,所以当我查看装饰器模块时,我主要尝试重现它的功能......不成功......
于是换个角度看问题,现在有了部分可行的解决方案,主要可以看这个commit来描述。它并不完美,因为它依赖于使用部分,这破坏了 REPL 中的帮助。这个想法是,不是替换应用装饰器的函数,而是增加属性。
+def s_repr(obj):
+ """ :param obj: object """
+ return (repr(obj) if not isinstance(obj, SikuliClass)
+ else "self._get_jython_object(%r)" % obj._str_get)
+
+
def run_on_remote(func):
...
- func.s_repr = lambda obj: (repr(obj)
- if not isinstance(obj, SikuliClass) else
- "self._get_jython_object(%r)" % obj._str_get)
-
- def _inner(self, *args):
- return self.remote._eval("self._get_jython_object(%r).%s(%s)" % (
- self._id,
- func.__name__,
- ', '.join([func.s_repr(x) for x in args])))
-
- func.func = _inner
+ gjo = "self._get_jython_object"
+ func._augment = {
+ 'inner': lambda self, *args: (self.remote._eval("%s(%r).%s(%s)"
+ % (gjo, self._id, func.__name__,
+ ', '.join([s_repr(x)for x in args]))))
+ }
@wraps(func)
def _outer(self, *args, **kwargs):
func(self, *args, **kwargs)
- if hasattr(func, "arg"):
- args, kwargs = func.arg(*args, **kwargs), {}
- result = func.func(*args, **kwargs)
- if hasattr(func, "post"):
+ if "arg" in func._augment:
+ args, kwargs = func._augment["arg"](self, *args, **kwargs), {}
+ result = func._augment['inner'](self, *args, **kwargs)
+ if "post" in func._augment:
return func.post(result)
else:
return result
def _arg(arg_func):
- func.arg = arg_func
- return _outer
+ func._augment['arg'] = arg_func
+ return func
def _post(post_func):
- func.post = post_func
- return _outer
+ func._augment['post'] = post_func
+ return func
def _func(func_func):
- func.func = func_func
- return _outer
- _outer.arg = _arg
- _outer.post = _post
- _outer.func = _func
- return _outer
+ func._augment['inner'] = func_func
+ return func
+
+ func.arg = _outer.arg = _arg
+ func.post = _outer.post = _post
+ func.func = _outer.func = _func
+ func.run = _outer.run = _outer
+ return func
所以这实际上并没有改变未绑定的方法,因此生成的文档保持不变。诡计的第二部分发生在类初始化时。
class ClientSikuliClass(ServerSikuliClass):
""" Base class for types based on the Sikuli native types """
...
def __init__(self, remote, server_id, *args, **kwargs):
"""
:type server_id: int
:type remote: SikuliClient
"""
super(ClientSikuliClass, self).__init__(None)
+ for key in dir(self):
+ try:
+ func = getattr(self, key)
+ except AttributeError:
+ pass
+ else:
+ try:
+ from functools import partial, wraps
+ run = wraps(func.run)(partial(func.run, self))
+ setattr(self, key, run)
+ except AttributeError:
+ pass
self.remote = remote
self.server_id = server_id
因此,在实例化任何继承类ClientSikuliClass
的实例时,会尝试获取该实例的每个属性的 run 属性,并使其在尝试获取该属性时返回,因此绑定方法现在是部分应用_outer
函数。
所以这个问题是多方面的:
- 在初始化时使用部分会导致丢失绑定的方法信息。
- 我担心破坏恰好具有属性的
run
属性...
因此,虽然我对自己的问题有答案,但我对此并不十分满意。
更新
好的,经过更多的工作,我最终得到了这个:
class ClientSikuliClass(ServerSikuliClass):
""" Base class for types based on the Sikuli native types """
...
def __init__(self, remote, server_id, *args, **kwargs):
"""
:type server_id: int
:type remote: SikuliClient
"""
super(ClientSikuliClass, self).__init__(None)
- for key in dir(self):
+
+ def _apply_key(key):
try:
func = getattr(self, key)
+ aug = func._augment
+ runner = func.run
except AttributeError:
- pass
- else:
- try:
- from functools import partial, wraps
- run = wraps(func.run)(partial(func.run, self))
- setattr(self, key, run)
- except AttributeError:
- pass
+ return
+
+ @wraps(func)
+ def _outer(*args, **kwargs):
+ return runner(self, *args, **kwargs)
+
+ setattr(self, key, _outer)
+
+ for key in dir(self):
+ _apply_key(key)
+
self.remote = remote
self.server_id = server_id
这可以防止对象上的文档丢失。您还将看到 func._augment 属性被访问,即使它没有被使用,所以如果它不存在,对象属性将不会被触及。
如果有人对此有任何意见,我会很感兴趣?