mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-21 03:05:38 -05:00
Python Plugins (#961)
* add test and example files * update config to include python plugins in build * add markdown plugin * remove full pyscript execution from pyodide * move loading of pyscript.py from pyodide loagInterpreter to main setupVirtualEnv and add function to create python CE plugins * add plugin class to pyscript.py * add missing import * fix plugin path * add fetchPythonPlugins to PyScriptApp * remove old comments * fix test * add support for python plugins beyond custom elements and add app to python namespace in main * inject reference to PyScript app onto python plugins * add example hook onto markdown plugin * change plugin events logs * remove unused PyPlugin * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix type import * add docstring to fetchPythonPlugins * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * rename addPythonPlugin method * address PR comment * call python plugins on hooks after the interpreted is ready * add test for event hooks and split the test in 2 separate plugins to isolte type of plugins tests * change python plugins initialization and registration, to inject the app from app itself instead of on the plugins themselves * handle case when plugin cannot load due to missing plugin attribute * add test for fail scenario when a plugin module does not have a plugin attribute * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add deprecation warning for pyscript objects loaded in global namespace * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * remove all from global scope * remove create_custom_element from global scope * rename create_custom_element to define_custom_element * rename attributes in define_custom_element and add docstrings * better handle connect event output * add warning to py_markdown plugin * remove debugging logs * improve tests * remove debugging log * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * remove unused import * add executable shebang * add pyodide mock module * fmt and lint * Update to pyodide.ffi.create_proxy per pyodide v21 api change * Mock pyodide as package instead of mdoule * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add __init__ to pyodide package * Update pyscriptjs/src/plugin.ts fix logger name Co-authored-by: Antonio Cuni <anto.cuni@gmail.com> * fix pyodide import but handling the diff in their API change * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * oops, conflict resolution blooper * Fix failing integration tests Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Jeff Glass <glass.jeffrey@gmail.com> Co-authored-by: Antonio Cuni <anto.cuni@gmail.com> Co-authored-by: FabioRosado <fabiorosado@outlook.com>
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import type { AppConfig } from './pyconfig';
|
||||
import type { Runtime } from './runtime';
|
||||
import type { UserError } from './exceptions';
|
||||
import { getLogger } from './logger';
|
||||
|
||||
const logger = getLogger('plugin');
|
||||
|
||||
export class Plugin {
|
||||
|
||||
/** Validate the configuration of the plugin and handle default values.
|
||||
*
|
||||
* Individual plugins are expected to check that the config keys/sections
|
||||
@@ -16,8 +18,7 @@ export class Plugin {
|
||||
* This hook should **NOT** contain expensive operations, else it delays
|
||||
* the download of the python interpreter which is initiated later.
|
||||
*/
|
||||
configure(config: AppConfig) {
|
||||
}
|
||||
configure(config: AppConfig) {}
|
||||
|
||||
/** The preliminary initialization phase is complete and we are about to
|
||||
* download and launch the Python interpreter.
|
||||
@@ -29,66 +30,107 @@ export class Plugin {
|
||||
* This hook should **NOT** contain expensive operations, else it delays
|
||||
* the download of the python interpreter which is initiated later.
|
||||
*/
|
||||
beforeLaunch(config: AppConfig) {
|
||||
}
|
||||
beforeLaunch(config: AppConfig) {}
|
||||
|
||||
/** The Python interpreter has been launched, the virtualenv has been
|
||||
* installed and we are ready to execute user code.
|
||||
*
|
||||
* The <py-script> tags will be executed after this hook.
|
||||
*/
|
||||
afterSetup(runtime: Runtime) {
|
||||
}
|
||||
|
||||
* installed and we are ready to execute user code.
|
||||
*
|
||||
* The <py-script> tags will be executed after this hook.
|
||||
*/
|
||||
afterSetup(runtime: Runtime) {}
|
||||
|
||||
/** Startup complete. The interpreter is initialized and ready, user
|
||||
* scripts have been executed: the main initialization logic ends here and
|
||||
* the page is ready to accept user interactions.
|
||||
*/
|
||||
afterStartup(runtime: Runtime) {
|
||||
}
|
||||
afterStartup(runtime: Runtime) {}
|
||||
|
||||
/** Called when an UserError is raised
|
||||
*/
|
||||
onUserError(error: UserError) {
|
||||
}
|
||||
onUserError(error: UserError) {}
|
||||
}
|
||||
|
||||
|
||||
export class PluginManager {
|
||||
_plugins: Plugin[];
|
||||
_pythonPlugins: any[];
|
||||
|
||||
constructor() {
|
||||
this._plugins = [];
|
||||
this._pythonPlugins = [];
|
||||
}
|
||||
|
||||
add(...plugins: Plugin[]) {
|
||||
for (const p of plugins)
|
||||
this._plugins.push(p);
|
||||
for (const p of plugins) this._plugins.push(p);
|
||||
}
|
||||
|
||||
addPythonPlugin(plugin: any) {
|
||||
this._pythonPlugins.push(plugin);
|
||||
}
|
||||
|
||||
configure(config: AppConfig) {
|
||||
for (const p of this._plugins)
|
||||
p.configure(config);
|
||||
for (const p of this._plugins) p.configure(config);
|
||||
|
||||
for (const p of this._pythonPlugins) p.configure?.(config);
|
||||
}
|
||||
|
||||
beforeLaunch(config: AppConfig) {
|
||||
for (const p of this._plugins)
|
||||
p.beforeLaunch(config);
|
||||
for (const p of this._plugins) p.beforeLaunch(config);
|
||||
}
|
||||
|
||||
afterSetup(runtime: Runtime) {
|
||||
for (const p of this._plugins)
|
||||
p.afterSetup(runtime);
|
||||
for (const p of this._plugins) p.afterSetup(runtime);
|
||||
|
||||
for (const p of this._pythonPlugins) p.afterSetup?.(runtime);
|
||||
}
|
||||
|
||||
afterStartup(runtime: Runtime) {
|
||||
for (const p of this._plugins)
|
||||
p.afterStartup(runtime);
|
||||
for (const p of this._plugins) p.afterStartup(runtime);
|
||||
|
||||
for (const p of this._pythonPlugins) p.afterStartup?.(runtime);
|
||||
}
|
||||
|
||||
onUserError(error: UserError) {
|
||||
for (const p of this._plugins)
|
||||
p.onUserError(error);
|
||||
for (const p of this._plugins) p.onUserError(error);
|
||||
|
||||
for (const p of this._pythonPlugins) p.onUserError?.(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a new CustomElement (via customElement.defines) with `tag`,
|
||||
* where the new CustomElement is a proxy that delegates the logic to
|
||||
* pyPluginClass.
|
||||
*
|
||||
* @param tag - tag that will be used to define the new CustomElement (i.e: "py-script")
|
||||
* @param pyPluginClass - class that will be used to create instance to be
|
||||
* used as CustomElement logic handler. Any DOM event
|
||||
* received by the newly created CustomElement will be
|
||||
* delegated to that instance.
|
||||
*/
|
||||
export function define_custom_element(tag: string, pyPluginClass: any): any {
|
||||
logger.info(`creating plugin: ${tag}`);
|
||||
class ProxyCustomElement extends HTMLElement {
|
||||
shadow: ShadowRoot;
|
||||
wrapper: HTMLElement;
|
||||
pyPluginInstance: any;
|
||||
originalInnerHTML: string;
|
||||
|
||||
constructor() {
|
||||
logger.debug(`creating ${tag} plugin instance`);
|
||||
super();
|
||||
|
||||
this.shadow = this.attachShadow({ mode: 'open' });
|
||||
this.wrapper = document.createElement('slot');
|
||||
this.shadow.appendChild(this.wrapper);
|
||||
this.originalInnerHTML = this.innerHTML;
|
||||
this.pyPluginInstance = pyPluginClass(this);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
const innerHTML = this.pyPluginInstance.connect();
|
||||
if (typeof innerHTML === 'string') this.innerHTML = innerHTML;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define(tag, ProxyCustomElement);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user