Open asmeurer opened 6 years ago
Hi @asmeurer,
I forgot to reply to this one.
The use case makes sense indeed. I'll consider adding an argument to the Prompt.prompt
function for accepting the input right away. If the purpose is to actually render the prompt to the output for feedback, then this does make sense. (Just give me some time for this. I'm preparing the 2.0 release and don't want to push too many new features right now.)
Great. Actually I figured out that the overabundance of CPR codes printed to my terminal is mostly coming from me using PipeInput. The stdout Output class basically implicitly assumes that the corresponding Input class is a stdin class. I'm still working on transferring to 2.0, so I can't say yet if this is fixed there. The separation of the two classes does still exist there, as far as I know. To me, this indicates that one should never use PipeInput for interactive applications (it should only be used for unit testing basically). Or if you do use it, you need to stub out the cpr request in the output class simultaneously. So in short, if a feature like this could let me completely eliminate PipeInput from my non-testing code, it should hopefully get rid of the CPR bugs for me.
The use case makes sense indeed. I'll consider adding an argument to the Prompt.prompt function for accepting the input right away. If the purpose is to actually render the prompt to the output for feedback, then this does make sense. (Just give me some time for this. I'm preparing the 2.0 release and don't want to push too many new features right now.)
How exactly would you do this? I would implement this myself, but I'm completely stumped on how it would be done. Maybe I'm missing something obvious. The eventloop code is still pretty opaque to me.
I'll try something today. Are you at PyCon?
Something like this should work (some details are still missing):
$ git diff
diff --git a/prompt_toolkit/shortcuts/prompt.py b/prompt_toolkit/shortcuts/prompt.py
index f5e7530..56eb90f 100644
--- a/prompt_toolkit/shortcuts/prompt.py
+++ b/prompt_toolkit/shortcuts/prompt.py
@@ -692,11 +692,15 @@ class PromptSession(object):
for name in self._fields:
setattr(self, name, backup[name])
+ def pre_run():
+ self.default_buffer.text = 'hello'
+ self.app.exit(result='hello')
+
def run_sync():
with self._auto_refresh_context():
try:
self.default_buffer.reset(Document(self.default))
- return self.app.run(inputhook=self.inputhook)
+ return self.app.run(inputhook=self.inputhook, pre_run=pre_run)
finally:
restore()
No I wasn't able to make it to PyCon this year.
That looks like it works. The self.default_buffer.text
part is unnecessary (just use the default
argument to set the text). Something like
diff --git a/prompt_toolkit/shortcuts/prompt.py b/prompt_toolkit/shortcuts/prompt.py
index 6ba8320..1d2bc3b 100644
--- a/prompt_toolkit/shortcuts/prompt.py
+++ b/prompt_toolkit/shortcuts/prompt.py
@@ -667,7 +667,7 @@ class PromptSession(object):
reserve_space_for_menu=None, enable_system_prompt=None,
enable_suspend=None, enable_open_in_editor=None,
tempfile_suffix=None, inputhook=None,
- async_=False):
+ async_=False, accept_immedietly=False):
"""
Display the prompt. All the arguments are the same as for the
:class:`~.PromptSession` class.
@@ -692,11 +692,15 @@ class PromptSession(object):
for name in self._fields:
setattr(self, name, backup[name])
+ def pre_run():
+ if accept_immedietly:
+ self.app.exit(result=default)
+
def run_sync():
with self._auto_refresh_context():
try:
self.default_buffer.reset(Document(self.default))
- return self.app.run(inputhook=self.inputhook)
+ return self.app.run(inputhook=self.inputhook, pre_run=pre_run)
finally:
restore()
Except the accept_handler is not called. If I try adding self.default_buffer.validate_and_handle()
I get
Traceback (most recent call last):
File "/Users/aaronmeurer/Documents/prompt-toolkit/prompt_toolkit/application/application.py", line 616, in _run_async2
result = yield f
File "/Users/aaronmeurer/Documents/prompt-toolkit/prompt_toolkit/eventloop/coroutine.py", line 86, in step_next
new_f = coroutine.send(None)
File "/Users/aaronmeurer/Documents/prompt-toolkit/prompt_toolkit/application/application.py", line 510, in _run_async
self._pre_run(pre_run)
File "/Users/aaronmeurer/Documents/prompt-toolkit/prompt_toolkit/application/application.py", line 483, in _pre_run
pre_run()
File "/Users/aaronmeurer/Documents/prompt-toolkit/prompt_toolkit/shortcuts/prompt.py", line 698, in pre_run
self.app.exit(result=default)
File "/Users/aaronmeurer/Documents/prompt-toolkit/prompt_toolkit/application/application.py", line 696, in exit
raise Exception('Return value already set.')
Exception: Return value already set.
In that case, self.app.exit
doesn't need to be called.
There is also a typo in immediately
. Further, I think, similar code needs to be added to run_async
.
Yeah I figured that I misspelled it :) That's probably a sign that the flag should be called something else.
Hmm, well validate_and_handle isn't actually what you want, because that adds a pre_run callable to reset the buffer, which would then clear the text when the app is actually run.
I'm trying to wrap my mind around the control flow here and what the right way to do this is.
To clarify, it's because _pre_run
calls pre_run
then the pre_run_callables
: https://github.com/jonathanslenders/python-prompt-toolkit/blob/f255927556353e6a0a2339947cceb8bf4d31f30e/prompt_toolkit/application/application.py#L480-L488
This patch "fixes it", but I don't think it's a necessarily a good one.
diff --git a/prompt_toolkit/application/application.py b/prompt_toolkit/application/application.py
index 34ae7ee..e51e623 100644
--- a/prompt_toolkit/application/application.py
+++ b/prompt_toolkit/application/application.py
@@ -483,7 +483,7 @@ class Application(object):
pre_run()
# Process registered "pre_run_callables" and clear list.
- for c in self.pre_run_callables:
+ for c in self.pre_run_callables[:]:
c()
del self.pre_run_callables[:]
diff --git a/prompt_toolkit/shortcuts/prompt.py b/prompt_toolkit/shortcuts/prompt.py
index f5e7530..a61e9c3 100644
--- a/prompt_toolkit/shortcuts/prompt.py
+++ b/prompt_toolkit/shortcuts/prompt.py
@@ -667,7 +667,7 @@ class PromptSession(object):
reserve_space_for_menu=None, enable_system_prompt=None,
enable_suspend=None, enable_open_in_editor=None,
tempfile_suffix=None, inputhook=None,
- async_=False):
+ async_=False, accept_immediately=False):
"""
Display the prompt. All the arguments are the same as for the
:class:`~.PromptSession` class.
@@ -692,10 +692,15 @@ class PromptSession(object):
for name in self._fields:
setattr(self, name, backup[name])
+ def pre_run():
+ if accept_immediately:
+ self.default_buffer.validate_and_handle()
+
def run_sync():
with self._auto_refresh_context():
try:
self.default_buffer.reset(Document(self.default))
+ self.app.pre_run_callables.append(pre_run)
return self.app.run(inputhook=self.inputhook)
finally:
restore()
@jonathanslenders any thoughts on this? This is the only thing keeping me from updating to 2.0.
Yes, this is one of the next things to do. I didn't want to postpone the 2.0 release any longer, but I'm coming back to this issue.
@asmeurer: Can you have a look at this pull request? https://github.com/jonathanslenders/python-prompt-toolkit/pull/641
It's close to the code that you proposed, with a couple of small changes.
I'll take a look.
I believe this is resolved, or can at least be closed as stale.
I've been using a queue, as described at https://github.com/jonathanslenders/python-prompt-toolkit/issues/519#issuecomment-318978114, to make it so that I can send text to a prompt programatically. For instance, I have a
-c
startup flag that allows to run a command as the first prompt. I also use this in the bracketed paste handler, as described in that issue.The way I've done this is to pass items in the command queue through a PipeInput and use that as the input to the cli when there is a queue (and a normal
input=None
otherwise).However, I'm not a fan of this approach, because it means that I have to manipulate the input command so that it gets accepted by the cli, for instance, by appending a key code that maps to
accept_line
.I would much rather just set the prompt text to the command manually, and have it accept immediately. Is there a way to do this? I basically want to
cli.current_buffer.insert_text(command)
then havecli.run()
bypass the eventloop and do a quick exit.