Skip to content

Commit

Permalink
Allow dynamic option in python plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikDeSmedt authored and cdecker committed Dec 14, 2023
1 parent b6c486c commit 557b5fe
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 6 deletions.
21 changes: 15 additions & 6 deletions contrib/pyln-client/pyln/client/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ def __init__(self, stdout: Optional[io.TextIOBase] = None,
invoice_features: Optional[Union[int, str, bytes]] = None,
custom_msgs: Optional[List[int]] = None):
self.methods = {
'init': Method('init', self._init, MethodType.RPCMETHOD)
'init': Method('init', self._init, MethodType.RPCMETHOD),
'setconfig': Method('setconfig', self._set_config, MethodType.RPCMETHOD)
}

self.options: Dict[str, Dict[str, Any]] = {}
Expand Down Expand Up @@ -389,7 +390,8 @@ def decorator(f: Callable[..., None]) -> Callable[..., None]:
def add_option(self, name: str, default: Optional[str],
description: Optional[str],
opt_type: str = "string", deprecated: bool = False,
multi: bool = False) -> None:
multi: bool = False,
dynamic=False) -> None:
"""Add an option that we'd like to register with lightningd.
Needs to be called before `Plugin.run`, otherwise we might not
Expand All @@ -414,18 +416,19 @@ def add_option(self, name: str, default: Optional[str],
'value': None,
'multi': multi,
'deprecated': deprecated,
"dynamic": dynamic
}

def add_flag_option(self, name: str, description: str,
deprecated: bool = False) -> None:
deprecated: bool = False, dynamic: bool = False) -> None:
"""Add a flag option that we'd like to register with lightningd.
Needs to be called before `Plugin.run`, otherwise we might not
end up getting it set.
"""
self.add_option(name, None, description, opt_type="flag",
deprecated=deprecated)
deprecated=deprecated, dynamic=dynamic)

def add_notification_topic(self, topic: str):
"""Announce that the plugin will emit notifications for the topic.
Expand Down Expand Up @@ -784,7 +787,7 @@ def print_usage(self):
""")

for method in self.methods.values():
if method.name in ['init', 'getmanifest']:
if method.name in ['init', 'getmanifest', 'setconfig']:
# Skip internal methods provided by all plugins
continue

Expand Down Expand Up @@ -864,7 +867,7 @@ def _getmanifest(self, **kwargs) -> JSONType:
hooks = []
for method in self.methods.values():
# Skip the builtin ones, they don't get reported
if method.name in ['getmanifest', 'init']:
if method.name in ['getmanifest', 'init', 'setconfig']:
continue

if method.mtype == MethodType.HOOK:
Expand Down Expand Up @@ -970,6 +973,12 @@ def verify_bool(d: Dict[str, JSONType], key: str) -> bool:
return self._exec_func(self.child_init, request)
return None

def _set_config(self, **_) -> None:
"""Called when the value of a dynamic option is changed
For now we don't do anything.
"""
pass


class PluginStream(object):
"""Sink that turns everything that is written to it into a notification.
Expand Down
12 changes: 12 additions & 0 deletions tests/plugins/dynamic_option.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env python3
from pyln.client import Plugin

plugin = Plugin()

plugin.add_option(
name="test-dynamic-config",
description="A config option which can be changed at run-time",
default="initial",
dynamic=True)

plugin.run()
11 changes: 11 additions & 0 deletions tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4258,6 +4258,17 @@ def test_all_subscription(node_factory, directory):
assert not l2.daemon.is_in_log(f'.*test_libplugin: all: connect.*')


def test_dynamic_option_python_plugin(node_factory):
plugin = os.path.join(os.getcwd(), "tests/plugins/dynamic_option.py")
ln = node_factory.get_node(options={"plugin": plugin})
result = ln.rpc.listconfigs("test-dynamic-config")

assert result["configs"]["test-dynamic-config"]["value_str"] == "initial"

result = ln.rpc.setconfig("test-dynamic-config", "changed")
assert result["config"]["value_str"] == "changed"


def test_renepay_not_important(node_factory):
# I mean, it's *important*, it's just not "mission-critical" just yet!
l1 = node_factory.get_node(options={'allow-deprecated-apis': True})
Expand Down

0 comments on commit 557b5fe

Please sign in to comment.