Refactor how py-script are executed, kill scriptQueue store, introduce pyExec#881
Conversation
…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.
…ly open the devtools panel
…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)
…t>, and the relevant test
…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
tedpatrick
left a comment
There was a problem hiding this comment.
Goodbye stores!
No issues.
| this.errorElement = this.outputElement; | ||
| } | ||
| } | ||
| export function make_PyScript(runtime: Runtime) { |
There was a problem hiding this comment.
Just curious - what's the advantage of using a function that defines the class rather than just the class definition?
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
| config: AppConfig; | ||
| loader: PyLoader; | ||
| runtime: Runtime; | ||
| PyScript: any; // XXX would be nice to have a more precise type for the class itself |
There was a problem hiding this comment.
[question] Why do we have PyScript here instead of pyscript does this mean something different?
There was a problem hiding this comment.
It stands for the PyScript object I guess... and the other ones are function names?
There was a problem hiding this comment.
to, this is the PyScript class, which now is created at runtime:
pyscript/pyscriptjs/src/components/pyscript.ts
Lines 8 to 11 in f9194cc
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) |
There was a problem hiding this comment.
[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?
There was a problem hiding this comment.
I don't understand this name. Could you please explain it to me?
pyExecexecutes python code inside atry/catch:
pyscript/pyscriptjs/src/pyexec.ts
Lines 14 to 23 in f9194cc
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.
| let _uniqueIdCounter = 0; | ||
| export function ensureUniqueId(el: HTMLElement) { | ||
| if (el.id === "") | ||
| el.id = "py-internal-" + _uniqueIdCounter++; |
There was a problem hiding this comment.
I like this =)
I think next step is to actually try to remove all of these ids.
There was a problem hiding this comment.
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.
|
Thanks @antocuni! ^-^ |
Yet another refactoring to untangle the old mess.
Highlights:
base.ts,pyscript.tsandpyrepl.tswere a tangled mess of code, in which each of them interacted with the others in non-obvious ways. NowPyScriptis no longer a subclass ofBaseEvalElementand it is much simpler. I removed code for handling the attributesstd-outandstd-errbecause they are no longer needed with the newdisplay()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 thepy-scriptweb 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 calledpyexecand notpyeval: 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 removesveltefrompackages.json, because I don't fully understand the implications: there are various options which mention svelte inrollup.jsandtsconfig.json, so it's probably better to kill it in its own PR.pyexec.tsis also responsible of handling the default target fordisplay()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
BaseEvalElementas the superclass of most components. Now the only class which inherits from it isPyRepl. 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 randompy-*IDs which are very hard to read for humans, we generatedpy-internal-XIDs, 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_serverfixture which was removed by that PR, and added a pytest option--no-fake-serverto use it instead, useful for debugging. By default we are still using the fakeserver though (which is faster and parallelizable).Similarly, I added
--devwhich implies--headedand also automatically open chrome dev tools.