I am trying to create a div containing a new pyscript and output section. But What do I need to call tun run the new pyscript code? Or at least a declared function within it?
let outputID = "blah"
let div = document.createElement("div");
let html = `
<py-script output="${outputID}">
... python goes here ...
</py-script>
<div id="${outputID}">
</div>
`;
div.innerHTML = html;
// and yes this div gets added to the body after here
This is typescript. A method execute of some object executes “text” as python. The object has a member variable output which is an HTMLDivElement (DOM div element), and a member variable pyScript in which is the script tag (first element child of an artificial div we create). We also use this to no add the tag repeatedly, but remove it before next time. I guess we could also remove it right after the execution. HTH.
execute(text: string) {
// prepare objects exposed to Python
(window as any).blah= ...
// pyscript
let div = document.createElement("div");
let html = `
<py-script output="${this.output.id}">
from js import blah
${text}
</py-script>
`;
div.innerHTML = html;
// if we did this before, remove the script from the body
if (this.pyScript) {
this.pyScript.remove();
}
// now remember the new script
this.pyScript = div.firstElementChild;
try {
// add it to the body - this will already augment the tag in certain ways
document.body.appendChild(this.pyScript);
// execute the code / evaluate the expression
(this.pyScript as any).evaluate();
} catch (error) {
console.error("Python error:");
console.error(error);
}
}
var myPyScript; // where I remember my pyscript object
var output = document.createElement("div"); // the div object where the output goes
output.id = "myPyScriptOutput";
document.body.appendChild(output);
function execute(text) {
// prepare objects exposed to Python
window.myCustomObject = ... ; // purely optional
// pyscript
let div = document.createElement("div");
let html = `
<py-script output="${output.id}">
from js import myCustomObject; # this is purely optional of course
${text}
</py-script>
`;
div.innerHTML = html;
// if we did this before, remove the script from the body
if (myPyScript) {
myPyScript.remove();
}
// now remember the new script
myPyScript = div.firstElementChild;
try {
// add it to the body - this will already augment the tag in certain ways
document.body.appendChild(myPyScript);
// execute the code / evaluate the expression
myPyScript.evaluate();
} catch (error) {
console.error("Python error:");
console.error(error);
}
}
Not impossible, but also not do-able out of the box. The web components like need to participate in both the page load sequence and the Svelte Store messaging.
I just posted a video which shows how to work in the source (look for the post.) You could make a new web component which, when connectedCallback ran, would grab the child nodes of another div and execute it. Possibly passing the current node if needed.
Would this work for userscripts that run in extensions like tampermonkey or the like? For instance create a pyscript tag, add pyscript or @include it, then execute it? Writing scripts here and there to accomplish things around the web would be amazing.
Yes, you can dynamically load and execute Python. At this time, each Python load goes into the same namespace, so be careful with duplicate names. IIRC Pyodide does support namespacing, but those features are not visible to PyScript.
Hi @john.hanley, your article really helped in understanding, but I somehow did not get it running.
Especially the line py_div.evaluate(); I always run into TypeError: py_div.evaluate is not a function, although I just copied your code.
I have the same problem with the example of @gunnar. Any idea why evaluate is not defined?
The TypeError was from the chrome debugger console. I mean it does execute the python correctly. It is just that the line py_div.evaluate(); does not do anything for me. If I comment it out, the python code still gets executed, but I have no error on the chrome debugger console.
If I use your code unedited, I get the following error in the chrome debugger console:
exec_py.js:36 Python error:
run_python @ exec_py.js:36
(anonymous) @ (index):26
exec_py.js:37 TypeError: py_div.evaluate is not a function
at run_python (exec_py.js:34:24)
at (index):26:1
These are the first messages on the console log and also the only error messages. The rest are just debug messages:
adding initializer async Ć’ mountElements() {
console.log('Collecting nodes to be mounted into python namespace...');
const pyodide = await pyodideReadyPromise$1;
const matches = document.querySelectorAl…
stores.ts:34 adding initializer async Ć’ mountElements() {
console.log('Collecting nodes to be mounted into python namespace...');
const pyodide = await pyodideReadyPromise$1;
const matches = document.querySelectorAl…
stores.ts:40 adding post initializer async Ć’ initHandlers() {
console.log('Collecting nodes...');
const pyodide = await pyodideReadyPromise$1;
for (const pysAttribute of pysAttributeToEvent.keys()) {
await cr…
stores.ts:42 adding post initializer async Ć’ initHandlers() {
console.log('Collecting nodes...');
const pyodide = await pyodideReadyPromise$1;
for (const pysAttribute of pysAttributeToEvent.keys()) {
await cr…
pyenv.ts:10 RUNTIME READY
pyconfig.ts:17 config set!
pyconfig.ts:22 initializers set
pyconfig.ts:27 post initializers set
pyconfig.ts:32 post initializers set
pyconfig.ts:37 post initializers set
pyconfig.ts:32 post initializers set
pyscript.ts:66 connected
pyconfig.ts:17 config set!
pyconfig.ts:112 config set {autoclose_loader: true, runtimes: Array(1)}
pyconfig.ts:124 Initializing runtimes...
interpreter.ts:5 creating pyodide runtime
pyodide.asm.js:14 Python initialization complete
interpreter.ts:15 loading micropip
load-package.ts:329 Loading micropip, pyparsing, packaging, distutils
load-package.ts:389 Loaded micropip, pyparsing, packaging, distutils
interpreter.ts:17 loading pyscript...
interpreter.ts:30 done setting up environment
pyenv.ts:10 RUNTIME READY
pyscript.ts:148 Collecting nodes to be mounted into python namespace...
base.ts:76 evaluate
pyconfig.ts:32 post initializers set
pyconfig.ts:83 ------ loader closed ------
2pyodide.asm.js:14 ----> changed out to myPyScriptOutput true
pyodide.asm.js:14 writing to myPyScriptOutput 2022-07-29 13:26:53.659000 true
pyodide.asm.js:14 APPENDING: True ==> myPyScriptOutput --> 2022-07-29 13:26:53.659000
pyodide.asm.js:14 myPyScriptOutput 2022-07-29 13:26:53.659000
pyodide.asm.js:14 writing to myPyScriptOutput
true
pyodide.asm.js:14 APPENDING: True ==> myPyScriptOutput -->
pyodide.asm.js:14 myPyScriptOutput
2pyodide.asm.js:14 ----> reverted
pyodide.asm.js:14 ----> reverted
pyscript.ts:111 Collecting nodes...