add einsum bit

This commit is contained in:
Tom 2025-01-07 16:21:37 +00:00
parent 3c325081be
commit c05024a5ab
31 changed files with 18780 additions and 1100 deletions

View File

@ -20,7 +20,7 @@ collections:
kramdown: kramdown:
syntax_highlighter: coderay syntax_highlighter: coderay
syntax_highlighter_opts: syntax_highlighter_opts:
line_numbers: inline line_numbers: false
flexible_include: flexible_include:
die_on_flexible_include_error: false die_on_flexible_include_error: false

View File

@ -54,7 +54,7 @@
<link rel="authorization_endpoint" href="https://indieauth.com/auth"> <link rel="authorization_endpoint" href="https://indieauth.com/auth">
<link rel="stylesheet" href="/assets/css/styles.css"> <link rel="stylesheet" href="/assets/css/styles.css">
<script src="/assets/js/index.js" async></script> <script src="/assets/js/index.js" defer></script>
{% if page.mathjax %} {% if page.mathjax %}
<script> <script>
@ -67,9 +67,15 @@
} }
}; };
</script> </script>
<script src="/assets/mathjax/tex-mml-svg.js" id="MathJax-script" async></script> <script src="/assets/mathjax/tex-mml-svg.js" id="MathJax-script" defer></script>
{% endif %} {% endif %}
{% if page.load_klipse %}
<link rel="stylesheet" type="text/css" href="/assets/klipse/codemirror.css">
<script src="/assets/klipse/setup_klipse.js" defer></script>
<script src="/assets/klipse/klipse_plugin.js" defer></script>
{% endif %}
<!-- Say hello to people in the console. --> <!-- Say hello to people in the console. -->
<script> <script>
console.log( console.log(

View File

@ -7,19 +7,7 @@ image: /assets/blog/REPL/repl.png
thumbnail: /assets/blog/REPL/thumbnail.png thumbnail: /assets/blog/REPL/thumbnail.png
image_class: invertable image_class: invertable
alt: A screenshot of a small javascript widget that lets you evaluate python code. It's showing some numpy code and its evaluated output. alt: A screenshot of a small javascript widget that lets you evaluate python code. It's showing some numpy code and its evaluated output.
head: | load_klipse: true
<link rel="stylesheet" type="text/css" href="/assets/klipse/codemirror.css">
<script>
window.klipse_settings = {
selector_pyodide: '.language-klipse-python', // css selector for the html elements to be klipsified
codemirror_options_in: {
theme: "jupyter",
},
};
</script>
<script src="/assets/klipse/klipse_plugin.min.js" defer></script>
--- ---
On this excellent [personal site](http://lambdafunk.com/) I saw [Klipse](https://github.com/viebel/klipse), a little js library that lets you modify and execute code snippets in blogs. How cute! On this excellent [personal site](http://lambdafunk.com/) I saw [Klipse](https://github.com/viebel/klipse), a little js library that lets you modify and execute code snippets in blogs. How cute!

View File

@ -12,6 +12,9 @@ assets:
alt: An image of an index the expression for $\epsilon_{ijk}\partial_j u_k$ which in words would be the curl of u alt: An image of an index the expression for $\epsilon_{ijk}\partial_j u_k$ which in words would be the curl of u
image_class: invertable image_class: invertable
load_klipse: true
--- ---
Just a short thought. Lately I've been starting to read though these [lecture notes on astrophysical fluid dynamics][notes] and this morning I came across [this nice blogpost][blogpost] about numpy's `einsum` function. Both reminded how lovely einstein summation is as a mathematical notation. Just a short thought. Lately I've been starting to read though these [lecture notes on astrophysical fluid dynamics][notes] and this morning I came across [this nice blogpost][blogpost] about numpy's `einsum` function. Both reminded how lovely einstein summation is as a mathematical notation.
@ -85,9 +88,58 @@ The final trick is that this insertion of the metric in the middle of tensor con
Now I've called these things hacks and tricks but they also connect to much deeper mathematical concepts such as [covariance and contravariance](https://en.wikipedia.org/wiki/Covariance_and_contravariance_of_vectors). This seems like it's usually the case with nice notation. It makes me think things like the relationship between the derivative operator $\tfrac{d}{dt}$ and and the infinitesimal $dt$. Now I've called these things hacks and tricks but they also connect to much deeper mathematical concepts such as [covariance and contravariance](https://en.wikipedia.org/wiki/Covariance_and_contravariance_of_vectors). This seems like it's usually the case with nice notation. It makes me think things like the relationship between the derivative operator $\tfrac{d}{dt}$ and and the infinitesimal $dt$.
## `np.einsum`
[`einsum`][einsum_docs] is a nice cross over between the theoretical math world and the get stuff done world of numerical programming. Because arrays in numpy know how many dimensions they have and how big they are they lend themselves naturally to the einstein summation syntax. The way this is implemented in numpy is that you pass your tensors to the function along with a special string that tells einsum how to contract the indices together.
Taking the simple matrix multiply again as a an example:
\\[ A_{ik} = B_{ij} C_{jk} \\]
Becomes:
```python
import numpy as np
B = np.array([[0,1], [1,0]])
C = np.array([[1,3], [2,4]])
A = np.einsum("ij, jk", B, C)
A
```
You can see how the `ij` are the indices of B, `jk` those of B and how this would generalise to more tensors or tensors with more dimensions. This is the `implicit` mode of einsum, in this mode it basically follows the normal einstein summation rules of contracting pairs of indices. And in those rules certain things are not allowed, the same index is not allowed to appear three times for example, nor can we express something like an elementwise multiplication of two matrices in normal einstein summation. To be fair such operations are relatively rare, so you can get around it by just writing "for this equation don't sum over `i`".
But einsum has an explicit mode that lets use express some of these operations:
```python
import numpy as np
B = np.array([[0,1], [1,0]])
C = np.array([[1,3], [2,4]])
np.einsum("ij, jk -> ik", B, C)
```
Using the `->` we can also express the indices of the output tensor. Using this we can express the element wise product of two matrices too:
```python
import numpy as np
B = np.array([[0,1], [1,0]])
C = np.array([[1,3], [2,4]])
np.einsum("ij, ij -> ij", B, C)
```
which I guess would be equivalent to the equation:
\\[ A_{ij} = B_{ij} C_{ij} \; \text{(No sum over indices)}\\]
[notes]: https://arxiv.org/abs/1604.03835 [notes]: https://arxiv.org/abs/1604.03835
[blogpost]: https://einsum.joelburget.com/ [blogpost]: https://einsum.joelburget.com/
[einsum_docs]: https://numpy.org/doc/stable/reference/generated/numpy.einsum.html
I used [this](https://viereck.ch/latex-to-svg/) to generate the thumbnail for this post. I used [this](https://viereck.ch/latex-to-svg/) to generate the thumbnail for this post.

View File

@ -38,8 +38,9 @@ const getCSSCustomProp = (propKey) => {
return response; return response;
}; };
const applySetting = (passedSetting) => { const applySetting = () => {
let currentSetting = passedSetting || localStorage.getItem(STORAGE_KEY); let currentSetting =
localStorage.getItem(STORAGE_KEY) || getCSSCustomProp(COLOR_MODE_KEY);
if (currentSetting) { if (currentSetting) {
document.documentElement.setAttribute( document.documentElement.setAttribute(
@ -47,6 +48,9 @@ const applySetting = (passedSetting) => {
currentSetting currentSetting
); );
} }
console.log(
`Mode Preference set on document.documentElement.getAttribute("data-user-color-scheme"): ${currentSetting}`
);
}; };
const toggleSetting = () => { const toggleSetting = () => {
@ -65,15 +69,28 @@ const toggleSetting = () => {
break; break;
} }
console.log("Saving preference to localStorage:", currentSetting);
localStorage.setItem(STORAGE_KEY, currentSetting); localStorage.setItem(STORAGE_KEY, currentSetting);
return currentSetting;
}; };
modeToggleButton.addEventListener("click", (evt) => { modeToggleButton.addEventListener("click", (evt) => {
evt.preventDefault(); evt.preventDefault();
applySetting(toggleSetting()); toggleSetting();
applySetting();
}); });
let localStorageSetting = localStorage.getItem(STORAGE_KEY);
let defaultValue = getCSSCustomProp(COLOR_MODE_KEY);
if (localStorageSetting) {
console.log(
`Night mode setting found in localStorage: ${localStorageSetting}`
);
} else {
console.log(
`Night mode setting not found in localStorage. Set to value from css --color-mode key: ${defaultValue}`
);
}
applySetting(); applySetting();

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -163,6 +163,49 @@
.cm-s-jupyter .cm-error {color: #f00;} .cm-s-jupyter .cm-error {color: #f00;}
.cm-s-jupyter .cm-hr {color: #999;} .cm-s-jupyter .cm-hr {color: #999;}
/*
Name: dracula
Author: Michael Kaminsky (http://github.com/mkaminsky11)
Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)
*/
.cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters {
background-color: #222 !important;
color: #f8f8f2 !important;
border: none;
}
.cm-s-dracula .CodeMirror-gutters { color: #282a36; }
.cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; }
.cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; }
.cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }
.cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }
.cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }
.cm-s-dracula span.cm-comment { color: #6272a4; }
.cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; }
.cm-s-dracula span.cm-number { color: #bd93f9; }
.cm-s-dracula span.cm-variable { color: #50fa7b; }
.cm-s-dracula span.cm-variable-2 { color: white; }
.cm-s-dracula span.cm-def { color: #50fa7b; }
.cm-s-dracula span.cm-operator { color: #ff79c6; }
.cm-s-dracula span.cm-keyword { color: #ff79c6; }
.cm-s-dracula span.cm-atom { color: #bd93f9; }
.cm-s-dracula span.cm-meta { color: #f8f8f2; }
.cm-s-dracula span.cm-tag { color: #ff79c6; }
.cm-s-dracula span.cm-attribute { color: #50fa7b; }
.cm-s-dracula span.cm-qualifier { color: #50fa7b; }
.cm-s-dracula span.cm-property { color: #66d9ef; }
.cm-s-dracula span.cm-builtin { color: #50fa7b; }
.cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; }
.cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); }
.cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }
/* end of dracula theme for codemirror */
/* neo theme for codemirror */ /* neo theme for codemirror */
/* Color scheme */ /* Color scheme */

File diff suppressed because one or more lines are too long

16273
assets/klipse/klipse_plugin.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,367 @@
// https://github.com/thejameskyle/pretty-format
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.prettyFormat = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict';
const printString = require('./printString');
const toString = Object.prototype.toString;
const toISOString = Date.prototype.toISOString;
const errorToString = Error.prototype.toString;
const regExpToString = RegExp.prototype.toString;
const symbolToString = Symbol.prototype.toString;
const SYMBOL_REGEXP = /^Symbol\((.*)\)(.*)$/;
const NEWLINE_REGEXP = /\n/ig;
const getSymbols = Object.getOwnPropertySymbols || (obj => []);
function isToStringedArrayType(toStringed) {
return (
toStringed === '[object Array]' ||
toStringed === '[object ArrayBuffer]' ||
toStringed === '[object DataView]' ||
toStringed === '[object Float32Array]' ||
toStringed === '[object Float64Array]' ||
toStringed === '[object Int8Array]' ||
toStringed === '[object Int16Array]' ||
toStringed === '[object Int32Array]' ||
toStringed === '[object Uint8Array]' ||
toStringed === '[object Uint8ClampedArray]' ||
toStringed === '[object Uint16Array]' ||
toStringed === '[object Uint32Array]'
);
}
function printNumber(val) {
if (val != +val) return 'NaN';
const isNegativeZero = val === 0 && (1 / val) < 0;
return isNegativeZero ? '-0' : '' + val;
}
function printFunction(val, printFunctionName) {
if (!printFunctionName) {
return '[Function]';
} else if (val.name === '') {
return '[Function anonymous]'
} else {
return '[Function ' + val.name + ']';
}
}
function printSymbol(val) {
return symbolToString.call(val).replace(SYMBOL_REGEXP, 'Symbol($1)');
}
function printError(val) {
return '[' + errorToString.call(val) + ']';
}
function printBasicValue(val, printFunctionName, escapeRegex) {
if (val === true || val === false) return '' + val;
if (val === undefined) return 'undefined';
if (val === null) return 'null';
const typeOf = typeof val;
if (typeOf === 'number') return printNumber(val);
if (typeOf === 'string') return '"' + printString(val) + '"';
if (typeOf === 'function') return printFunction(val, printFunctionName);
if (typeOf === 'symbol') return printSymbol(val);
const toStringed = toString.call(val);
if (toStringed === '[object WeakMap]') return 'WeakMap {}';
if (toStringed === '[object WeakSet]') return 'WeakSet {}';
if (toStringed === '[object Function]' || toStringed === '[object GeneratorFunction]') return printFunction(val, printFunctionName);
if (toStringed === '[object Symbol]') return printSymbol(val);
if (toStringed === '[object Date]') return toISOString.call(val);
if (toStringed === '[object Error]') return printError(val);
if (toStringed === '[object RegExp]') {
if (escapeRegex) {
return printString(regExpToString.call(val));
}
return regExpToString.call(val);
};
if (toStringed === '[object Arguments]' && val.length === 0) return 'Arguments []';
if (isToStringedArrayType(toStringed) && val.length === 0) return val.constructor.name + ' []';
if (val instanceof Error) return printError(val);
return false;
}
function printList(list, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex) {
let body = '';
if (list.length) {
body += edgeSpacing;
const innerIndent = prevIndent + indent;
for (let i = 0; i < list.length; i++) {
body += innerIndent + print(list[i], indent, innerIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex);
if (i < list.length - 1) {
body += ',' + spacing;
}
}
body += (min ? '' : ',') + edgeSpacing + prevIndent;
}
return '[' + body + ']';
}
function printArguments(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex) {
return (min ? '' : 'Arguments ') + printList(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex);
}
function printArray(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex) {
return (min ? '' : val.constructor.name + ' ') + printList(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex);
}
function printMap(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex) {
let result = 'Map {';
const iterator = val.entries();
let current = iterator.next();
if (!current.done) {
result += edgeSpacing;
const innerIndent = prevIndent + indent;
while (!current.done) {
const key = print(current.value[0], indent, innerIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex);
const value = print(current.value[1], indent, innerIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex);
result += innerIndent + key + ' => ' + value;
current = iterator.next();
if (!current.done) {
result += ',' + spacing;
}
}
result += (min ? '' : ',') + edgeSpacing + prevIndent;
}
return result + '}';
}
function printObject(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex) {
const constructor = min ? '' : (val.constructor ? val.constructor.name + ' ' : 'Object ');
let result = constructor + '{';
let keys = Object.keys(val).sort();
const symbols = getSymbols(val);
if (symbols.length) {
keys = keys
.filter(key => !(typeof key === 'symbol' || toString.call(key) === '[object Symbol]'))
.concat(symbols);
}
if (keys.length) {
result += edgeSpacing;
const innerIndent = prevIndent + indent;
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const name = print(key, indent, innerIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex);
const value = print(val[key], indent, innerIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex);
result += innerIndent + name + ': ' + value;
if (i < keys.length - 1) {
result += ',' + spacing;
}
}
result += (min ? '' : ',') + edgeSpacing + prevIndent;
}
return result + '}';
}
function printSet(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex) {
let result = 'Set {';
const iterator = val.entries();
let current = iterator.next();
if (!current.done) {
result += edgeSpacing;
const innerIndent = prevIndent + indent;
while (!current.done) {
result += innerIndent + print(current.value[1], indent, innerIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex);
current = iterator.next();
if (!current.done) {
result += ',' + spacing;
}
}
result += (min ? '' : ',') + edgeSpacing + prevIndent;
}
return result + '}';
}
function printComplexValue(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex) {
refs = refs.slice();
if (refs.indexOf(val) > -1) {
return '[Circular]';
} else {
refs.push(val);
}
currentDepth++;
const hitMaxDepth = currentDepth > maxDepth;
if (callToJSON && !hitMaxDepth && val.toJSON && typeof val.toJSON === 'function') {
return print(val.toJSON(), indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex);
}
const toStringed = toString.call(val);
if (toStringed === '[object Arguments]') {
return hitMaxDepth ? '[Arguments]' : printArguments(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex);
} else if (isToStringedArrayType(toStringed)) {
return hitMaxDepth ? '[Array]' : printArray(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex);
} else if (toStringed === '[object Map]') {
return hitMaxDepth ? '[Map]' : printMap(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex);
} else if (toStringed === '[object Set]') {
return hitMaxDepth ? '[Set]' : printSet(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex);
} else if (typeof val === 'object') {
return hitMaxDepth ? '[Object]' : printObject(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex);
}
}
function printPlugin(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex) {
let match = false;
let plugin;
for (let p = 0; p < plugins.length; p++) {
plugin = plugins[p];
if (plugin.test(val)) {
match = true;
break;
}
}
if (!match) {
return false;
}
function boundPrint(val) {
return print(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex);
}
function boundIndent(str) {
const indentation = prevIndent + indent;
return indentation + str.replace(NEWLINE_REGEXP, '\n' + indentation);
}
return plugin.print(val, boundPrint, boundIndent, {
edgeSpacing: edgeSpacing,
spacing: spacing
});
}
function print(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex) {
const basic = printBasicValue(val, printFunctionName, escapeRegex);
if (basic) return basic;
const plugin = printPlugin(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex);
if (plugin) return plugin;
return printComplexValue(val, indent, prevIndent, spacing, edgeSpacing, refs, maxDepth, currentDepth, plugins, min, callToJSON, printFunctionName, escapeRegex);
}
const DEFAULTS = {
callToJSON: true,
indent: 2,
maxDepth: Infinity,
min: false,
plugins: [],
printFunctionName: true,
escapeRegex: false,
};
function validateOptions(opts) {
Object.keys(opts).forEach(key => {
if (!DEFAULTS.hasOwnProperty(key)) {
throw new Error('prettyFormat: Invalid option: ' + key);
}
});
if (opts.min && opts.indent !== undefined && opts.indent !== 0) {
throw new Error('prettyFormat: Cannot run with min option and indent');
}
}
function normalizeOptions(opts) {
const result = {};
Object.keys(DEFAULTS).forEach(key =>
result[key] = opts.hasOwnProperty(key) ? opts[key] : DEFAULTS[key]
);
if (result.min) {
result.indent = 0;
}
return result;
}
function createIndent(indent) {
return new Array(indent + 1).join(' ');
}
function prettyFormat(val, opts) {
if (!opts) {
opts = DEFAULTS;
} else {
validateOptions(opts)
opts = normalizeOptions(opts);
}
let indent;
let refs;
const prevIndent = '';
const currentDepth = 0;
const spacing = opts.min ? ' ' : '\n';
const edgeSpacing = opts.min ? '' : '\n';
if (opts && opts.plugins.length) {
indent = createIndent(opts.indent);
refs = [];
var pluginsResult = printPlugin(val, indent, prevIndent, spacing, edgeSpacing, refs, opts.maxDepth, currentDepth, opts.plugins, opts.min, opts.callToJSON, opts.printFunctionName, opts.escapeRegex);
if (pluginsResult) return pluginsResult;
}
var basicResult = printBasicValue(val, opts.printFunctionName, opts.escapeRegex);
if (basicResult) return basicResult;
if (!indent) indent = createIndent(opts.indent);
if (!refs) refs = [];
return printComplexValue(val, indent, prevIndent, spacing, edgeSpacing, refs, opts.maxDepth, currentDepth, opts.plugins, opts.min, opts.callToJSON, opts.printFunctionName, opts.escapeRegex);
}
module.exports = prettyFormat;
},{"./printString":2}],2:[function(require,module,exports){
'use strict';
const ESCAPED_CHARACTERS = /(\\|\"|\')/g;
module.exports = function printString(val) {
return val.replace(ESCAPED_CHARACTERS, '\\$1');
}
},{}]},{},[1])(1)
});

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -0,0 +1,888 @@
/**
* The main bootstrap script for loading pyodide.
*/
/**
* The :ref:`js-api-pyodide` module object. Must be present as a global variable
* called
* ``pyodide`` in order for package loading to work properly.
*
* @type Object
*/
globalThis.pyodide = {};
/**
* Load the main Pyodide wasm module and initialize it. When finished stores the
* Pyodide module as a global object called ``pyodide``.
* @param {string} config.indexURL - The URL from which Pyodide will load
* packages
* @returns The Pyodide module.
* @async
*/
globalThis.loadPyodide = async function(config = {}) {
if (globalThis.__pyodideLoading) {
if (globalThis.languagePluginURL) {
throw new Error(
"Pyodide is already loading because languagePluginURL is defined.");
} else {
throw new Error("Pyodide is already loading.");
}
}
globalThis.__pyodideLoading = true;
let Module = {};
// Note: PYODIDE_BASE_URL is an environment variable replaced in
// in this template in the Makefile. It's recommended to always set
// indexURL in any case.
let baseURL = config.indexURL || "./";
if (baseURL.endsWith(".js")) {
baseURL = baseURL.substr(0, baseURL.lastIndexOf('/'));
}
if (!baseURL.endsWith("/")) {
baseURL += '/';
}
////////////////////////////////////////////////////////////
// Package loading
const DEFAULT_CHANNEL = "default channel";
// Regexp for validating package name and URI
const package_uri_regexp = /^.*?([^\/]*)\.js$/;
function _uri_to_package_name(package_uri) {
let match = package_uri_regexp.exec(package_uri);
if (match) {
return match[1];
}
};
let loadScript;
if (self.document) { // browser
loadScript = (url) => new Promise((res, rej) => {
const script = self.document.createElement('script');
script.src = url;
script.onload = res;
script.onerror = rej;
self.document.head.appendChild(script);
});
} else if (self.importScripts) { // webworker
loadScript = async (url) => { // This is async only for consistency
self.importScripts(url);
};
} else {
throw new Error("Cannot determine runtime environment");
}
function recursiveDependencies(names, _messageCallback, errorCallback,
sharedLibsOnly) {
const packages = Module.packages.dependencies;
const loadedPackages = Module.loadedPackages;
const sharedLibraries = Module.packages.shared_library;
const toLoad = new Map();
const addPackage = (pkg) => {
if (toLoad.has(pkg)) {
return;
}
toLoad.set(pkg, DEFAULT_CHANNEL);
// If the package is already loaded, we don't add dependencies, but warn
// the user later. This is especially important if the loaded package is
// from a custom url, in which case adding dependencies is wrong.
if (loadedPackages[pkg] !== undefined) {
return;
}
for (let dep of packages[pkg]) {
addPackage(dep);
}
};
for (let name of names) {
const pkgname = _uri_to_package_name(name);
if (pkgname !== undefined) {
if (toLoad.has(pkgname) && toLoad.get(pkgname) !== name) {
errorCallback(`Loading same package ${pkgname} from ${name} and ${
toLoad.get(pkgname)}`);
continue;
}
toLoad.set(pkgname, name);
} else if (name in packages) {
addPackage(name);
} else {
errorCallback(`Skipping unknown package '${name}'`);
}
}
if (sharedLibsOnly) {
let onlySharedLibs = new Map();
for (let c of toLoad) {
if (c[0] in sharedLibraries) {
onlySharedLibs.set(c[0], toLoad.get(c[0]));
}
}
return onlySharedLibs;
}
return toLoad;
}
async function _loadPackage(names, messageCallback, errorCallback) {
// toLoad is a map pkg_name => pkg_uri
let toLoad = recursiveDependencies(names, messageCallback, errorCallback);
// locateFile is the function used by the .js file to locate the .data
// file given the filename
Module.locateFile = (path) => {
// handle packages loaded from custom URLs
let pkg = path.replace(/\.data$/, "");
if (toLoad.has(pkg)) {
let package_uri = toLoad.get(pkg);
if (package_uri != DEFAULT_CHANNEL) {
return package_uri.replace(/\.js$/, ".data");
};
};
return baseURL + path;
};
if (toLoad.size === 0) {
return Promise.resolve('No new packages to load');
} else {
let packageNames = Array.from(toLoad.keys()).join(', ');
messageCallback(`Loading ${packageNames}`);
}
// If running in main browser thread, try to catch errors thrown when
// running a script. Since the script is added via a script tag, there is
// no good way to capture errors from the script only, so try to capture
// all errors them.
//
// windowErrorPromise rejects when any exceptions is thrown in the process
// of loading a script. The promise never resolves, and we combine it
// with other promises via Promise.race.
let windowErrorHandler;
let windowErrorPromise;
if (self.document) {
windowErrorPromise = new Promise((_res, rej) => {
windowErrorHandler = e => {
errorCallback(
"Unhandled error. We don't know what it is or whether it is related to 'loadPackage' but out of an abundance of caution we will assume that loading failed.");
errorCallback(e);
rej(e.message);
};
self.addEventListener('error', windowErrorHandler);
});
} else {
// This should be a promise that never resolves
windowErrorPromise = new Promise(() => {});
}
// This is a collection of promises that resolve when the package's JS file
// is loaded. The promises already handle error and never fail.
let scriptPromises = [];
for (let [pkg, uri] of toLoad) {
let loaded = Module.loadedPackages[pkg];
if (loaded !== undefined) {
// If uri is from the DEFAULT_CHANNEL, we assume it was added as a
// depedency, which was previously overridden.
if (loaded === uri || uri === DEFAULT_CHANNEL) {
messageCallback(`${pkg} already loaded from ${loaded}`);
continue;
} else {
errorCallback(
`URI mismatch, attempting to load package ${pkg} from ${uri} ` +
`while it is already loaded from ${
loaded}. To override a dependency, ` +
`load the custom package first.`);
continue;
}
}
let scriptSrc = uri === DEFAULT_CHANNEL ? `${baseURL}${pkg}.js` : uri;
messageCallback(`Loading ${pkg} from ${scriptSrc}`);
scriptPromises.push(loadScript(scriptSrc).catch(() => {
errorCallback(`Couldn't load package from URL ${scriptSrc}`);
toLoad.delete(pkg);
}));
}
// When the JS loads, it synchronously adds a runDependency to emscripten.
// It then loads the data file, and removes the runDependency from
// emscripten. This function returns a promise that resolves when there are
// no pending runDependencies.
function waitRunDependency() {
const promise = new Promise(r => {
Module.monitorRunDependencies = (n) => {
if (n === 0) {
r();
}
};
});
// If there are no pending dependencies left, monitorRunDependencies will
// never be called. Since we can't check the number of dependencies,
// manually trigger a call.
Module.addRunDependency("dummy");
Module.removeRunDependency("dummy");
return promise;
}
// We must start waiting for runDependencies *after* all the JS files are
// loaded, since the number of runDependencies may happen to equal zero
// between package files loading.
let successPromise = Promise.all(scriptPromises).then(waitRunDependency);
try {
await Promise.race([ successPromise, windowErrorPromise ]);
} finally {
delete Module.monitorRunDependencies;
if (windowErrorHandler) {
self.removeEventListener('error', windowErrorHandler);
}
}
let packageList = [];
for (let [pkg, uri] of toLoad) {
Module.loadedPackages[pkg] = uri;
packageList.push(pkg);
}
let resolveMsg;
if (packageList.length > 0) {
let packageNames = packageList.join(', ');
resolveMsg = `Loaded ${packageNames}`;
} else {
resolveMsg = 'No packages loaded';
}
Module.reportUndefinedSymbols();
messageCallback(resolveMsg);
// We have to invalidate Python's import caches, or it won't
// see the new files.
Module.runPythonSimple('import importlib\n' +
'importlib.invalidate_caches()\n');
};
// This is a promise that is resolved iff there are no pending package loads.
// It never fails.
let loadPackageChain = Promise.resolve();
/**
*
* The list of packages that Pyodide has loaded.
* Use ``Object.keys(pyodide.loadedPackages)`` to get the list of names of
* loaded packages, and ``pyodide.loadedPackages[package_name]`` to access
* install location for a particular ``package_name``.
*
* @type {object}
*/
Module.loadedPackages = {};
/**
* Load a package or a list of packages over the network. This installs the
* package in the virtual filesystem. The package needs to be imported from
* Python before it can be used.
* @param {String | Array | PyProxy} names Either a single package name or URL
* or a list of them. URLs can be absolute or relative. The URLs must have
* file name
* ``<package-name>.js`` and there must be a file called
* ``<package-name>.data`` in the same directory. The argument can be a
* ``PyProxy`` of a list, in which case the list will be converted to
* Javascript and the ``PyProxy`` will be destroyed.
* @param {function} messageCallback A callback, called with progress messages
* (optional)
* @param {function} errorCallback A callback, called with error/warning
* messages (optional)
* @async
*/
Module.loadPackage = async function(names, messageCallback, errorCallback) {
if (Module.isPyProxy(names)) {
let temp;
try {
temp = names.toJs();
} finally {
names.destroy();
}
names = temp;
}
if (!Array.isArray(names)) {
names = [ names ];
}
// get shared library packages and load those first
// otherwise bad things happen with linking them in firefox.
let sharedLibraryNames = [];
try {
let sharedLibraryPackagesToLoad =
recursiveDependencies(names, messageCallback, errorCallback, true);
for (let pkg of sharedLibraryPackagesToLoad) {
sharedLibraryNames.push(pkg[0]);
}
} catch (e) {
// do nothing - let the main load throw any errors
}
// override the load plugin so that it imports any dlls also
// this only needs to be done for shared library packages because
// we assume that if a package depends on a shared library
// it needs to have access to it.
// not needed for so in standard module because those are linked together
// correctly, it is only where linking goes across modules that it needs to
// be done. Hence we only put this extra preload plugin in during the shared
// library load
let oldPlugin;
for (let p in Module.preloadPlugins) {
if (Module.preloadPlugins[p].canHandle("test.so")) {
oldPlugin = Module.preloadPlugins[p];
break;
}
}
let dynamicLoadHandler = {
get : function(obj, prop) {
if (prop === 'handle') {
return function(bytes, name) {
obj[prop].apply(obj, arguments);
this["asyncWasmLoadPromise"] =
this["asyncWasmLoadPromise"].then(function() {
Module.loadDynamicLibrary(name,
{global : true, nodelete : true})
});
}
} else {
return obj[prop];
}
}
};
var loadPluginOverride = new Proxy(oldPlugin, dynamicLoadHandler);
// restore the preload plugin
Module.preloadPlugins.unshift(loadPluginOverride);
let promise = loadPackageChain.then(
() => _loadPackage(sharedLibraryNames, messageCallback || console.log,
errorCallback || console.error));
loadPackageChain = loadPackageChain.then(() => promise.catch(() => {}));
await promise;
Module.preloadPlugins.shift(loadPluginOverride);
promise = loadPackageChain.then(
() => _loadPackage(names, messageCallback || console.log,
errorCallback || console.error));
loadPackageChain = loadPackageChain.then(() => promise.catch(() => {}));
await promise;
};
////////////////////////////////////////////////////////////
// Fix Python recursion limit
function fixRecursionLimit(pyodide) {
// The Javascript/Wasm call stack may be too small to handle the default
// Python call stack limit of 1000 frames. This is generally the case on
// Chrom(ium), but not on Firefox. Here, we determine the Javascript call
// stack depth available, and then divide by 50 (determined heuristically)
// to set the maximum Python call stack depth.
let depth = 0;
function recurse() {
depth += 1;
recurse();
}
try {
recurse();
} catch (err) {
;
}
let recursionLimit = depth / 50;
if (recursionLimit > 1000) {
recursionLimit = 1000;
}
pyodide.runPythonSimple(
`import sys; sys.setrecursionlimit(int(${recursionLimit}))`);
};
////////////////////////////////////////////////////////////
// Rearrange namespace for public API
// clang-format off
let PUBLIC_API = [
'globals',
'pyodide_py',
'version',
'loadPackage',
'loadPackagesFromImports',
'loadedPackages',
'isPyProxy',
'pyimport',
'runPython',
'runPythonAsync',
'registerJsModule',
'unregisterJsModule',
'setInterruptBuffer',
'toPy',
'PythonError',
];
// clang-format on
function makePublicAPI(module, public_api) {
let namespace = {_module : module};
module.public_api = namespace;
for (let name of public_api) {
namespace[name] = module[name];
}
return namespace;
}
////////////////////////////////////////////////////////////
// Loading Pyodide
Module.noImageDecoding = true;
Module.noAudioDecoding = true;
Module.noWasmDecoding =
false; // we preload wasm using the built in plugin now
Module.preloadedWasm = {};
let fatal_error_occurred = false;
Module.fatal_error = function(e) {
if (fatal_error_occurred) {
console.error("Recursive call to fatal_error. Inner error was:");
console.error(e);
return;
}
fatal_error_occurred = true;
console.error("Pyodide has suffered a fatal error. " +
"Please report this to the Pyodide maintainers.");
console.error("The cause of the fatal error was:")
console.error(e);
try {
let fd_stdout = 1;
Module.__Py_DumpTraceback(fd_stdout,
Module._PyGILState_GetThisThreadState());
for (let key of PUBLIC_API) {
if (key === "version") {
continue;
}
Object.defineProperty(Module.public_api, key, {
enumerable : true,
configurable : true,
get : () => {
throw new Error(
"Pyodide already fatally failed and can no longer be used.");
}
});
}
if (Module.on_fatal) {
Module.on_fatal(e);
}
} catch (e) {
console.error("Another error occurred while handling the fatal error:");
console.error(e);
}
throw e;
};
/**
* An alias to the Python :py:mod:`pyodide` package.
*
* You can use this to call functions defined in the Pyodide Python package
* from Javascript.
*
* @type {PyProxy}
*/
Module.pyodide_py = {}; // actually defined in runPythonSimple below
/**
*
* An alias to the global Python namespace.
*
* For example, to access a variable called ``foo`` in the Python global
* scope, use ``pyodide.globals.get("foo")``
*
* @type {PyProxy}
*/
Module.globals = {}; // actually defined in runPythonSimple below
// clang-format off
/**
* A Javascript error caused by a Python exception.
*
* In order to reduce the risk of large memory leaks, the ``PythonError``
* contains no reference to the Python exception that caused it. You can find
* the actual Python exception that caused this error as `sys.last_value
* <https://docs.python.org/3/library/sys.html#sys.last_value>`_.
*
* See :ref:`type-translations-errors` for more information.
*
* .. admonition:: Avoid Stack Frames
* :class: warning
*
* If you make a :any:`PyProxy` of ``sys.last_value``, you should be
* especially careful to :any:`destroy() <PyProxy.destroy>` it when you are
* done. You may leak a large amount of memory including the local
* variables of all the stack frames in the traceback if you don't. The
* easiest way is to only handle the exception in Python.
*
* @class
*/
Module.PythonError = class PythonError {
// actually defined in error_handling.c. TODO: would be good to move this
// documentation and the definition of PythonError to error_handling.js
constructor(){
/**
* The Python traceback.
* @type {string}
*/
this.message;
}
};
// clang-format on
/**
*
* The Pyodide version.
*
* It can be either the exact release version (e.g. ``0.1.0``), or
* the latest release version followed by the number of commits since, and
* the git hash of the current commit (e.g. ``0.1.0-1-bd84646``).
*
* @type {string}
*/
Module.version = ""; // Hack to make jsdoc behave
/**
* Run Python code in the simplest way possible. The primary purpose of this
* method is for bootstrapping. It is also useful for debugging: If the Python
* interpreter is initialized successfully then it should be possible to use
* this method to run Python code even if everything else in the Pyodide
* `core` module fails.
*
* The differences are:
* 1. `runPythonSimple` doesn't return anything (and so won't leak
* PyProxies)
* 2. `runPythonSimple` doesn't require access to any state on the
* Javascript `pyodide` module.
* 3. `runPython` uses `pyodide.eval_code`, whereas `runPythonSimple` uses
* `PyRun_String` which is the C API for `eval` / `exec`.
* 4. `runPythonSimple` runs with `globals` a separate dict which is called
* `init_dict` (keeps global state private)
* 5. `runPythonSimple` doesn't dedent the argument
*
* When `core` initialization is completed, the globals for `runPythonSimple`
* is made available as `Module.init_dict`.
*
* @private
*/
Module.runPythonSimple = function(code) {
let code_c_string = Module.stringToNewUTF8(code);
let errcode;
try {
errcode = Module._run_python_simple_inner(code_c_string);
} catch (e) {
Module.fatal_error(e);
} finally {
Module._free(code_c_string);
}
if (errcode === -1) {
Module._pythonexc2js();
}
};
/**
* Runs a string of Python code from Javascript.
*
* The last part of the string may be an expression, in which case, its value
* is returned.
*
* @param {string} code Python code to evaluate
* @param {dict} 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.
* @returns The result of the Python code translated to Javascript. See the
* documentation for :any:`pyodide.eval_code` for more info.
*/
Module.runPython = function(code, globals = Module.globals) {
return Module.pyodide_py.eval_code(code, globals);
};
// clang-format off
/**
* Inspect a Python code chunk and use :js:func:`pyodide.loadPackage` to
* install any known packages that the code chunk imports. Uses the Python API
* :func:`pyodide.find\_imports` to inspect the code.
*
* For example, given the following code as input
*
* .. code-block:: python
*
* import numpy as np x = np.array([1, 2, 3])
*
* :js:func:`loadPackagesFromImports` will call
* ``pyodide.loadPackage(['numpy'])``. See also :js:func:`runPythonAsync`.
*
* @param {string} code The code to inspect.
* @param {Function} messageCallback The ``messageCallback`` argument of
* :any:`pyodide.loadPackage` (optional).
* @param {Function} errorCallback The ``errorCallback`` argument of
* :any:`pyodide.loadPackage` (optional).
* @async
*/
Module.loadPackagesFromImports = async function(code, messageCallback, errorCallback) {
let imports = Module.pyodide_py.find_imports(code).toJs();
if (imports.length === 0) {
return;
}
let packageNames = Module.packages.import_name_to_package_name;
let packages = new Set();
for (let name of imports) {
if (name in packageNames) {
packages.add(packageNames[name]);
}
}
if (packages.size) {
await Module.loadPackage(
Array.from(packages.keys()), messageCallback, errorCallback
);
}
};
// clang-format on
/**
* Access a Python object in the global namespace from Javascript.
*
* @deprecated This function will be removed in version 0.18.0. Use
* :any:`pyodide.globals.get('key') <pyodide.globals>` instead.
*
* @param {string} name Python variable name
* @returns The Python object translated to Javascript.
*/
Module.pyimport = name => {
console.warn(
"Access to the Python global namespace via pyodide.pyimport is deprecated and " +
"will be removed in version 0.18.0. Use pyodide.globals.get('key') instead.");
return Module.globals.get(name);
};
/**
* Runs Python code, possibly asynchronously loading any known packages that
* the code imports. For example, given the following code
*
* .. code-block:: python
*
* import numpy as np
* x = np.array([1, 2, 3])
*
* Pyodide will first call :any:`pyodide.loadPackage(['numpy'])
* <pyodide.loadPackage>`, and then run the code using the Python API
* :any:`pyodide.eval_code_async`, returning the result. The code is compiled
* with `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>`_.
*
* For example:
*
* .. code-block:: pyodide
*
* let result = await pyodide.runPythonAsync(`
* # numpy will automatically be loaded by loadPackagesFromImports
* import numpy as np
* # we can use top level await
* from js import fetch
* response = await fetch("./packages.json")
* packages = await response.json()
* # If final statement is an expression, its value is returned to
* Javascript len(packages.dependencies.object_keys())
* `);
* console.log(result); // 72
*
* @param {string} code Python code to evaluate
* @param {Function} messageCallback The ``messageCallback`` argument of
* :any:`pyodide.loadPackage`.
* @param {Function} errorCallback The ``errorCallback`` argument of
* :any:`pyodide.loadPackage`.
* @returns The result of the Python code translated to Javascript.
* @async
*/
Module.runPythonAsync = async function(code, messageCallback, errorCallback) {
await Module.loadPackagesFromImports(code, messageCallback, errorCallback);
let coroutine = Module.pyodide_py.eval_code_async(code, Module.globals);
try {
let result = await coroutine;
return result;
} finally {
coroutine.destroy();
}
};
// clang-format off
/**
* Registers the Javascript object ``module`` as a Javascript module named
* ``name``. This module can then be imported from Python using the standard
* Python import system. If another module by the same name has already been
* imported, this won't have much effect unless you also delete the imported
* module from ``sys.modules``. This calls the ``pyodide_py`` API
* :func:`pyodide.register_js_module`.
*
* @param {string} name Name of the Javascript module to add
* @param {object} module Javascript object backing the module
*/
Module.registerJsModule = function(name, module) {
Module.pyodide_py.register_js_module(name, module);
};
/**
* Unregisters a Javascript module with given name that has been previously
* registered with :js:func:`pyodide.registerJsModule` or
* :func:`pyodide.register_js_module`. If a Javascript module with that name
* does not already exist, will throw an error. Note that if the module has
* already been imported, this won't have much effect unless you also delete
* the imported module from ``sys.modules``. This calls the ``pyodide_py`` API
* :func:`pyodide.unregister_js_module`.
*
* @param {string} name Name of the Javascript module to remove
*/
Module.unregisterJsModule = function(name) {
Module.pyodide_py.unregister_js_module(name);
};
// clang-format on
/**
* Convert the Javascript object to a Python object as best as possible.
*
* This is similar to :any:`JsProxy.to_py` but for use from Javascript. If the
* object is immutable or a :any:`PyProxy`, it will be returned unchanged. If
* the object cannot be converted into Python, it will be returned unchanged.
*
* See :ref:`type-translations-jsproxy-to-py` for more information.
*
* @param {*} obj
* @param {number} depth Optional argument to limit the depth of the
* conversion.
* @returns {PyProxy} The object converted to Python.
*/
Module.toPy = function(obj, depth = -1) {
// No point in converting these, it'd be dumb to proxy them so they'd just
// get converted back by `js2python` at the end
// clang-format off
switch (typeof obj) {
case "string":
case "number":
case "boolean":
case "bigint":
case "undefined":
return obj;
}
// clang-format on
if (!obj || Module.isPyProxy(obj)) {
return obj;
}
let obj_id = 0;
let py_result = 0;
let result = 0;
try {
obj_id = Module.hiwire.new_value(obj);
py_result = Module.__js2python_convert(obj_id, new Map(), depth);
// clang-format off
if(py_result === 0){
// clang-format on
Module._pythonexc2js();
}
if (Module._JsProxy_Check(py_result)) {
// Oops, just created a JsProxy. Return the original object.
return obj;
// return Module.pyproxy_new(py_result);
}
result = Module._python2js(py_result);
// clang-format off
if (result === 0) {
// clang-format on
Module._pythonexc2js();
}
} finally {
Module.hiwire.decref(obj_id);
Module._Py_DecRef(py_result);
}
return Module.hiwire.pop_value(result);
};
/**
* Is the argument a :any:`PyProxy`?
* @param jsobj {any} Object to test.
* @returns {bool} Is ``jsobj`` a :any:`PyProxy`?
*/
Module.isPyProxy = function(jsobj) {
return !!jsobj && jsobj.$$ !== undefined && jsobj.$$.type === 'PyProxy';
};
Module.locateFile = (path) => baseURL + path;
let moduleLoaded = new Promise(r => Module.postRun = r);
const scriptSrc = `${baseURL}pyodide.asm.js`;
await loadScript(scriptSrc);
// _createPyodideModule is specified in the Makefile by the linker flag:
// `-s EXPORT_NAME="'_createPyodideModule'"`
await _createPyodideModule(Module);
// There is some work to be done between the module being "ready" and postRun
// being called.
await moduleLoaded;
// Bootstrap step: `runPython` needs access to `Module.globals` and
// `Module.pyodide_py`. Use `runPythonSimple` to add these. runPythonSimple
// doesn't dedent the argument so the indentation matters.
Module.runPythonSimple(`
def temp(Module):
import pyodide
import __main__
import builtins
globals = __main__.__dict__
globals.update(builtins.__dict__)
Module.version = pyodide.__version__
Module.globals = globals
Module.builtins = builtins.__dict__
Module.pyodide_py = pyodide
`);
Module.saveState = () => Module.pyodide_py._state.save_state();
Module.restoreState = (state) =>
Module.pyodide_py._state.restore_state(state);
Module.init_dict.get("temp")(Module);
// Module.runPython works starting from here!
// Wrap "globals" in a special Proxy that allows `pyodide.globals.x` access.
// TODO: Should we have this?
Module.globals = Module.wrapNamespace(Module.globals);
let response = await fetch(`${baseURL}packages.json`);
Module.packages = await response.json();
fixRecursionLimit(Module);
let pyodide = makePublicAPI(Module, PUBLIC_API);
Module.registerJsModule("js", globalThis);
Module.registerJsModule("pyodide_js", pyodide);
globalThis.pyodide = pyodide;
return pyodide;
};
if (globalThis.languagePluginUrl) {
console.warn(
"languagePluginUrl is deprecated and will be removed in version 0.18.0, " +
"instead use loadPyodide({ indexURL : <some_url>})");
/**
* A deprecated parameter that specifies the Pyodide ``indexURL``. If present,
* Pyodide will automatically invoke
* ``loadPyodide({indexURL : languagePluginUrl})``
* and will store the resulting promise in
* :any:`globalThis.languagePluginLoader`. Use :any:`loadPyodide`
* directly instead of defining this.
*
* @type String
* @deprecated Will be removed in version 0.18.0
*/
globalThis.languagePluginUrl;
/**
* A deprecated promise that resolves to ``undefined`` when Pyodide is
* finished loading. Only created if :any:`languagePluginUrl` is
* defined. Instead use :any:`loadPyodide`.
*
* @type Promise
* @deprecated Will be removed in version 0.18.0
*/
globalThis.languagePluginLoader =
loadPyodide({indexURL : globalThis.languagePluginUrl});
}

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,24 @@
const is_light_mode =
document.documentElement.getAttribute("data-user-color-scheme") == "light";
if (is_light_mode) {
console.log("Light mode detected, using theme 'jupyter' for klipse");
} else {
console.log("Dark mode detected, using theme 'dracula' for klipse");
}
window.klipse_settings = {
selector_pyodide: ".language-python pre", // css selector for the html elements to be klipsified
scripts_root: "/assets/klipse",
codemirror_root: "/assets/klipse/codemirror",
// no_dynamic_scripts: true,
codemirror_options_in: {
theme: is_light_mode ? "jupyter" : "dracula",
lineWrapping: true,
lineNumbers: true,
autoCloseBrackets: true,
},
codemirror_options_out: {
theme: is_light_mode ? "jupyter" : "dracula",
},
};