Index: /trunk/libs/python/test/test_builtin_converters.py =================================================================== --- /trunk/libs/python/test/test_builtin_converters.py (revision 40714) +++ /trunk/libs/python/test/test_builtin_converters.py (revision 54919) @@ -4,4 +4,22 @@ r""" >>> from builtin_converters_ext import * + +# Use ctypes to get native C type sizes +>>> from ctypes import sizeof, c_char, c_short, c_int, c_long, c_longlong +>>> def test_values_signed(t): +... base = 2 ** (8 * sizeof(t) - 1) +... return [[-base, -1, 1, base - 1], [-base - 1, base]] +>>> def test_values_unsigned(t): +... base = 2 ** (8 * sizeof(t)) +... return [[1, base - 1], [-1L, -1, base]] +>>> def should_pass(method, values): +... result = map(method, values) +... if result != values: +... print "Got %s but expected %s" % (result, values) +>>> def test_overflow(method, values): +... for v in values: +... try: method(v) +... except OverflowError: pass +... else: print "OverflowError expected" # Synthesize idendity functions in case long long not supported @@ -63,13 +81,35 @@ show that we have range checking. - ->>> try: rewrap_value_unsigned_short(-42) -... except OverflowError: pass -... else: print 'expected an OverflowError!' - ->>> try: rewrap_value_int(sys.maxint * 2) -... except OverflowError: pass -... else: print 'expected an OverflowError!' - + +>>> should_pass(rewrap_value_signed_char, test_values_signed(c_char)[0]) +>>> should_pass(rewrap_value_short, test_values_signed(c_short)[0]) +>>> should_pass(rewrap_value_int, test_values_signed(c_int)[0]) +>>> should_pass(rewrap_value_long, test_values_signed(c_long)[0]) +>>> should_pass(rewrap_value_long_long, test_values_signed(c_longlong)[0]) + +>>> should_pass(rewrap_value_unsigned_char, test_values_unsigned(c_char)[0]) +>>> should_pass(rewrap_value_unsigned_short, test_values_unsigned(c_short)[0]) +>>> should_pass(rewrap_value_unsigned_int, test_values_unsigned(c_int)[0]) +>>> should_pass(rewrap_value_unsigned_long, test_values_unsigned(c_long)[0]) +>>> should_pass(rewrap_value_unsigned_long_long, +... test_values_unsigned(c_longlong)[0]) + +>>> test_overflow(rewrap_value_signed_char, test_values_signed(c_char)[1]) +>>> test_overflow(rewrap_value_short, test_values_signed(c_short)[1]) +>>> test_overflow(rewrap_value_int, test_values_signed(c_int)[1]) +>>> test_overflow(rewrap_value_long, test_values_signed(c_long)[1]) +>>> test_overflow(rewrap_value_long_long, test_values_signed(c_longlong)[1]) + +>>> test_overflow(rewrap_value_unsigned_char, test_values_unsigned(c_char)[1]) +>>> test_overflow(rewrap_value_unsigned_short, test_values_unsigned(c_short)[1]) +>>> test_overflow(rewrap_value_unsigned_int, test_values_unsigned(c_int)[1]) +>>> test_overflow(rewrap_value_unsigned_long, test_values_unsigned(c_long)[1]) + +# Exceptionally for PyLong_AsUnsignedLongLong(), a negative value raises +# TypeError on Python versions prior to 2.7 +>>> for v in test_values_unsigned(c_longlong)[1]: +... try: rewrap_value_unsigned_long_long(v) +... except (OverflowError, TypeError): pass +... else: print "OverflowError or TypeError expected" >>> assert abs(rewrap_value_float(4.2) - 4.2) < .000001 Index: /trunk/libs/python/src/converter/builtin_converters.cpp =================================================================== --- /trunk/libs/python/src/converter/builtin_converters.cpp (revision 52299) +++ /trunk/libs/python/src/converter/builtin_converters.cpp (revision 54919) @@ -156,8 +156,25 @@ static T extract(PyObject* intermediate) { - return numeric_cast( - PyLong_Check(intermediate) - ? PyLong_AsUnsignedLong(intermediate) - : PyInt_AS_LONG(intermediate)); + if (PyLong_Check(intermediate)) { + // PyLong_AsUnsignedLong() checks for negative overflow, so no + // need to check it here. + unsigned long result = PyLong_AsUnsignedLong(intermediate); + if (PyErr_Occurred()) + throw_error_already_set(); + return numeric_cast(result); + } else { + // None of PyInt_AsUnsigned*() functions check for negative + // overflow, so use PyInt_AS_LONG instead and check if number is + // negative, issuing the exception appropriately. + long result = PyInt_AS_LONG(intermediate); + if (PyErr_Occurred()) + throw_error_already_set(); + if (result < 0) { + PyErr_SetString(PyExc_OverflowError, "can't convert negative" + " value to unsigned"); + throw_error_already_set(); + } + return numeric_cast(result); + } } };