標準ライブラリの外にあるあまりにもマックに依存することを避けたいので、デコレータモジュールを見ている間、私は主にその機能を再現しようとしました....失敗しました...
そこで、別の角度から問題を調べました。これで、部分的に機能する解決策が得られました。これは、主にこのコミットを見るだけで説明できます。部分的な使用に依存しているため、完全ではありません。これは、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
したがって、これは実際にはバインドされていないメソッドを変更しません。生成されたドキュメントは同じままです。トリックの2番目の部分は、クラスの初期化時に発生します。
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
た機能。
したがって、これに関する問題は複数あります。
- 初期化でpartialを使用すると、バインドされたメソッド情報が失われます。
- たまたま属性を持っている
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属性は使用されていなくてもアクセスされるため、存在しない場合はオブジェクト属性は変更されません。
誰かがこれについて何かコメントがあれば私は興味がありますか?