42 lines
1.2 KiB
Python
42 lines
1.2 KiB
Python
|
|
class PluginInvocation():
|
|
_plugins = {}
|
|
|
|
def __init__(self, name, data):
|
|
super().__init__()
|
|
if name not in self._plugins:
|
|
raise KeyError(f'No such plugin function: {name}')
|
|
self._name = name
|
|
self._data = data
|
|
|
|
def __call__(self):
|
|
return self._plugins[self._name](**self._data)
|
|
|
|
@classmethod
|
|
def register_plugin(cls, name, fn):
|
|
cls._plugins[name] = fn
|
|
|
|
|
|
def plugin_constructor(loader, node):
|
|
data = loader.construct_mapping(node)
|
|
return PluginInvocation(node.tag[1:], data)
|
|
|
|
|
|
def render_traverse(obj):
|
|
"""
|
|
Walk through a complex, JSON-serializable data structure, and
|
|
invoke plugins if for custom tags.
|
|
:param obj: The object to traverse.
|
|
"""
|
|
if isinstance(obj, list):
|
|
# list -> recurse into each item
|
|
return [render_traverse(x) for x in obj]
|
|
elif isinstance(obj, dict):
|
|
# dict -> recurse into the value of each (key, value)
|
|
return {k: render_traverse(v) for k, v in obj.items()}
|
|
elif isinstance(obj, PluginInvocation):
|
|
# PluginTag -> invoke the plugin with the stored arguments
|
|
return obj()
|
|
else:
|
|
# anything else -> return as-is
|
|
return obj
|