-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
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
There was a problem hiding this 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) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
There was a problem hiding this 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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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:
pyscript/pyscriptjs/src/components/pyscript.ts
Lines 8 to 11 in f9194cc
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) |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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 atry/catch
:
pyscript/pyscriptjs/src/pyexec.ts
Lines 14 to 23 in f9194cc
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.
let _uniqueIdCounter = 0; | ||
export function ensureUniqueId(el: HTMLElement) { | ||
if (el.id === "") | ||
el.id = "py-internal-" + _uniqueIdCounter++; |
There was a problem hiding this comment.
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 id
s.
There was a problem hiding this comment.
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 id
s around and start passing real HTMLElement
objects instead.
Thanks @antocuni! ^-^ |
Yet another refactoring to untangle the old mess.
Highlights:
base.ts
,pyscript.ts
andpyrepl.ts
were a tangled mess of code, in which each of them interacted with the others in non-obvious ways. NowPyScript
is no longer a subclass ofBaseEvalElement
and it is much simpler. I removed code for handling the attributesstd-out
andstd-err
because 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-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 calledpyexec
and 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 removesvelte
frompackages.json
, because I don't fully understand the implications: there are various options which mention svelte inrollup.js
andtsconfig.json
, so it's probably better to kill it in its own PR.pyexec.ts
is 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
BaseEvalElement
as 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-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.