This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Towards an asyncio-enabled command line
Type: enhancement Stage:
Components: asyncio, Interpreter Core Versions: Python 3.5
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: Nosy List: Martin.Teichmann, gvanrossum, mbussonn, pmpp, vstinner, yselivanov
Priority: normal Keywords:

Created on 2014-09-14 21:15 by Martin.Teichmann, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
patch Martin.Teichmann, 2014-09-14 21:15 Patch as described in comment
patch2 Martin.Teichmann, 2014-09-15 10:39 review
patch3 Martin.Teichmann, 2014-09-16 19:42
cl.py Martin.Teichmann, 2014-09-16 19:51 A complete asyncio-supported command line
Messages (11)
msg226887 - (view) Author: Martin Teichmann (Martin.Teichmann) * Date: 2014-09-14 21:15
This patch is supposed to facilitate using the asyncio 
package on the command line. It contains two things:

First, a coroutine version of builtin.input, so that
it can be called while a asyncio event loop is running.

Secondly, it adds a new flag to builtin.compile which
allows to use the yield and yield from statements on 
the module level, making compile always return a generator.

The latter part will enable us to run commands like the
following on the command line:

>>> from asyncio import sleep
>>> yield from sleep(3)

(This has been discussed on python-ideas,
https://mail.python.org/pipermail/python-ideas/2014-September/029293.html)
msg226888 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2014-09-14 23:16
1. Great that you're trying to implement this!

2. But I really recommend that you try to structure this as a 3rd party module first rather than patching the Python distribution -- it's much harder to get accepted.  Or as a pure-Python patch to asyncio, rather than patching the interpreter's C code.

3. Which Python version did you use as a starting point?  The patch doesn't apply cleanly to the repo head for either Python 3.4 or 3.5.  If you used a source distribution, please switch to the Mercurial repo.  I recommend 3.5, as this is a new feature.

4. If you really want to patch Python, you need to add docs.  In fact, I recommend writing the docs first.

5. What on earth is going on in your input() coroutine?  You create a Future and then immediately yield from it.  How is it becoming done?

6. Surely all that C code you are adding to readline.c was copied from some other place.  From where?  Perhaps it can be refactored rather than copied?

7. See #2.
msg226901 - (view) Author: Martin Teichmann (Martin.Teichmann) * Date: 2014-09-15 07:28
Hi Guido, 

thanks for the quick response, so my response to your post:

to 1: thanks!

to 2: I am trying to put most of the stuff into a 3rd party module,
unfortunately I need changes in the python compiler, and given that
monkey patching is not so simple in C, changing the compiler would
mean to re-write it, I know that has been done, but I figured that 
starting a project like PyPy or jython is a bit too much for me 
right now.

to 3: I started with the repo head of friday night... apparently
I was not fast enough to submit this patch before someone changes
the python code to the point that my patch doesn't apply anymore.
I'll try to fix that soon.

to 4: I'll write some docs.

to 5: well, there is a callback function just some lines up which
will call future.set_result(None). Maybe that was a bit too slick,
I'm open for comments on how to do that better.

to 6: the C code I put into readline.c is mostly a merge of
readline_until_enter_or_signal (in readline.c) and builtin_input
(in bltinmodule.c). You're definitely right that those functions
could do with some refactoring, but I didn't dare to since they
are full of complicated special code for special platforms, and
I have no chance to test the code on a machine which has a
completely outdated version of the readline library, so I picked
the code I needed.


to 6: I thought about writing the input part of my patch as a 3rd
party library, but then I saw the state of builtin.input and
thought that it would actually be a good idea to shine some light
onto the state of that function, it is actually a horrible mess.
Just look at all the awful dirty tricks that the people over at
IPython have to do to use it (im)properly (I'm talking about
IPython.lib.inputhook*).
msg226910 - (view) Author: Martin Teichmann (Martin.Teichmann) * Date: 2014-09-15 10:39
As promised, a new patch now for the current head.
Last time I apparently got confused with how hg
works, sorry.
msg226929 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2014-09-15 17:51
OK. Trying to understand the patch, there seem to be three parts to it:

(a) Changes to CPython to add a new flag to the exec/eval flags argument that sets the GENERATOR flag in the resulting code object. This seems the most fundamental, but it also feels the trickiest. It is a change to a built-in function so it must be supported by other Python implementations (esp. PyPy, Jython, IronPython, Cython). It also feels "wrong" -- it may be the case that you or your colleagues *want* to be able to write "yield from XXX" at the >>> prompt, but that doesn't make it a good idea. Especially since it will never be supported by the *regular* REPL -- it will only work in your customized REPL, but it isn't particularly obvious why it works there and not elsewhere. Also, I think supporting "yield from" in the repl() will teach the wrong thing -- people used to this working will attempt to use "yield from" at the top level in a script or module and be confused why it doesn't work there. I really think that it's must better to give them a helper function such as was shown early in the python-ideas thread. (Once imported, it's less typing too!)

(b) Changes to the readline module to support some kind of async behavior, so that you can use the readline module's editing behavior from within an asyncio event loop. I can see the advantage of this, and I am not philosophically opposed (as I am for (a)). Without this, your users would either not have the benefit of convenient input editing, or you would have to reimplement the entire set of features of GNU readline afresh, which does sound excessive. However, once you give up on (a), you don't need (b) either. On the pragmatics of the patch, I don't have time to review it in detail, and I don't know if you can easily find anyone who does -- I just want to note that as it stands, there is a bug in the code, at least on OS X, since I get this:

>>> import time, readline
>>> time.sleep(1); readline._input()
asd
asd
>>> python.exe(67432,0x7fff7d790310) malloc: *** error for object 0x7fc8506046f0: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6

(Immediately after the second input line, I quickly typed 'asd' and pressed Return. The 'asd' got echoed, I got a new >>> prompt, and then something crashed.)

(c) A new function input() in the asyncio module. If (a) and (b) were accepted in Python, this might as well be part of your third-party module that implements your interactive shell, so there's not strictly a need for this. It also doesn't work on all platforms (e.g. I don't think it will work on Windows).

My recommendation to you is to give up on (a) and instead add the suggested helper function to the globals in your own REPL, and tell your users to use that at the top-level. Python doesn't support yield-from at the top level in scripts and modules, and they may as well get used to that idea. You can still, separately, work on (b), but you will have to find someone in the core dev team who is motivated enough to help you make it work properly or fail safely (on platforms where it can't be made to work). For (c), I don't think that's ready for inclusion in the stdlib either (though perhaps we could add some descendant of it to Python 3.5 if you got (b) accepted).
msg226941 - (view) Author: Martin Teichmann (Martin.Teichmann) * Date: 2014-09-16 06:57
well, I beg to differ. Again, to your points:

to a) You claim that my flag would have to be supported by every
python platform. Well, the very same built-in function has another
flag, PyCF_ONLY_AST, whose precise meaning, according to the docs,
"might change with each Python release". And I am not sure if
all your mentioned distributions (PyPy, Jython, IronPython, Cython)
will ever have an actually compatible support for PyCF_ONLY_AST.
While I think that it would be not too hard to support my GENERATOR
flag on those platforms (maybe except Cython).

to b) This is by far the most important part of my patch, something
like this certainly should go into CPython. I am actually of the
opinion that something like it is very important even without a)
getting accepted. I just looked at the event loop of IPython, what
they are doing there is just sheer horror: First they have to
cripple each event loop they support (be it Qt, GTK or WX) to
accept only some events at a time. Then they use ctypes (yes, 
ctypes: they cannot do it in Python!) to install this as a
hook to PyOS_InputHook, a function so undocumented even google
couldn't find docs (but many discussions where people had major
problems properly using it).

