import {LocalPlugin} from 'app/common/plugin'; import {BaseComponent, createRpcLogger} from 'app/common/PluginInstance'; import {GristServer} from 'app/server/lib/GristServer'; import {ISandbox} from 'app/server/lib/ISandbox'; import log from 'app/server/lib/log'; import {IMsgCustom, IMsgRpcCall} from 'grain-rpc'; // TODO safePython component should be able to call other components function // TODO calling a function on safePython component with a name that was not register chould fail // gracefully. /** * The safePython component used by a PluginInstance. * * It uses `NSandbox` implementation of rpc for calling methods within the sandbox. */ export class SafePythonComponent extends BaseComponent { private _sandbox?: ISandbox; private _logMeta: log.ILogMeta; // safe python component does not need pluginInstance.rpc because it is not possible to forward // calls to other component from within python constructor(_localPlugin: LocalPlugin, private _tmpDir: string, docName: string, private _server: GristServer, rpcLogger = createRpcLogger(log, `PLUGIN ${_localPlugin.id} SafePython:`)) { super(_localPlugin.manifest, rpcLogger); this._logMeta = {plugin: _localPlugin.id, docId: docName}; } /** * `SafePythonComponent` activation creates the Sandbox. Throws if the plugin has no `safePyton` * components. */ protected async activateImplementation(): Promise { if (!this._tmpDir) { throw new Error("Sanbox should have a tmpDir"); } this._sandbox = this._server.create.NSandbox({ importMount: this._tmpDir, logTimes: true, logMeta: this._logMeta, preferredPythonVersion: '3', }); } protected async deactivateImplementation(): Promise { log.info('SafePython deactivating ...'); if (!this._sandbox) { log.info(' sandbox is undefined'); } if (this._sandbox) { await this._sandbox.shutdown(); log.info('SafePython done deactivating the sandbox'); delete this._sandbox; } } protected doForwardCall(c: IMsgRpcCall): Promise { if (!this._sandbox) { throw new Error("Component should have be activated"); } const {meth, iface, args} = c; const funcName = meth === "invoke" ? iface : iface + "." + meth; return this._sandbox.pyCall(funcName, ...args); } protected doForwardMessage(c: IMsgCustom): Promise { throw new Error("Forwarding messages to python sandbox is not supported"); } }