Is yield-based coroutine is REAL coroutine?
Question: I was implementing greenlet API just for practicing.

    
    
    from greenlet import greenlet
    
    def test1():
        print 12
        gr2.switch()
        print 34
    
    def test2():
        print 56
        gr1.switch()
       print 78
    
    gr1 = greenlet(test1)
    gr2 = greenlet(test2)
    gr1.switch()
    

here's my messy code

    
    
    def test1():
        tmp1=yield
        print 12
        try:
            gv2.send(1)
        except StopIteration:
            pass
        tmp1=yield
        print 34
    
    def test2():
        tmp2=yield
        print 56
        try:
            gv2.send(1)
        except StopIteration:
            pass
        tmp1=yield
        print 78
    
    gv1=test1()
    gv1.next()
    gv2=test2()
    gv2.next()
    
    gv1.send(1)
    

Shows,

    
    
    12
    56
    Traceback (most recent call last):
      File "prog.py", line 26, in <module>
        gv1.send(1)
      File "prog.py", line 5, in test1
        gv2.send(1)
      File "prog.py", line 15, in test2
        gv2.send(1)
    ValueError: generator already executing
    

So, I don't know I guess correctly,

but It looks that after test1 send '1' to test2, it still has something,

no control-flow switching happens unlike gevent. test1 still have the flow.

if not, I don't understand what greenlet can do but python "coroutine" can't
exists.

My question is

  1. is python coroutine(yield-based) is the real thing(comparing it of others...lisp,ruby,&c)
  2. if right, would you please give some tips for that spagetti code?


Answer: The generator instance of `test2()` is sending a value to itself.

    
    
    def test2():
        tmp2=yield
        print 56
        try:
            gv2.send(1) # this is the offending line
        except StopIteration:
            pass
        tmp1=yield
        print 78
    

`send()` will _resume_ the generator, but whenever code is executed inside
`test2()`, it is already running. That is why it throws up.

Did you want to do: `gv1.send(1)`? That would not work either.

Here is why:

  * Current situation: before `gv1.send(1)` at the very end of your example 
    * gv1 is dormant
    * gv2 is dormant
  * `gv1.send(1)` is invoked, this resumes `gv1`
  * `gv1` proceeds to `gv2.send(1)`
  * this resumes `gv2`
  * `gv2` proceeds to `gv1.send(1)`
  * `gv1` is being resumed, **however** , `gv1` has not reached a `yield` statement since it was last resumed. Ergo, it is still running, which is why it would also throw.

Essentially, the difference could be summarized like this:

  * greenlets **share** an inherent connection with each other: `.switch()` pauses the currently executing greenlet and will resume however.
  * generators, on the other hand, are completely **independent** from each other. There is no shared context in which generators are executed. 
    * `yield` will "pause" a generator
    * `next()`/ `send()` will resume a paused generator, invoking them on running generators will result in an exception.

Why are you accessing `gv2` (which represents one _particular_ instance of
`test2`) at all? The generator `test2()` should be self-contained and not make
any assumptions about how it is being used. What if you decide, that you want
to invoke the generator from some other scope? It doesn't make any sense to
send values to yourself anyway: You already have them.

