Skip to content
This repository has been archived by the owner on Jan 19, 2024. It is now read-only.

DUNE plugin proposal #105

Merged
merged 3 commits into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,10 @@ $ dune --start-container
As mentioned above all commands that use the container will automatically create a new container if one does not exist
and automatically start the container if is stopped.

# DUNE plugins

DUNE can be extended with custom functionality using plugins: [Documentation of DUNE plugins](docs/PLUGIN.md)

# Preparing DUNE release

[Steps for preparing a new DUNE release](docs/RELEASE.md)
24 changes: 24 additions & 0 deletions docs/PLUGIN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# DUNE plugins

DUNE allows users to extend its functionality through the use of plugins. DUNE plugins are simply Python scripts which are fulfilling specific requirements explained below.

## Plugin requirements
1. Plugin needs to be placed in the subdirectory of [../src/plugin/](../src/plugin/).
2. In the aforementioned subdirectory you need to create script `main.py`.
3. `main.py` needs to define 3 functions:
1. `add_parsing(parser)` - function that receives instance of [argparse.ArgumentParser](https://docs.python.org/3/library/argparse.html). It is used to add new DUNE command parsing arguments.
2. `handle_args(args)` - function that receives populated namespace returned by [ArgumentParser.parse_args](https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.parse_args). It is used to handle new DUNE command arguments.
3. (optionally) `set_dune(dune)` - function that receives instance of DUNE so the user could interact with DUNE. It might be stored for later usage if needed.

## Plugin examples
You can find example plugins in [plugin_example directory](../plugin_example/).
To test the example plugins, copy the contents of the [../plugin_example/](../plugin_example) directory into the [../src/plugin/](../src/plugin/) directory. This way, DUNE will automatically discover the new plugins.

### dune_hello
The simplest plugin, which adds `--hello` to DUNE commands. When command `dune --hello` is executed then an example output is printed.

### account_setup
Plugin adds command `--bootstrap-account` to DUNE commands. When it is executed 3 example accounts are created: `alice`, `bob` and `cindy`.
Additionally the contract `eosio.token` is deployed to all above accounts.

In this example you can see how `set_dune` function is being used to store `dune` instance and later use it to create and prepare accounts.
5 changes: 5 additions & 0 deletions plugin_example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## DUNE plugins

This directory contains DUNE example plugins. To test the example plugins, copy the contents of the [../plugin_example/](../plugin_example) directory into the [../src/plugin/](../src/plugin/) directory. This way, DUNE will automatically discover the new plugins.
mikelik marked this conversation as resolved.
Show resolved Hide resolved

For more information please check [plugin documentation](../docs/PLUGIN.md)
38 changes: 38 additions & 0 deletions plugin_example/account_setup/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
class account_setup_plugin:
ScottBailey marked this conversation as resolved.
Show resolved Hide resolved
_dune = None

@staticmethod
def set_dune(in_dune):
account_setup_plugin._dune = in_dune

@staticmethod
def create_accounts():
account_setup_plugin._dune.create_account('alice')
account_setup_plugin._dune.create_account('bob')
account_setup_plugin._dune.create_account('cindy')

@staticmethod
def deploy_contracts():
account_setup_plugin._dune.deploy_contract(
'/app/reference-contracts/build/contracts/eosio.token',
'alice')
account_setup_plugin._dune.deploy_contract(
'/app/reference-contracts/build/contracts/eosio.token',
'bob')
account_setup_plugin._dune.deploy_contract(
'/app/reference-contracts/build/contracts/eosio.token',
'cindy')

def handle_args(args):
if args.bootstrap_account:
print('Starting account bootstrapping')
account_setup_plugin.create_accounts()
account_setup_plugin.deploy_contracts()
print('Created accounts and deployed contracts')

def set_dune(in_dune):
account_setup_plugin.set_dune(in_dune)

def add_parsing(parser):
parser.add_argument('--bootstrap-account', action='store_true',
help='Set up 3 example accounts together with their token contracts')
7 changes: 7 additions & 0 deletions plugin_example/dune_hello/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
def handle_args(args):
if args.hello:
print('Hello from DUNE plugin!')

def add_parsing(parser):
parser.add_argument('--hello', action='store_true',
help='outputs "Hello World"')
52 changes: 52 additions & 0 deletions src/dune/__main__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os # path
import sys # sys.exit()
import importlib.util

from args import arg_parser
from args import parse_optional
Expand All @@ -18,16 +19,63 @@ def handle_simple_args():
handle_version()
sys.exit(0)

def load_module(absolute_path):
module_name, _ = os.path.splitext(os.path.split(absolute_path)[-1])
module_root = os.path.dirname(absolute_path)

sys.path.append(module_root)
spec = importlib.util.spec_from_file_location(module_name, absolute_path)
py_mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(py_mod)
mikelik marked this conversation as resolved.
Show resolved Hide resolved
return py_mod

def load_all_modules_from_dir(plugin_dir):
loaded_modules = []

if not os.path.exists(plugin_dir):
return loaded_modules

for subdir in os.listdir(plugin_dir):
subdir_path = os.path.join(plugin_dir, subdir)
ScottBailey marked this conversation as resolved.
Show resolved Hide resolved
main_py = os.path.join(subdir_path, 'main.py')
if not os.path.exists(main_py):
print(f'main.py not found in {subdir_path}')
continue

loaded_module = load_module(main_py)
if not hasattr(loaded_module, 'handle_args'):
print('Plugin ' + main_py + ' does not have handle_args() method')
continue
if not hasattr(loaded_module, 'add_parsing'):
print('Plugin ' + main_py + ' does not have add_parsing() method')
continue

loaded_modules.append(loaded_module)

return loaded_modules

if __name__ == '__main__':

parser = arg_parser()

current_script_path = os.path.abspath(__file__)
current_script_dir = os.path.dirname(current_script_path)

modules = load_all_modules_from_dir(current_script_dir + '/../plugin/')

for module in modules:
module.add_parsing(parser.get_parser())

args = parser.parse()

handle_simple_args()

dune_sys = dune(args)

for module in modules:
if hasattr(module, 'set_dune'):
module.set_dune(dune_sys)

if parser.is_forwarding():
dune_sys.execute_interactive_cmd(parser.get_forwarded_args())
else:
Expand Down Expand Up @@ -198,6 +246,10 @@ def handle_simple_args():
dune_sys.execute_interactive_cmd(['apt','list','leap'])
dune_sys.execute_interactive_cmd(['apt','list','cdt'])

else:
for module in modules:
module.handle_args(args)

except KeyboardInterrupt:
pass
except dune_node_not_found as err:
Expand Down
3 changes: 3 additions & 0 deletions src/dune/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ def get_forwarded_args():
def parse(self):
return self._parser.parse_args()

def get_parser(self):
return self._parser

def exit_with_help_message(self, *args, return_value=1):
self._parser.print_help(sys.stderr)
print("\nError: ", *args, file=sys.stderr)
Expand Down
1 change: 0 additions & 1 deletion tests/test_boostrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
"""

import subprocess
import pytest

from common import DUNE_EXE

Expand Down