diff options
-rw-r--r-- | CHANGES.current | 19 | ||||
-rw-r--r-- | Examples/test-suite/primitive_types.i | 1 | ||||
-rw-r--r-- | Examples/test-suite/python/primitive_types_runme.py | 137 | ||||
-rw-r--r-- | Lib/python/pyprimtypes.swg | 23 |
4 files changed, 167 insertions, 13 deletions
diff --git a/CHANGES.current b/CHANGES.current index 050ff54cc..e501bf836 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -5,6 +5,25 @@ See the RELEASENOTES file for a summary of changes in each release. Version 3.0.8 (in progress) =========================== +2015-12-23: ahnolds + [Python] Fixes for conversion of signed and unsigned integer types: + + No longer check for PyInt objects in Python3. Because PyInt_Check + and friends are #defined to the corresponding PyLong methods, this + had caused errors in Python3 where values greater than what could be + stored in a long were incorrectly interpreted as the value -1 with + the Python error indicator set to OverflowError. This applies to + both the conversions PyLong->long and PyLong->double. + + Conversion from PyLong to long, unsigned long, long long, and + unsigned long long now raise OverflowError instead of TypeError in + both Python2 and Python3 for PyLong values outside the range + expressible by the corresponding C type. This matches the existing + behavior for other integral types (signed and unsigned ints, shorts, + and chars), as well as the conversion for PyInt to all numeric + types. This also indirectly applies to the size_t and ptrdiff_t + types, which depend on the conversions for unsigned long and long. + 2015-12-19: wsfulton [Python] Python 2 Unicode UTF-8 strings can be used as inputs to char * or std::string types if the generated C/C++ code has SWIG_PYTHON_2_UNICODE defined. diff --git a/Examples/test-suite/primitive_types.i b/Examples/test-suite/primitive_types.i index 82a673536..29b44ec8c 100644 --- a/Examples/test-suite/primitive_types.i +++ b/Examples/test-suite/primitive_types.i @@ -365,6 +365,7 @@ macro(size_t, pfx, sizet) %define ovr_decl(type, pfx, name) virtual int pfx##_##val(type x) { return 1; } virtual int pfx##_##ref(const type& x) { return 1; } + virtual const char* pfx##_##str(type x) { return "name"; } %enddef diff --git a/Examples/test-suite/python/primitive_types_runme.py b/Examples/test-suite/python/primitive_types_runme.py index 2e7ed7db7..2f8b2d99c 100644 --- a/Examples/test-suite/python/primitive_types_runme.py +++ b/Examples/test-suite/python/primitive_types_runme.py @@ -1,3 +1,5 @@ +import ctypes +import sys from primitive_types import * var_init() @@ -436,3 +438,138 @@ if s != "hello": v = SetPos(1, 3) if v != 4: raise RuntimeError, "bad int typemap" + +# +# Check the bounds for converting various types +# + +# Get the minimum and maximum values that fit in signed char, short, int, long, and long long +overchar = 2 ** 7 +while ctypes.c_byte(overchar).value > 0: + overchar *= 2 +minchar = -overchar +maxchar = overchar - 1 +maxuchar = 2 * maxchar + 1 +overshort = overchar +while ctypes.c_short(overshort).value > 0: + overshort *= 2 +minshort = -overshort +maxshort = overshort - 1 +maxushort = 2 * maxshort + 1 +overint = overshort +while ctypes.c_int(overint).value > 0: + overint *= 2 +minint = -overint +maxint = overint - 1 +maxuint = 2 * maxint + 1 +overlong = overint +while ctypes.c_long(overlong).value > 0: + overlong *= 2 +minlong = -overlong +maxlong = overlong - 1 +maxulong = 2 * maxlong + 1 +overllong = overlong +while ctypes.c_longlong(overllong).value > 0: + overllong *= 2 +minllong = -overllong +maxllong = overllong - 1 +maxullong = 2 * maxllong + 1 + +# Make sure Python 2's sys.maxint is the same as the maxlong we calculated +if sys.version_info[0] <= 2 and maxlong != sys.maxint: + raise RuntimeError, "sys.maxint is not the maximum value of a signed long" + +def checkType(t, e, val, delta): + """t = Test object, e = type name (e.g. ulong), val = max or min allowed value, delta = +1 for max, -1 for min""" + error = 0 + # Set the extreme valid value for var_* + setattr(t, 'var_' + e, val) + # Make sure it was set properly and works properly in the val_* and ref_* methods + if getattr(t, 'var_' + e) != val or getattr(t, 'val_' + e)(val) != val or getattr(t, 'ref_' + e)(val) != val: + error = 1 + # Make sure setting a more extreme value fails without changing the value + try: + a = getattr(t, 'var_' + e) + setattr(t, 'var_' + e, val + delta) + error = 1 + except OverflowError: + if a != getattr(t, 'var_' + e): + error = 1 + # Make sure the val_* and ref_* methods fail with a more extreme value + try: + getattr(t, 'val_' + e)(val + delta) + error = 1 + except OverflowError: + pass + try: + getattr(t, 'ref_' + e)(val + delta) + error = 1 + except OverflowError: + pass + if error: + raise RuntimeError, "bad " + e + " typemap" + +def checkFull(t, e, maxval, minval): + """Check the maximum and minimum bounds for the type given by e""" + checkType(t, e, maxval, 1) + checkType(t, e, minval, -1) + +checkFull(t, 'llong', maxllong, minllong) +checkFull(t, 'long', maxlong, minlong) +checkFull(t, 'int', maxint, minint) +checkFull(t, 'short', maxshort, minshort) +checkFull(t, 'schar', maxchar, minchar) +checkFull(t, 'ullong', maxullong, 0) +checkFull(t, 'ulong', maxulong, 0) +checkFull(t, 'uint', maxuint, 0) +checkFull(t, 'ushort', maxushort, 0) +checkFull(t, 'uchar', maxuchar, 0) + +def checkOverload(t, name, val, delta, prevval, limit): + """ + Check that overloading works + t = Test object + name = type name (e.g. ulong) + val = max or min allowed value + delta = +1 for max, -1 for min + prevval = corresponding value for one smaller type + limit = most extreme value for any type + """ + # If val == prevval, then the smaller typemap will win + if val != prevval: + # Make sure the most extreme value of this type gives the name of this type + if t.ovr_str(val) != name: + raise RuntimeError, "bad " + name + " typemap" + # Make sure a more extreme value doesn't give the name of this type + try: + if t.ovr_str(val + delta) == name: + raise RuntimeError, "bad " + name + " typemap" + if val == limit: + # Should raise NotImplementedError here since this is the largest integral type + raise RuntimeError, "bad " + name + " typemap" + except NotImplementedError: + # NotImplementedError is expected only if this is the most extreme type + if val != limit: + raise RuntimeError, "bad " + name + " typemap" + except TypeError: + # TypeError is raised instead if swig is run with -O or -fastdispatch + if val != limit: + raise RuntimeError, "bad " + name + " typemap" + +# Check that overloading works: uchar > schar > ushort > short > uint > int > ulong > long > ullong > llong +checkOverload(t, 'uchar', maxuchar, +1, 0, maxullong) +checkOverload(t, 'ushort', maxushort, +1, maxuchar, maxullong) +checkOverload(t, 'uint', maxuint, +1, maxushort, maxullong) +checkOverload(t, 'ulong', maxulong, +1, maxuint, maxullong) +checkOverload(t, 'ullong', maxullong, +1, maxulong, maxullong) +checkOverload(t, 'schar', minchar, -1, 0, minllong) +checkOverload(t, 'short', minshort, -1, minchar, minllong) +checkOverload(t, 'int', minint, -1, minshort, minllong) +checkOverload(t, 'long', minlong, -1, minint, minllong) +checkOverload(t, 'llong', minllong, -1, minlong, minllong) + +# Make sure that large ints can be converted to doubles properly +if val_double(sys.maxint + 1) != float(sys.maxint + 1): + raise RuntimeError, "bad double typemap" +if val_double(-sys.maxint - 2) != float(-sys.maxint - 2): + raise RuntimeError, "bad double typemap" diff --git a/Lib/python/pyprimtypes.swg b/Lib/python/pyprimtypes.swg index 30bb64f66..73a97bc5a 100644 --- a/Lib/python/pyprimtypes.swg +++ b/Lib/python/pyprimtypes.swg @@ -75,16 +75,20 @@ SWIGINTERNINLINE PyObject* SWIGINTERN int SWIG_AsVal_dec(long)(PyObject *obj, long* val) { +%#if PY_VERSION_HEX < 0x03000000 if (PyInt_Check(obj)) { if (val) *val = PyInt_AsLong(obj); return SWIG_OK; - } else if (PyLong_Check(obj)) { + } else +%#endif + if (PyLong_Check(obj)) { long v = PyLong_AsLong(obj); if (!PyErr_Occurred()) { if (val) *val = v; return SWIG_OK; } else { PyErr_Clear(); + return SWIG_OverflowError; } } %#ifdef SWIG_PYTHON_CAST_MODE @@ -146,18 +150,7 @@ SWIG_AsVal_dec(unsigned long)(PyObject *obj, unsigned long *val) return SWIG_OK; } else { PyErr_Clear(); -%#if PY_VERSION_HEX >= 0x03000000 - { - long v = PyLong_AsLong(obj); - if (!PyErr_Occurred()) { - if (v < 0) { - return SWIG_OverflowError; - } - } else { - PyErr_Clear(); - } - } -%#endif + return SWIG_OverflowError; } } %#ifdef SWIG_PYTHON_CAST_MODE @@ -212,6 +205,7 @@ SWIG_AsVal_dec(long long)(PyObject *obj, long long *val) return SWIG_OK; } else { PyErr_Clear(); + res = SWIG_OverflowError; } } else { long v; @@ -266,6 +260,7 @@ SWIG_AsVal_dec(unsigned long long)(PyObject *obj, unsigned long long *val) return SWIG_OK; } else { PyErr_Clear(); + res = SWIG_OverflowError; } } else { unsigned long v; @@ -305,9 +300,11 @@ SWIG_AsVal_dec(double)(PyObject *obj, double *val) if (PyFloat_Check(obj)) { if (val) *val = PyFloat_AsDouble(obj); return SWIG_OK; +%#if PY_VERSION_HEX < 0x03000000 } else if (PyInt_Check(obj)) { if (val) *val = PyInt_AsLong(obj); return SWIG_OK; +%#endif } else if (PyLong_Check(obj)) { double v = PyLong_AsDouble(obj); if (!PyErr_Occurred()) { |