[Python-ideas] A send() built-in function to drive coroutines

Steven D'Aprano steve at pearwood.info
Tue Feb 24 12:00:22 CET 2015


On Mon, Feb 23, 2015 at 10:22:05AM -0300, Luciano Ramalho wrote:
> On Mon, Feb 23, 2015 at 10:12 AM, Victor Stinner
> <victor.stinner at gmail.com> wrote:
> > The use case for your send() function is unclear to me. Why not using
> > send() directly?
> 
> Bacause the generator may not have started yet, so gen.send() would fail.
> 
> > inspect.getgeneratorstate(generator) == 'GEN_CREATED' test looks weird
> > (why do you need it?).
> 
> To know that the generator must be primed.
> 
> > You should already know if the generator
> > started or not.
> 
> I would, if I had written the generator myself. But my code may need
> to work with a generator that was created by some code that I do not
> control.

I must admit I don't understand the problem here. Here's a 
simple coroutine:

def runningsum():
    total = 0
    x = (yield None)
    while True:
        total += x
        x = (yield total)


If I have a function which expects a *primed* coroutine, and you provide 
an unprimed one, we get an obvious error:

py> def handle_primed(co):
...     for i in (1, 3, 5):
...             y = co.send(i)
...     return y
...
py> handle_primed(runningsum())  # Oops, unprimed!
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in handle_primed
TypeError: can't send non-None value to a just-started generator


And if I have a function which expects an *unprimed* coroutine, and you 
provide a primed one, we will usually get an exception:

py> def handle_unprimed(co):
...     next(co)
...     for i in (1, 3, 5):
...             y = co.send(i)
...     return y
...
py> rs = runningsum()
py> next(rs)  # Oops, primed!
py> handle_unprimed(rs)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in handle_unprimed
  File "<stdin>", line 5, in runningsum
TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'


I say "usually" because I suppose there are some coroutines which might 
accept being sent None and fail to cause an error. But they should be 
pretty rare, and in this case I think "consenting adults" should apply: 
if I write such a function, I can always inspect the coroutine myself.

So it seems to me that this is a case for documentation, not a built-in 
function. Document what you expect, and then it is up to the caller to 
provide what you ask for.

Have I missed something? Can you give an example of when you would 
legitimately want to accept coroutines regardless of whether they are 
primed or not, but don't want to explicitly inspect the coroutine 
yourself?



-- 
Steve


More information about the Python-ideas mailing list