Ticket #3189: boost_python_unsigned_converter_fix_v2.patch

File boost_python_unsigned_converter_fix_v2.patch, 4.9 KB (added by anderson.lizardo@…, 13 years ago)

Fix builtin converters for unsigned integers (v2)

  • libs/python/test/test_builtin_converters.py

     
    44r"""
    55>>> from builtin_converters_ext import *
    66
     7# Use ctypes to get native C type sizes
     8>>> from ctypes import sizeof, c_char, c_short, c_int, c_long, c_longlong
     9>>> def test_values_signed(t):
     10...     base = 2 ** (8 * sizeof(t) - 1)
     11...     return [[-base, -1, 1, base - 1], [-base - 1, base]]
     12>>> def test_values_unsigned(t):
     13...     base = 2 ** (8 * sizeof(t))
     14...     return [[1, base - 1], [-1L, -1, base]]
     15>>> def should_pass(method, values):
     16...     result = map(method, values)
     17...     if result != values:
     18...         print "Got %s but expected %s" % (result, values)
     19>>> def test_overflow(method, values):
     20...     for v in values:
     21...         try: method(v)
     22...         except OverflowError: pass
     23...         else: print "OverflowError expected"
     24
    725# Synthesize idendity functions in case long long not supported
    826>>> if not 'rewrap_value_long_long' in dir():
    927...     def rewrap_value_long_long(x): return long(x)
     
    628042L
    6381
    6482   show that we have range checking.
    65  
    66 >>> try: rewrap_value_unsigned_short(-42)
    67 ... except OverflowError: pass
    68 ... else: print 'expected an OverflowError!'
    6983
    70 >>> try: rewrap_value_int(sys.maxint * 2)
    71 ... except OverflowError: pass
    72 ... else: print 'expected an OverflowError!'
     84>>> should_pass(rewrap_value_signed_char, test_values_signed(c_char)[0])
     85>>> should_pass(rewrap_value_short, test_values_signed(c_short)[0])
     86>>> should_pass(rewrap_value_int, test_values_signed(c_int)[0])
     87>>> should_pass(rewrap_value_long, test_values_signed(c_long)[0])
     88>>> should_pass(rewrap_value_long_long, test_values_signed(c_longlong)[0])
    7389
     90>>> should_pass(rewrap_value_unsigned_char, test_values_unsigned(c_char)[0])
     91>>> should_pass(rewrap_value_unsigned_short, test_values_unsigned(c_short)[0])
     92>>> should_pass(rewrap_value_unsigned_int, test_values_unsigned(c_int)[0])
     93>>> should_pass(rewrap_value_unsigned_long, test_values_unsigned(c_long)[0])
     94>>> should_pass(rewrap_value_unsigned_long_long,
     95...     test_values_unsigned(c_longlong)[0])
    7496
     97>>> test_overflow(rewrap_value_signed_char, test_values_signed(c_char)[1])
     98>>> test_overflow(rewrap_value_short, test_values_signed(c_short)[1])
     99>>> test_overflow(rewrap_value_int, test_values_signed(c_int)[1])
     100>>> test_overflow(rewrap_value_long, test_values_signed(c_long)[1])
     101>>> test_overflow(rewrap_value_long_long, test_values_signed(c_longlong)[1])
     102
     103>>> test_overflow(rewrap_value_unsigned_char, test_values_unsigned(c_char)[1])
     104>>> test_overflow(rewrap_value_unsigned_short, test_values_unsigned(c_short)[1])
     105>>> test_overflow(rewrap_value_unsigned_int, test_values_unsigned(c_int)[1])
     106>>> test_overflow(rewrap_value_unsigned_long, test_values_unsigned(c_long)[1])
     107
     108# Exceptionally for PyLong_AsUnsignedLongLong(), a negative value raises
     109# TypeError on Python versions prior to 2.7
     110>>> for v in test_values_unsigned(c_longlong)[1]:
     111...     try: rewrap_value_unsigned_long_long(v)
     112...     except (OverflowError, TypeError): pass
     113...     else: print "OverflowError or TypeError expected"
     114
    75115>>> assert abs(rewrap_value_float(4.2) - 4.2) < .000001
    76116>>> rewrap_value_double(4.2) - 4.2
    771170.0
  • libs/python/src/converter/builtin_converters.cpp

     
    155155  {
    156156      static T extract(PyObject* intermediate)
    157157      {
    158           return numeric_cast<T>(
    159               PyLong_Check(intermediate)
    160               ? PyLong_AsUnsignedLong(intermediate)
    161               : PyInt_AS_LONG(intermediate));
     158          if (PyLong_Check(intermediate)) {
     159              // PyLong_AsUnsignedLong() checks for negative overflow, so no
     160              // need to check it here.
     161              unsigned long result = PyLong_AsUnsignedLong(intermediate);
     162              if (PyErr_Occurred())
     163                  throw_error_already_set();
     164              return numeric_cast<T>(result);
     165          } else {
     166              // None of PyInt_AsUnsigned*() functions check for negative
     167              // overflow, so use PyInt_AS_LONG instead and check if number is
     168              // negative, issuing the exception appropriately.
     169              long result = PyInt_AS_LONG(intermediate);
     170              if (PyErr_Occurred())
     171                  throw_error_already_set();
     172              if (result < 0) {
     173                  PyErr_SetString(PyExc_OverflowError, "can't convert negative"
     174                                  " value to unsigned");
     175                  throw_error_already_set();
     176              }
     177              return numeric_cast<T>(result);
     178          }
    162179      }
    163180  };
    164181