Skip to content

Commit

Permalink
pylightning: Add hooks as a new type of method
Browse files Browse the repository at this point in the history
Instead of creating a new map I opted to re-use the Plugin.methods
map, since the semantics are really similar and we don't allow
duplicates. The only difference is in how they are announced to
lightningd, so we use an enum to differentiate rpcmethods from hooks,
since only the former will get added to the JSON-RPC dispatch table in
lightningd.

Signed-off-by: Christian Decker <[email protected]>
  • Loading branch information
cdecker authored and rustyrussell committed Jan 17, 2019
1 parent 71934f9 commit a707ae0
Showing 1 changed file with 37 additions and 5 deletions.
42 changes: 37 additions & 5 deletions contrib/pylightning/lightning/plugin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from collections import OrderedDict
from lightning import LightningRpc
from enum import Enum

import inspect
import json
Expand All @@ -9,6 +10,11 @@
import traceback


class MethodType(Enum):
RPCMETHOD = 0
HOOK = 1


class Plugin(object):
"""Controls interactions with lightningd, and bundles functionality.
Expand Down Expand Up @@ -66,7 +72,7 @@ def add_method(self, name, func):
)

# Register the function with the name
self.methods[name] = func
self.methods[name] = (func, MethodType.RPCMETHOD)

def add_subscription(self, topic, func):
"""Add a subscription to our list of subscriptions.
Expand Down Expand Up @@ -133,6 +139,25 @@ def decorator(f):
return f
return decorator

def add_hook(self, name, func):
"""Register a hook that is called synchronously by lightningd on events
"""
if name in self.methods:
raise ValueError(
"Method {} was already registered".format(name, self.methods[name])
)
self.methods[name] = (func, MethodType.HOOK)

def hook(self, method_name):
"""Decorator to add a plugin hook to the dispatch table.
Internally uses add_hook.
"""
def decorator(f):
self.add_hook(method_name, f)
return f
return decorator

def _exec_func(self, func, request):
params = request['params']
sig = inspect.signature(func)
Expand Down Expand Up @@ -173,7 +198,7 @@ def _dispatch_request(self, request):

if name not in self.methods:
raise ValueError("No method {} found.".format(name))
func = self.methods[name]
func, _ = self.methods[name]

try:
result = {
Expand Down Expand Up @@ -244,7 +269,7 @@ def run(self):
# then unstash this and call it.
if 'init' in self.methods:
self.init = self.methods['init']
self.methods['init'] = self._init
self.methods['init'] = (self._init, MethodType.RPCMETHOD)

partial = ""
for l in self.stdin:
Expand All @@ -258,11 +283,17 @@ def run(self):

def _getmanifest(self):
methods = []
for name, func in self.methods.items():
hooks = []
for name, entry in self.methods.items():
func, typ = entry
# Skip the builtin ones, they don't get reported
if name in ['getmanifest', 'init']:
continue

if typ == MethodType.HOOK:
hooks.append(name)
continue

doc = inspect.getdoc(func)
doc = re.sub('\n+', ' ', doc)
if not doc:
Expand All @@ -280,6 +311,7 @@ def _getmanifest(self):
'options': list(self.options.values()),
'rpcmethods': methods,
'subscriptions': list(self.subscriptions.keys()),
'hooks': hooks,
}

def _init(self, options, configuration, request):
Expand All @@ -293,7 +325,7 @@ def _init(self, options, configuration, request):
# Swap the registered `init` method handler back in and
# re-dispatch
if self.init:
self.methods['init'] = self.init
self.methods['init'], _ = self.init
self.init = None
return self._exec_func(self.methods['init'], request)
return None
Expand Down

0 comments on commit a707ae0

Please sign in to comment.