10000 bpo-43751: Fix anext() bug where it erroneously returned None by sweeneyde · Pull Request #25238 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

bpo-43751: Fix anext() bug where it erroneously returned None #25238

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Apr 11, 2021

Conversation

sweeneyde
Copy link
Member
@sweeneyde sweeneyde commented Apr 7, 2021

@pablogsal
Copy link
Member

There are a bunch of problems in the CI. The main one is:

======================================================================
ERROR: test_async_gen_anext (test.test_asyncgen.AsyncGenAsyncioTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/runner/work/cpython/cpython/Lib/test/test_asyncgen.py", line 401, in test_async_gen_anext
    res = self.loop.run_until_complete(consume())
  File "/Users/runner/work/cpython/cpython/Lib/asyncio/base_events.py", line 641, in run_until_complete
    return future.result()
  File "/Users/runner/work/cpython/cpython/Lib/test/test_asyncgen.py", line 399, in consume
    results.append(await anext(g, 'buckle my shoe'))
TypeError: anext() argument was not async iterable.

----------------------------------------------------------------------

and there is also some trailing whitespace. Could you check these?

@sweeneyde
Copy link
Member Author

This should probably have been a draft PR. I wrote a test for the erroneous None returns, then fixed it. And then I applied the same test to async iterables that are not generators, and that is still failing.

@sweeneyde
Copy link
Member Author

This should be ready for review now.

@pablogsal pablogsal requested review from 1st1 and gvanrossum April 8, 2021 23:55
Copy link
Contributor
@jab jab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching this!

@gvanrossum
Copy link
Member

Side comment on the PR title: please don't use "should no longer". Say something like "Fix anext() bug where it erroneously returned None".

Fix inconsistent variable names in comment

Co-authored-by: Joshua Bronson <jabronson@gmail.com>
@sweeneyde sweeneyde changed the title bpo-43751: anext() should no longer erroneously return None. bpo-43751: Fix anext() bug where it erroneously returned None Apr 9, 2021
@gvanrossum
Copy link
Member

I'm requesting that someone else reviews this instead of me. @pablogsal? @1st1?

return NULL;
}
unaryfunc getter = Py_TYPE(awaitable)->tp_as_async->am_await;
PyObject *new_awaitable = getter(awaitable);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can raise, you need to check for errors and propagate accordingly

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, can you add a test that checks this code path?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the check, but I couldn't figure out how to make a test to check the code path, since awaitable is always a coroutine, so getter points to this in genobject.c:

static PyObject *
coro_await(PyCoroObject *coro)
{
    PyCoroWrapper *cw = PyObject_GC_New(PyCoroWrapper, &_PyCoroWrapper_Type);
    if (cw == NULL) {
        return NULL;
    }
    Py_INCREF(coro);
    cw->cw_coroutine = coro;
    _PyObject_GC_TRACK(cw);
    return (PyObject *)cw;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to add a test to the _testcapi module, but this is going to be a bit too much for just this, so is fine if we don't add such a test.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, isn't this something you can get with a custom __await__ in a class?
 

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_PyCoro_GetAwaitableIter calls am_await and ensures it's iterable; this if block is only for coroutines.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test_anext_bad_await covers __await__ methods that raise

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

…use awaitable is always a coroutine in that case.
}
Py_SETREF(awaitable, new_awaitable);
if (Py_TYPE(awaitable)->tp_iternext == NULL) {
PyErr_SetString(PyExc_TypeError,
Copy link
Member
@pablogsal pablogsal Apr 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is leaking awaitable.

return NULL;
}
}
PyObject *result = (*Py_TYPE(awaitable)->tp_iternext)(awaitable);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to decrement awaitable once you are done with it

@sweeneyde
Copy link
Member Author

./python -m test test_asyncgen -R3:3 now passes

@pablogsal
Copy link
Member

Thanks for the PR @sweeneyde

@sweeneyde sweeneyde deleted the fix_anext branch December 19, 2021 05:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants
0