Skip to content

Refactor how py-script are executed, kill scriptQueue store, introduce pyExec #881

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 34 commits into from
Oct 23, 2022

Conversation

antocuni
Copy link
Contributor

@antocuni antocuni commented Oct 22, 2022

Yet another refactoring to untangle the old mess.
Highlights:

base.ts, pyscript.ts and pyrepl.ts were a tangled mess of code, in which each of them interacted with the others in non-obvious ways. Now PyScript is no longer a subclass of BaseEvalElement and it is much simpler. I removed code for handling the attributes std-out and std-err because they are no longer needed with the new display() logic.

The logic for executing python code is now in pyexec.ts: so we are decoupling the process of "finding" the python code (handled by the py-script web component) and the logic to actually execute it. This has many advantages, including the fact that it will be more easily usable by other components (e.g. pyrepl). Also, note that it's called pyexec and not pyeval: in the vast majority of cases in Python you have statements to execute, and almost never expressions to evaluate.

I killed the last remaining global store, scriptQueue 🎉. As a bonus effect, now we automatically do the correct thing when a <py-script> tag is dynamically added to the DOM (I added a test for it). I did not remove svelte from packages.json, because I don't fully understand the implications: there are various options which mention svelte in rollup.js and tsconfig.json, so it's probably better to kill it in its own PR.

pyexec.ts is also responsible of handling the default target for display() and correct handling/visualization of exceptions. I fixed/improved/added display/output tests in the process.
I also found a problem though, see issue #878, so I improved the test and marked it as xfail.

I removed BaseEvalElement as the superclass of most components. Now the only class which inherits from it is PyRepl. In a follow-up PR, I plan to merge them into a single class and do more cleanup.

During the refactoring, I killed guidGenerator: now instead of generating random py-* IDs which are very hard to read for humans, we generated py-internal-X IDs, where X is 0, 1, 2, 3, etc. This makes writing tests and debugging much easier.

I improved a lot our test machinery: it turns out that PR #829 broke the ability to use/view sourcemaps inside the playwright browser (at least on my machine).
For some reason chromium is unable to find sourcemaps if you use playwrights internal routing. So I reintroduced the http_server fixture which was removed by that PR, and added a pytest option --no-fake-server to use it instead, useful for debugging. By default we are still using the fakeserver though (which is faster and parallelizable).

Similarly, I added --dev which implies --headed and also automatically open chrome dev tools.

…comment which explains some of the problems of the current approach. But I don't want to tackled it in this PR
When using this option, you use a real http server instead of using
playwright's internal routing.

There are cases in which it's useful, in particular:

1) if you want to test the page on different browser

2) if you want to use the chrome dev tools, because apparently when using the
fake server chromium is unable to locate the sourcemaps

This commit reintroduces some of the code which was killed by PR #829.
…easier to test and to use in the debugger for humans
…eEvalElement. This contains a bit of mechnical copy&paste but I will simplify the code in the next commits
…at we can execute a very simple python code, no need to check the behavior of display()
… using a queue of scripts, we immediately run the python code inside connectedCallback. This has also the advantage that if we dynamically add a <py-script> tag to the DOM, the python code is automatically executed, as the new test demonstrates (the test would have been failed before this commit)
…level because you would need to mock the whole Runtime, and you would end up testing the mock instead of something actually useful. PyScript is battle-tested a lot by integration tests
…out multiple display. It is about implicit display() target, so move it it the correct class and improve the test to be less dependent on the exact <div> id
@antocuni antocuni changed the title WIP: refactor how py-script are executed, kill scriptQueue store, introduce pyExec Refactor how py-script are executed, kill scriptQueue store, introduce pyExec Oct 23, 2022
@antocuni antocuni marked this pull request as ready for review October 23, 2022 17:44
Copy link
Contributor

@tedpatrick tedpatrick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Goodbye stores!
No issues.

this.errorElement = this.outputElement;
}
}
export function make_PyScript(runtime: Runtime) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious - what's the advantage of using a function that defines the class rather than just the class definition?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the fact that I can pass runtime from the outside. Note that runtime is used from connectedCallback, and since this is a method called by the browser, we don't really have any other way to pass it explicitly.
The alternative was to use a global variable as it was before, but we saw that it caused way too many problems.

}


function displayPyException(err: any, errElem: HTMLElement) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this as a start - having a unified way to intercept and handle errors will be nice, but really good to have this behavior in one place.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. The next step will be to have an unified place where to display errors when there is no other obvious place (e.g. if you raise an exception from an event handler).

@antocuni antocuni merged commit f9194cc into main Oct 23, 2022
@antocuni antocuni deleted the antocuni/kill-script-queue branch October 23, 2022 21:31
Copy link
Contributor

@marimeireles marimeireles left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool PR! :)

@@ -58,11 +52,11 @@ class PyScriptApp {
config: AppConfig;
loader: PyLoader;
runtime: Runtime;
PyScript: any; // XXX would be nice to have a more precise type for the class itself
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[question] Why do we have PyScript here instead of pyscript does this mean something different?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It stands for the PyScript object I guess... and the other ones are function names?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to, this is the PyScript class, which now is created at runtime:

export function make_PyScript(runtime: Runtime) {
class PyScript extends HTMLElement {

so basically, it's the equivalent of what formerly was import { PyScript } from './components/pyscript'



// XXX this is used by base.ts but should be removed once we complete the refactoring
export async function pyExecDontHandleErrors(runtime: Runtime, pysrc: string, out: HTMLElement)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion/question] I don't understand this name. Could you please explain it to me?
This piece of code is evaluating the strings coming from the front-end into python code. Should it be renamed?
Why aren't we catching errors here? Why keep this on base.ts?

Copy link
Contributor Author

@antocuni antocuni Oct 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this name. Could you please explain it to me?
pyExec executes python code inside a try/catch:

try {
await runtime.run(pysrc);
}
catch (err) {
// XXX: currently we display exceptions in the same position as
// the output. But we probably need a better way to do that,
// e.g. allowing plugins to intercept exceptions and display them
// in a configurable way.
displayPyException(err, outElem);
}

pyExecDontHandleErrors does the same but without the try/catch: this means that if a python-level exception is raised, it will be propagated outside (and thus needs to be intercepted by someone else).

Why aren't we catching errors here? Why keep this on base.ts?

as the comment says, the plan is to kill it eventually and refactor base.ts/pyrepl.ts to use pyExec instead. But the old code was too messy and I didn't want to touch too many things at once, it's what I plan to do next.

Comment on lines +41 to +44
let _uniqueIdCounter = 0;
export function ensureUniqueId(el: HTMLElement) {
if (el.id === "")
el.id = "py-internal-" + _uniqueIdCounter++;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this =)
I think next step is to actually try to remove all of these ids.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes but it's not completely easy because Element.write relies on it. But yes, we should stop passing ids around and start passing real HTMLElement objects instead.

@marimeireles
Copy link
Contributor

Thanks @antocuni! ^-^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

5 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy