diff -r 51af3498f404 Lib/test/test_asyncio/test_pep492.py --- a/Lib/test/test_asyncio/test_pep492.py Sat Jun 20 17:34:31 2015 -0400 +++ b/Lib/test/test_asyncio/test_pep492.py Sat Jun 20 17:34:53 2015 -0400 @@ -1,6 +1,7 @@ """Tests support for new syntax introduced by PEP 492.""" import collections.abc +import types import unittest from test import support @@ -119,7 +120,7 @@ self.assertEqual(coro.send(None), 'spam') coro.close() - def test_async_ded_coroutines(self): + def test_async_def_coroutines(self): async def bar(): return 'spam' async def foo(): @@ -134,5 +135,25 @@ data = self.loop.run_until_complete(foo()) self.assertEqual(data, 'spam') + def test_types_coroutine(self): + import _types + + def gen(): + yield from () + return 'spam' + + @types.coroutine + def func(): + return gen() + + async def coro(): + wrapper = func() + self.assertTrue(isinstance(wrapper, _types._GeneratorWrapper)) + return await func() + + data = self.loop.run_until_complete(coro()) + self.assertEqual(data, 'spam') + + if __name__ == '__main__': unittest.main() diff -r 51af3498f404 Lib/test/test_types.py --- a/Lib/test/test_types.py Sat Jun 20 17:34:31 2015 -0400 +++ b/Lib/test/test_types.py Sat Jun 20 17:34:53 2015 -0400 @@ -1265,15 +1265,23 @@ def close(self): pass def __iter__(self): return self def __next__(self): pass + @property + def gi_running(self): + return 'spam' gen = GenLike() @types.coroutine def foo(): return gen - self.assertIs(foo().__await__(), gen) - self.assertTrue(isinstance(foo(), collections.abc.Coroutine)) + wrapper = foo() + self.assertIn('GeneratorWrapper', repr(wrapper)) + self.assertIs(wrapper.__await__(), gen) + self.assertTrue(isinstance(wrapper, collections.abc.Coroutine)) + self.assertTrue(isinstance(wrapper, collections.abc.Awaitable)) with self.assertRaises(AttributeError): - foo().gi_code + wrapper.gi_code + self.assertEqual(wrapper.gi_running, 'spam') + self.assertEqual(wrapper.cr_running, 'spam') def test_gen(self): def gen(): yield @@ -1292,16 +1300,7 @@ def gen(): yield - self.assertFalse(isinstance(gen(), collections.abc.Coroutine)) - self.assertFalse(isinstance(gen(), collections.abc.Awaitable)) - - gen_code = gen.__code__ - decorated_gen = types.coroutine(gen) - self.assertIs(decorated_gen, gen) - self.assertIsNot(decorated_gen.__code__, gen_code) - - decorated_gen2 = types.coroutine(decorated_gen) - self.assertIs(decorated_gen2.__code__, decorated_gen.__code__) + self.assertIs(types.coroutine(gen), gen) self.assertTrue(gen.__code__.co_flags & inspect.CO_ITERABLE_COROUTINE) self.assertFalse(gen.__code__.co_flags & inspect.CO_COROUTINE) @@ -1313,6 +1312,19 @@ self.assertTrue(isinstance(g, collections.abc.Awaitable)) g.close() # silence warning + def test_wrapper_object(self): + def gen(): + yield + @types.coroutine + def coro(): + return gen() + + wrapper = coro() + self.assertIn('GeneratorWrapper', repr(wrapper)) + self.assertTrue(set(dir(wrapper)).issuperset({ + '__await__', '__iter__', '__next__', 'cr_code', 'cr_running', + 'cr_frame', 'gi_code', 'gi_frame', 'gi_running', 'send', + 'close', 'throw'})) if __name__ == '__main__': unittest.main() diff -r 51af3498f404 Lib/types.py --- a/Lib/types.py Sat Jun 20 17:34:31 2015 -0400 +++ b/Lib/types.py Sat Jun 20 17:34:53 2015 -0400 @@ -165,6 +165,7 @@ import functools as _functools import collections.abc as _collections_abc +import _types def coroutine(func): """Convert regular generator function to a coroutine.""" @@ -172,65 +173,15 @@ if not callable(func): raise TypeError('types.coroutine() expects a callable') - if (func.__class__ is FunctionType and - getattr(func, '__code__', None).__class__ is CodeType): - - co_flags = func.__code__.co_flags - - # Check if 'func' is a coroutine function. - # (0x180 == CO_COROUTINE | CO_ITERABLE_COROUTINE) - if co_flags & 0x180: - return func - - # Check if 'func' is a generator function. - # (0x20 == CO_GENERATOR) - if co_flags & 0x20: - # TODO: Implement this in C. - co = func.__code__ - func.__code__ = CodeType( - co.co_argcount, co.co_kwonlyargcount, co.co_nlocals, - co.co_stacksize, - co.co_flags | 0x100, # 0x100 == CO_ITERABLE_COROUTINE - co.co_code, - co.co_consts, co.co_names, co.co_varnames, co.co_filename, - co.co_name, co.co_firstlineno, co.co_lnotab, co.co_freevars, - co.co_cellvars) - return func + try: + return _types._coroutine(func) + except TypeError: + pass # The following code is primarily to support functions that # return generator-like objects (for instance generators # compiled with Cython). - class GeneratorWrapper: - def __init__(self, gen): - self.__wrapped__ = gen - self.__name__ = getattr(gen, '__name__', None) - self.__qualname__ = getattr(gen, '__qualname__', None) - def send(self, val): - return self.__wrapped__.send(val) - def throw(self, *args): - return self.__wrapped__.throw(*args) - def close(self): - return self.__wrapped__.close() - @property - def gi_code(self): - return self.__wrapped__.gi_code - @property - def gi_frame(self): - return self.__wrapped__.gi_frame - @property - def gi_running(self): - return self.__wrapped__.gi_running - cr_code = gi_code - cr_frame = gi_frame - cr_running = gi_running - def __next__(self): - return next(self.__wrapped__) - def __iter__(self): - return self.__wrapped__ - def __await__(self): - return self.__wrapped__ - @_functools.wraps(func) def wrapped(*args, **kwargs): coro = func(*args, **kwargs) @@ -243,7 +194,7 @@ # 'coro' is either a pure Python generator iterator, or it # implements collections.abc.Generator (and does not implement # collections.abc.Coroutine). - return GeneratorWrapper(coro) + return _types._GeneratorWrapper(coro) # 'coro' is either an instance of collections.abc.Coroutine or # some other object -- pass it through. return coro diff -r 51af3498f404 Modules/Setup.dist --- a/Modules/Setup.dist Sat Jun 20 17:34:31 2015 -0400 +++ b/Modules/Setup.dist Sat Jun 20 17:34:53 2015 -0400 @@ -119,6 +119,7 @@ atexit atexitmodule.c # Register functions to be run at interpreter-shutdown _stat _stat.c # stat.h interface time timemodule.c # -lm # time operations and variables +_types _typesmodule.c # Types module speedups # access to ISO C locale support _locale _localemodule.c # -lintl diff -r 51af3498f404 Modules/_typesmodule.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_typesmodule.c Sat Jun 20 17:34:53 2015 -0400 @@ -0,0 +1,288 @@ +/* Helpers for types module. */ + +#include "Python.h" + +static PyObject * +types_coroutine(PyObject *self, PyObject *func) +{ + PyCodeObject *func_code; + + if (!PyFunction_Check(func)) { + PyErr_Format(PyExc_TypeError, + "callable expected, got %.50s", + Py_TYPE(func)->tp_name); + return NULL; + } + + func_code = (PyCodeObject *)PyFunction_GET_CODE(func); + assert(func_code); + + if (func_code->co_flags & CO_COROUTINE) { + Py_INCREF(func); + return func; + } + + if (!(func_code->co_flags & CO_GENERATOR)) { + PyErr_SetString(PyExc_TypeError, + "generator function expected"); + return NULL; + } + + func_code->co_flags |= CO_ITERABLE_COROUTINE; + + Py_INCREF(func); + return func; +} + +PyDoc_STRVAR(types_coroutine_doc, +"_coroutine(func) -> None\n\ +\n\ +Applies CO_ITERABLE_COROUTINE to generator functions code object.\n\ +This is an internal helper for types.coroutine(), do not use this \n\ +function directly.\n"); + +typedef struct { + PyObject_HEAD + PyObject *gw_wrapped; +} PyGenWrapper; + +static PyObject * +PyGenWrapper_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *gen; + PyGenWrapper *gw; + + if (!PyArg_UnpackTuple(args, "GeneratorWrapper", 1, 1, &gen)) + return NULL; + + gw = PyObject_GC_New(PyGenWrapper, type); + if (gw == NULL) + return NULL; + + Py_INCREF(gen); + gw->gw_wrapped = gen; + _PyObject_GC_TRACK(gw); + return (PyObject *)gw; +} + +static void +PyGenWrapper_dealloc(PyObject *gw) +{ + _PyObject_GC_UNTRACK(gw); + Py_CLEAR(((PyGenWrapper*)gw)->gw_wrapped); + PyObject_GC_Del(gw); +} + +static int +PyGenWrapper_traverse(PyGenWrapper *gw, visitproc visit, void *arg) +{ + Py_VISIT((PyObject *)gw->gw_wrapped); + return 0; +} + +static PyObject * +PyGenWrapper_await(PyGenWrapper *gw) +{ + PyObject *wrapped = gw->gw_wrapped; + + if (PyGen_CheckExact(wrapped)) { + Py_INCREF(wrapped); + return wrapped; + } + + return PyObject_GetIter(gw->gw_wrapped); +} + +static PyObject * +PyGenWrapper_iter(PyGenWrapper *gw) +{ + return PyGenWrapper_await(gw); +} + +static PyObject * +PyGenWrapper_iternext(PyGenWrapper *gw) +{ + iternextfunc next = NULL; + PyObject *wrapped = gw->gw_wrapped; + PyTypeObject *type = Py_TYPE(wrapped); + + next = type->tp_iternext; + if (next != NULL) { + return (*next)(wrapped); + } + + PyErr_Format(PyExc_TypeError, + "%.50s is not an iterator", + type->tp_name); + return NULL; +} + +static PyObject * +PyGenWrapper_send(PyGenWrapper *gw, PyObject *arg) +{ + _Py_IDENTIFIER(send); + return _PyObject_CallMethodIdObjArgs(gw->gw_wrapped, &PyId_send, + arg, NULL); +} + +static PyObject * +PyGenWrapper_throw(PyGenWrapper *gw, PyObject *args) +{ + _Py_IDENTIFIER(throw); + PyObject *typ; + PyObject *tb = NULL; + PyObject *val = NULL; + + if (!PyArg_UnpackTuple(args, "throw", 1, 3, &typ, &val, &tb)) + return NULL; + + return _PyObject_CallMethodIdObjArgs(gw->gw_wrapped, &PyId_throw, + typ, val, tb, NULL); +} + +static PyObject * +PyGenWrapper_close(PyGenWrapper *gw, PyObject *arg) +{ + _Py_IDENTIFIER(close); + return _PyObject_CallMethodId(gw->gw_wrapped, &PyId_close, NULL); +} + +static PyObject * +PyGenWrapper_get_name(PyGenWrapper *gw) +{ + _Py_IDENTIFIER(__name__); + return _PyObject_GetAttrId(gw->gw_wrapped, &PyId___name__); +} + +static PyObject * +PyGenWrapper_get_qualname(PyGenWrapper *gw) +{ + _Py_IDENTIFIER(__qualname__); + return _PyObject_GetAttrId(gw->gw_wrapped, &PyId___qualname__); +} + +static PyObject * +PyGenWrapper_get_gi_running(PyGenWrapper *gw) +{ + _Py_IDENTIFIER(gi_running); + return _PyObject_GetAttrId(gw->gw_wrapped, &PyId_gi_running); +} + +static PyObject * +PyGenWrapper_get_gi_frame(PyGenWrapper *gw) +{ + _Py_IDENTIFIER(gi_frame); + return _PyObject_GetAttrId(gw->gw_wrapped, &PyId_gi_frame); +} + +static PyObject * +PyGenWrapper_get_gi_code(PyGenWrapper *gw) +{ + _Py_IDENTIFIER(gi_code); + return _PyObject_GetAttrId(gw->gw_wrapped, &PyId_gi_code); +} + +static PyAsyncMethods GeneratorWrapperType_as_async = { + (unaryfunc)PyGenWrapper_await, /* am_await */ + 0, /* am_aiter */ + 0 /* am_anext */ +}; + +static PyMethodDef GeneratorWrapperType_methods[] = { + {"send", (PyCFunction)PyGenWrapper_send, METH_O, NULL}, + {"throw", (PyCFunction)PyGenWrapper_throw, METH_VARARGS, NULL}, + {"close", (PyCFunction)PyGenWrapper_close, METH_NOARGS, NULL}, + {NULL, NULL} /* Sentinel */ +}; + +static PyGetSetDef GeneratorWrapperType_getsetlist[] = { + {"__name__", (getter)PyGenWrapper_get_name, NULL, NULL}, + {"__qualname__", (getter)PyGenWrapper_get_qualname, NULL, NULL}, + {"gi_running", (getter)PyGenWrapper_get_gi_running, NULL, NULL}, + {"gi_code", (getter)PyGenWrapper_get_gi_code, NULL, NULL}, + {"gi_frame", (getter)PyGenWrapper_get_gi_frame, NULL, NULL}, + {"cr_running", (getter)PyGenWrapper_get_gi_running, NULL, NULL}, + {"cr_code", (getter)PyGenWrapper_get_gi_code, NULL, NULL}, + {"cr_frame", (getter)PyGenWrapper_get_gi_frame, NULL, NULL}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject GeneratorWrapperType = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "GeneratorWrapper", + sizeof(PyGenWrapper), /* tp_basicsize */ + 0, /* tp_itemsize */ + PyGenWrapper_dealloc, /* destructor tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + &GeneratorWrapperType_as_async, /* tp_as_async */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + "Wrapper around generator objects with __await__ metod", + (traverseproc)PyGenWrapper_traverse, /* traverseproc tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)PyGenWrapper_iter, /* tp_iter */ + (iternextfunc)PyGenWrapper_iternext, /* tp_iternext */ + GeneratorWrapperType_methods, /* tp_methods */ + 0, /* tp_members */ + GeneratorWrapperType_getsetlist, /* tp_getset */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + PyGenWrapper_new, /* tp_new */ + PyObject_Del, /* tp_free */ +}; + + +static PyMethodDef types_methods[] = { + {"_coroutine", types_coroutine, METH_O, types_coroutine_doc}, + {NULL, NULL} /* sentinel */ +}; + +PyDoc_STRVAR(module_doc, "Helpers for types module.\n"); + +static struct PyModuleDef _typesmodule = { + PyModuleDef_HEAD_INIT, + "_types", + module_doc, + -1, + types_methods, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit__types(void) +{ + PyObject *m; + + m = PyModule_Create(&_typesmodule); + if (m == NULL) + return NULL; + + if (PyType_Ready(&GeneratorWrapperType) < 0) + return NULL; + Py_INCREF(&GeneratorWrapperType); + PyModule_AddObject(m, "_GeneratorWrapper", + (PyObject *)&GeneratorWrapperType); + + return m; +} diff -r 51af3498f404 PC/config.c --- a/PC/config.c Sat Jun 20 17:34:31 2015 -0400 +++ b/PC/config.c Sat Jun 20 17:34:53 2015 -0400 @@ -38,6 +38,7 @@ extern PyObject* PyInit__collections(void); extern PyObject* PyInit__heapq(void); extern PyObject* PyInit__bisect(void); +extern PyObject* PyInit__types(void); extern PyObject* PyInit__symtable(void); extern PyObject* PyInit_mmap(void); extern PyObject* PyInit__csv(void); @@ -117,6 +118,7 @@ {"itertools", PyInit_itertools}, {"_collections", PyInit__collections}, {"_symtable", PyInit__symtable}, + {"_types", PyInit__types}, {"mmap", PyInit_mmap}, {"_csv", PyInit__csv}, {"_sre", PyInit__sre}, diff -r 51af3498f404 PCbuild/pythoncore.vcxproj --- a/PCbuild/pythoncore.vcxproj Sat Jun 20 17:34:31 2015 -0400 +++ b/PCbuild/pythoncore.vcxproj Sat Jun 20 17:34:53 2015 -0400 @@ -228,6 +228,7 @@ + @@ -417,4 +418,4 @@ - \ No newline at end of file + diff -r 51af3498f404 PCbuild/pythoncore.vcxproj.filters --- a/PCbuild/pythoncore.vcxproj.filters Sat Jun 20 17:34:31 2015 -0400 +++ b/PCbuild/pythoncore.vcxproj.filters Sat Jun 20 17:34:53 2015 -0400 @@ -473,6 +473,9 @@ Modules + + Modules + Modules @@ -974,4 +977,4 @@ Resource Files - \ No newline at end of file +