summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam S Fulton <wsf@fultondesigns.co.uk>2015-12-24 08:52:50 +0000
committerWilliam S Fulton <wsf@fultondesigns.co.uk>2015-12-24 08:52:50 +0000
commitee867a61432f210f228a7fe113ddd444fabdde1a (patch)
tree4f896236f1958ffdfa7d3d87dac51f6dafa176e6
parent0a07cd4c3018618dfc8b73c98e619ac5c155c607 (diff)
parent79371b9a797bbf41c55250242ebf2fbb3c776b0d (diff)
downloadswig-ee867a61432f210f228a7fe113ddd444fabdde1a.tar.gz
Merge branch 'ahnolds-Python3Int'
* ahnolds-Python3Int: Adding information about PyInt/PyLong conversion updates to CHANGES.current Adding unit tests for Python primitive type conversions Adding unit tests for operator overloading to determine which overload was chosen Allow TypeError when testing overloads since it is generated instead of NotImplementedError when swig is run with -O or -fastdispatch Fixing Python primitive conversions Don't mistakenly treat PyLong objects as PyInt objects in Python3. This resolves issues of large integers being incorrectly treated as -1 while also having an OverflowError set internally for converting PyLong->long and PyLong->double Conversions from PyLong to long, unsigned long, long long, and unsigned long long now raise OverflowError rather than TypeError when given an out of range value. Removing unnecessary check for PyLong_AsLong when converting PyLong->unsigned long since the call to PyLong_AsUnsignedLong will have covered this case.
-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()) {