-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* Add Components Recommender mock plugin. Closes #63 * WIP- Refactor PythonPluginBase and script to common * WIP- Fix eslint issues * WIP- Add PythonPluginBase to README * WIP- Fix test case description in mock plugin * WIP- Fix typo and add missing type annotations in python plugin files * Fix run_python_plugin.py in notes
- Loading branch information
1 parent
d2afe9a
commit 12643ea
Showing
13 changed files
with
937 additions
and
783 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
/*globals define*/ | ||
|
||
define([ | ||
'plugin/PluginBase', | ||
'module' | ||
], function ( | ||
PluginBase, | ||
module | ||
) { | ||
const START_PORT = 5555; | ||
const COMMAND = 'python'; | ||
const path = require('path'); | ||
const SCRIPT_FILE = path.join(path.dirname(module.uri), 'run_python_plugin.py'); | ||
|
||
class PythonPluginBase extends PluginBase { | ||
constructor(pluginMetadata) { | ||
super(); | ||
this.pluginMetadata = pluginMetadata; | ||
} | ||
|
||
async main(callback) { | ||
const CoreZMQ = require('webgme-bindings').CoreZMQ; | ||
|
||
// due to the limited options on the script return values, we need this hack | ||
this.result.setSuccess(null); | ||
|
||
const corezmq = new CoreZMQ( | ||
this.project, | ||
this.core, | ||
this.logger, | ||
{ port: START_PORT, plugin: this} | ||
); | ||
|
||
const port = await corezmq.startServer(); | ||
|
||
this.logger.info(`zmq-server listening at port ${port}`); | ||
|
||
try { | ||
await this.callScript(COMMAND, SCRIPT_FILE, port); | ||
await corezmq.stopServer(); | ||
callback(null, this.result); | ||
} catch (err) { | ||
this.logger.error(err.stack); | ||
corezmq.stopServer() | ||
.finally(() => { | ||
// Result success is false at invocation. | ||
callback(err, this.result); | ||
}); | ||
} | ||
} | ||
|
||
async callScript(program, scriptPath, port) { | ||
const cp = require('child_process'); | ||
let options = {}, | ||
args = [ | ||
scriptPath, | ||
this.getId(), | ||
port, | ||
`"${this.commitHash}"`, | ||
`"${this.branchName}"`, | ||
`"${this.core.getPath(this.activeNode)}"`, | ||
`"${this.activeSelection.map(node => this.core.getPath(node)).join(',')}"`, | ||
`"${this.namespace}"`, | ||
]; | ||
|
||
const childProc = cp.spawn(program, args, options); | ||
|
||
return new Promise((resolve, reject) => { | ||
childProc.stdout.on('data', data => { | ||
this.logger.info(data.toString()); | ||
}); | ||
|
||
childProc.stderr.on('data', data => { | ||
this.logger.error(data.toString()); | ||
}); | ||
|
||
childProc.on('close', (code) => { | ||
if (code > 0) { | ||
// This means an execution error or crash, so we are failing the plugin | ||
reject(new Error(`${program} ${args.join(' ')} exited with code ${code}.`)); | ||
this.result.setSuccess(false); | ||
} else { | ||
if(this.result.getSuccess() === null) { | ||
// The result have not been set inside the python, but it suceeded, so we go with the true value | ||
this.result.setSuccess(true); | ||
} | ||
resolve(); | ||
} | ||
}); | ||
|
||
childProc.on('error', (err) => { | ||
// This is a hard execution error, like the child process cannot be instantiated... | ||
this.logger.error(err); | ||
this.result.setSuccess(false); | ||
reject(err); | ||
}); | ||
}); | ||
} | ||
} | ||
|
||
return PythonPluginBase; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
""" | ||
This script is called by the plugin-wrapper, PythonPluginBase.js, which passes down the | ||
plugin context via arguments. These can be modified to include more information if needed. | ||
Notes: | ||
- The current working directory when called from a plugin is the root of your webgme repo. | ||
- At the point of invocation of this plugin - it is assumed that a coreZMQ-server is running at 127.0.0.1:PORT. | ||
""" | ||
import json | ||
import logging | ||
import sys | ||
from importlib.util import module_from_spec, spec_from_file_location | ||
from pathlib import Path | ||
|
||
from webgme_bindings import WebGME | ||
|
||
WEBGME_SETUP = Path(f"{__file__}/../../../../webgme-setup.json").resolve() | ||
IMPORT_MODULE_NAME = "electric_circuits.plugins" | ||
|
||
|
||
def _import_python_class(plugin_file: Path, class_name: str) -> type: | ||
spec = spec_from_file_location(IMPORT_MODULE_NAME, plugin_file) | ||
base_module = module_from_spec(spec) | ||
spec.loader.exec_module(base_module) | ||
|
||
return getattr(base_module, class_name) | ||
|
||
|
||
def get_python_plugin_classes() -> dict: | ||
plugin_classes = {} | ||
with open(WEBGME_SETUP, "r") as webgme_setup: | ||
plugins = json.load(webgme_setup)["components"]["plugins"] | ||
|
||
for plugin_name in plugins: | ||
plugin_file = Path( | ||
f"{__file__}/../../../../{plugins[plugin_name]['src']}/{plugin_name}/__init__.py" | ||
).resolve() | ||
|
||
if plugin_file.exists(): | ||
plugin_classes[plugin_name] = _import_python_class( | ||
plugin_file, plugin_name | ||
) | ||
|
||
return plugin_classes | ||
|
||
|
||
PLUGINS = get_python_plugin_classes() | ||
|
||
if len(PLUGINS) == 0: | ||
raise Exception("No Python Plugins available in the current deployment") | ||
|
||
logger = logging.getLogger(sys.argv[1]) | ||
|
||
|
||
logger.info("sys.args: {0}".format(sys.argv)) | ||
|
||
PORT = sys.argv[2] | ||
COMMIT_HASH = sys.argv[3].strip('"') | ||
BRANCH_NAME = sys.argv[4].strip('"') | ||
ACTIVE_NODE_PATH = sys.argv[5].strip('"') | ||
ACTIVE_SELECTION_PATHS = [] | ||
|
||
if sys.argv[6] != '""': | ||
ACTIVE_SELECTION_PATHS = sys.argv[6].strip('"').split(",") | ||
if ACTIVE_SELECTION_PATHS[0] == "": | ||
ACTIVE_SELECTION_PATHS.pop(0) | ||
|
||
NAMESPACE = sys.argv[7].strip('"') | ||
|
||
|
||
logger.debug("commit-hash: {0}".format(COMMIT_HASH)) | ||
logger.debug("branch-name: {0}".format(BRANCH_NAME)) | ||
logger.debug("active-node-path: {0}".format(ACTIVE_NODE_PATH)) | ||
logger.debug("active-selection-paths: {0}".format(ACTIVE_SELECTION_PATHS)) | ||
logger.debug("name-space: {0}".format(NAMESPACE)) | ||
|
||
# Create an instance of WebGME and the plugin | ||
webgme = WebGME(PORT, logger) | ||
plugin = PLUGINS[sys.argv[1]]( | ||
webgme, | ||
COMMIT_HASH, | ||
BRANCH_NAME, | ||
ACTIVE_NODE_PATH, | ||
ACTIVE_SELECTION_PATHS, | ||
NAMESPACE, | ||
) | ||
|
||
# Do the work | ||
plugin.main() | ||
|
||
# Finally disconnect from the zmq-server | ||
webgme.disconnect() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.