All of this would be solved with an input function which does not
block I/O. All the event loops (including the asyncio event loop)
would just install a callback on stdin and call my input once
they need to. I am certainly open to all kinds of proposals on how
it precisely should work, and sure bugs need to be fixed.

c) sure, with b) accepted c) is just trivial. It was a mere example
of the usage of b). b) was written under the principle "write as
much as you can in python, only the bare minimum in C" which I think
is a good idea given that it is so much nicer to program in Python than in C.

The fact that it would not work under Windows to me is actually
not a point, we would just have to make it run under Windows with
different means (unfortunately, I'm not such a Windows expert that
I would now which those means would be).
msg226961 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2014-09-16 16:52
There's a sage piece of advice somewhere in the developer docs. "Know when to give up."

I think you need to write a PEP trying to argue that supporting "yield from" in the REPL is essential to a large category of users, so you can lobby for support.

In the mean time, you have not even responded to the repeated option of providing a simple helper function. If you are concerned about integrating with a platform-provide UI, perhaps you can use a second thread? Asyncio and threads actually have a well-defined interface.

I am actually more sympathetic to getting the readline thing fixed -- perhaps you can work with the IPython folks on a proposal.
msg226963 - (view) Author: Martin Teichmann (Martin.Teichmann) * Date: 2014-09-16 19:42
Well, so I am giving up, apparenty my work is not wanted here.

But not before submitting at least the last version of my
patch. I seperated out my _input function into two, start_input
and continue_input, which are supposed to work in a loop
as in 

start_input(prompt)
while True:
   r = continue_input()
   if r is not None: return r

This would help at least people like IPython.
msg226964 - (view) Author: Martin Teichmann (Martin.Teichmann) * Date: 2014-09-16 19:51
And as a last comment, just for completeness, a complete 
async console. With it you can do cool things like

>>> from asyncio import sleep, async
>>> def f():
...    yield from sleep(3)
...    print("done")
>>> yield from f()
[after 3 seconds]
done
>>> async(f())
>>> [wait another 3 seconds] done

Just see how async puts something in the background, while
yield from keeps it up, and how the event loop runs even
while we're doing nothing!

Let's hope other projects out there are more open to making 
their event loops asyncio-compatible than CPython is -
otherwise asyncio will soon cease to exist.
msg226968 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2014-09-17 00:00
I'm sorry you feel that way. I hope one day you will understand the other side of this kind of issue.
msg341039 - (view) Author: Matthias Bussonnier (mbussonn) * Date: 2019-04-28 23:57
I believe https://bugs.python.org/issue34616 is related as it track async-exec and seem to have a couple of devs that are favorable to it.
History
Date User Action Args
2022-04-11 14:58:08adminsetgithub: 66602
2019-04-28 23:57:01mbussonnsetnosy: + mbussonn
messages: + msg341039
2018-11-15 04:12:12pmppsetnosy: + pmpp
2014-09-17 00:00:41gvanrossumsetstatus: open -> closed
resolution: rejected
messages: + msg226968
2014-09-16 19:51:05Martin.Teichmannsetfiles: + cl.py

messages: + msg226964
2014-09-16 19:42:07Martin.Teichmannsetfiles: + patch3

messages: + msg226963
2014-09-16 16:52:52gvanrossumsetmessages: + msg226961
2014-09-16 06:57:02Martin.Teichmannsetmessages: + msg226941
2014-09-15 17:51:21gvanrossumsetmessages: + msg226929
2014-09-15 10:39:31Martin.Teichmannsetfiles: + patch2

messages: + msg226910
2014-09-15 07:28:02Martin.Teichmannsetmessages: + msg226901
2014-09-14 23:16:55gvanrossumsetmessages: + msg226888
2014-09-14 21:15:35Martin.Teichmanncreate