8000 [PROPOSAL] REPL output replacement (#439) · TutorExilius/pyscript@f712b13 · GitHub
[go: up one dir, main page]

Skip to content

Commit f712b13

Browse files
marianoweberpre-commit-ci[bot]woxtufpliger
authored
[PROPOSAL] REPL output replacement (pyscript#439)
* fix OutputManager _append setter * fix OutputManager change parameters * fix OutputCtxManager __init__ and change methods * replacing OutputManager pyscript.write with write function * add optional output-append attribute to py-repl * add appendOutput(default: true) to base component * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update pyscriptjs/src/components/pyrepl.ts Co-authored-by: woxtu <woxtup@gmail.com> * change from output-append flag to output-mode attribute * removed type annotation * repositioned setOutputMode call for auto-generated REPLs to work * fixed indentation error for indented input * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added preEvaluate method * moved output-mode logic to preEvaluate * remove static write method from PyScript, add write method to Element * removed err parameter from OutputCtxManager * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add PyScript.write back with a deprecation warning * fix wrong input name Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: woxtu <woxtup@gmail.com> Co-authored-by: Fabio Pliger <fabio.pliger@gmail.com>
1 parent 2f03b18 commit f712b13

File tree

3 files changed

+93
-46
lines changed

3 files changed

+93
-46
lines changed

pyscriptjs/src/components/base.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export class BaseEvalElement extends HTMLElement {
3232
outputElement: HTMLElement;
3333
errorElement: HTMLElement;
3434
theme: string;
35+
appendOutput: boolean;
3536

3637
constructor() {
3738
super();
@@ -40,16 +41,38 @@ export class BaseEvalElement extends HTMLElement {
4041
this.shadow = this.attachShadow({ mode: 'open' });
4142
this.wrapper = document.createElement('slot');
4243
this.shadow.appendChild(this.wrapper);
44+
this.setOutputMode("append");
4345
}
4446

4547
addToOutput(s: string) {
4648
this.outputElement.innerHTML += '<div>' + s + '</div>';
4749
this.outputElement.hidden = false;
4850
}
4951

52+
setOutputMode(defaultMode = "append") {
53+
const mode = this.hasAttribute('output-mode') ? this.getAttribute('output-mode') : defaultMode;
54+
55+
switch (mode) {
56+
case "append":
57+
this.appendOutput = true;
58+
break;
59+
case "replace":
60+
this.appendOutput = false;
61+
break;
62+
default:
63+
console.log(`${this.id}: custom output-modes are currently not implemented`);
64+
}
65+
}
66+
67+
// subclasses should overwrite this method to define custom logic
68+
// before code gets evaluated
69+
preEvaluate(): void {
70+
return null;
71+
}
72+
5073
// subclasses should overwrite this method to define custom logic
5174
// after code has been evaluated
52-
postEvaluate() {
75+
postEvaluate(): void {
5376
return null;
5477
}
5578

@@ -99,6 +122,8 @@ export class BaseEvalElement extends HTMLElement {
99122

100123
async evaluate(): Promise<void> {
101124
console.log('evaluate');
125+
this.preEvaluate();
126+
102127
const pyodide = runtime;
103128
let source: string;
104129
let output;
@@ -110,13 +135,13 @@ export class BaseEvalElement extends HTMLElement {
110135

111136
if (source.includes('asyncio')) {
112137
await pyodide.runPythonAsync(
113-
`output_manager.change("` + this.outputElement.id + `", "` + this.errorElement.id + `")`,
138+
`output_manager.change(out="${this.outputElement.id}", err="${this.errorElement.id}", append=${this.appendOutput ? 'True' : 'False'})`,
114139
);
115140
output = await pyodide.runPythonAsync(source);
116141
await pyodide.runPythonAsync(`output_manager.revert()`);
117142
} else {
118143
output = pyodide.runPython(
119-
`output_manager.change("` + this.outputElement.id + `", "` + this.errorElement.id + `")`,
144+
`output_manager.change(out="${this.outputElement.id}", err="${this.errorElement.id}", append=${this.appendOutput ? 'True' : 'False'})`,
120145
);
121146
output = pyodide.runPython(source);
122147
pyodide.runPython(`output_manager.revert()`);
@@ -143,8 +168,8 @@ export class BaseEvalElement extends HTMLElement {
143168
this.errorElement.style.removeProperty('display');
144169
}
145170
}
146-
removeClasses(this.errorElement, ['bg-red-200', 'p-2']);
147171
}
172+
removeClasses(this.errorElement, ['bg-red-200', 'p-2']);
148173

149174
this.postEvaluate();
150175
} catch (err) {

pyscriptjs/src/components/pyrepl.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,6 @@ export class PyRepl extends BaseEvalElement {
141141

142142
if (this.hasAttribute('output')) {
143143
this.errorElement = this.outputElement = document.getElementById(this.getAttribute('output'));
144-
145-
// in this case, the default output-mode is append, if hasn't been specified
146-
if (!this.hasAttribute('output-mode')) {
147-
this.setAttribute('output-mode', 'append');
148-
}
149144
} else {
150145
if (this.hasAttribute('std-out')) {
151146
this.outputElement = document.getElementById(this.getAttribute('std-out'));
@@ -176,6 +171,13 @@ export class PyRepl extends BaseEvalElement {
176171
this.outputElement.hidden = false;
177172
}
178173

174+
preEvaluate(): void {
175+
this.setOutputMode("replace");
176+
if(!this.appendOutput) {
177+
this.outputElement.innerHTML = '';
178+
}
179+
}
180+
179181
postEvaluate(): void {
180182
this.outputElement.hidden = false;
181183
this.outputElement.style.display = 'block';
@@ -189,8 +191,15 @@ export class PyRepl extends BaseEvalElement {
189191
const newPyRepl = document.createElement('py-repl');
190192
newPyRepl.setAttribute('root', this.getAttribute('root'));
191193
newPyRepl.id = this.getAttribute('root') + '-' + nextExecId.toString();
192-
newPyRepl.setAttribute('auto-generate', '');
193-
this.removeAttribute('auto-generate');
194+
195+
if(this.hasAttribute('auto-generate')) {
196+
newPyRepl.setAttribute('auto-generate', '');
197+
this.removeAttribute('auto-generate');
198+
}
199+
200+
if(this.hasAttribute('output-mode')) {
201+
newPyRepl.setAttribute('output-mode', this.getAttribute('output-mode'));
202+
}
194203

195204
const addReplAttribute = (attribute: string) => {
196205
if (this.hasAttribute(attribute)) {
@@ -209,9 +218,10 @@ export class PyRepl extends BaseEvalElement {
209218

210219
getSourceFromElement(): string {
211220
const sourceStrings = [
212-
`output_manager.change("` + this.outputElement.id + `")`,
221+
`output_manager.change(out="${this.outputElement.id}", append=True)`,
213222
...this.editor.state.doc.toString().split('\n'),
214223
];
224+
215225
return sourceStrings.join('\n');
216226
}
217227

pyscriptjs/src/pyscript.py

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -107,31 +107,22 @@ def format_mime(obj):
107107
class PyScript:
108108
loop = loop
109109

110-
@staticmethod
111-
def write(element_id, value, append=False, exec_id=0):
112-
"""Writes value to the element with id "element_id"""
113-
console.log(f"APPENDING: {append} ==> {element_id} --> {value}")
114-
if append:
115-
child = document.createElement("div")
116-
element = document.querySelector(f"#{element_id}")
117-
if not element:
118-
return
119-
exec_id = exec_id or element.childElementCount + 1
120-
element_id = child.id = f"{element_id}-{exec_id}"
121-
element.appendChild(child)
122-
123-
element = document.getElementById(element_id)
124-
html, mime_type = format_mime(value)
125-
if mime_type in ("application/javascript", "text/html"):
126-
script_element = document.createRange().createContextualFragment(html)
127-
element.appendChild(script_element)
128-
else:
129-
element.innerHTML = html
130-
131110
@staticmethod
132111
def run_until_complete(f):
133112
_ = loop.run_until_complete(f)
134113

114+
@staticmethod
115+
def write(element_id, value, append=False, exec_id=0):
116+
"""Writes value to the element with id "element_id"""
117+
Element(element_id).write(value=value, append=append)
118+
console.warn(
119+
dedent(
120+
"""PyScript Deprecation Warning: PyScript.write is
121+
marked as deprecated and will be removed sometime soon. Please, use
122+
Element(<id>).write instead."""
123+
)
124+
)
125+
135126

136127
class Element:
137128
def __init__(self, element_id, element=None):
@@ -159,9 +150,25 @@ def innerHtml(self):
159150

160151
def write(self, value, append=False):
161152
console.log(f"Element.write: {value} --> {append}")
162-
# TODO: it should be the opposite... pyscript.write should use the Element.write
163-
# so we can consolidate on how we write depending on the element type
164-
pyscript.write(self._id, value, append=append)
153+
154+
out_element_id = self.id
155+
156+
if append:
157+
child = document.createElement("div")
158+
exec_id = self.element.childElementCount + 1
159+
out_element_id = child.id = f"{self.id}-{exec_id}"
160+
self.element.appendChild(child)
161+
162+
out_element = document.querySelector(f"#{out_element_id}")
163+
164+
html, mime_type = format_mime(value)
165+
if mime_type in ("application/javascript", "text/html"):
166+
script_element = document.createRange().createContextualFragment(html)
167+
out_element.appendChild(script_element)
168+
else:
169+
if html == "\n":
170+
return
171+
out_element.innerHTML = html
165172

166173
def clear(self):
167174
if hasattr(self.element, "value"):
@@ -374,7 +381,7 @@ def __init__(self, out=None, output_to_console=True, append=True):
374381
self.output_to_console = output_to_console
375382
self._append = append
376383

377-
def change(self, out=None, err=None, output_to_console=True, append=True):
384+
def change(self, out=None, output_to_console=True, append=True):
378385
self._prev = self._out
379386
self._out = out
380387
self.output_to_console = output_to_console
@@ -385,32 +392,37 @@ def revert(self):
385392
console.log("----> reverted")
386393
self._out = self._prev
387394

388-
def write(self, txt):
389-
console.log("writing to", self._out, txt, self._append)
395+
def write(self, value):
396+
console.log("writing to", self._out, value, self._append)
390397
if self._out:
391-
pyscript.write(self._out, txt, append=self._append)
398+
Element(self._out).write(value, self._append)
399+
392400
if self.output_to_console:
393-
console.log(self._out, txt)
401+
console.log(self._out, value)
394402

395403

396404
class OutputManager:
397405
def __init__(self, out=None, err=None, output_to_console=True, append=True):
398406
sys.stdout = self._out_manager = OutputCtxManager(
399-
out, output_to_console, append
407+
out=out, output_to_console=output_to_console, append=append
400408
)
401409
sys.stderr = self._err_manager = OutputCtxManager(
402-
err, output_to_console, append
410+
out=err, output_to_console=output_to_console, append=append
403411
)
404412
self.output_to_console = output_to_console
405413
self._append = append
406414

407415
def change(self, out=None, err=None, output_to_console=True, append=True):
408-
self._out_manager.change(out, output_to_console, append)
416+
self._out_manager.change(
417+
out=out, output_to_console=output_to_console, append=append
418+
)
409419
sys.stdout = self._out_manager
410-
self._err_manager.change(err, output_to_console, append)
420+
self._err_manager.change(
421+
out=err, output_to_console=output_to_console, append=append
422+
)
411423
sys.stderr = self._err_manager
412424
self.output_to_console = output_to_console
413-
self.append = append
425+
self._append = append
414426

415427
def revert(self):
416428
self._out_manager.revert()

0 commit comments

Comments
 (0)
0