Commit e341ed9d authored by Christoph Groth's avatar Christoph Groth
Browse files

Merge remote-tracking branch 'joseph/python3'

parents b3e6fd4a ade3d6f2
......@@ -20,10 +20,12 @@ STATIC_VERSION_FILE = 'src/version.hh'
TEST_MODULE = 'test_tinyarray.py'
CLASSIFIERS = """\
Development Status :: 5 - Production/Stable
Development Status :: 4 - Beta
Intended Audience :: Science/Research
Intended Audience :: Developers
License :: OSI Approved :: BSD License
Programming Language :: Python :: 2
Programming Language :: Python :: 3
Programming Language :: C++
Topic :: Software Development
Topic :: Scientific/Engineering
......@@ -44,7 +46,7 @@ def get_version_from_git():
if p.wait() != 0:
return
version = p.communicate()[0].strip()
version = p.communicate()[0].strip().decode()
if version[0] == 'v':
version = version[1:]
......@@ -127,7 +129,7 @@ class test(Command):
self.run_command('build')
major, minor = sys.version_info[:2]
lib_dir = "build/lib.{0}-{1}.{2}".format(get_platform(), major, minor)
print '**************** Tests ****************'
print('**************** Tests ****************')
if not run(argv=[__file__, '-v', '-w', lib_dir,
'-w', '../../' + TEST_MODULE]):
raise DistutilsError('at least one of the tests failed')
......
......@@ -357,6 +357,48 @@ PyObject *Binary_op<Floor_divide>::ufunc<Complex>(int, const size_t*,
return 0;
}
#if PY_MAJOR_VERSION >= 3
template <>
template <>
PyObject *Binary_op<Divide>::ufunc<long>(int ndim, const size_t *shape,
PyObject *a_, const ptrdiff_t *hops_a,
PyObject *b_, const ptrdiff_t *hops_b)
{
typedef long I;
typedef double O;
assert(Array<I>::check_exact(a_)); Array<I> *a = (Array<I>*)a_;
assert(Array<I>::check_exact(b_)); Array<I> *b = (Array<I>*)b_;
Array<O> *temp_a = 0;
Array<O> *temp_b = 0;
PyObject* result = 0;
I *src;
O *dest;
size_t size;
// convert arrays to double, required by semantics of true divide
temp_a = Array<O>::make(ndim, shape, &size);
if(!temp_a) goto end;
src = a->data();
dest = temp_a->data();
for (size_t i = 0; i < size; ++i) dest[i] = src[i];
temp_b = Array<O>::make(ndim, shape, &size);
if(!temp_b) goto end;
src = b->data();
dest = temp_b->data();
for (size_t i = 0; i < size; ++i) dest[i] = src[i];
result = Binary_op<Divide>::ufunc<O>(ndim, shape,
(PyObject*)temp_a, hops_a,
(PyObject*)temp_b, hops_b);
end:
if(temp_a) Py_DECREF(temp_a);
if(temp_b) Py_DECREF(temp_b);
return result;
}
#endif
template <typename T>
struct Divide {
bool operator()(T &result, T x, T y) {
......@@ -365,12 +407,16 @@ struct Divide {
}
};
#if PY_MAJOR_VERSION < 3
// not needed in Python 3.x as Array<long> will be converted
// to Array<double> for true-division
template <>
bool Divide<long>::operator()(long &result, long x, long y)
{
Floor_divide<long> floor_divide;
return floor_divide(result, x, y);
}
#endif
PyObject *dot_product(PyObject *a, PyObject *b)
{
......@@ -564,6 +610,51 @@ struct Floor { double operator()(double x) { return std::floor(x); } };
struct Ceil { double operator()(double x) { return std::ceil(x); } };
#if PY_MAJOR_VERSION >= 3
template <typename T>
PyNumberMethods Array<T>::as_number = {
Binary_op<Add>::apply, // nb_add
Binary_op<Subtract>::apply, // nb_subtract
Binary_op<Multiply>::apply, // nb_multiply
Binary_op<Remainder>::apply, // nb_remainder
(binaryfunc)0, // nb_divmod
(ternaryfunc)0, // nb_power
apply_unary_ufunc<Negative<T> >, // nb_negative
apply_unary_ufunc<Positive<T> >, // nb_positive
apply_unary_ufunc<Absolute<T> >, // nb_absolute
(inquiry)0, // nb_bool
(unaryfunc)0, // nb_invert
(binaryfunc)0, // nb_lshift
(binaryfunc)0, // nb_rshift
(binaryfunc)0, // nb_and
(binaryfunc)0, // nb_xor
(binaryfunc)0, // nb_or
(unaryfunc)0, // nb_int
(void*)0, // *nb_reserved
(unaryfunc)0, // nb_float
(binaryfunc)0, // nb_inplace_add
(binaryfunc)0, // nb_inplace_subtract
(binaryfunc)0, // nb_inplace_multiply
(binaryfunc)0, // nb_inplace_remainder
(ternaryfunc)0, // nb_inplace_power
(binaryfunc)0, // nb_inplace_lshift
(binaryfunc)0, // nb_inplace_rshift
(binaryfunc)0, // nb_inplace_and
(binaryfunc)0, // nb_inplace_xor
(binaryfunc)0, // nb_inplace_or
Binary_op<Floor_divide>::apply, // nb_floor_divide
Binary_op<Divide>::apply, // nb_true_divide
(binaryfunc)0, // nb_inplace_floor_divide
(binaryfunc)0, // nb_inplace_true_divide
(unaryfunc)0 // nb_index
};
#else // Python 2.x
template <typename T>
PyNumberMethods Array<T>::as_number = {
Binary_op<Add>::apply, // nb_add
......@@ -610,6 +701,8 @@ PyNumberMethods Array<T>::as_number = {
(unaryfunc)0 // nb_index
};
#endif // Python 3 check
// Explicit instantiations.
template PyNumberMethods Array<long>::as_number;
template PyNumberMethods Array<double>::as_number;
......
......@@ -791,56 +791,39 @@ fail:
// in Python. As tinyarrays compare equal to equivalent tuples it is important
// for the hashes to agree. If not, there will be problems with dictionaries.
long hash(long x)
#if PY_MAJOR_VERSION >= 3
// the only documentation for this is in the Python sourcecode
#define PLUS_INFTY_HASH 314159
#define MINUS_INFTY_HASH -314159
#define HASH_TYPE Py_hash_t
#define HASH_IMAG _PyHASH_IMAG
#else
#define PLUS_INFTY_HASH 314159
#define MINUS_INFTY_HASH -271828
#define HASH_TYPE long
#define HASH_IMAG 1000003L
#endif
HASH_TYPE hash(long x)
{
return x != -1 ? x : -2;
}
long hash(double x)
HASH_TYPE hash(double x)
{
const double two_to_31st = 2147483648.0;
double intpart, fractpart;
if (x == std::numeric_limits<double>::infinity())
return 314159;
else if (x == -std::numeric_limits<double>::infinity())
return -271828;
else if (x != x)
return 0; // NaN
fractpart = modf(x, &intpart);
if (fractpart == 0) {
// This must return the same hash as an equal int or long.
if (intpart > std::numeric_limits<long>::max() ||
intpart < std::numeric_limits<long>::min()) {
// Convert to Python long and use its hash.
PyObject *plong = PyLong_FromDouble(x);
if (plong == NULL) return -1;
long result = PyObject_Hash(plong);
Py_DECREF(plong);
return result;
}
return hash(long(intpart));
}
int expo;
x = frexp(x, &expo) * two_to_31st;
long hipart = x; // Take the top 32 bits.
x = (x - double(hipart)) * two_to_31st; // Get the next 32 bits.
return hash(hipart + long(x) + (expo << 15));
return _Py_HashDouble(x) ;
}
long hash(Complex x)
HASH_TYPE hash(Complex x)
{
// x.imag == 0 => hash(x.imag) == 0 => hash(x) == hash(x.real)
return hash(x.real()) + 1000003L * hash(x.imag());
return hash(x.real()) + HASH_IMAG * hash(x.imag());
}
// This routine calculates the hash of a multi-dimensional array. The hash is
// equal to that of an arrangement of nested tuples equivalent to the array.
template <typename T>
long hash(PyObject *obj)
HASH_TYPE hash(PyObject *obj)
{
int ndim;
size_t *shape;
......@@ -1186,7 +1169,7 @@ Whenever an operation is missing from Tinyarray, NumPy can be used directly,\n\
e.g.: numpy.linalg.det(my_tinyarray).");
extern "C"
void inittinyarray()
MOD_INIT_FUNC(tinyarray)
{
// Determine storage formats.
bool be = is_big_endian();
......@@ -1205,11 +1188,12 @@ void inittinyarray()
else
format_by_dtype[int(LONG)] = UNKNOWN;
if (PyType_Ready(&Array<long>::pytype) < 0) return;
if (PyType_Ready(&Array<double>::pytype) < 0) return;
if (PyType_Ready(&Array<Complex>::pytype) < 0) return;
if (PyType_Ready(&Array<long>::pytype) < 0) return MOD_ERROR_VAL;
if (PyType_Ready(&Array<double>::pytype) < 0) return MOD_ERROR_VAL;
if (PyType_Ready(&Array<Complex>::pytype) < 0) return MOD_ERROR_VAL;
PyObject *m = Py_InitModule3("tinyarray", functions, tinyarray_doc);
PyObject *m;
MOD_DEF(m, "tinyarray", functions, tinyarray_doc);
reconstruct = PyObject_GetAttrString(m, "_reconstruct");
......@@ -1239,15 +1223,17 @@ void inittinyarray()
// interpreter does the same, see try_complex_special_method in
// complexobject.c
int_str = PyString_InternFromString("__int__");
if (int_str == 0) return;
if (int_str == 0) return MOD_ERROR_VAL;
long_str = PyString_InternFromString("__long__");
if (long_str == 0) return;
if (long_str == 0) return MOD_ERROR_VAL;
float_str = PyString_InternFromString("__float__");
if (float_str == 0) return;
if (float_str == 0) return MOD_ERROR_VAL;
complex_str = PyString_InternFromString("__complex__");
if (complex_str == 0) return;
if (complex_str == 0) return MOD_ERROR_VAL;
index_str = PyString_InternFromString("__index__");
if (complex_str == 0) return;
if (complex_str == 0) return MOD_ERROR_VAL;
return MOD_SUCCESS_VAL(m);
}
int load_index_seq_as_long(PyObject *obj, long *out, int maxlen)
......@@ -1637,7 +1623,7 @@ PyObject *reduce(PyObject *self_, PyObject*)
for (int i=0; i < ndim; ++i)
PyTuple_SET_ITEM(pyshape, i, PyInt_FromSize_t(shape[i]));
PyObject *format = PyInt_FromLong(format_by_dtype[int(get_dtype(self_))]);
PyObject *data = PyString_FromStringAndSize((char*)self->data(),
PyObject *data = PyBytes_FromStringAndSize((char*)self->data(),
size_in_bytes);
PyTuple_SET_ITEM(result, 0, reconstruct);
......@@ -1668,6 +1654,13 @@ PyMappingMethods Array<T>::as_mapping = {
getitem<T> // mp_subscript
};
#if PY_MAJOR_VERSION >= 3
template <typename T>
PyBufferProcs Array<T>::as_buffer = {
getbuffer<T>, // bf_getbuffer
0, // bf_releasebuffer
};
#else
template <typename T>
PyBufferProcs Array<T>::as_buffer = {
// We only support the new buffer protocol.
......@@ -1677,6 +1670,7 @@ PyBufferProcs Array<T>::as_buffer = {
0, // bf_getcharbuffer
getbuffer<T> // bf_getbuffer
};
#endif
template <typename T>
PyMethodDef Array<T>::methods[] = {
......@@ -1686,6 +1680,14 @@ PyMethodDef Array<T>::methods[] = {
{0, 0} // Sentinel
};
#if PY_MAJOR_VERSION >= 3 // don't need flags for buffers or checking types
static unsigned long _tp_flags = Py_TPFLAGS_DEFAULT;
#else
static long _tp_flags = Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_HAVE_NEWBUFFER |
Py_TPFLAGS_CHECKTYPES;
#endif
template <typename T>
PyTypeObject Array<T>::pytype = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
......@@ -1696,7 +1698,7 @@ PyTypeObject Array<T>::pytype = {
0, // tp_print
0, // tp_getattr
0, // tp_setattr
0, // tp_compare
0, // tp_compare (tp_reserved in Python 3.x)
repr<T>, // tp_repr
&as_number, // tp_as_number
&as_sequence, // tp_as_sequence
......@@ -1707,9 +1709,7 @@ PyTypeObject Array<T>::pytype = {
PyObject_GenericGetAttr, // tp_getattro
0, // tp_setattro
&as_buffer, // tp_as_buffer
Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_HAVE_NEWBUFFER |
Py_TPFLAGS_CHECKTYPES, // tp_flags
_tp_flags, // tp_flags
0, // tp_doc
0, // tp_traverse
0, // tp_clear
......
......@@ -55,7 +55,40 @@ protected:
PyVarObject ob_base;
};
extern "C" void inittinyarray();
// Python 3 support macros for module creation
#if PY_MAJOR_VERSION >= 3
// module creation
#define MOD_DEF(ob, name, methods, doc) \
static struct PyModuleDef moduledef = { \
PyModuleDef_HEAD_INIT, \
name, \
doc, \
-1, \
methods, \
}; \
ob = PyModule_Create(&moduledef);
// init function declaration
#define MOD_INIT_FUNC(name) PyMODINIT_FUNC PyInit_##name()
// init function declaration for use in Array class def
#define MOD_INIT_FRIEND(name) PyObject* PyInit_##name()
// init function return values
#define MOD_ERROR_VAL NULL
#define MOD_SUCCESS_VAL(val) val
// Python 2
#else
// module creation
#define MOD_DEF(ob, name, methods, doc) \
ob = Py_InitModule3(name, methods, doc);
// init function declaration
#define MOD_INIT_FUNC(name) PyMODINIT_FUNC init##name()
// init function declaration for use in Array class def
#define MOD_INIT_FRIEND(name) void init##name()
// init function return values
#define MOD_ERROR_VAL
#define MOD_SUCCESS_VAL(val)
#endif
MOD_INIT_FUNC(tinyarray);
template <typename T>
class Array : public Array_base {
......@@ -90,7 +123,7 @@ private:
static PyTypeObject pytype;
friend Dtype get_dtype(PyObject *obj);
friend void inittinyarray();
friend MOD_INIT_FRIEND(tinyarray);
};
int load_index_seq_as_long(PyObject *obj, long *out, int maxlen);
......
......@@ -9,6 +9,24 @@
#ifndef CONVERSION_HH
#define CONVERSION_HH
#if PY_MAJOR_VERSION >= 3
// numeric types
#define PyInt_Type PyLong_Type
#define PyInt_FromLong PyLong_FromLong
#define PyInt_AsLong PyLong_AsLong
#define PyInt_FromSize_t PyLong_FromSize_t
#define PyInt_FromSsize_t PyLong_FromSsize_t
#define PyInt_Check PyLong_Check
// string types
#define PyString_FromString PyUnicode_FromString
#define PyString_AsString PyUnicode_AsUTF8
#define PyString_FromStringAndSize PyUnicode_FromStringAndSize
#define PyString_InternFromString PyUnicode_InternFromString
#define PyString_Check(p) (PyUnicode_Check(p) || PyBytes_Check(p))
#else // Python 2.x
#define PyBytes_FromStringAndSize PyString_FromStringAndSize
#endif
#include <complex>
typedef std::complex<double> Complex;
......
......@@ -17,8 +17,12 @@ int dtype_converter(const PyObject *ob, Dtype *dtype)
{
if (ob == Py_None) {
*dtype = default_dtype;
#if PY_MAJOR_VERSION < 3
} else if (ob == (PyObject *)(&PyInt_Type) ||
ob == (PyObject *)(&PyLong_Type)) {
#else
} else if (ob == (PyObject *)(&PyLong_Type)) {
#endif
*dtype = LONG;
} else if (ob == (PyObject *)(&PyFloat_Type)) {
*dtype = DOUBLE;
......
......@@ -135,12 +135,12 @@ def test_conversion():
# Check for overflow.
long_overflow = [1e300, np.array([1e300])]
# This check only works for Python 2.
if 18446744073709551615 > sys.maxint:
if 18446744073709551615 > sys.maxsize:
long_overflow.extend([np.array([18446744073709551615], np.uint64),
18446744073709551615])
for s in long_overflow:
assert_raises(OverflowError, ta.array, s, long)
assert_raises(OverflowError, ta.array, s, int)
def test_special_constructors():
......@@ -220,11 +220,11 @@ def test_as_dict_key():
n = 100
d = {}
for dtype in dtypes + dtypes:
for i in xrange(n):
d[ta.array(xrange(i), dtype)] = i
for i in range(n):
d[ta.array(range(i), dtype)] = i
assert_equal(len(d), n)
for i in xrange(n):
assert_equal(d[tuple(xrange(i))], i)
for i in range(n):
assert_equal(d[tuple(range(i))], i)
def test_hash_equality():
......@@ -262,10 +262,17 @@ def test_promotion():
def test_binary_operators():
ops = operator
operations = [ops.add, ops.sub, ops.mul, ops.mod, ops.floordiv]
if sys.version_info[0] >= 3:
# Python 3 removes div in preference to truediv
operations.append(ops.truediv)
else:
operations.append(ops.div)
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=RuntimeWarning)
for op in [ops.add, ops.sub, ops.mul, ops.div, ops.mod, ops.floordiv]:
for op in operations:
for dtype in dtypes:
for shape in [(), 1, 3, (3, 2)]:
if dtype is complex and op in [ops.mod, ops.floordiv]:
......@@ -333,10 +340,7 @@ def test_unary_ufuncs():
def test_pickle():
try:
import cPickle as pickle
except ImportError:
import pickle
import pickle
for dtype in dtypes:
for shape in some_shapes:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment