Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion cypress/e2e/spec-wc-pyodide.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import {
stopProject,
} from "../helpers/editor.js";

const origin = "http://localhost:3011/web-component.html";
const origin =
Cypress.env("EDITOR_ORIGIN") || "http://localhost:3011/web-component.html";

beforeEach(() => {
cy.intercept("*", (req) => {
Expand Down Expand Up @@ -226,6 +227,20 @@ print(text_out)
);
});

it("clears the console when os.system('clear') is called", () => {
runCode('print("before")\nimport os\nos.system("clear")\nprint("after")');
getPythonConsoleOutput().should("contain", "after");
getPythonConsoleOutput().should("not.contain.text", "before");
});

it("clears buffered output written without a newline before clearing", () => {
runCode(
'import os\nprint("partial", end="")\nos.system("clear")\nprint("after")',
);
getPythonConsoleOutput().should("contain", "after");
getPythonConsoleOutput().should("not.contain.text", "partial");
});

it("clears user-defined variables between code runs", () => {
runCode("a = 1\nprint(a)");
getPythonConsoleOutput().should("contain", "1");
Expand Down
16 changes: 16 additions & 0 deletions src/PyodideWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ const PyodideWorker = () => {
postMessage({ method: "handleFileWrite", filename, content, mode });
},
locals: () => pyodide.runPython("globals()"),
clear_console: () => postMessage({ method: "handleClear" }),
},
};

Expand Down Expand Up @@ -443,6 +444,21 @@ const PyodideWorker = () => {

pyodide.registerJsModule("basthon", fakeBasthonPackage);

await pyodide.runPythonAsync(`
import os as _os, sys as _sys, basthon as _basthon
_real_system = _os.system
def _patched_system(command):
if isinstance(command, str) and command.strip().lower() in ("clear", "cls"):
_sys.stdout.write("\\n")
_sys.stderr.write("\\n")
_sys.stdout.flush()
_sys.stderr.flush()
_basthon.kernel.clear_console()
return 0
return _real_system(command)
_os.system = _patched_system
`);

await pyodide.runPythonAsync(`
__old_input__ = input
def __patched_input__(prompt=False):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ const PyodideRunner = ({
case "handleOutput":
handleOutput(data.stream, data.content);
break;
case "handleClear":
clearConsole();
break;
case "handleError":
handleError(
data.file,
Expand Down Expand Up @@ -210,6 +213,10 @@ const PyodideRunner = ({
node.scrollTop = node.scrollHeight;
};

const clearConsole = () => {
output.current.innerHTML = "";
};
Comment on lines +216 to +218

const handleError = (file, line, mistake, type, info) => {
let errorMessage;

Expand Down Expand Up @@ -290,7 +297,7 @@ const PyodideRunner = ({
};

const handleRun = async () => {
output.current.innerHTML = "";
clearConsole();
dispatch(setError(""));
dispatch(setFriendlyError(null));
setVisuals([]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,28 @@ describe("When output is received", () => {
});
});

describe("When a clear console message is received", () => {
beforeEach(() => {
render(
<Provider store={store}>
<PyodideRunner active={true} />,
</Provider>,
);

const worker = PyodideWorker.getLastInstance();
worker.postMessageFromWorker({
method: "handleOutput",
stream: "stdout",
content: "hello",
});
worker.postMessageFromWorker({ method: "handleClear" });
});

test("it clears previously printed output", () => {
expect(screen.queryByText("hello")).not.toBeInTheDocument();
});
});

describe("When file write event is received", () => {
let worker;
beforeEach(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,29 @@ describe("PyodideWorker", () => {
);
});

test("it patches os.system to handle clear/cls", async () => {
expect(pyodide.runPythonAsync).toHaveBeenCalledWith(
expect.stringMatching(/os\.system/),
);
});
Comment on lines +99 to +103

test("it forces batched output to flush before clearing", async () => {
expect(pyodide.runPythonAsync).toHaveBeenCalledWith(
expect.stringMatching(/_sys\.stdout\.write\("\\n"\)/),
);
expect(pyodide.runPythonAsync).toHaveBeenCalledWith(
expect.stringMatching(/_sys\.stderr\.write\("\\n"\)/),
);
});
Comment on lines +105 to +112

test("the basthon clear_console bridge posts handleClear", () => {
const basthonCall = pyodide.registerJsModule.mock.calls.find(
([name]) => name === "basthon",
);
basthonCall[1].kernel.clear_console();
expect(global.postMessage).toHaveBeenCalledWith({ method: "handleClear" });
});
Comment on lines +114 to +120

test("it patches urllib and requests modules", async () => {
expect(pyodide.runPythonAsync).toHaveBeenCalledWith(
expect.stringMatching(/pyodide_http.patch_all()/),
Expand Down
Loading