summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.current19
-rw-r--r--Examples/test-suite/primitive_types.i1
-rw-r--r--Examples/test-suite/python/primitive_types_runme.py137
-rw-r--r--Lib/python/pyprimtypes.swg23
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()) {