From 9b19cce707dfe60828352867327a36d3c74c1b8b Mon Sep 17 00:00:00 2001 From: Dag Sverre Seljebotn Date: Mon, 13 Oct 2008 22:11:27 +0200 Subject: [PATCH] Buffers: Better error messages + bugfix --- Cython/Compiler/Buffer.py | 56 +++++++++++++++++++++++++++------------ tests/run/bufaccess.pyx | 32 +++++++++++++--------- tests/run/numpy_test.pyx | 2 +- 3 files changed, 60 insertions(+), 30 deletions(-) diff --git a/Cython/Compiler/Buffer.py b/Cython/Compiler/Buffer.py index f1b5d89e..e8f092e0 100644 --- a/Cython/Compiler/Buffer.py +++ b/Cython/Compiler/Buffer.py @@ -520,8 +520,7 @@ def create_typestringchecker(protocode, defcode, name, dtype): def put_assert(cond, msg): defcode.putln("if (!(%s)) {" % cond) - msg += ", got '%s'" - defcode.putln('PyErr_Format(PyExc_ValueError, "%s", ts);' % msg) + defcode.putln('PyErr_Format(PyExc_ValueError, "Buffer dtype mismatch (%s)", __Pyx_DescribeTokenInFormatString(ts));' % msg) defcode.putln("return NULL;") defcode.putln("}") @@ -583,14 +582,7 @@ def create_typestringchecker(protocode, defcode, name, dtype): (char, ctype, against, ctype)) defcode.putln("default: ok = 0;") defcode.putln("}") - defcode.putln("if (!ok) {") - if dtype.typestring is not None: - errmsg = "Buffer datatype mismatch (expected '%s', got '%%s')" % dtype.typestring - else: - errmsg = "Buffer datatype mismatch (rejecting on '%s')" - defcode.putln('PyErr_Format(PyExc_ValueError, "%s", ts);' % errmsg) - defcode.putln("return NULL;"); - defcode.putln("}") + put_assert("ok", "expected %s, got %%s" % dtype) defcode.putln("++ts;") elif complex_possible: # Could be a struct representing a complex number, so allow @@ -605,9 +597,7 @@ def create_typestringchecker(protocode, defcode, name, dtype): real_t.declaration_code(""), imag_t.declaration_code(""))) defcode.putln('PyErr_SetString(PyExc_ValueError, "Cannot store complex number in \'%s\' as \'%s\' differs from \'%s\' in size.");' % ( - dtype.declaration_code("", for_display=True), - real_t.declaration_code("", for_display=True), - imag_t.declaration_code("", for_display=True))) + dtype, real_t, imag_t)) defcode.putln("return NULL;") defcode.putln("}") check_real, check_imag = [x[2] for x in field_blocks] @@ -624,21 +614,23 @@ def create_typestringchecker(protocode, defcode, name, dtype): defcode.putln("int n, count;") defcode.putln("ts = __Pyx_ConsumeWhitespace(ts); if (!ts) return NULL;") - for n, type, checker in field_blocks: + next_types = [x[1] for x in field_blocks[1:]] + ["end"] + for (n, type, checker), next_type in zip(field_blocks, next_types): if n == 1: defcode.putln("if (*ts == '1') ++ts;") else: defcode.putln("n = %d;" % n); defcode.putln("do {") defcode.putln("ts = __Pyx_ParseTypestringRepeat(ts, &count); n -= count;") + put_assert("n >= 0", "expected %s, got %%s" % next_type) simple = type.is_simple_buffer_dtype() if not simple: - put_assert("*ts == 'T' && *(ts+1) == '{'", "Expected start of %s" % type.declaration_code("", for_display=True)) + put_assert("*ts == 'T' && *(ts+1) == '{'", "expected %s, got %%s" % type) defcode.putln("ts += 2;") defcode.putln("ts = %s(ts); if (!ts) return NULL;" % checker) if not simple: - put_assert("*ts == '}'", "Expected end of '%s'" % type.declaration_code("", for_display=True)) + put_assert("*ts == '}'", "expected end of %s struct, got %%s" % type) defcode.putln("++ts;") if n > 1: @@ -689,7 +681,8 @@ def get_getbuffer_code(dtype, code): if (!ts) goto fail; if (*ts != 0) { PyErr_Format(PyExc_ValueError, - "Buffer format string specifies more data than '%(dtype_name)s' can hold (expected end, got '%%s')", ts); + "Buffer dtype mismatch (expected end, got %%s)", + __Pyx_DescribeTokenInFormatString(ts)); goto fail; } } else { @@ -822,6 +815,7 @@ static INLINE void __Pyx_SafeReleaseBuffer(Py_buffer* info); static INLINE void __Pyx_ZeroBuffer(Py_buffer* buf); /*proto*/ static INLINE const char* __Pyx_ConsumeWhitespace(const char* ts); /*proto*/ static void __Pyx_BufferNdimError(Py_buffer* buffer, int expected_ndim); /*proto*/ +static const char* __Pyx_DescribeTokenInFormatString(const char* ts); /*proto*/ """, """ static INLINE void __Pyx_SafeReleaseBuffer(Py_buffer* info) { if (info->buf == NULL) return; @@ -864,6 +858,34 @@ static void __Pyx_BufferNdimError(Py_buffer* buffer, int expected_ndim) { expected_ndim, buffer->ndim); } +static const char* __Pyx_DescribeTokenInFormatString(const char* ts) { + switch (*ts) { + case 'b': return "char"; + case 'B': return "unsigned char"; + case 'h': return "short"; + case 'H': return "unsigned short"; + case 'i': return "int"; + case 'I': return "unsigned int"; + case 'l': return "long"; + case 'L': return "unsigned long"; + case 'q': return "long long"; + case 'Q': return "unsigned long long"; + case 'f': return "float"; + case 'd': return "double"; + case 'g': return "long double"; + case 'Z': switch (*(ts+1)) { + case 'f': return "complex float"; + case 'd': return "complex double"; + case 'g': return "complex long double"; + default: return "unparseable format string"; + } + case 'T': return "a struct"; + case 'O': return "Python object"; + case 'P': return "a pointer"; + default: return "unparseable format string"; + } +} + """] diff --git a/tests/run/bufaccess.pyx b/tests/run/bufaccess.pyx index cdd7b4d3..ce228cf4 100644 --- a/tests/run/bufaccess.pyx +++ b/tests/run/bufaccess.pyx @@ -373,14 +373,14 @@ def alignment_string(object[int] buf): @testcase def wrong_string(object[int] buf): """ - >>> wrong_string(IntMockBuffer(None, [1,2], format="iasdf")) + >>> wrong_string(IntMockBuffer(None, [1,2], format="if")) Traceback (most recent call last): ... - ValueError: Buffer format string specifies more data than 'int' can hold (expected end, got 'asdf') + ValueError: Buffer dtype mismatch (expected end, got float) >>> wrong_string(IntMockBuffer(None, [1,2], format="$$")) Traceback (most recent call last): ... - ValueError: Buffer datatype mismatch (expected 'i', got '$$') + ValueError: Buffer dtype mismatch (expected int, got unparseable format string) """ print buf[1] @@ -532,7 +532,7 @@ def fmtst1(buf): >>> fmtst1(IntMockBuffer("A", range(3))) Traceback (most recent call last): ... - ValueError: Buffer datatype mismatch (expected 'f', got 'i') + ValueError: Buffer dtype mismatch (expected float, got int) """ cdef object[float] a = buf @@ -542,7 +542,7 @@ def fmtst2(object[int] buf): >>> fmtst2(FloatMockBuffer("A", range(3))) Traceback (most recent call last): ... - ValueError: Buffer datatype mismatch (expected 'i', got 'f') + ValueError: Buffer dtype mismatch (expected int, got float) """ @testcase @@ -849,7 +849,7 @@ def printbuf_td_cy_int(object[td_cy_int] buf, shape): >>> printbuf_td_cy_int(ShortMockBuffer(None, range(3)), (3,)) Traceback (most recent call last): ... - ValueError: Buffer datatype mismatch (rejecting on 'h') + ValueError: Buffer dtype mismatch (expected bufaccess.td_cy_int, got short) """ cdef int i @@ -865,7 +865,7 @@ def printbuf_td_h_short(object[td_h_short] buf, shape): >>> printbuf_td_h_short(IntMockBuffer(None, range(3)), (3,)) Traceback (most recent call last): ... - ValueError: Buffer datatype mismatch (rejecting on 'i') + ValueError: Buffer dtype mismatch (expected bufaccess.td_h_short, got int) """ cdef int i for i in range(shape[0]): @@ -880,7 +880,7 @@ def printbuf_td_h_cy_short(object[td_h_cy_short] buf, shape): >>> printbuf_td_h_cy_short(IntMockBuffer(None, range(3)), (3,)) Traceback (most recent call last): ... - ValueError: Buffer datatype mismatch (rejecting on 'i') + ValueError: Buffer dtype mismatch (expected bufaccess.td_h_cy_short, got int) """ cdef int i for i in range(shape[0]): @@ -895,7 +895,7 @@ def printbuf_td_h_ushort(object[td_h_ushort] buf, shape): >>> printbuf_td_h_ushort(ShortMockBuffer(None, range(3)), (3,)) Traceback (most recent call last): ... - ValueError: Buffer datatype mismatch (rejecting on 'h') + ValueError: Buffer dtype mismatch (expected bufaccess.td_h_ushort, got short) """ cdef int i for i in range(shape[0]): @@ -910,7 +910,7 @@ def printbuf_td_h_double(object[td_h_double] buf, shape): >>> printbuf_td_h_double(FloatMockBuffer(None, [0.25, 1, 3.125]), (3,)) Traceback (most recent call last): ... - ValueError: Buffer datatype mismatch (rejecting on 'f') + ValueError: Buffer dtype mismatch (expected bufaccess.td_h_double, got float) """ cdef int i for i in range(shape[0]): @@ -1328,10 +1328,14 @@ def basic_struct(object[MyStruct] buf): 1 2 3 4 5 >>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="bbqii")) 1 2 3 4 5 + >>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="23bqii")) + Traceback (most recent call last): + ... + ValueError: Buffer dtype mismatch (expected long long, got char) >>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="i")) Traceback (most recent call last): ... - ValueError: Buffer datatype mismatch (expected 'b', got 'i') + ValueError: Buffer dtype mismatch (expected char, got int) """ print buf[0].a, buf[0].b, buf[0].c, buf[0].d, buf[0].e @@ -1345,7 +1349,11 @@ def nested_struct(object[NestedStruct] buf): >>> nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="iiiii")) Traceback (most recent call last): ... - ValueError: Expected start of SmallStruct, got 'iiiii' + ValueError: Buffer dtype mismatch (expected SmallStruct, got int) + >>> nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="T{iii}T{ii}i")) + Traceback (most recent call last): + ... + ValueError: Buffer dtype mismatch (expected end of SmallStruct struct, got int) """ print buf[0].x.a, buf[0].x.b, buf[0].y.a, buf[0].y.b, buf[0].z diff --git a/tests/run/numpy_test.pyx b/tests/run/numpy_test.pyx index db514110..fb2113c6 100644 --- a/tests/run/numpy_test.pyx +++ b/tests/run/numpy_test.pyx @@ -144,7 +144,7 @@ try: ]))) Traceback (most recent call last): ... - ValueError: Buffer datatype mismatch (expected 'i', got 'f}T{ii}') + ValueError: Buffer dtype mismatch (expected int, got float) >>> test_good_cast() True -- 2.26.2