pyscript/pyscript

input() should raise a clean error in the main thread

antocuni opened this issue · 13 comments

Consider the following snippet:

        <py-script>
          input("what's your name?")
        </py-script>

I get the following:
image

which is technically correct because you cannot read from stdin, so it is an I/O error.
However, I think we could be more user friendly and emit a clearer error message. This could be done by monkey-patching input in the main thread:

import builtins
def input(prompt=""):
    raise Exception("input() doesn't work when PyScript runs in the main thread. Consider using the worker attribute, see https://docs.pyscript.net/blablabla")

builtins.input = input

This could be done by monkey-patching input in the main thread:

what about workers? they'll have the exact same issue ... should we monkey patch those too? because that looks hostile for the interactive terminal if applied by default.

could also delegate to window.prompt

what about workers? they'll have the exact same issue ... should we monkey patch those too? because that looks hostile for the interactive terminal if applied by default.

IMHO we should do the following:

  • input() without a terminal: raise an error explaining that you need a terminal
  • input() with a terminal in main: raise an error explaining that you need a worker
  • input() with a terminal in a worker: it just works

could also delegate to window.prompt

we could, but I don't think it's a good idea: this is surely not what people expect.

well ... AFAIK input can work as window.propmpt in both main and worker ... are we sure we want to break explicit extensibility? if builtins.input can be overridden after, should that work? I am just trying to figure out the best way forward ... so far we made a few choices in PyScript that lead users to use polyscript only as PyScript might be too opinionated for them, hence limiting their use cases ... these are mostly more advanced users though, but we need to consider them too, imho.

well ... AFAIK input can work as window.propmpt in both main and worker ... are we sure we want to break explicit extensibility? if builtins.input can be overridden after, should that work?

I don't understand: when is "later" and by who?
I don't know if it answer your question, but basically you can patch builtins.input at any point in time.

I am just trying to figure out the best way forward ... so far we made a few choices in PyScript that lead users to use polyscript only as PyScript might be too opinionated for them, hence limiting their use cases ... these are mostly more advanced users though, but we need to consider them too, imho.

I agree that we should support advanced use cases. My personal opinion is that for such cases we should:

  1. by default, do the "best thing" for beginners (in this case, input() should give a nice error)
  2. if/when it makes sense, make it possible for advanced users to override the default behavior in (1); for example, we could provide a config option to disable the overriding of input(), or something like that.

so far we made a few choices in PyScript that lead users to use polyscript only as PyScript might be too opinionated for them, hence limiting their use cases

For what it's worth input() -> window.prompt is an explicit choice being made by Pyodide. Users of Pyodide/PolyScript/PyScript can always restore the default behavior from JS or Python using pyodide.setStdin() if they want to.

input() should give a nice error

I'm +1 on this - the input() shows a prompt, but in general, what to do with that hasn't been super clear to users, especially because the text of the prompt itself is not shown.

Pyodide provides an out of the box option to error on stdin: pyodide.setStdin({ error: true }); This should provide a consistent error from Pyodide, that we in PyScript can catch and re-raise/clarify with the appropriate clarifying text, as Antonio suggests.

my 2cents, pyscript should remove as much friction for first time users as possible and I feel like showing an error is worse than defaulting to some behavior unless you are actually highly prefer the user to make the change:

input() with a terminal in main: raise an error explaining that you need a worker

I think this is fair, but defaulting to window.prompt is probably ok too.

input() with a terminal in a worker: it just works

nice

input() without a terminal: raise an error explaining that you need a terminal

are you trying to lead the user to adding a terminal whenever they try to use input()? if so cool, if not, I would think window.prompt should be defaulted to.

Imagine a user tries to input on the main thread without a terminal (this is the easiest case right)? the message asks him to add a terminal. he adds one and another message shows up telling him to use worker. I would've probably quit already.

are you trying to lead the user to adding a terminal whenever they try to use input()? if so cool, if not, I would think window.prompt should be defaulted to.

IMHO yes, we should.

The three most prominent use cases that I have in mind are:

  1. simple exercises for beginners such as the guess the number game
  2. debugging (such as pdb.set_trace())
  3. console-based REPL

For all of them, you need a terminal: you cannot really do them using window.prompt.

Genuine question ahead: do you have any real use case in mind in which window.prompt would be actually useful?

Imagine a user tries to input on the main thread without a terminal (this is the easiest case right)? the message asks him to add a terminal. he adds one and another message shows up telling him to use worker. I would've probably quit already.

that's a fair point, the message should tell him to use a terminal AND worker together :)

the good thing about the window.prompt is that we can show a message before whatever prompt was meant ... so here my counter proposal for first 2 points + a question around the third:

  • input() without a terminal: prepend in the window.prompt: "did you mean to use the terminal attribute?"
  • input() with a terminal in main: prepend in the window.prompt: "*did you know: if you use worker attribute this prompt will be a better experience?"
  • input() with a terminal in a worker: it just works it cannot work, unless we patch the input() to automatically invoke pyscript.repl() or pyscript.interactive() in case that's not the case already ... how else could we ask for an input in a non-interactive terminal?

It seems to me that this conversation is going a bit off topic.
My original proposal was very easy: currently input() produces an error, and I would like a better error message. This should be pretty uncontroversial.

Then, if we want to change the behavior and use window.prompt, we should start a separate discussion/proposal and see what the other maintainers think.


  • input() with a terminal in a worker: it just works it cannot work, unless we path the input() to automatically invoke pyscript.repl() or pyscript.interactive() in case that's not the case already ... how else could we ask for an input in a non-interactive terminal?

I'm very confused. Why input() in a terminal+worker would not work? Why is it related to calling or not calling the hypotetical pyscript.repl()?
🤔

Why input() in a terminal+worker would not work?

because it needs to be interactive? anyway, I'll throw a nicer error and call it a day.

Why input() in a terminal+worker would not work?

because it needs to be interactive? anyway, I'll throw a nicer error and call it a day.

yes but in a worker the terminal IS interactive, even without the repl.
E.g., this example "just works"
https://pyscript.com/@antocuni/2023-11-input/latest