8000 load code from the attr src of py-repl (#1292) · patrickloeber/pyscript@d7e80ad · GitHub
[go: up one dir, main page]

Skip to content

Commit d7e80ad

Browse files
load code from the attr src of py-repl (pyscript#1292)
* load code from the attr src of py-repl * load code from the attr src of py-repl * load code from the attr src of py-repl * load code from the attr src of py-repl * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent b53ddd4 commit d7e80ad

File tree

3 files changed

+102
-1
lines changed

3 files changed

+102
-1
lines changed

docs/changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Features
2121
- The `output` attribute of `py-repl` tags now specifies the id of the DOM element that `sys.stdout`, `sys.stderr`, and the results of a REPL execution are written to. It no longer affects the location of cal 8000 ls to `display()`
2222
- Added a `stderr` attribute of `py-repl` tags to route `sys.stderr` to a DOM element with the given ID. ([#1106](https://github.com/pyscript/pyscript/pull/1106))
2323
- Resored the `output-mode` attribute of `py-repl` tags. If `output-mode` == 'append', the DOM element where output is printed is _not_ cleared before writing new results.
24+
- Load code from the attribute src of py-repl and preload it into the corresponding py-repl tag by use the attribute `str` in your `py-repl` tag([#1292](https://github.com/pyscript/pyscript/pull/1292))
2425

2526
### Plugins
2627
- Plugins may now implement the `beforePyReplExec()` and `afterPyReplExec()` hooks, which are called immediately before and after code in a `py-repl` tag is executed. ([#1106](https://github.com/pyscript/pyscript/pull/1106))

pyscriptjs/src/components/pyrepl.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import { getLogger } from '../logger';
1212
import { InterpreterClient } from '../interpreter_client';
1313
import type { PyScriptApp } from '../main';
1414
import { Stdio } from '../stdio';
15+
import { robustFetch } from '../fetch';
16+
import { _createAlertBanner } from '../exceptions';
1517

1618
const logger = getLogger('py-repl');
1719
const RUNBUTTON = `<svg style="height:20px;width:20px;vertical-align:-.125em;transform-origin:center;overflow:visible;color:green" viewBox="0 0 384 512" aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg"><g transform="translate(192 256)" transform-origin="96 0"><g transform="translate(0,0) scale(1,1)"><path d="M361 215C375.3 223.8 384 239.3 384 256C384 272.7 375.3 288.2 361 296.1L73.03 472.1C58.21 482 39.66 482.4 24.52 473.9C9.377 465.4 0 449.4 0 432V80C0 62.64 9.377 46.63 24.52 38.13C39.66 29.64 58.21 29.99 73.03 39.04L361 215z" fill="currentColor" transform="translate(-192 -256)"></path></g></g></svg>`;
@@ -31,7 +33,7 @@ export function make_PyRepl(interpreter: InterpreterClient, app: PyScriptApp) {
3133
editor: EditorView;
3234
stdout_manager: Stdio | null;
3335
stderr_manager: Stdio | null;
34-
36+
static observedAttributes = ['src'];
3537
connectedCallback() {
3638
ensureUniqueId(this);
3739

@@ -51,6 +53,42 @@ export function make_PyRepl(interpreter: InterpreterClient, app: PyScriptApp) {
5153
logger.debug(`element ${this.id} successfully connected`);
5254
}
5355

56+
get src() {
57+
return this.getAttribute('src');
58+
}
59+
60+
set src(value) {
61+
this.setAttribute('src', value);
62+
}
63+
64+
attributeChangedCallback(name: string, oldVal: string, newVal: string) {
65+
if (name === 'src' 8000 ; && newVal !== oldVal) {
66+
void this.loadReplSrc();
67+
}
68+
}
69+
70+
/**
71+
* Fetch url from src attribute of py-repl tags and
72+
* preload the code from fetch response into the Corresponding py-repl tag,
73+
* but please note that they will not be pre-run unless you click the runbotton.
74+
*/
75+
async loadReplSrc() {
76+
try {
77+
const response = await robustFetch(this.src);
78+
if (!response.ok) {
79+
return;
80+
}
81+
const cmcontentElement = this.querySelector("div[class='cm-content']");
82+
const { lastElementChild } = cmcontentElement;
83+
cmcontentElement.replaceChildren(lastElementChild);
84+
lastElementChild.textContent = await response.text();
85+
logger.info(`loading code from ${this.src} to repl...success`);
86+
} catch (err) {
87+
const e = err as Error;
88+
_createAlertBanner(e.message);
89+
}
90+
}
91+
5492
/** Create and configure the codemirror editor
5593
*/
5694
makeEditor(pySrc: string): EditorView {

pyscriptjs/tests/integration/test_py_repl.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,3 +575,65 @@ def test_repl_output_element_id_change(self):
575575
)
576576
alert_banner = self.page.wait_for_selector(".alert-banner")
577577
assert expected_alert_banner_msg in alert_banner.inner_text()
578+
579+
def test_repl_load_content_from_src(self):
580+
self.writefile("loadReplSrc1.py", "print('1')")
581+
self.pyscript_run(
582+
"""
583+
<py-repl id="py-repl1" output="replOutput1" src="./loadReplSrc1.py"></py-repl>
584+
<div id="replOutput1"></div>
585+
"""
586+
)
587+
successMsg = "[py-repl] loading code from ./loadReplSrc1.py to repl...success"
588+
assert self.console.log.lines[0] == self.PY_COMPLETE
589+
assert self.console.info.lines[-1] == successMsg
590+
591+
py_repl = self.page.locator("py-repl")
592+
code = py_repl.inner_text()
593+
assert "print('1')" in code
594+
595+
def test_repl_src_change(self):
596+
self.writefile("loadReplSrc2.py", "2")
597+
self.writefile("loadReplSrc3.py", "print('3')")
598+
self.pyscript_run(
599+
"""
600+
<py-repl id="py-repl2" output="replOutput2" src="./loadReplSrc2.py"></py-repl>
601+
<div id="replOutput2"></div>
602+
603+
<py-repl id="py-repl3" output="replOutput3">
604+
import js
605+
target_tag = js.document.getElementById("py-repl2")
606+
target_tag.setAttribute("src", "./loadReplSrc3.py")
607+
</py-repl>
608+
<div id="replOutput3"></div>
609+
"""
610+
)
611+
612+
successMsg1 = "[py-repl] loading code from ./loadReplSrc2.py to repl...success"
613+
assert self.console.log.lines[0] == self.PY_COMPLETE
614+
assert self.console.info.lines[-1] == successMsg1
615+
616+
py_repl3 = self.page.locator("py-repl#py-repl3")
617+
py_repl3.locator("button").click()
618+
py_repl2 = self.page.locator("py-repl#py-repl2")
619+
py_repl2.locator("button").click()
620+
self.page.wait_for_selector("py-terminal")
621+
assert self.console.log.lines[-1] == "3"
622+
623+
successMsg2 = "[py-repl] loading code from ./loadReplSrc3.py to repl...success"
624+
assert self.console.info.lines[-1] == successMsg2
625+
626+
def test_repl_src_path_that_do_not_exist(self):
627+
self.pyscript_run(
628+
"""
629+
<py-repl id="py-repl4" output="replOutput4" src="./loadReplSrc4.py"></py-repl>
630+
<div id="replOutput4"></div>
631+
"""
632+
)
633+
errorMsg = (
634+
"(PY0404): Fetching from URL ./loadReplSrc4.py "
635+
"failed with error 404 (Not Found). "
636+
"Are your filename and path correct?"
637+
)
638+
assert self.console.log.lines[0] == self.PY_COMPLETE
639+
assert self.console.error.lines[-1] == errorMsg

0 commit comments

Comments
 (0)
0