You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
q.put(some_tuple) fails when PYTHONASYNCIODEBUG=1 #65408
I'll be darned. It appears that generator's send() method uses METH_O, which means that it really expects a single argument, but if you pass it a tuple, it assumes that you meant each item in the tuple as a separate argument. I think a more correct fix is attached -- don't add a dummy *args to the send() method, but call self.gen.send((value,)).
I'd like to fix this upstream and add some tests first; also see http://code.google.com/p/tulip/issues/detail?id=163 (which touches upon a different problem in CoroWrapper not emulating the real generator object well enough).
Heh. METH_O was *also* a red herring. But upstream (Tulip) issue 163 *was* a good clue. I now believe that the real bug is that CoroWrapper.__iter__() has "return self" rather than "return iter(self.gen)". That fix is in the 2nd attachment.
The error occurs at line "v = yield from q.get()":
Traceback (most recent call last):
File "/home/haypo/prog/python/default/Lib/asyncio/events.py", line 39, in _runself._callback(*self._args)
File "/home/haypo/prog/python/default/Lib/asyncio/tasks.py", line 357, in _wakeupself._step(value, None)
File "/home/haypo/prog/python/default/Lib/asyncio/tasks.py", line 309, in _stepself.set_exception(exc)
File "/home/haypo/prog/python/default/Lib/asyncio/tasks.py", line 301, in _step
result = coro.send(value)
File "put_get_bug.py", line 23, in t2
v =yield from q.get()
TypeError: send() takes 2 positional arguments but 7 were given
Task._step() is called with value=(0, 1, 2, 3, 4, 5) (and exc is None).
I don't think that the bug comes from asyncio, but it looks like a bug in the implementation of "yield from" in CPython directly! Try with ceval.patch.
ceval.c has a fast path if the object is a generator. With PYTHONASYNCIODEBUG=1, the object is a CoroWrapper, not a generator. In this case, the slow path is used:
Yuri, thanks for the test, but why would the patch need a version check? Shouldn't the work-around work equally well in Python versions that don't need it? Maybe all we need is a comment explaining that it is a work-around and a hint that eventually we should change it back?
OK, looks good. I tried your test with my earlier workaround and the wrapper got deallocated too early, proving that my workaround was indeed wrong and your test is useful. I am still concerned theoretically that the CoroWrapper.send() signature is different from a real generator's send() method, but I think that send() to a coroutine is an internal detail anyway, so I can live with that, and I don't see another work-around.
When you commit, can you do upstgream (Tulip) first?
[...] CoroWrapper.send() signature is different from a real generator's send() method, but I think that send() to a coroutine is an internal detail anyway [...]
Yeah, and since it's used in debug mode only, I think we should be safe.
When you commit, can you do upstgream (Tulip) first?
Sure, this patch was for tulip code.
python-dev commented on Apr 15, 2014
python-devmannequin
Mannequin
More actions
New changeset 0c35d3616df5 by Yury Selivanov in branch '3.4':
asyncio.tasks: Fix CoroWrapper to workaround yield-from bug in CPython < 3.4.1 http://hg.python.org/cpython/rev/0c35d3616df5
New changeset 13ff8645be57 by Yury Selivanov in branch 'default':
syncio.tasks: Fix CoroWrapper to workaround yield-from bug in CPython < 3.4.1 http://hg.python.org/cpython/rev/13ff8645be57
Guido, I'm feeling a bit uncomfortable with the patch I pushed. I think we should adjust the solution, to avoid having arguments to 'gen.send' packed in two nested tuples. Please take a look at the new patch (corowrapper_03.patch). It adds some amount of ugliness, but with it in place, I'd be more sure that we don't brake anything.
New changeset 2729823525fe by Yury Selivanov in branch '3.4':
asyncio.tasks: Make sure CoroWrapper.send proxies one argument correctly http://hg.python.org/cpython/rev/2729823525fe
New changeset 552ee474f3e7 by Yury Selivanov in branch 'default':
asyncio.tasks: Make sure CoroWrapper.send proxies one argument correctly http://hg.python.org/cpython/rev/552ee474f3e7
I should check, but I think that Python create a tuple for you if you don't pass directly a tuple, so it's not very different.
That's what I thought, but still, better to have the code clearly expressing what it does, than relying on obscure implementation/protocol details.
Arfrever commented on Apr 20, 2014
Arfrevermannequin
Mannequin
More actions
The added comment contains "This workaround should be removed in 3.5.0.". Since default branch now contains Python 3.5, maybe it is time to remove workaround on default branch?
Activity
RichardKiss commentedon Apr 13, 2014
When PYTHONASYNCIODEBUG is set to 1, this causes a strange error:
TypeError: send() takes 2 positional arguments but 7 were given
See also https://gist.github.com/richardkiss/10564363
RichardKiss commentedon Apr 13, 2014
For a reason that I don't understand, this patch to asyncio fixes the problem:
Maybe the problem really is somewhere else, but this works.
gvanrossum commentedon Apr 13, 2014
I'll be darned. It appears that generator's send() method uses METH_O, which means that it really expects a single argument, but if you pass it a tuple, it assumes that you meant each item in the tuple as a separate argument. I think a more correct fix is attached -- don't add a dummy *args to the send() method, but call self.gen.send((value,)).
I'd like to fix this upstream and add some tests first; also see http://code.google.com/p/tulip/issues/detail?id=163 (which touches upon a different problem in CoroWrapper not emulating the real generator object well enough).
gvanrossum commentedon Apr 14, 2014
Heh. METH_O was *also* a red herring. But upstream (Tulip) issue 163 *was* a good clue. I now believe that the real bug is that CoroWrapper.__iter__() has "return self" rather than "return iter(self.gen)". That fix is in the 2nd attachment.
vstinner commentedon Apr 14, 2014
The error occurs at line "v = yield from q.get()":
Task._step() is called with value=(0, 1, 2, 3, 4, 5) (and exc is None).
vstinner commentedon Apr 14, 2014
gen_send.diff doesn't look like a fix but a workaround.
gen_send_2.diff lacks a unit test.
vstinner commentedon Apr 14, 2014
I don't think that the bug comes from asyncio, but it looks like a bug in the implementation of "yield from" in CPython directly! Try with ceval.patch.
ceval.c has a fast path if the object is a generator. With PYTHONASYNCIODEBUG=1, the object is a CoroWrapper, not a generator. In this case, the slow path is used:
This line comes from the initial commit introducing yield-from:
---
changeset: 74356:d64ac9ab4cd0
user: Nick Coghlan <ncoghlan@gmail.com>
date: Fri Jan 13 21:43:40 2012 +1000
files: Doc/library/dis.rst Doc/library/exceptions.rst Doc/reference/expressions.rst Doc/reference/simple_stmts.rst Doc/whatsnew/3.
description:
Implement PEP-380 - 'yield from' (closes bpo-11682)
---
(The exact line changed and the line was moved, but "O" format didn't change.)
Still no unit test, I'm too tired to write one, and I'm not sure that it's a bug in ceval.c.
gvanrossum commentedon Apr 14, 2014
Wow. So many fixes! :-) Are you going to be at the CPython sprint tomorrow? I'll be there in the morning but my plane leaves in the afternoon.
vstinner commentedon Apr 14, 2014
I organize a "Port OpenStack to Python3" sprint, but I may come also
to the CPython sprint.
python-dev commentedon Apr 14, 2014
New changeset 05b3a23b3836 by Benjamin Peterson in branch '3.4':
fix sending tuples to custom generator objects with yield from (closes bpo-21209)
http://hg.python.org/cpython/rev/05b3a23b3836
New changeset d1eba2645b80 by Benjamin Peterson in branch 'default':
merge 3.4 (bpo-21209)
http://hg.python.org/cpython/rev/d1eba2645b80
1st1 commentedon Apr 14, 2014
Hm... Can we also commit this to 3.3?
7 remaining items
gvanrossum commentedon Apr 14, 2014
Yuri, thanks for the test, but why would the patch need a version check? Shouldn't the work-around work equally well in Python versions that don't need it? Maybe all we need is a comment explaining that it is a work-around and a hint that eventually we should change it back?
1st1 commentedon Apr 14, 2014
Please see the corowrapper_02.patch. I've removed the version check, now it's much simpler.
gvanrossum commentedon Apr 15, 2014
OK, looks good. I tried your test with my earlier workaround and the wrapper got deallocated too early, proving that my workaround was indeed wrong and your test is useful. I am still concerned theoretically that the CoroWrapper.send() signature is different from a real generator's send() method, but I think that send() to a coroutine is an internal detail anyway, so I can live with that, and I don't see another work-around.
When you commit, can you do upstgream (Tulip) first?
1st1 commentedon Apr 15, 2014
Yeah, and since it's used in debug mode only, I think we should be safe.
Sure, this patch was for tulip code.
python-dev commentedon Apr 15, 2014
New changeset 0c35d3616df5 by Yury Selivanov in branch '3.4':
asyncio.tasks: Fix CoroWrapper to workaround yield-from bug in CPython < 3.4.1
http://hg.python.org/cpython/rev/0c35d3616df5
New changeset 13ff8645be57 by Yury Selivanov in branch 'default':
syncio.tasks: Fix CoroWrapper to workaround yield-from bug in CPython < 3.4.1
http://hg.python.org/cpython/rev/13ff8645be57
1st1 commentedon Apr 15, 2014
Guido, I'm feeling a bit uncomfortable with the patch I pushed. I think we should adjust the solution, to avoid having arguments to 'gen.send' packed in two nested tuples. Please take a look at the new patch (corowrapper_03.patch). It adds some amount of ugliness, but with it in place, I'd be more sure that we don't brake anything.
vstinner commentedon Apr 15, 2014
"I think we should adjust the solution, to avoid having arguments to 'gen.send' packed in two nested tuples."
I should check, but I think that Python create a tuple for you if you don't pass directly a tuple, so it's not very different.
Anyway, it is only used for debug, so I don't think that performances matter here.
gvanrossum commentedon Apr 15, 2014
I agree with Yuri and I approve of the patch.
python-dev commentedon Apr 15, 2014
New changeset 2729823525fe by Yury Selivanov in branch '3.4':
asyncio.tasks: Make sure CoroWrapper.send proxies one argument correctly
http://hg.python.org/cpython/rev/2729823525fe
New changeset 552ee474f3e7 by Yury Selivanov in branch 'default':
asyncio.tasks: Make sure CoroWrapper.send proxies one argument correctly
http://hg.python.org/cpython/rev/552ee474f3e7
1st1 commentedon Apr 15, 2014
That's what I thought, but still, better to have the code clearly expressing what it does, than relying on obscure implementation/protocol details.
Arfrever commentedon Apr 20, 2014
The added comment contains "This workaround should be removed in 3.5.0.". Since default branch now contains Python 3.5, maybe it is time to remove workaround on default branch?
gvanrossum commentedon Apr 20, 2014
IMO the comment is too aggressive. I want the workaround to stay in the codebase so CPython asyncio ans Tulip asyncio (== upstream) don't diverge.
python-dev commentedon Jun 30, 2014
New changeset defd09a5339a by Victor Stinner in branch '3.4':
asyncio: sync with Tulip
http://hg.python.org/cpython/rev/defd09a5339a
New changeset 8dc8c93e74c9 by Victor Stinner in branch 'default':
asyncio: sync with Tulip
http://hg.python.org/cpython/rev/8dc8c93e74c9