Comparing version
110
api.ts
@@ -36,18 +36,18 @@ import { Module, API, Hiwire } from "./module"; | ||
/** | ||
* Runs a string of Python code from JavaScript. | ||
* Runs a string of Python code from JavaScript, using :any:`pyodide.eval_code` | ||
* to evaluate the code. If the last statement in the Python code is an | ||
* expression (and the code doesn't end with a semicolon), the value of the | ||
* expression is returned. | ||
* | ||
* The last part of the string may be an expression, in which case, its value is | ||
* returned. | ||
* .. admonition:: Positional globals argument | ||
* :class: warning | ||
* | ||
* .. admonition:: Positional globals argument :class: warning | ||
* In Pyodide v0.19, this function took the globals parameter as a positional | ||
* argument rather than as a named argument. In v0.20 this will still work | ||
* but it is deprecated. It will be removed in v0.21. | ||
* | ||
* In Pyodide v0.19, this function took the globals parameter as a | ||
* positional argument rather than as a named argument. In v0.20 this will | ||
* still work but it is deprecated. It will be removed in v0.21. | ||
* | ||
* @param code Python code to evaluate | ||
* @param options | ||
* @param options.globals An optional Python dictionary to use as the globals. | ||
* Defaults to :any:`pyodide.globals`. Uses the Python API | ||
* :any:`pyodide.eval_code` to evaluate the code. | ||
* Defaults to :any:`pyodide.globals`. | ||
* @returns The result of the Python code translated to JavaScript. See the | ||
@@ -64,3 +64,3 @@ * documentation for :any:`pyodide.eval_code` for more info. | ||
console.warn( | ||
"Passing a PyProxy as the second argument to runPython is deprecated. Use 'runPython(code, {globals : some_dict})' instead." | ||
"Passing a PyProxy as the second argument to runPython is deprecated and will be removed in v0.21. Use 'runPython(code, {globals : some_dict})' instead." | ||
); | ||
@@ -127,4 +127,7 @@ runPythonPositionalGlobalsDeprecationWarned = true; | ||
/** | ||
* Runs Python code using `PyCF_ALLOW_TOP_LEVEL_AWAIT | ||
* <https://docs.python.org/3/library/ast.html?highlight=pycf_allow_top_level_await#ast.PyCF_ALLOW_TOP_LEVEL_AWAIT>`_. | ||
* Run a Python code string with top level await using | ||
* :any:`pyodide.eval_code_async` to evaluate the code. Returns a promise which | ||
* resolves when execution completes. If the last statement in the Python code | ||
* is an expression (and the code doesn't end with a semicolon), the returned | ||
* promise will resolve to the value of this expression. | ||
* | ||
@@ -144,3 +147,4 @@ * For example: | ||
* | ||
* .. admonition:: Python imports :class: warning | ||
* .. admonition:: Python imports | ||
* :class: warning | ||
* | ||
@@ -151,3 +155,4 @@ * Since pyodide 0.18.0, you must call :js:func:`loadPackagesFromImports` to | ||
* | ||
* .. admonition:: Positional globals argument :class: warning | ||
* .. admonition:: Positional globals argument | ||
* :class: warning | ||
* | ||
@@ -161,4 +166,3 @@ * In Pyodide v0.19, this function took the globals parameter as a | ||
* @param options.globals An optional Python dictionary to use as the globals. | ||
* Defaults to :any:`pyodide.globals`. Uses the Python API | ||
* :any:`pyodide.eval_code_async` to evaluate the code. | ||
* Defaults to :any:`pyodide.globals`. | ||
* @returns The result of the Python code translated to JavaScript. | ||
@@ -175,3 +179,3 @@ * @async | ||
console.warn( | ||
"Passing a PyProxy as the second argument to runPython is deprecated. Use 'runPythonAsync(code, {globals : some_dict})' instead." | ||
"Passing a PyProxy as the second argument to runPythonAsync is deprecated and will be removed in v0.21. Use 'runPythonAsync(code, {globals : some_dict})' instead." | ||
); | ||
@@ -332,11 +336,22 @@ runPythonPositionalGlobalsDeprecationWarned = true; | ||
let unpackArchivePositionalExtractDirDeprecationWarned = false; | ||
/** | ||
* Unpack an archive into a target directory. | ||
* | ||
* .. admonition:: Positional globals argument :class: warning | ||
* | ||
* In Pyodide v0.19, this function took the extract_dir parameter as a | ||
* positional argument rather than as a named argument. In v0.20 this will | ||
* still work but it is deprecated. It will be removed in v0.21. | ||
* | ||
* @param buffer The archive as an ArrayBuffer or TypedArray. | ||
* @param format The format of the archive. Should be one of the formats recognized by `shutil.unpack_archive`. | ||
* By default the options are 'bztar', 'gztar', 'tar', 'zip', and 'wheel'. Several synonyms are accepted for each format, e.g., | ||
* for 'gztar' any of '.gztar', '.tar.gz', '.tgz', 'tar.gz' or 'tgz' are considered to be synonyms. | ||
* @param format The format of the archive. Should be one of the formats | ||
* recognized by `shutil.unpack_archive`. By default the options are 'bztar', | ||
* 'gztar', 'tar', 'zip', and 'wheel'. Several synonyms are accepted for each | ||
* format, e.g., for 'gztar' any of '.gztar', '.tar.gz', '.tgz', 'tar.gz' or | ||
* 'tgz' are considered to be synonyms. | ||
* | ||
* @param extract_dir The directory to unpack the archive into. Defaults to the working directory. | ||
* @param options | ||
* @param options.extractDir The directory to unpack the archive into. Defaults | ||
* to the working directory. | ||
*/ | ||
@@ -346,8 +361,18 @@ export function unpackArchive( | ||
format: string, | ||
extract_dir?: string | ||
options: { | ||
extractDir?: string; | ||
} = {} | ||
) { | ||
if (!API._util_module) { | ||
API._util_module = pyimport("pyodide._util"); | ||
if (typeof options === "string") { | ||
if (!unpackArchivePositionalExtractDirDeprecationWarned) { | ||
console.warn( | ||
"Passing a string as the third argument to unpackArchive is deprecated and will be removed in v0.21. Instead use { extract_dir : 'some_path' }" | ||
); | ||
unpackArchivePositionalExtractDirDeprecationWarned = true; | ||
} | ||
options = { extractDir: options }; | ||
} | ||
API._util_module.unpack_buffer_archive.callKwargs(buffer, { | ||
let extract_dir = options.extractDir; | ||
API.package_loader.unpack_buffer.callKwargs({ | ||
buffer, | ||
format, | ||
@@ -369,11 +394,24 @@ extract_dir, | ||
/** | ||
* Sets the interrupt buffer to be `interrupt_buffer`. This is only useful when | ||
* Pyodide is used in a webworker. The buffer should be a `SharedArrayBuffer` | ||
* shared with the main browser thread (or another worker). To request an | ||
* interrupt, a `2` should be written into `interrupt_buffer` (2 is the posix | ||
* constant for SIGINT). | ||
* Sets the interrupt buffer to be ``interrupt_buffer``. This is only useful | ||
* when Pyodide is used in a webworker. The buffer should be a | ||
* ``SharedArrayBuffer`` shared with the main browser thread (or another | ||
* worker). In that case, signal ``signum`` may be sent by writing ``signum`` | ||
* into the interrupt buffer. If ``signum`` does not satisfy 0 < ``signum`` < | ||
* ``NSIG`` it will be silently ignored. NSIG is 65 (internally signals are | ||
* indicated by a bitflag). | ||
* | ||
* You can disable interrupts by calling `setInterruptBuffer(undefined)`. | ||
* | ||
* If you wish to trigger a ``KeyboardInterrupt``, write ``SIGINT`` (a 2), into | ||
* the interrupt buffer. | ||
* | ||
* By default ``SIGINT`` raises a ``KeyboardInterrupt`` and all other signals | ||
* are ignored. You can install custom signal handlers with the signal module. | ||
* Even signals that normally have special meaning and can't be overridden like | ||
* ``SIGKILL`` and ``SIGSEGV`` are ignored by default and can be used for any | ||
* purpose you like. | ||
*/ | ||
export function setInterruptBuffer(interrupt_buffer: TypedArray) { | ||
API.interrupt_buffer = interrupt_buffer; | ||
Module._set_pyodide_callback(!!interrupt_buffer); | ||
Module.HEAP8[Module._Py_EMSCRIPTEN_SIGNAL_HANDLING] = !!interrupt_buffer; | ||
Module.Py_EmscriptenSignalBuffer = interrupt_buffer; | ||
} | ||
@@ -390,6 +428,4 @@ | ||
export function checkInterrupt() { | ||
if (API.interrupt_buffer[0] === 2) { | ||
API.interrupt_buffer[0] = 0; | ||
Module._PyErr_SetInterrupt(); | ||
API.runPython(""); | ||
if (Module.__PyErr_CheckSignals()) { | ||
Module._pythonexc2js(); | ||
} | ||
@@ -396,0 +432,0 @@ } |
@@ -14,2 +14,34 @@ import ErrorStackParser from "error-stack-parser"; | ||
function ensureCaughtObjectIsError(e: any) { | ||
if (typeof e === "string") { | ||
// Sometimes emscripten throws a raw string... | ||
e = new Error(e); | ||
} else if ( | ||
typeof e !== "object" || | ||
e === null || | ||
typeof e.stack !== "string" || | ||
typeof e.message !== "string" | ||
) { | ||
// We caught something really weird. Be brave! | ||
let msg = `A value of type ${typeof e} with tag ${Object.prototype.toString.call( | ||
e | ||
)} was thrown as an error!`; | ||
try { | ||
msg += `\nString interpolation of the thrown value gives """${e}""".`; | ||
} catch (e) { | ||
msg += `\nString interpolation of the thrown value fails.`; | ||
} | ||
try { | ||
msg += `\nThe thrown value's toString method returns """${e.toString()}""".`; | ||
} catch (e) { | ||
msg += `\nThe thrown value's toString method fails.`; | ||
} | ||
e = new Error(msg); | ||
} | ||
// Post conditions: | ||
// 1. typeof e is object | ||
// 2. hiwire_is_error(e) returns true | ||
return e; | ||
} | ||
let fatal_error_occurred = false; | ||
@@ -29,3 +61,3 @@ /** | ||
API.fatal_error = function (e: any) { | ||
if (e.pyodide_fatal_error) { | ||
if (e && e.pyodide_fatal_error) { | ||
return; | ||
@@ -39,10 +71,6 @@ } | ||
if (typeof e === "number") { | ||
// A C++ exception. Have to do some conversion work. | ||
// Hopefully a C++ exception? Have to do some conversion work. | ||
e = convertCppException(e); | ||
} else if (typeof e === "string") { | ||
e = new Error(e); | ||
} else if (typeof e !== "object") { | ||
e = new Error( | ||
`An object of type ${typeof e} was thrown as an error. toString returns ${e.toString()}` | ||
); | ||
} else { | ||
e = ensureCaughtObjectIsError(e); | ||
} | ||
@@ -199,3 +227,3 @@ // Mark e so we know not to handle it later in EM_JS wrappers | ||
Module.handle_js_error = function (e: any) { | ||
if (e.pyodide_fatal_error) { | ||
if (e && e.pyodide_fatal_error) { | ||
throw e; | ||
@@ -214,2 +242,12 @@ } | ||
} | ||
let stack: any; | ||
let weirdCatch; | ||
try { | ||
stack = ErrorStackParser.parse(e); | ||
} catch (_) { | ||
weirdCatch = true; | ||
} | ||
if (weirdCatch) { | ||
e = ensureCaughtObjectIsError(e); | ||
} | ||
if (!restored_error) { | ||
@@ -223,3 +261,6 @@ // Wrap the JavaScript error | ||
} | ||
let stack = ErrorStackParser.parse(e); | ||
if (weirdCatch) { | ||
// In this case we have no stack frames so we can quit | ||
return; | ||
} | ||
if (isErrorStart(stack[0])) { | ||
@@ -226,0 +267,0 @@ while (isPyodideFrame(stack[0])) { |
@@ -162,2 +162,7 @@ import { expectType, expectAssignable } from "tsd"; | ||
} | ||
pyodide.unpackArchive(new Uint8Array(40), "tar"); | ||
pyodide.unpackArchive(new Uint8Array(40), "tar", { | ||
extractDir: "/some/path", | ||
}); | ||
} |
@@ -1,2 +0,2 @@ | ||
import { Module, API } from "./module.js"; | ||
import { Module, API, Tests } from "./module.js"; | ||
import { IN_NODE, nodeFsPromisesMod, _loadBinaryFile } from "./compat.js"; | ||
@@ -174,9 +174,10 @@ import { PyProxy, isPyProxy } from "./pyproxy.gen"; | ||
} | ||
const file_name = pkg.file_name; | ||
const filename = pkg.file_name; | ||
// This Python helper function unpacks the buffer and lists out any so files therein. | ||
const dynlibs = API.package_loader.unpack_buffer( | ||
file_name, | ||
const dynlibs = API.package_loader.unpack_buffer.callKwargs({ | ||
buffer, | ||
pkg.install_dir | ||
); | ||
filename, | ||
target: pkg.install_dir, | ||
calculate_dynlibs: true, | ||
}); | ||
for (const dynlib of dynlibs) { | ||
@@ -252,2 +253,10 @@ await loadDynlib(dynlib, pkg.shared_library); | ||
} | ||
} catch (e) { | ||
if (e.message.includes("need to see wasm magic number")) { | ||
console.warn( | ||
`Failed to load dynlib ${lib}. We probably just tried to load a linux .so file or something.` | ||
); | ||
return; | ||
} | ||
throw e; | ||
} finally { | ||
@@ -257,2 +266,3 @@ releaseDynlibLock(); | ||
} | ||
Tests.loadDynlib = loadDynlib; | ||
@@ -359,2 +369,3 @@ const acquirePackageLock = createLock(); | ||
.catch((err) => { | ||
console.warn(err); | ||
failed[name] = err; | ||
@@ -373,2 +384,3 @@ }); | ||
.catch((err) => { | ||
console.warn(err); | ||
failed[name] = err; | ||
@@ -375,0 +387,0 @@ }); |
{ | ||
"name": "pyodide", | ||
"version": "0.20.0-alpha.1", | ||
"version": "0.20.0", | ||
"description": "The Pyodide JavaScript package", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -24,2 +24,86 @@ // This file is generated by applying the C preprocessor to core/pyproxy.ts | ||
/** | ||
@@ -293,3 +377,8 @@ * Every public Python entrypoint goes through this file! The main entrypoint is | ||
} | ||
return Hiwire.pop_value(idresult); | ||
let result = Hiwire.pop_value(idresult); | ||
// Automatically schedule coroutines | ||
if (result && result.type === "coroutine" && result._ensure_future) { | ||
result._ensure_future(); | ||
} | ||
return result; | ||
}; | ||
@@ -296,0 +385,0 @@ Module.callPyObject = function (ptrobj: number, ...jsargs: any) { |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
113667
3.11%3370
2.96%1
-50%