Skip to content

Commit

Permalink
pyln: Plugins can be nice if invoked from CLI too
Browse files Browse the repository at this point in the history
I had way too much fun with this and got a bit carried away with the
letter writing. The idea is to be helpful when users start the plugin
from the command line, rather than run it under the control of
lightningd. We also print detailed information about the user-visible
things such as the methods and options exposed by the plugin.

Changelog-Added: pyln: Plugins that are run from the command line print helpful information on how to configure c-lightning to include them and print metadata about what RPC methods and options are exposed.
Suggested-by: Rusty Russell <@rustyrussell>
  • Loading branch information
cdecker committed Mar 9, 2021
1 parent 59287c2 commit f947faf
Showing 1 changed file with 94 additions and 0 deletions.
94 changes: 94 additions & 0 deletions contrib/pyln-client/pyln/client/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,101 @@ def _multi_dispatch(self, msgs: List[bytes]) -> bytes:

return msgs[-1]

def print_usage(self):
import textwrap

executable = os.path.abspath(sys.argv[0])
overview = textwrap.dedent("""
Hi, it looks like you're trying to run a plugin from the
command line. Plugins are usually started and controlled by
lightningd, which allows you to simply specify which plugins
you'd like to run using the --plugin command line option when
starting lightningd. The following is an example of how that'd
look:
$ lightningd --plugin={executable}
Since we're here however let me tell you about this plugin.
""").format(executable=executable)

methods_header = textwrap.dedent("""
RPC methods
===========
Plugins may provide additional RPC methods that you can simply
call as if they were built-in methods from lightningd
itself. To call them just use lightning-cli or any other
frontend. The following methods are defined by this plugin:
""")

parts = [overview]

method_tpl = textwrap.dedent("""
{name}
{doc}
""")

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

if method.mtype != MethodType.RPCMETHOD:
# Don't include non-rpc-methods in the rpc-method
# section
continue

if methods_header is not None:
# Listen carefully, I shall say this only once :-)
parts.append(methods_header)
methods_header = None

doc = method.long_desc if method.long_desc is not None else "No documentation found"
parts.append(method_tpl.format(
name=method.name,
doc=textwrap.indent(doc, prefix=" ")
))

options_header = textwrap.dedent("""
Command line options
====================
This plugin exposes the following command line options. They
can be specified just like any other you might gice lightning
at startup. The following options are exposed by this plugin:
""")

option_tpl = textwrap.dedent("""
--{name}={typ} (default: {default}
{doc}
""")
for opt in self.options.values():
if options_header is not None:
parts.append(options_header)
options_header = None

doc = textwrap.indent(opt['description'], prefix=" ")

if opt['multi']:
doc += "\n\n This option can be specified multiple times"

parts.append(option_tpl.format(
name=opt['name'],
doc=doc,
default=opt['default'],
typ=opt['type'],
))

sys.stdout.write("".join(parts))
sys.stdout.write("\n")

def run(self) -> None:
# If we are not running inside lightningd we'll print usage
# and some information about the plugin.
if os.environ.get('LIGHTNINGD_PLUGIN', None) != '1':
return self.print_usage()

partial = b""
for l in self.stdin.buffer:
partial += l
Expand Down

0 comments on commit f947faf

Please sign in to comment.