Commit f8c0aafb authored by Stefan Behnel's avatar Stefan Behnel

implement some error cases from the PEP 492 test suite

parent d78673fc
......@@ -8,8 +8,23 @@ static CYTHON_INLINE PyObject* __Pyx_Generator_Yield_From(__pyx_CoroutineObject
static CYTHON_INLINE PyObject* __Pyx_Generator_Yield_From(__pyx_CoroutineObject *gen, PyObject *source) {
PyObject *source_gen, *retval;
source_gen = PyObject_GetIter(source);
if (unlikely(!source_gen))
if (unlikely(!source_gen)) {
#ifdef __Pyx_Coroutine_USED
#if CYTHON_COMPILING_IN_CPYTHON
// avoid exception instantiation if possible
if (PyErr_Occurred() == PyExc_TypeError
#else
if (PyErr_ExceptionMatches(PyExc_TypeError)
#endif
&& __Pyx_Coroutine_CheckExact(source)) {
PyErr_Clear();
// TODO: this should only happen for types.coroutine()ed generators, but we can't determine that here
Py_INCREF(source);
source_gen = source;
} else
#endif
return NULL;
}
// source_gen is now the iterator, make the first next() call
retval = Py_TYPE(source_gen)->tp_iternext(source_gen);
if (likely(retval)) {
......@@ -871,6 +886,54 @@ static __pyx_CoroutineObject *__Pyx__Coroutine_New(PyTypeObject* type, __pyx_cor
//@requires: CoroutineBase
//@requires: PatchGeneratorABC
static void __Pyx_Coroutine_check_and_dealloc(PyObject *self) {
__pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self;
if (gen->resume_label == 0 && !PyErr_Occurred()) {
#if PY_VERSION_HEX >= 0x03030000 || defined(PyErr_WarnFormat)
PyErr_WarnFormat(PyExc_RuntimeWarning, 1, "coroutine '%.50S' was never awaited", gen->gi_qualname);
#else
PyObject *msg, *qualname;
char *cname, *cmsg;
#if PY_MAJOR_VERSION >= 3
qualname = PyUnicode_AsUTF8String(gen->gi_qualname);
if (likely(qualname)) {
cname = PyBytes_AS_STRING(qualname);
} else {
PyErr_Clear();
cname = (char*) "?";
}
msg = PyBytes_FromFormat(
#else
qualname = gen->gi_qualname;
cname = PyString_AS_STRING(qualname);
msg = PyString_FromFormat(
#endif
"coroutine '%.50s' was never awaited", cname);
#if PY_MAJOR_VERSION >= 3
Py_XDECREF(qualname);
#endif
if (unlikely(!msg)) {
PyErr_Clear();
cmsg = (char*) "coroutine was never awaited";
} else {
#if PY_MAJOR_VERSION >= 3
cmsg = PyBytes_AS_STRING(msg);
#else
cmsg = PyString_AS_STRING(msg);
#endif
}
if (unlikely(PyErr_WarnEx(PyExc_RuntimeWarning, cmsg, 1) < 0))
PyErr_WriteUnraisable(self);
Py_XDECREF(msg);
#endif
}
__Pyx_Coroutine_dealloc(self);
}
#if PY_VERSION_HEX >= 0x030500B1
static PyAsyncMethods __pyx_Coroutine_as_async {
0, /*am_await*/
......@@ -884,7 +947,7 @@ static PyTypeObject __pyx_CoroutineType_type = {
"coroutine", /*tp_name*/
sizeof(__pyx_CoroutineObject), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor) __Pyx_Coroutine_dealloc,/*tp_dealloc*/
(destructor) __Pyx_Coroutine_check_and_dealloc,/*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
......
......@@ -3,7 +3,7 @@
import re
import gc
import sys
import types
#import types
import os.path
import inspect
import unittest
......@@ -11,6 +11,35 @@ import warnings
import contextlib
# fake types.coroutine() decorator
class types_coroutine(object):
def __init__(self, gen):
self._gen = gen
class as_coroutine(object):
def __init__(self, gen):
self._gen = gen
self.send = gen.send
self.throw = gen.throw
self.close = gen.close
def __await__(self):
return self._gen
def __iter__(self):
return self._gen
def __call__(self, *args, **kwargs):
return self.as_coroutine(self._gen(*args, **kwargs))
# compiled exec()
def exec(code_string, l, g):
from Cython.Shadow import inline
ns = inline(code_string, locals=l, globals=g, lib_dir=os.path.dirname(__file__))
g.update(ns)
class AsyncYieldFrom:
def __init__(self, obj):
self.obj = obj
......@@ -29,7 +58,7 @@ class AsyncYield:
def run_async(coro):
#assert coro.__class__ is types.GeneratorType
assert coro.__class__.__name__ == 'coroutine'
assert coro.__class__.__name__ in ('coroutine', 'as_coroutine')
buffer = []
result = None
......@@ -53,9 +82,6 @@ def silence_coro_gc():
class TokenizerRegrTest(unittest.TestCase):
def test_oneline_defs(self):
import Cython.Shadow
compile_dir = os.path.dirname(__file__)
buf = []
for i in range(500):
buf.append('def i{i}(): return {i}'.format(i=i))
......@@ -63,16 +89,14 @@ class TokenizerRegrTest(unittest.TestCase):
# Test that 500 consequent, one-line defs is OK
ns = {}
#exec(buf, ns, ns)
ns = Cython.Shadow.inline(buf, locals=ns, globals=ns, lib_dir=compile_dir)
exec(buf, ns, ns)
self.assertEqual(ns['i499'](), 499)
# Test that 500 consequent, one-line defs *and*
# one 'async def' following them is OK
buf += '\nasync def foo():\n return'
ns = {}
#exec(buf, ns, ns)
ns = Cython.Shadow.inline(buf, locals=ns, globals=ns, lib_dir=compile_dir)
exec(buf, ns, ns)
self.assertEqual(ns['i499'](), 499)
if hasattr(inspect, 'iscoroutinefunction'):
self.assertTrue(inspect.iscoroutinefunction(ns['foo']))
......@@ -80,6 +104,20 @@ class TokenizerRegrTest(unittest.TestCase):
class CoroutineTest(unittest.TestCase):
def setUpClass(cls):
# never mark warnings as "already seen" to prevent them from being suppressed
from warnings import simplefilter
simplefilter("always")
@contextlib.contextmanager
def assertRaises(self, exc_type):
try:
yield
except exc_type:
self.assertTrue(True)
else:
self.assertTrue(False)
@contextlib.contextmanager
def assertRaisesRegex(self, exc_type, regex):
# the error messages usually don't match, so we just ignore them
......@@ -90,6 +128,28 @@ class CoroutineTest(unittest.TestCase):
else:
self.assertTrue(False)
@contextlib.contextmanager
def assertWarnsRegex(self, exc_type, regex):
from warnings import catch_warnings
with catch_warnings(record=True) as log:
yield
first_match = None
for warning in log:
w = warning.message
if not isinstance(w, exc_type):
continue
if first_match is None:
first_match = w
if re.search(regex, str(w)):
self.assertTrue(True)
return
if first_match is None:
self.assertTrue(False, "no warning was raised of type '%s'" % exc_type.__name__)
else:
self.assertTrue(False, "'%s' did not match '%s'" % (first_match, regex))
def assertRegex(self, value, regex):
self.assertTrue(re.search(regex, str(value)),
"'%s' did not match '%s'" % (value, regex))
......@@ -160,7 +220,7 @@ class CoroutineTest(unittest.TestCase):
[i for i in foo()]
def test_func_5(self):
@types.coroutine
@types_coroutine
def bar():
yield 1
......@@ -181,7 +241,7 @@ class CoroutineTest(unittest.TestCase):
self.assertEqual(next(iter(bar())), 1)
def test_func_6(self):
@types.coroutine
@types_coroutine
def bar():
yield 1
yield 2
......@@ -209,7 +269,7 @@ class CoroutineTest(unittest.TestCase):
list(foo())
def test_func_8(self):
@types.coroutine
@types_coroutine
def bar():
return (yield from foo())
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment