Skip to content

Commit

Permalink
pyln: add support for dependent hooks.
Browse files Browse the repository at this point in the history
And use that to add simple tests.

Signed-off-by: Rusty Russell <[email protected]>
  • Loading branch information
rustyrussell committed Oct 29, 2020
1 parent 1da5384 commit 8809845
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 4 deletions.
16 changes: 12 additions & 4 deletions contrib/pyln-client/pyln/client/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,9 @@ def decorator(f: Callable[..., JSONType]) -> Callable[..., JSONType]:
return decorator

def add_hook(self, name: str, func: Callable[..., JSONType],
background: bool = False) -> None:
background: bool = False,
before: List[str] = [],
after: List[str] = []) -> None:
"""Register a hook that is called synchronously by lightningd on events
"""
if name in self.methods:
Expand All @@ -427,15 +429,19 @@ def add_hook(self, name: str, func: Callable[..., JSONType],

method = Method(name, func, MethodType.HOOK)
method.background = background
method.before = before
method.after = after
self.methods[name] = method

def hook(self, method_name: str) -> JsonDecoratorType:
def hook(self, method_name: str,
before: List[str] = [],
after: List[str] = []) -> JsonDecoratorType:
"""Decorator to add a plugin hook to the dispatch table.
Internally uses add_hook.
"""
def decorator(f: Callable[..., JSONType]) -> Callable[..., JSONType]:
self.add_hook(method_name, f, background=False)
self.add_hook(method_name, f, background=False, before=before, after=after)
return f
return decorator

Expand Down Expand Up @@ -689,7 +695,9 @@ def _getmanifest(self, **kwargs) -> JSONType:
continue

if method.mtype == MethodType.HOOK:
hooks.append(method.name)
hooks.append({'name': method.name,
'before': method.before,
'after': method.after})
continue

doc = inspect.getdoc(method.func)
Expand Down
17 changes: 17 additions & 0 deletions tests/plugins/dep_a.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env python3
from pyln.client import Plugin
from hashlib import sha256
from binascii import hexlify

"""A simple plugin that must come before dep_b.
"""
plugin = Plugin()


@plugin.hook('htlc_accepted', before=['dep_b.py'])
def on_htlc_accepted(htlc, plugin, **kwargs):
print("htlc_accepted called")
return {'result': 'continue'}


plugin.run()
17 changes: 17 additions & 0 deletions tests/plugins/dep_b.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env python3
from pyln.client import Plugin
from hashlib import sha256
from binascii import hexlify

"""A simple plugin that must come after dep_a, before dep_c.
"""
plugin = Plugin()


@plugin.hook('htlc_accepted', before=['dep_c.py'], after=['dep_a.py'])
def on_htlc_accepted(htlc, plugin, **kwargs):
print("htlc_accepted called")
return {'result': 'continue'}


plugin.run()
18 changes: 18 additions & 0 deletions tests/plugins/dep_c.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env python3
from pyln.client import Plugin
from hashlib import sha256
from binascii import hexlify

"""A simple plugin that must come before dep_a.
"""
plugin = Plugin()


@plugin.hook('htlc_accepted', before=['dep_a.py'])
def on_htlc_accepted(htlc, plugin, **kwargs):
print("htlc_accepted called")
return {'result': 'continue'}


plugin.run()

23 changes: 23 additions & 0 deletions tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1990,3 +1990,26 @@ def test_htlc_accepted_hook_failcodes(node_factory):
inv = l2.rpc.invoice(42, 'failcode{}'.format(failcode), '')['bolt11']
with pytest.raises(RpcError, match=r'failcodename.: .{}.'.format(expected)):
l1.rpc.pay(inv)


def test_hook_dep(node_factory):
dep_a = os.path.join(os.path.dirname(__file__), 'plugins/dep_a.py')
dep_b = os.path.join(os.path.dirname(__file__), 'plugins/dep_b.py')
dep_c = os.path.join(os.path.dirname(__file__), 'plugins/dep_c.py')
l1, l2 = node_factory.line_graph(2, opts=[{}, {'plugin': dep_b}])

# A says it has to be before B.
l2.rpc.plugin_start(plugin=dep_a)
l2.daemon.wait_for_log(r"started.*dep_a.py")

l1.pay(l2, 100000)
# They must be called in this order!
l2.daemon.wait_for_log(r"dep_a.py: htlc_accepted called")
l2.daemon.wait_for_log(r"dep_b.py: htlc_accepted called")

# But depc will not load, due to cyclical dep
with pytest.raises(RpcError, match=r'Cannot correctly order hook htlc_accepted'):
l2.rpc.plugin_start(plugin=dep_c)

l1.rpc.plugin_start(plugin=dep_c)
l1.daemon.wait_for_log(r"started.*dep_c.py")

0 comments on commit 8809845

Please sign in to comment.