diff -r 44253ce374fc Doc/library/types.rst
--- a/Doc/library/types.rst Mon Jun 22 12:31:24 2015 -0400
+++ b/Doc/library/types.rst Mon Jun 22 18:06:17 2015 -0400
@@ -287,9 +287,13 @@
.. function:: coroutine(gen_func)
The function transforms a generator function to a :term:`coroutine function`,
- so that it returns a :term:`coroutine` object.
+ so that it returns a generator-based :term:`coroutine` object.
- *gen_func* is modified in-place, hence the function can be used as a
- decorator.
+ If *gen_func* is a generator function, it will be modified in-place.
+
+ If *gen_func* is not a generator function, it will be wrapped. If it
+ returns an instance of :class:`collections.abc.Generator`, it will be
+ wrapped in an *awaitable* proxy object. All other types of objects will
+ be returned as is.
.. versionadded:: 3.5
diff -r 44253ce374fc Lib/asyncio/coroutines.py
--- a/Lib/asyncio/coroutines.py Mon Jun 22 12:31:24 2015 -0400
+++ b/Lib/asyncio/coroutines.py Mon Jun 22 18:06:17 2015 -0400
@@ -143,6 +143,11 @@
def gi_code(self):
return self.gen.gi_code
+ if _PY35:
+ cr_frame = gi_frame
+ cr_code = gi_code
+ cr_running = gi_running
+
def __del__(self):
# Be careful accessing self.gen.frame -- self.gen might not exist.
gen = getattr(self, 'gen', None)
diff -r 44253ce374fc Lib/test/test_asyncio/test_pep492.py
--- a/Lib/test/test_asyncio/test_pep492.py Mon Jun 22 12:31:24 2015 -0400
+++ b/Lib/test/test_asyncio/test_pep492.py Mon Jun 22 18:06:17 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.assertIsInstance(wrapper, _types.GeneratorWrapper)
+ return await wrapper
+
+ data = self.loop.run_until_complete(coro())
+ self.assertEqual(data, 'spam')
+
+
if __name__ == '__main__':
unittest.main()
diff -r 44253ce374fc Lib/test/test_types.py
--- a/Lib/test/test_types.py Mon Jun 22 12:31:24 2015 -0400
+++ b/Lib/test/test_types.py Mon Jun 22 18:06:17 2015 -0400
@@ -7,7 +7,9 @@
import locale
import sys
import types
-import unittest
+import _types
+import unittest.mock
+import weakref
class TypesTests(unittest.TestCase):
@@ -1191,23 +1193,27 @@
class CoroutineTests(unittest.TestCase):
def test_wrong_args(self):
- class Foo:
- def __call__(self):
- pass
- def bar(): pass
-
samples = [None, 1, object()]
for sample in samples:
with self.assertRaisesRegex(TypeError,
'types.coroutine.*expects a callable'):
types.coroutine(sample)
- def test_wrong_func(self):
+ def test_non_gen_values(self):
@types.coroutine
def foo():
return 'spam'
self.assertEqual(foo(), 'spam')
+ class Awaitable:
+ def __await__(self):
+ return ()
+ aw = Awaitable()
+ @types.coroutine
+ def foo():
+ return aw
+ self.assertIs(aw, foo())
+
def test_async_def(self):
# Test that types.coroutine passes 'async def' coroutines
# without modification
@@ -1258,29 +1264,172 @@
self.assertIs(foo(), coro)
self.assertIs(foo().__await__(), coro)
+ def test_wrapper_wrong_arg(self):
+ with self.assertRaisesRegex(TypeError, 'int is not an iterator'):
+ _types.GeneratorWrapper(1)
+
+ class NonIter:
+ def __iter__(self):
+ return self
+ with self.assertRaisesRegex(TypeError, 'NonIter is not an iterator'):
+ _types.GeneratorWrapper(NonIter())
+
def test_duck_gen(self):
class GenLike:
def send(self): pass
def throw(self): pass
def close(self): pass
- def __iter__(self): return self
+ def __iter__(self): pass
def __next__(self): pass
- gen = GenLike()
+ # Setup generator mock object
+ gen = unittest.mock.MagicMock(GenLike)
+ gen.__iter__ = lambda gen: gen
+ gen.__name__ = 'gen'
+ gen.__qualname__ = 'test.gen'
+ self.assertIsInstance(gen, collections.abc.Generator)
+ self.assertIs(gen, iter(gen))
+
@types.coroutine
- def foo():
- return gen
- self.assertIs(foo().__await__(), gen)
- self.assertTrue(isinstance(foo(), collections.abc.Coroutine))
- with self.assertRaises(AttributeError):
- foo().gi_code
+ def foo(): return gen
+
+ wrapper = foo()
+ self.assertIsInstance(wrapper, _types.GeneratorWrapper)
+ self.assertIs(wrapper.__await__(), wrapper)
+ # Wrapper proxies duck generators completely:
+ self.assertIs(iter(wrapper), wrapper)
+
+ self.assertIsInstance(wrapper, collections.abc.Coroutine)
+ self.assertIsInstance(wrapper, collections.abc.Awaitable)
+
+ self.assertIs(wrapper.__qualname__, gen.__qualname__)
+ self.assertIs(wrapper.__name__, gen.__name__)
+
+ # Test AttributeErrors
+ for name in {'gi_running', 'gi_frame', 'gi_code',
+ 'cr_running', 'cr_frame', 'cr_code'}:
+ with self.assertRaises(AttributeError):
+ getattr(wrapper, name)
+
+ # Test attributes pass-through
+ gen.gi_running = object()
+ gen.gi_frame = object()
+ gen.gi_code = object()
+ self.assertIs(wrapper.gi_running, gen.gi_running)
+ self.assertIs(wrapper.gi_frame, gen.gi_frame)
+ self.assertIs(wrapper.gi_code, gen.gi_code)
+ self.assertIs(wrapper.cr_running, gen.gi_running)
+ self.assertIs(wrapper.cr_frame, gen.gi_frame)
+ self.assertIs(wrapper.cr_code, gen.gi_code)
+
+ wrapper.close()
+ gen.close.assert_called_once_with()
+
+ wrapper.send(1)
+ gen.send.assert_called_once_with(1)
+
+ wrapper.throw(1, 2, 3)
+ gen.throw.assert_called_once_with(1, 2, 3)
+ gen.reset_mock()
+
+ wrapper.throw(1, 2)
+ gen.throw.assert_called_once_with(1, 2)
+ gen.reset_mock()
+
+ wrapper.throw(1)
+ gen.throw.assert_called_once_with(1)
+ gen.reset_mock()
+
+ # Test exceptions propagation
+ error = Exception()
+ gen.throw.side_effect = error
+ try:
+ wrapper.throw(1)
+ except Exception as ex:
+ self.assertIs(ex, error)
+ else:
+ self.fail('wrapper did not propagate an exception')
+
+ # Test invalid args
+ gen.reset_mock()
+ with self.assertRaises(TypeError):
+ wrapper.throw()
+ self.assertFalse(gen.throw.called)
+ with self.assertRaises(TypeError):
+ wrapper.close(1)
+ self.assertFalse(gen.close.called)
+ with self.assertRaises(TypeError):
+ wrapper.send()
+ self.assertFalse(gen.send.called)
+
+ # Test that we do not double wrap
+ @types.coroutine
+ def bar(): return wrapper
+ self.assertIs(wrapper, bar())
+
+ # Test weakrefs support
+ ref = weakref.ref(wrapper)
+ self.assertIs(ref(), wrapper)
+
+ def test_duck_functional_gen(self):
+ class Generator:
+ """Emulates the following generator (very clumsy):
+
+ def gen(fut):
+ result = yield fut
+ return result * 2
+ """
+ def __init__(self, fut):
+ self._i = 0
+ self._fut = fut
+ def __iter__(self):
+ return self
+ def __next__(self):
+ return self.send(None)
+ def send(self, v):
+ try:
+ if self._i == 0:
+ assert v is None
+ return self._fut
+ if self._i == 1:
+ raise StopIteration(v * 2)
+ if self._i > 1:
+ raise StopIteration
+ finally:
+ self._i += 1
+ def throw(self, tp, *exc):
+ self._i = 100
+ if tp is not GeneratorExit:
+ raise tp
+ def close(self):
+ self.throw(GeneratorExit)
+
+ @types.coroutine
+ def foo(): return Generator('spam')
+
+ wrapper = foo()
+ self.assertIsInstance(wrapper, _types.GeneratorWrapper)
+
+ async def corofunc():
+ return await foo() + 100
+ coro = corofunc()
+
+ self.assertEqual(coro.send(None), 'spam')
+ try:
+ coro.send(20)
+ except StopIteration as ex:
+ self.assertEqual(ex.args[0], 140)
+ else:
+ self.fail('StopIteration was expected')
def test_gen(self):
def gen(): yield
gen = gen()
@types.coroutine
def foo(): return gen
- self.assertIs(foo().__await__(), gen)
+ wrapper = foo()
+ self.assertIsInstance(wrapper, _types.GeneratorWrapper)
+ self.assertIs(wrapper.__await__(), gen)
for name in ('__name__', '__qualname__', 'gi_code',
'gi_running', 'gi_frame'):
@@ -1289,19 +1438,8 @@
self.assertIs(foo().cr_code, gen.gi_code)
def test_genfunc(self):
- 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__)
+ def gen(): yield
+ 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)
@@ -1309,10 +1447,27 @@
g = gen()
self.assertTrue(g.gi_code.co_flags & inspect.CO_ITERABLE_COROUTINE)
self.assertFalse(g.gi_code.co_flags & inspect.CO_COROUTINE)
- self.assertTrue(isinstance(g, collections.abc.Coroutine))
- self.assertTrue(isinstance(g, collections.abc.Awaitable))
+ self.assertIsInstance(g, collections.abc.Coroutine)
+ self.assertIsInstance(g, collections.abc.Awaitable)
g.close() # silence warning
+ self.assertIs(types.coroutine(gen), gen)
+
+ def test_wrapper_object(self):
+ def gen():
+ yield
+ @types.coroutine
+ def coro():
+ return gen()
+
+ wrapper = coro()
+ self.assertIn('GeneratorWrapper', repr(wrapper))
+ self.assertEqual(repr(wrapper), str(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 44253ce374fc Lib/types.py
--- a/Lib/types.py Mon Jun 22 12:31:24 2015 -0400
+++ b/Lib/types.py Mon Jun 22 18:06:17 2015 -0400
@@ -165,74 +165,25 @@
import functools as _functools
import collections.abc as _collections_abc
+import _types
def coroutine(func):
- """Convert regular generator function to a coroutine."""
+ """Convert a regular generator function to a coroutine."""
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):
+ def wrapper(*args, **kwargs):
coro = func(*args, **kwargs)
if coro.__class__ is CoroutineType:
# 'coro' is a native coroutine object.
@@ -243,12 +194,12 @@
# '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
- return wrapped
+ return wrapper
__all__ = [n for n in globals() if n[:1] != '_']
diff -r 44253ce374fc Modules/Setup.dist
--- a/Modules/Setup.dist Mon Jun 22 12:31:24 2015 -0400
+++ b/Modules/Setup.dist Mon Jun 22 18:06:17 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 44253ce374fc Modules/_typesmodule.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Modules/_typesmodule.c Mon Jun 22 18:06:17 2015 -0400
@@ -0,0 +1,298 @@
+/* Helpers for types module. */
+
+#include "Python.h"
+#include "structmember.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 | CO_ITERABLE_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 function's 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;
+ PyObject *gw_weakreflist;
+} 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;
+
+ if (!PyIter_Check(gen)) {
+ PyErr_Format(PyExc_TypeError,
+ "%.50s is not an iterator",
+ Py_TYPE(gen)->tp_name);
+ return NULL;
+ }
+
+ gw = PyObject_GC_New(PyGenWrapper, type);
+ if (gw == NULL)
+ return NULL;
+
+ gw->gw_weakreflist = NULL;
+
+ Py_INCREF(gen);
+ gw->gw_wrapped = gen;
+ _PyObject_GC_TRACK(gw);
+ return (PyObject *)gw;
+}
+
+static void
+PyGenWrapper_dealloc(PyGenWrapper *gw)
+{
+ _PyObject_GC_UNTRACK(gw);
+
+ Py_CLEAR(((PyGenWrapper*)gw)->gw_wrapped);
+ if (gw->gw_weakreflist != NULL)
+ PyObject_ClearWeakRefs((PyObject *)gw);
+
+ 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)) {
+ /* Speed things up a little bit for pure generators. */
+ Py_INCREF(wrapped);
+ return wrapped;
+ }
+
+ Py_INCREF(gw);
+ return 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) {
+ PyErr_Format(PyExc_TypeError,
+ "%.50s is not an iterator",
+ type->tp_name);
+ return NULL;
+ }
+
+ return (*next)(wrapped);
+}
+
+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 */
+ (destructor)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__ method",
+ (traverseproc)PyGenWrapper_traverse, /* traverseproc tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ offsetof(PyGenWrapper, gw_weakreflist), /* tp_weaklistoffset */
+ PyObject_SelfIter, /* 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 44253ce374fc PC/config.c
--- a/PC/config.c Mon Jun 22 12:31:24 2015 -0400
+++ b/PC/config.c Mon Jun 22 18:06:17 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 44253ce374fc PCbuild/pythoncore.vcxproj
--- a/PCbuild/pythoncore.vcxproj Mon Jun 22 12:31:24 2015 -0400
+++ b/PCbuild/pythoncore.vcxproj Mon Jun 22 18:06:17 2015 -0400
@@ -228,6 +228,7 @@
+
@@ -417,4 +418,4 @@
-
\ No newline at end of file
+
diff -r 44253ce374fc PCbuild/pythoncore.vcxproj.filters
--- a/PCbuild/pythoncore.vcxproj.filters Mon Jun 22 12:31:24 2015 -0400
+++ b/PCbuild/pythoncore.vcxproj.filters Mon Jun 22 18:06:17 2015 -0400
@@ -473,6 +473,9 @@
Modules
+
+ Modules
+
Modules
@@ -974,4 +977,4 @@
Resource Files
-
\ No newline at end of file
+