mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-19 18:27:29 -05:00
kill unwrapped_remote (#1490)
* kill unwrapped_remote * linting * don't use callKwargs for python plugins * fix tests and improve types
This commit is contained in:
@@ -13,7 +13,6 @@ InterpreterClient class is responsible to request code execution
|
|||||||
*/
|
*/
|
||||||
export class InterpreterClient extends Object {
|
export class InterpreterClient extends Object {
|
||||||
_remote: Synclink.Remote<RemoteInterpreter>;
|
_remote: Synclink.Remote<RemoteInterpreter>;
|
||||||
_unwrapped_remote: RemoteInterpreter;
|
|
||||||
config: AppConfig;
|
config: AppConfig;
|
||||||
/**
|
/**
|
||||||
* global symbols table for the underlying interface.
|
* global symbols table for the underlying interface.
|
||||||
@@ -21,16 +20,10 @@ export class InterpreterClient extends Object {
|
|||||||
globals: Synclink.Remote<PyProxyDict>;
|
globals: Synclink.Remote<PyProxyDict>;
|
||||||
stdio: Stdio;
|
stdio: Stdio;
|
||||||
|
|
||||||
constructor(
|
constructor(config: AppConfig, stdio: Stdio, remote: Synclink.Remote<RemoteInterpreter>) {
|
||||||
config: AppConfig,
|
|
||||||
stdio: Stdio,
|
|
||||||
remote: Synclink.Remote<RemoteInterpreter>,
|
|
||||||
unwrapped_remote: RemoteInterpreter,
|
|
||||||
) {
|
|
||||||
super();
|
super();
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this._remote = remote;
|
this._remote = remote;
|
||||||
this._unwrapped_remote = unwrapped_remote;
|
|
||||||
this.stdio = stdio;
|
this.stdio = stdio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ export class PyScriptApp {
|
|||||||
*/
|
*/
|
||||||
const interpreterURL = interpreter_cfg.src;
|
const interpreterURL = interpreter_cfg.src;
|
||||||
await import(interpreterURL);
|
await import(interpreterURL);
|
||||||
return { remote_interpreter, wrapped_remote_interpreter };
|
return wrapped_remote_interpreter;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _startInterpreter_worker(interpreter_cfg: InterpreterConfig) {
|
async _startInterpreter_worker(interpreter_cfg: InterpreterConfig) {
|
||||||
@@ -222,8 +222,7 @@ export class PyScriptApp {
|
|||||||
const worker = new Worker(base_url + '/interpreter_worker.js');
|
const worker = new Worker(base_url + '/interpreter_worker.js');
|
||||||
const worker_initialize: any = Synclink.wrap(worker);
|
const worker_initialize: any = Synclink.wrap(worker);
|
||||||
const wrapped_remote_interpreter = await worker_initialize(interpreter_cfg);
|
const wrapped_remote_interpreter = await worker_initialize(interpreter_cfg);
|
||||||
const remote_interpreter = undefined; // this is _unwrapped_remote
|
return wrapped_remote_interpreter;
|
||||||
return { remote_interpreter, wrapped_remote_interpreter };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// lifecycle (4)
|
// lifecycle (4)
|
||||||
@@ -238,19 +237,17 @@ export class PyScriptApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cfg = this.config.interpreters[0];
|
const cfg = this.config.interpreters[0];
|
||||||
let x;
|
let wrapped_remote_interpreter;
|
||||||
if (this.config.execution_thread == 'worker') {
|
if (this.config.execution_thread == 'worker') {
|
||||||
x = await this._startInterpreter_worker(cfg);
|
wrapped_remote_interpreter = await this._startInterpreter_worker(cfg);
|
||||||
} else {
|
} else {
|
||||||
x = await this._startInterpreter_main(cfg);
|
wrapped_remote_interpreter = await this._startInterpreter_main(cfg);
|
||||||
}
|
}
|
||||||
const { remote_interpreter, wrapped_remote_interpreter } = x;
|
|
||||||
|
|
||||||
this.interpreter = new InterpreterClient(
|
this.interpreter = new InterpreterClient(
|
||||||
this.config,
|
this.config,
|
||||||
this._stdioMultiplexer,
|
this._stdioMultiplexer,
|
||||||
wrapped_remote_interpreter as Synclink.Remote<RemoteInterpreter>,
|
wrapped_remote_interpreter as Synclink.Remote<RemoteInterpreter>,
|
||||||
remote_interpreter,
|
|
||||||
);
|
);
|
||||||
await this.afterInterpreterLoad(this.interpreter);
|
await this.afterInterpreterLoad(this.interpreter);
|
||||||
}
|
}
|
||||||
@@ -413,11 +410,9 @@ export class PyScriptApp {
|
|||||||
// TODO: This is very specific to Pyodide API and will not work for other interpreters,
|
// TODO: This is very specific to Pyodide API and will not work for other interpreters,
|
||||||
// when we add support for other interpreters we will need to move this to the
|
// when we add support for other interpreters we will need to move this to the
|
||||||
// interpreter API level and allow each one to implement it in its own way
|
// interpreter API level and allow each one to implement it in its own way
|
||||||
|
const module = await interpreter.pyimport(modulename);
|
||||||
// eventually replace with interpreter.pyimport(modulename);
|
|
||||||
const module = interpreter._unwrapped_remote.pyimport(modulename);
|
|
||||||
if (typeof (await module.plugin) !== 'undefined') {
|
if (typeof (await module.plugin) !== 'undefined') {
|
||||||
const py_plugin = module.plugin as PythonPlugin;
|
const py_plugin = (await module.plugin) as PythonPlugin;
|
||||||
py_plugin.init(this);
|
py_plugin.init(this);
|
||||||
this.plugins.addPythonPlugin(py_plugin);
|
this.plugins.addPythonPlugin(py_plugin);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ import { UserError, ErrorCode } from './exceptions';
|
|||||||
import { getLogger } from './logger';
|
import { getLogger } from './logger';
|
||||||
import { make_PyScript } from './components/pyscript';
|
import { make_PyScript } from './components/pyscript';
|
||||||
import { InterpreterClient } from './interpreter_client';
|
import { InterpreterClient } from './interpreter_client';
|
||||||
|
import { make_PyRepl } from './components/pyrepl';
|
||||||
|
|
||||||
const logger = getLogger('plugin');
|
const logger = getLogger('plugin');
|
||||||
type PyScriptTag = InstanceType<ReturnType<typeof make_PyScript>>;
|
type PyScriptTag = InstanceType<ReturnType<typeof make_PyScript>>;
|
||||||
|
type PyReplTag = InstanceType<ReturnType<typeof make_PyRepl>>;
|
||||||
|
|
||||||
export class Plugin {
|
export class Plugin {
|
||||||
/** Validate the configuration of the plugin and handle default values.
|
/** Validate the configuration of the plugin and handle default values.
|
||||||
@@ -84,7 +86,12 @@ export class Plugin {
|
|||||||
* @param options.outEl The element that the result of the REPL evaluation will be output to.
|
* @param options.outEl The element that the result of the REPL evaluation will be output to.
|
||||||
* @param options.pyReplTag The <py-repl> HTML tag the originated the evaluation
|
* @param options.pyReplTag The <py-repl> HTML tag the originated the evaluation
|
||||||
*/
|
*/
|
||||||
beforePyReplExec(options: { interpreter: InterpreterClient; src: string; outEl: HTMLElement; pyReplTag: any }) {
|
beforePyReplExec(options: {
|
||||||
|
interpreter: InterpreterClient;
|
||||||
|
src: string;
|
||||||
|
outEl: HTMLElement;
|
||||||
|
pyReplTag: PyReplTag;
|
||||||
|
}) {
|
||||||
/* empty */
|
/* empty */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +107,7 @@ export class Plugin {
|
|||||||
interpreter: InterpreterClient;
|
interpreter: InterpreterClient;
|
||||||
src: string;
|
src: string;
|
||||||
outEl: HTMLElement;
|
outEl: HTMLElement;
|
||||||
pyReplTag: HTMLElement;
|
pyReplTag: PyReplTag;
|
||||||
result: any;
|
result: any;
|
||||||
}) {
|
}) {
|
||||||
/* empty */
|
/* empty */
|
||||||
@@ -126,10 +133,26 @@ export type PythonPlugin = {
|
|||||||
configure?: (config: AppConfig) => Promise<void>;
|
configure?: (config: AppConfig) => Promise<void>;
|
||||||
afterSetup?: (interpreter: InterpreterClient) => Promise<void>;
|
afterSetup?: (interpreter: InterpreterClient) => Promise<void>;
|
||||||
afterStartup?: (interpreter: InterpreterClient) => Promise<void>;
|
afterStartup?: (interpreter: InterpreterClient) => Promise<void>;
|
||||||
beforePyScriptExec?: { callKwargs: (options: any) => Promise<void> };
|
beforePyScriptExec?: (interpreter: InterpreterClient, src: string, pyScriptTag: PyScriptTag) => Promise<void>;
|
||||||
afterPyScriptExec?: { callKwargs: (options: any) => Promise<void> };
|
afterPyScriptExec?: (
|
||||||
beforePyReplExec?: { callKwargs: (options: any) => Promise<void> };
|
interpreter: InterpreterClient,
|
||||||
afterPyReplExec?: { callKwargs: (options: any) => Promise<void> };
|
src: string,
|
||||||
|
pyScriptTag: PyScriptTag,
|
||||||
|
result: any,
|
||||||
|
) => Promise<void>;
|
||||||
|
beforePyReplExec?: (
|
||||||
|
interpreter: InterpreterClient,
|
||||||
|
src: string,
|
||||||
|
outEl: HTMLElement,
|
||||||
|
pyReplTag: PyReplTag,
|
||||||
|
) => Promise<void>;
|
||||||
|
afterPyReplExec?: (
|
||||||
|
interpreter: InterpreterClient,
|
||||||
|
src: string,
|
||||||
|
outEl: HTMLElement,
|
||||||
|
pyReplTag: PyReplTag,
|
||||||
|
result: any,
|
||||||
|
) => Promise<void>;
|
||||||
onUserError?: (error: UserError) => Promise<void>;
|
onUserError?: (error: UserError) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -188,7 +211,9 @@ export class PluginManager {
|
|||||||
|
|
||||||
async beforePyScriptExec(options: { interpreter: InterpreterClient; src: string; pyScriptTag: PyScriptTag }) {
|
async beforePyScriptExec(options: { interpreter: InterpreterClient; src: string; pyScriptTag: PyScriptTag }) {
|
||||||
await Promise.all(this._plugins.map(p => p.beforePyScriptExec?.(options)));
|
await Promise.all(this._plugins.map(p => p.beforePyScriptExec?.(options)));
|
||||||
await Promise.all(this._pythonPlugins.map(p => p.beforePyScriptExec.callKwargs(options)));
|
await Promise.all(
|
||||||
|
this._pythonPlugins.map(p => p.beforePyScriptExec?.(options.interpreter, options.src, options.pyScriptTag)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async afterPyScriptExec(options: {
|
async afterPyScriptExec(options: {
|
||||||
@@ -198,22 +223,47 @@ export class PluginManager {
|
|||||||
result: any;
|
result: any;
|
||||||
}) {
|
}) {
|
||||||
await Promise.all(this._plugins.map(p => p.afterPyScriptExec?.(options)));
|
await Promise.all(this._plugins.map(p => p.afterPyScriptExec?.(options)));
|
||||||
await Promise.all(this._pythonPlugins.map(p => p.afterPyScriptExec.callKwargs(options)));
|
await Promise.all(
|
||||||
|
this._pythonPlugins.map(
|
||||||
|
p => p.afterPyScriptExec?.(options.interpreter, options.src, options.pyScriptTag, options.result),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async beforePyReplExec(options: {
|
async beforePyReplExec(options: {
|
||||||
interpreter: InterpreterClient;
|
interpreter: InterpreterClient;
|
||||||
src: string;
|
src: string;
|
||||||
outEl: HTMLElement;
|
outEl: HTMLElement;
|
||||||
pyReplTag: any;
|
pyReplTag: PyReplTag;
|
||||||
}) {
|
}) {
|
||||||
await Promise.all(this._plugins.map(p => p.beforePyReplExec?.(options)));
|
await Promise.all(this._plugins.map(p => p.beforePyReplExec?.(options)));
|
||||||
await Promise.all(this._pythonPlugins.map(p => p.beforePyReplExec.callKwargs(options)));
|
await Promise.all(
|
||||||
|
this._pythonPlugins.map(
|
||||||
|
p => p.beforePyReplExec?.(options.interpreter, options.src, options.outEl, options.pyReplTag),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async afterPyReplExec(options: { interpreter: InterpreterClient; src: string; outEl; pyReplTag; result }) {
|
async afterPyReplExec(options: {
|
||||||
|
interpreter: InterpreterClient;
|
||||||
|
src: string;
|
||||||
|
outEl: HTMLElement;
|
||||||
|
pyReplTag: PyReplTag;
|
||||||
|
result: any;
|
||||||
|
}) {
|
||||||
await Promise.all(this._plugins.map(p => p.afterPyReplExec?.(options)));
|
await Promise.all(this._plugins.map(p => p.afterPyReplExec?.(options)));
|
||||||
await Promise.all(this._pythonPlugins.map(p => p.afterPyReplExec.callKwargs(options)));
|
await Promise.all(
|
||||||
|
this._pythonPlugins.map(
|
||||||
|
p =>
|
||||||
|
p.afterPyReplExec?.(
|
||||||
|
options.interpreter,
|
||||||
|
options.src,
|
||||||
|
options.outEl,
|
||||||
|
options.pyReplTag,
|
||||||
|
options.result,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onUserError(error: UserError) {
|
async onUserError(error: UserError) {
|
||||||
|
|||||||
@@ -63,12 +63,10 @@ class ExecTestLogger(Plugin):
|
|||||||
async def beforePyScriptExec(self, interpreter, src, pyScriptTag):
|
async def beforePyScriptExec(self, interpreter, src, pyScriptTag):
|
||||||
console.log(f'beforePyScriptExec called')
|
console.log(f'beforePyScriptExec called')
|
||||||
console.log(f'before_src:{src}')
|
console.log(f'before_src:{src}')
|
||||||
console.log(f'before_id:{pyScriptTag.id}')
|
|
||||||
|
|
||||||
async def afterPyScriptExec(self, interpreter, src, pyScriptTag, result):
|
async def afterPyScriptExec(self, interpreter, src, pyScriptTag, result):
|
||||||
console.log(f'afterPyScriptExec called')
|
console.log(f'afterPyScriptExec called')
|
||||||
console.log(f'after_src:{src}')
|
console.log(f'after_src:{src}')
|
||||||
console.log(f'after_id:{pyScriptTag.id}')
|
|
||||||
console.log(f'result:{result}')
|
console.log(f'result:{result}')
|
||||||
|
|
||||||
|
|
||||||
@@ -88,12 +86,10 @@ class PyReplTestLogger(Plugin):
|
|||||||
def beforePyReplExec(self, interpreter, src, outEl, pyReplTag):
|
def beforePyReplExec(self, interpreter, src, outEl, pyReplTag):
|
||||||
console.log(f'beforePyReplExec called')
|
console.log(f'beforePyReplExec called')
|
||||||
console.log(f'before_src:{src}')
|
console.log(f'before_src:{src}')
|
||||||
console.log(f'before_id:{pyReplTag.id}')
|
|
||||||
|
|
||||||
def afterPyReplExec(self, interpreter, src, outEl, pyReplTag, result):
|
def afterPyReplExec(self, interpreter, src, outEl, pyReplTag, result):
|
||||||
console.log(f'afterPyReplExec called')
|
console.log(f'afterPyReplExec called')
|
||||||
console.log(f'after_src:{src}')
|
console.log(f'after_src:{src}')
|
||||||
console.log(f'after_id:{pyReplTag.id}')
|
|
||||||
console.log(f'result:{result}')
|
console.log(f'result:{result}')
|
||||||
|
|
||||||
|
|
||||||
@@ -261,9 +257,7 @@ class TestPlugin(PyScriptTest):
|
|||||||
# These could be made better with a utility function that found log lines
|
# These could be made better with a utility function that found log lines
|
||||||
# that match a filter function, or start with something
|
# that match a filter function, or start with something
|
||||||
assert "before_src:x=2; x" in log_lines
|
assert "before_src:x=2; x" in log_lines
|
||||||
assert "before_id:pyid" in log_lines
|
|
||||||
assert "after_src:x=2; x" in log_lines
|
assert "after_src:x=2; x" in log_lines
|
||||||
assert "after_id:pyid" in log_lines
|
|
||||||
assert "result:2" in log_lines
|
assert "result:2" in log_lines
|
||||||
|
|
||||||
@skip_worker("FIXME: relative paths")
|
@skip_worker("FIXME: relative paths")
|
||||||
@@ -286,9 +280,7 @@ class TestPlugin(PyScriptTest):
|
|||||||
# These could be made better with a utility function that found log lines
|
# These could be made better with a utility function that found log lines
|
||||||
# that match a filter function, or start with something
|
# that match a filter function, or start with something
|
||||||
assert "before_src:x=2; x" in log_lines
|
assert "before_src:x=2; x" in log_lines
|
||||||
assert "before_id:pyid" in log_lines
|
|
||||||
assert "after_src:x=2; x" in log_lines
|
assert "after_src:x=2; x" in log_lines
|
||||||
assert "after_id:pyid" in log_lines
|
|
||||||
assert "result:2" in log_lines
|
assert "result:2" in log_lines
|
||||||
|
|
||||||
@skip_worker("FIXME: relative paths")
|
@skip_worker("FIXME: relative paths")
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ describe('RemoteInterpreter', () => {
|
|||||||
config,
|
config,
|
||||||
stdio,
|
stdio,
|
||||||
wrapped_remote_interpreter as Synclink.Remote<RemoteInterpreter>,
|
wrapped_remote_interpreter as Synclink.Remote<RemoteInterpreter>,
|
||||||
remote_interpreter,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,10 +68,6 @@ describe('RemoteInterpreter', () => {
|
|||||||
expect(interpreter).toBeInstanceOf(InterpreterClient);
|
expect(interpreter).toBeInstanceOf(InterpreterClient);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check if interpreter is an instance of RemoteInterpreter', async () => {
|
|
||||||
expect(interpreter._unwrapped_remote).toBeInstanceOf(RemoteInterpreter);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should check if interpreter can run python code asynchronously', async () => {
|
it('should check if interpreter can run python code asynchronously', async () => {
|
||||||
expect((await interpreter.run('2+3')).result).toBe(5);
|
expect((await interpreter.run('2+3')).result).toBe(5);
|
||||||
});
|
});
|
||||||
@@ -85,7 +80,7 @@ describe('RemoteInterpreter', () => {
|
|||||||
|
|
||||||
it('should check if interpreter is able to load a package', async () => {
|
it('should check if interpreter is able to load a package', async () => {
|
||||||
stdio.reset();
|
stdio.reset();
|
||||||
await interpreter._unwrapped_remote.loadPackage('numpy');
|
await interpreter._remote.loadPackage('numpy');
|
||||||
await interpreter.run('import numpy as np');
|
await interpreter.run('import numpy as np');
|
||||||
await interpreter.run('x = np.ones((10,))');
|
await interpreter.run('x = np.ones((10,))');
|
||||||
await interpreter.run('print(x)');
|
await interpreter.run('print(x)');
|
||||||
|
|||||||
Reference in New Issue
Block a user