mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-19 18:27:29 -05:00
Fix many ESlint errors (#1265)
* Unvendor toml package * Fix many ESlint errors For mysterious reasons, these errors appear on my branch #1262 even though they are not related to changes there. The eslint config seems a bit unstable. Anyways this fixes them. * Put back Record * Fix typescript compilation * Fix lints * Try @iarna/toml instead * Fix import * Use @ltd/j-toml * Update test * Use toml-j0.4 * Some changes * Fix toml import * Try adding eslint gha job * Add forgotten checkout action * Force CI to run * Blah * Fix * Revert changes to github workflow * Fix lints * wget toml-j0.4 type definitions * Add toml-j types workaround to eslint workflow * Apply formatter * Use @hoodmane/toml-j0.4 * Import from @hoodmane/toml-j0.4
This commit is contained in:
@@ -18,16 +18,16 @@ module.exports = {
|
|||||||
plugins: ['@typescript-eslint'],
|
plugins: ['@typescript-eslint'],
|
||||||
ignorePatterns: ['node_modules'],
|
ignorePatterns: ['node_modules'],
|
||||||
rules: {
|
rules: {
|
||||||
'no-prototype-builtins': 'warn',
|
'no-prototype-builtins': 'error',
|
||||||
'@typescript-eslint/no-unused-vars': 'warn',
|
'@typescript-eslint/no-unused-vars': ['error', { args: 'none' }],
|
||||||
'@typescript-eslint/no-explicit-any': 'warn',
|
'@typescript-eslint/no-explicit-any': 'error',
|
||||||
'@typescript-eslint/no-unsafe-assignment': 'warn',
|
'@typescript-eslint/no-unsafe-assignment': 'error',
|
||||||
'@typescript-eslint/no-unsafe-argument': 'warn',
|
'@typescript-eslint/no-unsafe-argument': 'error',
|
||||||
'@typescript-eslint/no-unsafe-member-access': 'warn',
|
'@typescript-eslint/no-unsafe-member-access': 'error',
|
||||||
'@typescript-eslint/no-unsafe-call': 'warn',
|
'@typescript-eslint/no-unsafe-call': 'error',
|
||||||
'@typescript-eslint/no-unsafe-return': 'warn',
|
'@typescript-eslint/no-unsafe-return': 'error',
|
||||||
'@typescript-eslint/no-floating-promises': 'warn',
|
'@typescript-eslint/no-floating-promises': 'error',
|
||||||
'@typescript-eslint/restrict-plus-operands': 'warn',
|
'@typescript-eslint/restrict-plus-operands': 'error',
|
||||||
'@typescript-eslint/no-empty-function': 'warn',
|
'@typescript-eslint/no-empty-function': 'error',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
24
pyscriptjs/package-lock.json
generated
24
pyscriptjs/package-lock.json
generated
@@ -14,8 +14,8 @@
|
|||||||
"@codemirror/state": "6.1.2",
|
"@codemirror/state": "6.1.2",
|
||||||
"@codemirror/theme-one-dark": "6.1.0",
|
"@codemirror/theme-one-dark": "6.1.0",
|
||||||
"@codemirror/view": "6.3.0",
|
"@codemirror/view": "6.3.0",
|
||||||
"codemirror": "6.0.1",
|
"@hoodmane/toml-j0.4": "^1.1.2",
|
||||||
"toml-j0.4": "^1.1.1"
|
"codemirror": "6.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@jest/globals": "29.1.2",
|
"@jest/globals": "29.1.2",
|
||||||
@@ -711,6 +711,11 @@
|
|||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@hoodmane/toml-j0.4": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hoodmane/toml-j0.4/-/toml-j0.4-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-vQvFpjssFt39GcYwsFLDts6eReKSWeDwFt3AufXnM62ACIG2MgvOVomNRo4kQUYV0G3+zpu4LezUWw5JCFgF0Q=="
|
||||||
|
},
|
||||||
"node_modules/@humanwhocodes/config-array": {
|
"node_modules/@humanwhocodes/config-array": {
|
||||||
"version": "0.10.7",
|
"version": "0.10.7",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -5309,11 +5314,6 @@
|
|||||||
"node": ">=8.0"
|
"node": ">=8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/toml-j0.4": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/toml-j0.4/-/toml-j0.4-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-lYK5otg0+cto8YmsWcPEfeiTiC/VU6P6HA6ooaYI9K/KYT24Jg0BrYtRZK1K3cwakSMyh6nttfJL9RmQH0gyCg=="
|
|
||||||
},
|
|
||||||
"node_modules/tough-cookie": {
|
"node_modules/tough-cookie": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -6166,6 +6166,11 @@
|
|||||||
"strip-json-comments": "^3.1.1"
|
"strip-json-comments": "^3.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@hoodmane/toml-j0.4": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hoodmane/toml-j0.4/-/toml-j0.4-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-vQvFpjssFt39GcYwsFLDts6eReKSWeDwFt3AufXnM62ACIG2MgvOVomNRo4kQUYV0G3+zpu4LezUWw5JCFgF0Q=="
|
||||||
|
},
|
||||||
"@humanwhocodes/config-array": {
|
"@humanwhocodes/config-array": {
|
||||||
"version": "0.10.7",
|
"version": "0.10.7",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -9148,11 +9153,6 @@
|
|||||||
"is-number": "^7.0.0"
|
"is-number": "^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"toml-j0.4": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/toml-j0.4/-/toml-j0.4-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-lYK5otg0+cto8YmsWcPEfeiTiC/VU6P6HA6ooaYI9K/KYT24Jg0BrYtRZK1K3cwakSMyh6nttfJL9RmQH0gyCg=="
|
|
||||||
},
|
|
||||||
"tough-cookie": {
|
"tough-cookie": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
"@codemirror/state": "6.1.2",
|
"@codemirror/state": "6.1.2",
|
||||||
"@codemirror/theme-one-dark": "6.1.0",
|
"@codemirror/theme-one-dark": "6.1.0",
|
||||||
"@codemirror/view": "6.3.0",
|
"@codemirror/view": "6.3.0",
|
||||||
"codemirror": "6.0.1",
|
"@hoodmane/toml-j0.4": "^1.1.2",
|
||||||
"toml-j0.4": "^1.1.1"
|
"codemirror": "6.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { basicSetup, EditorView } from 'codemirror';
|
|||||||
import { python } from '@codemirror/lang-python';
|
import { python } from '@codemirror/lang-python';
|
||||||
import { indentUnit } from '@codemirror/language';
|
import { indentUnit } from '@codemirror/language';
|
||||||
import { Compartment } from '@codemirror/state';
|
import { Compartment } from '@codemirror/state';
|
||||||
import { keymap } from '@codemirror/view';
|
import { keymap, Command } from '@codemirror/view';
|
||||||
import { defaultKeymap } from '@codemirror/commands';
|
import { defaultKeymap } from '@codemirror/commands';
|
||||||
import { oneDarkTheme } from '@codemirror/theme-one-dark';
|
import { oneDarkTheme } from '@codemirror/theme-one-dark';
|
||||||
|
|
||||||
@@ -68,8 +68,8 @@ export function make_PyRepl(interpreter: InterpreterClient) {
|
|||||||
languageConf.of(python()),
|
languageConf.of(python()),
|
||||||
keymap.of([
|
keymap.of([
|
||||||
...defaultKeymap,
|
...defaultKeymap,
|
||||||
{ key: 'Ctrl-Enter', run: this.execute.bind(this), preventDefault: true },
|
{ key: 'Ctrl-Enter', run: this.execute.bind(this) as Command, preventDefault: true },
|
||||||
{ key: 'Shift-Enter', run: this.execute.bind(this), preventDefault: true },
|
{ key: 'Shift-Enter', run: this.execute.bind(this) as Command, preventDefault: true },
|
||||||
]),
|
]),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ export function make_PyRepl(interpreter: InterpreterClient) {
|
|||||||
runButton.id = 'runButton';
|
runButton.id = 'runButton';
|
||||||
runButton.className = 'absolute py-repl-run-button';
|
runButton.className = 'absolute py-repl-run-button';
|
||||||
runButton.innerHTML = RUNBUTTON;
|
runButton.innerHTML = RUNBUTTON;
|
||||||
runButton.addEventListener('click', this.execute.bind(this));
|
runButton.addEventListener('click', this.execute.bind(this) as (e: MouseEvent) => void);
|
||||||
return runButton;
|
return runButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,6 +166,7 @@ export function make_PyRepl(interpreter: InterpreterClient) {
|
|||||||
outEl.innerHTML = '';
|
outEl.innerHTML = '';
|
||||||
|
|
||||||
// execute the python code
|
// execute the python code
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
const pyResult = (await pyExec(interpreter, pySrc, outEl)).result;
|
const pyResult = (await pyExec(interpreter, pySrc, outEl)).result;
|
||||||
|
|
||||||
// display the value of the last evaluated expression (REPL-style)
|
// display the value of the last evaluated expression (REPL-style)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export function make_PyScript(interpreter: InterpreterClient, app: PyScriptApp)
|
|||||||
*
|
*
|
||||||
* Concurrent access to the multiple py-script tags is thus avoided.
|
* Concurrent access to the multiple py-script tags is thus avoided.
|
||||||
*/
|
*/
|
||||||
let releaseLock: any;
|
let releaseLock: () => void;
|
||||||
try {
|
try {
|
||||||
releaseLock = await app.tagExecutionLock();
|
releaseLock = await app.tagExecutionLock();
|
||||||
ensureUniqueId(this);
|
ensureUniqueId(this);
|
||||||
@@ -35,6 +35,7 @@ export function make_PyScript(interpreter: InterpreterClient, app: PyScriptApp)
|
|||||||
this.innerHTML = '';
|
this.innerHTML = '';
|
||||||
|
|
||||||
app.plugins.beforePyScriptExec({ interpreter: interpreter, src: pySrc, pyScriptTag: this });
|
app.plugins.beforePyScriptExec({ interpreter: interpreter, src: pySrc, pyScriptTag: this });
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
const result = (await pyExec(interpreter, pySrc, this)).result;
|
const result = (await pyExec(interpreter, pySrc, this)).result;
|
||||||
app.plugins.afterPyScriptExec({
|
app.plugins.afterPyScriptExec({
|
||||||
interpreter: interpreter,
|
interpreter: interpreter,
|
||||||
@@ -42,6 +43,7 @@ export function make_PyScript(interpreter: InterpreterClient, app: PyScriptApp)
|
|||||||
pyScriptTag: this,
|
pyScriptTag: this,
|
||||||
result: result,
|
result: result,
|
||||||
});
|
});
|
||||||
|
/* eslint-enable @typescript-eslint/no-unsafe-assignment */
|
||||||
} finally {
|
} finally {
|
||||||
releaseLock();
|
releaseLock();
|
||||||
}
|
}
|
||||||
@@ -53,7 +55,8 @@ export function make_PyScript(interpreter: InterpreterClient, app: PyScriptApp)
|
|||||||
try {
|
try {
|
||||||
const response = await robustFetch(url);
|
const response = await robustFetch(url);
|
||||||
return await response.text();
|
return await response.text();
|
||||||
} catch (e) {
|
} catch (err) {
|
||||||
|
const e = err as Error;
|
||||||
_createAlertBanner(e.message);
|
_createAlertBanner(e.message);
|
||||||
this.innerHTML = '';
|
this.innerHTML = '';
|
||||||
throw e;
|
throw e;
|
||||||
@@ -163,15 +166,15 @@ const pyAttributeToEvent: Map<string, string> = new Map<string, string>([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
/** Initialize all elements with py-* handlers attributes */
|
/** Initialize all elements with py-* handlers attributes */
|
||||||
export function initHandlers(interpreter: InterpreterClient) {
|
export async function initHandlers(interpreter: InterpreterClient) {
|
||||||
logger.debug('Initializing py-* event handlers...');
|
logger.debug('Initializing py-* event handlers...');
|
||||||
for (const pyAttribute of pyAttributeToEvent.keys()) {
|
for (const pyAttribute of pyAttributeToEvent.keys()) {
|
||||||
createElementsWithEventListeners(interpreter, pyAttribute);
|
await createElementsWithEventListeners(interpreter, pyAttribute);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Initializes an element with the given py-on* attribute and its handler */
|
/** Initializes an element with the given py-on* attribute and its handler */
|
||||||
function createElementsWithEventListeners(interpreter: InterpreterClient, pyAttribute: string) {
|
async function createElementsWithEventListeners(interpreter: InterpreterClient, pyAttribute: string) {
|
||||||
const matches: NodeListOf<HTMLElement> = document.querySelectorAll(`[${pyAttribute}]`);
|
const matches: NodeListOf<HTMLElement> = document.querySelectorAll(`[${pyAttribute}]`);
|
||||||
for (const el of matches) {
|
for (const el of matches) {
|
||||||
// If the element doesn't have an id, let's add one automatically!
|
// If the element doesn't have an id, let's add one automatically!
|
||||||
@@ -195,7 +198,7 @@ function createElementsWithEventListeners(interpreter: InterpreterClient, pyAttr
|
|||||||
// the source code may contain a syntax error, which will cause
|
// the source code may contain a syntax error, which will cause
|
||||||
// the splashscreen to not be removed.
|
// the splashscreen to not be removed.
|
||||||
try {
|
try {
|
||||||
interpreter.run(source);
|
await interpreter.run(source);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error((e as Error).message);
|
logger.error((e as Error).message);
|
||||||
}
|
}
|
||||||
@@ -204,7 +207,8 @@ function createElementsWithEventListeners(interpreter: InterpreterClient, pyAttr
|
|||||||
void (async () => {
|
void (async () => {
|
||||||
try {
|
try {
|
||||||
await interpreter.run(handlerCode);
|
await interpreter.run(handlerCode);
|
||||||
} catch (err) {
|
} catch (e) {
|
||||||
|
const err = e as Error;
|
||||||
displayPyException(err, el.parentElement);
|
displayPyException(err, el.parentElement);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { PyProxy } from 'pyodide';
|
import type { PyProxy, PyProxyCallable } from 'pyodide';
|
||||||
import { getLogger } from '../logger';
|
import { getLogger } from '../logger';
|
||||||
import { robustFetch } from '../fetch';
|
import { robustFetch } from '../fetch';
|
||||||
import { InterpreterClient } from '../interpreter_client';
|
import { InterpreterClient } from '../interpreter_client';
|
||||||
@@ -13,8 +13,8 @@ function createWidget(interpreter: InterpreterClient, name: string, code: string
|
|||||||
name: string = name;
|
name: string = name;
|
||||||
klass: string = klass;
|
klass: string = klass;
|
||||||
code: string = code;
|
code: string = code;
|
||||||
proxy: PyProxy;
|
proxy: PyProxy & { connect(): void };
|
||||||
proxyClass: any;
|
proxyClass: PyProxyCallable;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@@ -28,8 +28,8 @@ function createWidget(interpreter: InterpreterClient, name: string, code: string
|
|||||||
|
|
||||||
async connectedCallback() {
|
async connectedCallback() {
|
||||||
await interpreter.runButDontRaise(this.code);
|
await interpreter.runButDontRaise(this.code);
|
||||||
this.proxyClass = interpreter.globals.get(this.klass);
|
this.proxyClass = interpreter.globals.get(this.klass) as PyProxyCallable;
|
||||||
this.proxy = this.proxyClass(this);
|
this.proxy = this.proxyClass(this) as PyProxy & { connect(): void };
|
||||||
this.proxy.connect();
|
this.proxy.connect();
|
||||||
this.registerWidget();
|
this.registerWidget();
|
||||||
}
|
}
|
||||||
@@ -39,9 +39,8 @@ function createWidget(interpreter: InterpreterClient, name: string, code: string
|
|||||||
interpreter.globals.set(this.id, this.proxy);
|
interpreter.globals.set(this.id, this.proxy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const xPyWidget = customElements.define(name, CustomWidget);
|
const xPyWidget = customElements.define(name, CustomWidget);
|
||||||
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function make_PyWidget(interpreter: InterpreterClient) {
|
export function make_PyWidget(interpreter: InterpreterClient) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { AppConfig } from './pyconfig';
|
import type { AppConfig } from './pyconfig';
|
||||||
import { RemoteInterpreter } from './remote_interpreter';
|
import { RemoteInterpreter } from './remote_interpreter';
|
||||||
import type { PyProxy } from 'pyodide';
|
import type { PyProxyDict } from 'pyodide';
|
||||||
import { getLogger } from './logger';
|
import { getLogger } from './logger';
|
||||||
import type { Stdio } from './stdio';
|
import type { Stdio } from './stdio';
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ export class InterpreterClient extends Object {
|
|||||||
/**
|
/**
|
||||||
* global symbols table for the underlying interface.
|
* global symbols table for the underlying interface.
|
||||||
* */
|
* */
|
||||||
globals: PyProxy;
|
globals: PyProxyDict;
|
||||||
stdio: Stdio;
|
stdio: Stdio;
|
||||||
|
|
||||||
constructor(config: AppConfig, stdio: Stdio) {
|
constructor(config: AppConfig, stdio: Stdio) {
|
||||||
@@ -32,7 +32,7 @@ export class InterpreterClient extends Object {
|
|||||||
* */
|
* */
|
||||||
async initializeRemote(): Promise<void> {
|
async initializeRemote(): Promise<void> {
|
||||||
await this._remote.loadInterpreter(this.config, this.stdio);
|
await this._remote.loadInterpreter(this.config, this.stdio);
|
||||||
this.globals = this._remote.globals;
|
this.globals = this._remote.globals as PyProxyDict;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,6 +40,7 @@ export class InterpreterClient extends Object {
|
|||||||
* the remote interpreter.
|
* the remote interpreter.
|
||||||
* Python exceptions are turned into JS exceptions.
|
* Python exceptions are turned into JS exceptions.
|
||||||
* */
|
* */
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
async run(code: string): Promise<{ result: any }> {
|
async run(code: string): Promise<{ result: any }> {
|
||||||
return await this._remote.run(code);
|
return await this._remote.run(code);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ interface Logger {
|
|||||||
debug(message: string, ...args: unknown[]): void;
|
debug(message: string, ...args: unknown[]): void;
|
||||||
info(message: string, ...args: unknown[]): void;
|
info(message: string, ...args: unknown[]): void;
|
||||||
warn(message: string, ...args: unknown[]): void;
|
warn(message: string, ...args: unknown[]): void;
|
||||||
error(message: string, ...args: unknown[]): void;
|
error(message: string | Error, ...args: unknown[]): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const _cache = new Map<string, Logger>();
|
const _cache = new Map<string, Logger>();
|
||||||
@@ -44,8 +44,8 @@ function getLogger(prefix: string): Logger {
|
|||||||
function _makeLogger(prefix: string): Logger {
|
function _makeLogger(prefix: string): Logger {
|
||||||
prefix = `[${prefix}] `;
|
prefix = `[${prefix}] `;
|
||||||
|
|
||||||
function make(level: string) {
|
function make(level: 'info' | 'debug' | 'warn' | 'error') {
|
||||||
const out_fn = console[level].bind(console);
|
const out_fn = console[level].bind(console) as typeof console.log;
|
||||||
function fn(fmt: string, ...args: unknown[]) {
|
function fn(fmt: string, ...args: unknown[]) {
|
||||||
out_fn(prefix + fmt, ...args);
|
out_fn(prefix + fmt, ...args);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { loadConfigFromElement } from './pyconfig';
|
|||||||
import type { AppConfig } from './pyconfig';
|
import type { AppConfig } from './pyconfig';
|
||||||
import { InterpreterClient } from './interpreter_client';
|
import { InterpreterClient } from './interpreter_client';
|
||||||
import { version } from './version';
|
import { version } from './version';
|
||||||
import { PluginManager, define_custom_element } from './plugin';
|
import { PluginManager, define_custom_element, Plugin } from './plugin';
|
||||||
import { make_PyScript, initHandlers, mountElements } from './components/pyscript';
|
import { make_PyScript, initHandlers, mountElements } from './components/pyscript';
|
||||||
import { getLogger } from './logger';
|
import { getLogger } from './logger';
|
||||||
import { showWarning, globalExport, createLock } from './utils';
|
import { showWarning, globalExport, createLock } from './utils';
|
||||||
@@ -16,6 +16,7 @@ import { PyTerminalPlugin } from './plugins/pyterminal';
|
|||||||
import { SplashscreenPlugin } from './plugins/splashscreen';
|
import { SplashscreenPlugin } from './plugins/splashscreen';
|
||||||
import { ImportmapPlugin } from './plugins/importmap';
|
import { ImportmapPlugin } from './plugins/importmap';
|
||||||
import { StdioDirector as StdioDirector } from './plugins/stdiodirector';
|
import { StdioDirector as StdioDirector } from './plugins/stdiodirector';
|
||||||
|
import type { PyProxy } from 'pyodide';
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import pyscript from './python/pyscript/__init__.py';
|
import pyscript from './python/pyscript/__init__.py';
|
||||||
@@ -190,7 +191,7 @@ export class PyScriptApp {
|
|||||||
// lifecycle (8)
|
// lifecycle (8)
|
||||||
createCustomElements(interpreter);
|
createCustomElements(interpreter);
|
||||||
|
|
||||||
initHandlers(interpreter);
|
await initHandlers(interpreter);
|
||||||
|
|
||||||
// NOTE: interpreter message is used by integration tests to know that
|
// NOTE: interpreter message is used by integration tests to know that
|
||||||
// pyscript initialization has complete. If you change it, you need to
|
// pyscript initialization has complete. If you change it, you need to
|
||||||
@@ -208,8 +209,8 @@ export class PyScriptApp {
|
|||||||
logger.info('importing pyscript');
|
logger.info('importing pyscript');
|
||||||
|
|
||||||
// Save and load pyscript.py from FS
|
// Save and load pyscript.py from FS
|
||||||
interpreter._remote.interface.FS.mkdirTree('/home/pyodide/pyscript');
|
interpreter._remote.FS.mkdirTree('/home/pyodide/pyscript');
|
||||||
interpreter._remote.interface.FS.writeFile('pyscript/__init__.py', pyscript);
|
interpreter._remote.FS.writeFile('pyscript/__init__.py', pyscript as string);
|
||||||
//Refresh the module cache so Python consistently finds pyscript module
|
//Refresh the module cache so Python consistently finds pyscript module
|
||||||
interpreter._remote.invalidate_module_path_cache();
|
interpreter._remote.invalidate_module_path_cache();
|
||||||
|
|
||||||
@@ -218,6 +219,7 @@ export class PyScriptApp {
|
|||||||
const pyscript_module = interpreter._remote.interface.pyimport('pyscript');
|
const pyscript_module = interpreter._remote.interface.pyimport('pyscript');
|
||||||
pyscript_module.define_custom_element = define_custom_element;
|
pyscript_module.define_custom_element = define_custom_element;
|
||||||
pyscript_module.showWarning = showWarning;
|
pyscript_module.showWarning = showWarning;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
pyscript_module._set_version_info(version);
|
pyscript_module._set_version_info(version);
|
||||||
pyscript_module.destroy();
|
pyscript_module.destroy();
|
||||||
|
|
||||||
@@ -303,7 +305,7 @@ export class PyScriptApp {
|
|||||||
const blobFile = new File([pluginBlob], 'plugin.js', { type: 'text/javascript' });
|
const blobFile = new File([pluginBlob], 'plugin.js', { type: 'text/javascript' });
|
||||||
const fileUrl = URL.createObjectURL(blobFile);
|
const fileUrl = URL.createObjectURL(blobFile);
|
||||||
|
|
||||||
const module = await import(fileUrl);
|
const module = (await import(fileUrl)) as { default: { new (): Plugin } };
|
||||||
// Note: We have to put module.default in a variable
|
// Note: We have to put module.default in a variable
|
||||||
// because we have seen weird behaviour when doing
|
// because we have seen weird behaviour when doing
|
||||||
// new module.default() directly.
|
// new module.default() directly.
|
||||||
@@ -346,7 +348,7 @@ export class PyScriptApp {
|
|||||||
// 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 = interpreter._remote.interface.pyimport(modulename);
|
const module = interpreter._remote.interface.pyimport(modulename);
|
||||||
if (typeof module.plugin !== 'undefined') {
|
if (typeof module.plugin !== 'undefined') {
|
||||||
const py_plugin = module.plugin;
|
const py_plugin = module.plugin as PyProxy & { init(app: PyScriptApp): void };
|
||||||
py_plugin.init(this);
|
py_plugin.init(this);
|
||||||
this.plugins.addPythonPlugin(py_plugin);
|
this.plugins.addPythonPlugin(py_plugin);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access,
|
||||||
|
@typescript-eslint/no-unsafe-call,
|
||||||
|
@typescript-eslint/no-explicit-any,
|
||||||
|
@typescript-eslint/no-unsafe-assignment */
|
||||||
|
|
||||||
import type { AppConfig } from './pyconfig';
|
import type { AppConfig } from './pyconfig';
|
||||||
import type { UserError } from './exceptions';
|
import type { UserError } from './exceptions';
|
||||||
import { getLogger } from './logger';
|
import { getLogger } from './logger';
|
||||||
@@ -20,7 +25,9 @@ export class Plugin {
|
|||||||
* This hook should **NOT** contain expensive operations, else it delays
|
* This hook should **NOT** contain expensive operations, else it delays
|
||||||
* the download of the python interpreter which is initiated later.
|
* the download of the python interpreter which is initiated later.
|
||||||
*/
|
*/
|
||||||
configure(config: AppConfig) {}
|
configure(_config: AppConfig) {
|
||||||
|
/* empty */
|
||||||
|
}
|
||||||
|
|
||||||
/** The preliminary initialization phase is complete and we are about to
|
/** The preliminary initialization phase is complete and we are about to
|
||||||
* download and launch the Python interpreter.
|
* download and launch the Python interpreter.
|
||||||
@@ -32,14 +39,18 @@ export class Plugin {
|
|||||||
* This hook should **NOT** contain expensive operations, else it delays
|
* This hook should **NOT** contain expensive operations, else it delays
|
||||||
* the download of the python interpreter which is initiated later.
|
* the download of the python interpreter which is initiated later.
|
||||||
*/
|
*/
|
||||||
beforeLaunch(config: AppConfig) {}
|
beforeLaunch(_config: AppConfig) {
|
||||||
|
/* empty */
|
||||||
|
}
|
||||||
|
|
||||||
/** The Python interpreter has been launched, the virtualenv has been
|
/** The Python interpreter has been launched, the virtualenv has been
|
||||||
* installed and we are ready to execute user code.
|
* installed and we are ready to execute user code.
|
||||||
*
|
*
|
||||||
* The <py-script> tags will be executed after this hook.
|
* The <py-script> tags will be executed after this hook.
|
||||||
*/
|
*/
|
||||||
afterSetup(interpreter: InterpreterClient) {}
|
afterSetup(_interpreter: InterpreterClient) {
|
||||||
|
/* empty */
|
||||||
|
}
|
||||||
|
|
||||||
/** The source of a <py-script>> tag has been fetched, and we're about
|
/** The source of a <py-script>> tag has been fetched, and we're about
|
||||||
* to evaluate that source using the provided interpreter.
|
* to evaluate that source using the provided interpreter.
|
||||||
@@ -48,7 +59,9 @@ export class Plugin {
|
|||||||
* @param options.src {string} The Python source code to be evaluated
|
* @param options.src {string} The Python source code to be evaluated
|
||||||
* @param options.pyScriptTag The <py-script> HTML tag that originated the evaluation
|
* @param options.pyScriptTag The <py-script> HTML tag that originated the evaluation
|
||||||
*/
|
*/
|
||||||
beforePyScriptExec(options: { interpreter: InterpreterClient; src: string; pyScriptTag: PyScriptTag }) {}
|
beforePyScriptExec(_options: { interpreter: InterpreterClient; src: string; pyScriptTag: PyScriptTag }) {
|
||||||
|
/* empty */
|
||||||
|
}
|
||||||
|
|
||||||
/** The Python in a <py-script> has just been evaluated, but control
|
/** The Python in a <py-script> has just been evaluated, but control
|
||||||
* has not been ceded back to the JavaScript event loop yet
|
* has not been ceded back to the JavaScript event loop yet
|
||||||
@@ -58,22 +71,28 @@ export class Plugin {
|
|||||||
* @param options.pyScriptTag The <py-script> HTML tag that originated the evaluation
|
* @param options.pyScriptTag The <py-script> HTML tag that originated the evaluation
|
||||||
* @param options.result The returned result of evaluating the Python (if any)
|
* @param options.result The returned result of evaluating the Python (if any)
|
||||||
*/
|
*/
|
||||||
afterPyScriptExec(options: {
|
afterPyScriptExec(_options: {
|
||||||
interpreter: InterpreterClient;
|
interpreter: InterpreterClient;
|
||||||
src: string;
|
src: string;
|
||||||
pyScriptTag: PyScriptTag;
|
pyScriptTag: PyScriptTag;
|
||||||
result: any;
|
result: any;
|
||||||
}) {}
|
}) {
|
||||||
|
/* empty */
|
||||||
|
}
|
||||||
|
|
||||||
/** Startup complete. The interpreter is initialized and ready, user
|
/** Startup complete. The interpreter is initialized and ready, user
|
||||||
* scripts have been executed: the main initialization logic ends here and
|
* scripts have been executed: the main initialization logic ends here and
|
||||||
* the page is ready to accept user interactions.
|
* the page is ready to accept user interactions.
|
||||||
*/
|
*/
|
||||||
afterStartup(interpreter: InterpreterClient) {}
|
afterStartup(_interpreter: InterpreterClient) {
|
||||||
|
/* empty */
|
||||||
|
}
|
||||||
|
|
||||||
/** Called when an UserError is raised
|
/** Called when an UserError is raised
|
||||||
*/
|
*/
|
||||||
onUserError(error: UserError) {}
|
onUserError(_error: UserError) {
|
||||||
|
/* empty */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PluginManager {
|
export class PluginManager {
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ export class ImportmapPlugin extends Plugin {
|
|||||||
const importmap: ImportMapType = (() => {
|
const importmap: ImportMapType = (() => {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(node.textContent) as ImportMapType;
|
return JSON.parse(node.textContent) as ImportMapType;
|
||||||
} catch (error) {
|
} catch (e) {
|
||||||
|
const error = e as Error;
|
||||||
showWarning('Failed to parse import map: ' + error.message);
|
showWarning('Failed to parse import map: ' + error.message);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export class PyTerminalPlugin extends Plugin {
|
|||||||
this.app = app;
|
this.app = app;
|
||||||
}
|
}
|
||||||
|
|
||||||
configure(config: AppConfig) {
|
configure(config: AppConfig & { terminal?: boolean | 'auto' }) {
|
||||||
// validate the terminal config and handle default values
|
// validate the terminal config and handle default values
|
||||||
const t = config.terminal;
|
const t = config.terminal;
|
||||||
if (t !== undefined && t !== true && t !== false && t !== 'auto') {
|
if (t !== undefined && t !== true && t !== false && t !== 'auto') {
|
||||||
@@ -32,7 +32,7 @@ export class PyTerminalPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeLaunch(config: AppConfig) {
|
beforeLaunch(config: AppConfig & { terminal?: boolean | 'auto' }) {
|
||||||
// if config.terminal is "yes" or "auto", let's add a <py-terminal> to
|
// if config.terminal is "yes" or "auto", let's add a <py-terminal> to
|
||||||
// the document, unless it's already present.
|
// the document, unless it's already present.
|
||||||
const t = config.terminal;
|
const t = config.terminal;
|
||||||
@@ -46,7 +46,7 @@ export class PyTerminalPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
afterSetup(interpreter: InterpreterClient) {
|
afterSetup(_interpreter: InterpreterClient) {
|
||||||
// the Python interpreter has been initialized and we are ready to
|
// the Python interpreter has been initialized and we are ready to
|
||||||
// execute user code:
|
// execute user code:
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ export class SplashscreenPlugin extends Plugin {
|
|||||||
autoclose: boolean;
|
autoclose: boolean;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
|
||||||
configure(config: AppConfig) {
|
configure(
|
||||||
|
config: AppConfig & { splashscreen?: { autoclose?: boolean; enabled?: boolean }; autoclose_loader?: boolean },
|
||||||
|
) {
|
||||||
// the officially supported setting is config.splashscreen.autoclose,
|
// the officially supported setting is config.splashscreen.autoclose,
|
||||||
// but we still also support the old config.autoclose_loader (with a
|
// but we still also support the old config.autoclose_loader (with a
|
||||||
// deprecation warning)
|
// deprecation warning)
|
||||||
@@ -40,7 +42,7 @@ export class SplashscreenPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeLaunch(config: AppConfig) {
|
beforeLaunch(_config: AppConfig) {
|
||||||
if (!this.enabled) {
|
if (!this.enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -50,18 +52,18 @@ export class SplashscreenPlugin extends Plugin {
|
|||||||
this.elem = <PySplashscreen>document.createElement('py-splashscreen');
|
this.elem = <PySplashscreen>document.createElement('py-splashscreen');
|
||||||
document.body.append(this.elem);
|
document.body.append(this.elem);
|
||||||
document.addEventListener('py-status-message', (e: CustomEvent) => {
|
document.addEventListener('py-status-message', (e: CustomEvent) => {
|
||||||
const msg = e.detail;
|
const msg = e.detail as string;
|
||||||
this.elem.log(msg);
|
this.elem.log(msg);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
afterStartup(interpreter: InterpreterClient) {
|
afterStartup(_interpreter: InterpreterClient) {
|
||||||
if (this.autoclose && this.enabled) {
|
if (this.autoclose && this.enabled) {
|
||||||
this.elem.close();
|
this.elem.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onUserError(error: UserError) {
|
onUserError(_error: UserError) {
|
||||||
if (this.elem !== undefined && this.enabled) {
|
if (this.elem !== undefined && this.enabled) {
|
||||||
// Remove the splashscreen so users can see the banner better
|
// Remove the splashscreen so users can see the banner better
|
||||||
this.elem.close();
|
this.elem.close();
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export class StdioDirector extends Plugin {
|
|||||||
interpreter: InterpreterClient;
|
interpreter: InterpreterClient;
|
||||||
src: string;
|
src: string;
|
||||||
pyScriptTag: PyScriptTag;
|
pyScriptTag: PyScriptTag;
|
||||||
result: any;
|
result: any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||||
}): void {
|
}): void {
|
||||||
if (options.pyScriptTag.stdout_manager != null) {
|
if (options.pyScriptTag.stdout_manager != null) {
|
||||||
this._stdioMultiplexer.removeListener(options.pyScriptTag.stdout_manager);
|
this._stdioMultiplexer.removeListener(options.pyScriptTag.stdout_manager);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import toml from 'toml-j0.4';
|
import toml from '@hoodmane/toml-j0.4';
|
||||||
import { getLogger } from './logger';
|
import { getLogger } from './logger';
|
||||||
import { version } from './version';
|
import { version } from './version';
|
||||||
import { getAttribute, readTextFromPath, htmlDecode, createDeprecationWarning } from './utils';
|
import { getAttribute, readTextFromPath, htmlDecode, createDeprecationWarning } from './utils';
|
||||||
@@ -6,6 +6,7 @@ import { UserError, ErrorCode } from './exceptions';
|
|||||||
|
|
||||||
const logger = getLogger('py-config');
|
const logger = getLogger('py-config');
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export interface AppConfig extends Record<string, any> {
|
export interface AppConfig extends Record<string, any> {
|
||||||
name?: string;
|
name?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
@@ -42,11 +43,11 @@ export type PyScriptMetadata = {
|
|||||||
time?: string;
|
time?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const allKeys = {
|
const allKeys = Object.entries({
|
||||||
string: ['name', 'description', 'version', 'type', 'author_name', 'author_email', 'license'],
|
string: ['name', 'description', 'version', 'type', 'author_name', 'author_email', 'license'],
|
||||||
number: ['schema_version'],
|
number: ['schema_version'],
|
||||||
array: ['runtimes', 'interpreters', 'packages', 'fetch', 'plugins'],
|
array: ['runtimes', 'interpreters', 'packages', 'fetch', 'plugins'],
|
||||||
};
|
});
|
||||||
|
|
||||||
export const defaultConfig: AppConfig = {
|
export const defaultConfig: AppConfig = {
|
||||||
schema_version: 1,
|
schema_version: 1,
|
||||||
@@ -106,6 +107,7 @@ function fillUserData(inputConfig: AppConfig, resultConfig: AppConfig): AppConfi
|
|||||||
for (const key in inputConfig) {
|
for (const key in inputConfig) {
|
||||||
// fill in all extra keys ignored by the validator
|
// fill in all extra keys ignored by the validator
|
||||||
if (!(key in defaultConfig)) {
|
if (!(key in defaultConfig)) {
|
||||||
|
// eslint-disable-next-line
|
||||||
resultConfig[key] = inputConfig[key];
|
resultConfig[key] = inputConfig[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,13 +124,14 @@ function mergeConfig(inlineConfig: AppConfig, externalConfig: AppConfig): AppCon
|
|||||||
} else {
|
} else {
|
||||||
let merged: AppConfig = {};
|
let merged: AppConfig = {};
|
||||||
|
|
||||||
for (const keyType in allKeys) {
|
for (const [keyType, keys] of allKeys) {
|
||||||
const keys: string[] = allKeys[keyType];
|
|
||||||
keys.forEach(function (item: string) {
|
keys.forEach(function (item: string) {
|
||||||
if (keyType === 'boolean') {
|
if (keyType === 'boolean') {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
merged[item] =
|
merged[item] =
|
||||||
typeof inlineConfig[item] !== 'undefined' ? inlineConfig[item] : externalConfig[item];
|
typeof inlineConfig[item] !== 'undefined' ? inlineConfig[item] : externalConfig[item];
|
||||||
} else {
|
} else {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
merged[item] = inlineConfig[item] || externalConfig[item];
|
merged[item] = inlineConfig[item] || externalConfig[item];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -143,7 +146,7 @@ function mergeConfig(inlineConfig: AppConfig, externalConfig: AppConfig): AppCon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseConfig(configText: string, configType = 'toml') {
|
function parseConfig(configText: string, configType = 'toml'): AppConfig {
|
||||||
if (configType === 'toml') {
|
if (configType === 'toml') {
|
||||||
// TOML parser is soft and can parse even JSON strings, this additional check prevents it.
|
// TOML parser is soft and can parse even JSON strings, this additional check prevents it.
|
||||||
if (configText.trim()[0] === '{') {
|
if (configText.trim()[0] === '{') {
|
||||||
@@ -153,8 +156,9 @@ function parseConfig(configText: string, configType = 'toml') {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return toml.parse(configText);
|
return toml.parse(configText) as AppConfig;
|
||||||
} catch (err) {
|
} catch (e) {
|
||||||
|
const err = e as Error;
|
||||||
const errMessage: string = err.toString();
|
const errMessage: string = err.toString();
|
||||||
|
|
||||||
throw new UserError(
|
throw new UserError(
|
||||||
@@ -164,8 +168,9 @@ function parseConfig(configText: string, configType = 'toml') {
|
|||||||
}
|
}
|
||||||
} else if (configType === 'json') {
|
} else if (configType === 'json') {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(configText);
|
return JSON.parse(configText) as AppConfig;
|
||||||
} catch (err) {
|
} catch (e) {
|
||||||
|
const err = e as Error;
|
||||||
const errMessage: string = err.toString();
|
const errMessage: string = err.toString();
|
||||||
throw new UserError(
|
throw new UserError(
|
||||||
ErrorCode.BAD_CONFIG,
|
ErrorCode.BAD_CONFIG,
|
||||||
@@ -185,17 +190,17 @@ function validateConfig(configText: string, configType = 'toml') {
|
|||||||
|
|
||||||
const finalConfig: AppConfig = {};
|
const finalConfig: AppConfig = {};
|
||||||
|
|
||||||
for (const keyType in allKeys) {
|
for (const [keyType, keys] of allKeys) {
|
||||||
const keys: string[] = allKeys[keyType];
|
|
||||||
keys.forEach(function (item: string) {
|
keys.forEach(function (item: string) {
|
||||||
if (validateParamInConfig(item, keyType, config)) {
|
if (validateParamInConfig(item, keyType, config)) {
|
||||||
if (item === 'interpreters') {
|
if (item === 'interpreters') {
|
||||||
finalConfig[item] = [];
|
finalConfig[item] = [];
|
||||||
const interpreters = config[item] as InterpreterConfig[];
|
const interpreters = config[item];
|
||||||
interpreters.forEach(function (eachInterpreter: InterpreterConfig) {
|
interpreters.forEach(function (eachInterpreter: InterpreterConfig) {
|
||||||
const interpreterConfig: InterpreterConfig = {};
|
const interpreterConfig: InterpreterConfig = {};
|
||||||
for (const eachInterpreterParam in eachInterpreter) {
|
for (const eachInterpreterParam in eachInterpreter) {
|
||||||
if (validateParamInConfig(eachInterpreterParam, 'string', eachInterpreter)) {
|
if (validateParamInConfig(eachInterpreterParam, 'string', eachInterpreter)) {
|
||||||
|
// eslint-disable-next-line
|
||||||
interpreterConfig[eachInterpreterParam] = eachInterpreter[eachInterpreterParam];
|
interpreterConfig[eachInterpreterParam] = eachInterpreter[eachInterpreterParam];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -214,11 +219,12 @@ function validateConfig(configText: string, configType = 'toml') {
|
|||||||
'',
|
'',
|
||||||
);
|
);
|
||||||
finalConfig['interpreters'] = [];
|
finalConfig['interpreters'] = [];
|
||||||
const interpreters = config[item] as InterpreterConfig[];
|
const interpreters = config[item];
|
||||||
interpreters.forEach(function (eachInterpreter: InterpreterConfig) {
|
interpreters.forEach(function (eachInterpreter: InterpreterConfig) {
|
||||||
const interpreterConfig: InterpreterConfig = {};
|
const interpreterConfig: InterpreterConfig = {};
|
||||||
for (const eachInterpreterParam in eachInterpreter) {
|
for (const eachInterpreterParam in eachInterpreter) {
|
||||||
if (validateParamInConfig(eachInterpreterParam, 'string', eachInterpreter)) {
|
if (validateParamInConfig(eachInterpreterParam, 'string', eachInterpreter)) {
|
||||||
|
// eslint-disable-next-line
|
||||||
interpreterConfig[eachInterpreterParam] = eachInterpreter[eachInterpreterParam];
|
interpreterConfig[eachInterpreterParam] = eachInterpreter[eachInterpreterParam];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,18 +232,20 @@ function validateConfig(configText: string, configType = 'toml') {
|
|||||||
});
|
});
|
||||||
} else if (item === 'fetch') {
|
} else if (item === 'fetch') {
|
||||||
finalConfig[item] = [];
|
finalConfig[item] = [];
|
||||||
const fetchList = config[item] as FetchConfig[];
|
const fetchList = config[item];
|
||||||
fetchList.forEach(function (eachFetch: FetchConfig) {
|
fetchList.forEach(function (eachFetch: FetchConfig) {
|
||||||
const eachFetchConfig: FetchConfig = {};
|
const eachFetchConfig: FetchConfig = {};
|
||||||
for (const eachFetchConfigParam in eachFetch) {
|
for (const eachFetchConfigParam in eachFetch) {
|
||||||
const targetType = eachFetchConfigParam === 'files' ? 'array' : 'string';
|
const targetType = eachFetchConfigParam === 'files' ? 'array' : 'string';
|
||||||
if (validateParamInConfig(eachFetchConfigParam, targetType, eachFetch)) {
|
if (validateParamInConfig(eachFetchConfigParam, targetType, eachFetch)) {
|
||||||
|
// eslint-disable-next-line
|
||||||
eachFetchConfig[eachFetchConfigParam] = eachFetch[eachFetchConfigParam];
|
eachFetchConfig[eachFetchConfigParam] = eachFetch[eachFetchConfigParam];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finalConfig[item].push(eachFetchConfig);
|
finalConfig[item].push(eachFetchConfig);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
// eslint-disable-next-line
|
||||||
finalConfig[item] = config[item];
|
finalConfig[item] = config[item];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,20 @@ import { getLogger } from './logger';
|
|||||||
import { ensureUniqueId } from './utils';
|
import { ensureUniqueId } from './utils';
|
||||||
import { UserError, ErrorCode } from './exceptions';
|
import { UserError, ErrorCode } from './exceptions';
|
||||||
import { InterpreterClient } from './interpreter_client';
|
import { InterpreterClient } from './interpreter_client';
|
||||||
|
import type { PyProxy, PyProxyCallable } from 'pyodide';
|
||||||
|
|
||||||
const logger = getLogger('pyexec');
|
const logger = getLogger('pyexec');
|
||||||
|
|
||||||
export async function pyExec(interpreter: InterpreterClient, pysrc: string, outElem: HTMLElement) {
|
export async function pyExec(
|
||||||
//This is pyscript.py
|
interpreter: InterpreterClient,
|
||||||
const pyscript_py = interpreter._remote.interface.pyimport('pyscript');
|
pysrc: string,
|
||||||
|
outElem: HTMLElement,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
): Promise<{ result: any }> {
|
||||||
|
const pyscript_py = interpreter._remote.interface.pyimport('pyscript') as PyProxy & {
|
||||||
|
set_current_display_target(id: string): void;
|
||||||
|
uses_top_level_await(code: string): boolean;
|
||||||
|
};
|
||||||
ensureUniqueId(outElem);
|
ensureUniqueId(outElem);
|
||||||
pyscript_py.set_current_display_target(outElem.id);
|
pyscript_py.set_current_display_target(outElem.id);
|
||||||
try {
|
try {
|
||||||
@@ -23,7 +31,8 @@ export async function pyExec(interpreter: InterpreterClient, pysrc: string, outE
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return await interpreter.run(pysrc);
|
return await interpreter.run(pysrc);
|
||||||
} catch (err) {
|
} catch (e) {
|
||||||
|
const err = e as Error;
|
||||||
// XXX: currently we display exceptions in the same position as
|
// XXX: currently we display exceptions in the same position as
|
||||||
// the output. But we probably need a better way to do that,
|
// the output. But we probably need a better way to do that,
|
||||||
// e.g. allowing plugins to intercept exceptions and display them
|
// e.g. allowing plugins to intercept exceptions and display them
|
||||||
@@ -44,15 +53,17 @@ export async function pyExec(interpreter: InterpreterClient, pysrc: string, outE
|
|||||||
* pyDisplay(interpreter, obj);
|
* pyDisplay(interpreter, obj);
|
||||||
* pyDisplay(interpreter, obj, { target: targetID });
|
* pyDisplay(interpreter, obj, { target: targetID });
|
||||||
*/
|
*/
|
||||||
export function pyDisplay(interpreter: InterpreterClient, obj: any, kwargs: object) {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const display = interpreter.globals.get('display');
|
export function pyDisplay(interpreter: InterpreterClient, obj: any, kwargs: { [k: string]: any } = {}) {
|
||||||
if (kwargs === undefined) display(obj);
|
const display = interpreter.globals.get('display') as PyProxyCallable;
|
||||||
else {
|
try {
|
||||||
display.callKwargs(obj, kwargs);
|
display.callKwargs(obj, kwargs);
|
||||||
|
} finally {
|
||||||
|
display.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function displayPyException(err: any, errElem: HTMLElement) {
|
export function displayPyException(err: Error, errElem: HTMLElement) {
|
||||||
//addClasses(errElem, ['py-error'])
|
//addClasses(errElem, ['py-error'])
|
||||||
const pre = document.createElement('pre');
|
const pre = document.createElement('pre');
|
||||||
pre.className = 'py-error';
|
pre.className = 'py-error';
|
||||||
@@ -65,8 +76,8 @@ export function displayPyException(err: any, errElem: HTMLElement) {
|
|||||||
} else {
|
} else {
|
||||||
// this is very likely a normal JS exception. The best we can do is to
|
// this is very likely a normal JS exception. The best we can do is to
|
||||||
// display it as is.
|
// display it as is.
|
||||||
logger.error('Non-python exception:\n' + err);
|
logger.error('Non-python exception:\n' + err.toString());
|
||||||
pre.innerText = err;
|
pre.innerText = err.toString();
|
||||||
}
|
}
|
||||||
errElem.appendChild(pre);
|
errElem.appendChild(pre);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,10 +11,23 @@ const logger = getLogger('pyscript/pyodide');
|
|||||||
export type InterpreterInterface = PyodideInterface | null;
|
export type InterpreterInterface = PyodideInterface | null;
|
||||||
|
|
||||||
interface Micropip extends PyProxy {
|
interface Micropip extends PyProxy {
|
||||||
install: (packageName: string | string[]) => Promise<void>;
|
install(packageName: string | string[]): Promise<void>;
|
||||||
destroy: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FSInterface = {
|
||||||
|
writeFile(path: string, data: Uint8Array | string, options?: { canOwn: boolean }): void;
|
||||||
|
mkdirTree(path: string): void;
|
||||||
|
mkdir(path: string): void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PATHFSInterface = {
|
||||||
|
resolve(path: string): string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PATHInterface = {
|
||||||
|
dirname(path: string): string;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
RemoteInterpreter class is responsible to process requests from the
|
RemoteInterpreter class is responsible to process requests from the
|
||||||
`InterpreterClient` class -- these can be requests for installation of
|
`InterpreterClient` class -- these can be requests for installation of
|
||||||
@@ -33,6 +46,10 @@ such as MicroPython.
|
|||||||
export class RemoteInterpreter extends Object {
|
export class RemoteInterpreter extends Object {
|
||||||
src: string;
|
src: string;
|
||||||
interface: InterpreterInterface;
|
interface: InterpreterInterface;
|
||||||
|
FS: FSInterface;
|
||||||
|
PATH: PATHInterface;
|
||||||
|
PATH_FS: PATHFSInterface;
|
||||||
|
|
||||||
globals: PyProxy;
|
globals: PyProxy;
|
||||||
// TODO: Remove this once `runtimes` is removed!
|
// TODO: Remove this once `runtimes` is removed!
|
||||||
interpreter: InterpreterInterface;
|
interpreter: InterpreterInterface;
|
||||||
@@ -74,6 +91,12 @@ export class RemoteInterpreter extends Object {
|
|||||||
},
|
},
|
||||||
fullStdLib: false,
|
fullStdLib: false,
|
||||||
});
|
});
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
this.FS = this.interface.FS;
|
||||||
|
// eslint-disable-next-line
|
||||||
|
this.PATH = (this.interface as any)._module.PATH;
|
||||||
|
// eslint-disable-next-line
|
||||||
|
this.PATH_FS = (this.interface as any)._module.PATH_FS;
|
||||||
|
|
||||||
// TODO: Remove this once `runtimes` is removed!
|
// TODO: Remove this once `runtimes` is removed!
|
||||||
this.interpreter = this.interface;
|
this.interpreter = this.interface;
|
||||||
@@ -131,14 +154,15 @@ export class RemoteInterpreter extends Object {
|
|||||||
// but one of our tests tries to use a locally downloaded older version of pyodide
|
// but one of our tests tries to use a locally downloaded older version of pyodide
|
||||||
// for which the signature of `loadPackage` accepts the above params as args i.e.
|
// for which the signature of `loadPackage` accepts the above params as args i.e.
|
||||||
// the call uses `logger.info.bind(logger), logger.info.bind(logger)`.
|
// the call uses `logger.info.bind(logger), logger.info.bind(logger)`.
|
||||||
const pyodide_version = (await this.run("import sys; sys.modules['pyodide'].__version__")).result.toString();
|
const messageCallback = logger.info.bind(logger) as typeof logger.info;
|
||||||
if (pyodide_version.startsWith('0.22')) {
|
if (this.interpreter.version.startsWith('0.22')) {
|
||||||
await this.interface.loadPackage(names, {
|
await this.interface.loadPackage(names, {
|
||||||
messageCallback: logger.info.bind(logger),
|
messageCallback,
|
||||||
errorCallback: logger.info.bind(logger),
|
errorCallback: messageCallback,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await this.interface.loadPackage(names, logger.info.bind(logger), logger.info.bind(logger));
|
// @ts-expect-error Types don't include this deprecated call signature
|
||||||
|
await this.interface.loadPackage(names, messageCallback, messageCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,8 +181,15 @@ export class RemoteInterpreter extends Object {
|
|||||||
try {
|
try {
|
||||||
await micropip.install(package_name);
|
await micropip.install(package_name);
|
||||||
micropip.destroy();
|
micropip.destroy();
|
||||||
} catch (e) {
|
} catch (err) {
|
||||||
let exceptionMessage = `Unable to install package(s) '` + package_name + `'.`;
|
const e = err as Error;
|
||||||
|
let fmt_names: string;
|
||||||
|
if (Array.isArray(package_name)) {
|
||||||
|
fmt_names = package_name.join(', ');
|
||||||
|
} else {
|
||||||
|
fmt_names = package_name;
|
||||||
|
}
|
||||||
|
let exceptionMessage = `Unable to install package(s) '${fmt_names}'.`;
|
||||||
|
|
||||||
// If we can't fetch `package_name` micropip.install throws a huge
|
// If we can't fetch `package_name` micropip.install throws a huge
|
||||||
// Python traceback in `e.message` this logic is to handle the
|
// Python traceback in `e.message` this logic is to handle the
|
||||||
@@ -166,9 +197,8 @@ export class RemoteInterpreter extends Object {
|
|||||||
// huge traceback.
|
// huge traceback.
|
||||||
if (e.message.includes("Can't find a pure Python 3 wheel")) {
|
if (e.message.includes("Can't find a pure Python 3 wheel")) {
|
||||||
exceptionMessage +=
|
exceptionMessage +=
|
||||||
` Reason: Can't find a pure Python 3 Wheel for package(s) '` +
|
` Reason: Can't find a pure Python 3 Wheel for package(s) '${fmt_names}'.` +
|
||||||
package_name +
|
`See: https://pyodide.org/en/stable/usage/faq.html#micropip-can-t-find-a-pure-python-wheel ` +
|
||||||
`'. See: https://pyodide.org/en/stable/usage/faq.html#micropip-can-t-find-a-pure-python-wheel ` +
|
|
||||||
`for more information.`;
|
`for more information.`;
|
||||||
} else if (e.message.includes("Can't fetch metadata")) {
|
} else if (e.message.includes("Can't fetch metadata")) {
|
||||||
exceptionMessage +=
|
exceptionMessage +=
|
||||||
@@ -176,7 +206,7 @@ export class RemoteInterpreter extends Object {
|
|||||||
'Please make sure you have entered a correct package name.';
|
'Please make sure you have entered a correct package name.';
|
||||||
} else {
|
} else {
|
||||||
exceptionMessage +=
|
exceptionMessage +=
|
||||||
` Reason: ${e.message as string}. Please open an issue at ` +
|
` Reason: ${e.message}. Please open an issue at ` +
|
||||||
`https://github.com/pyscript/pyscript/issues/new if you require help or ` +
|
`https://github.com/pyscript/pyscript/issues/new if you require help or ` +
|
||||||
`you think it's a bug.`;
|
`you think it's a bug.`;
|
||||||
}
|
}
|
||||||
@@ -212,20 +242,16 @@ export class RemoteInterpreter extends Object {
|
|||||||
* and `/a/b.py` will be placed into `/a/b.py`.
|
* and `/a/b.py` will be placed into `/a/b.py`.
|
||||||
*/
|
*/
|
||||||
async loadFromFile(path: string, fetch_path: string): Promise<void> {
|
async loadFromFile(path: string, fetch_path: string): Promise<void> {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
path = this.PATH_FS.resolve(path);
|
||||||
// @ts-ignore
|
const dir: string = this.PATH.dirname(path);
|
||||||
path = this.interface._module.PATH_FS.resolve(path);
|
this.FS.mkdirTree(dir);
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
const dir = this.interface._module.PATH.dirname(path);
|
|
||||||
this.interface.FS.mkdirTree(dir);
|
|
||||||
|
|
||||||
// `robustFetch` checks for failures in getting a response
|
// `robustFetch` checks for failures in getting a response
|
||||||
const response = await robustFetch(fetch_path);
|
const response = await robustFetch(fetch_path);
|
||||||
const buffer = await response.arrayBuffer();
|
const buffer = await response.arrayBuffer();
|
||||||
const data = new Uint8Array(buffer);
|
const data = new Uint8Array(buffer);
|
||||||
|
|
||||||
this.interface.FS.writeFile(path, data, { canOwn: true });
|
this.FS.writeFile(path, data, { canOwn: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -233,7 +259,7 @@ export class RemoteInterpreter extends Object {
|
|||||||
* caches to the underlying interface
|
* caches to the underlying interface
|
||||||
*/
|
*/
|
||||||
invalidate_module_path_cache(): void {
|
invalidate_module_path_cache(): void {
|
||||||
const importlib = this.interface.pyimport('importlib');
|
const importlib = this.interface.pyimport('importlib') as PyProxy & { invalidate_caches(): void };
|
||||||
importlib.invalidate_caches();
|
importlib.invalidate_caches();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ export function createSingularWarning(msg: string, sentinelText: string | null =
|
|||||||
* @returns A new asynchronous lock
|
* @returns A new asynchronous lock
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
export function createLock() {
|
export function createLock(): () => Promise<() => void> {
|
||||||
// This is a promise that is resolved when the lock is open, not resolved when lock is held.
|
// This is a promise that is resolved when the lock is open, not resolved when lock is held.
|
||||||
let _lock = Promise.resolve();
|
let _lock = Promise.resolve();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user