From 17879958ca67c787c0c661dfe525b0ddf55cfab6 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Thu, 26 Sep 2002 00:16:16 +0000 Subject: [PATCH] NumPy (Numeric and numarray) support [SVN r15521] --- Jamfile | 2 + include/boost/python/dict.hpp | 2 +- include/boost/python/list.hpp | 2 +- include/boost/python/long.hpp | 2 +- include/boost/python/numeric.hpp | 230 ++++++++++++++++++++ include/boost/python/object_core.hpp | 14 +- include/boost/python/str.hpp | 2 +- include/boost/python/tuple.hpp | 2 +- src/numeric.cpp | 313 +++++++++++++++++++++++++++ test/Jamfile | 2 + test/numpy.cpp | 105 +++++++++ test/numpy.py | 175 +++++++++++++++ 12 files changed, 839 insertions(+), 12 deletions(-) create mode 100644 include/boost/python/numeric.hpp create mode 100644 src/numeric.cpp create mode 100644 test/numpy.cpp create mode 100644 test/numpy.py diff --git a/Jamfile b/Jamfile index d5a9e729..243e4829 100644 --- a/Jamfile +++ b/Jamfile @@ -13,6 +13,8 @@ if $(UNIX) && ( $(OS) = AIX ) dll bpl : + src/numeric.cpp + src/list.cpp src/long.cpp src/dict.cpp diff --git a/include/boost/python/dict.hpp b/include/boost/python/dict.hpp index b593f902..d78ccaf8 100644 --- a/include/boost/python/dict.hpp +++ b/include/boost/python/dict.hpp @@ -105,7 +105,7 @@ class dict : public object BOOST_PYTHON_DECL list values() const; public: // implementation detail -- for internal use only - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(dict) + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(dict, object) private: static BOOST_PYTHON_DECL detail::new_reference call(object const&); diff --git a/include/boost/python/list.hpp b/include/boost/python/list.hpp index 501eeed5..e6696878 100644 --- a/include/boost/python/list.hpp +++ b/include/boost/python/list.hpp @@ -94,7 +94,7 @@ class list : public object } public: // implementation detail -- for internal use only - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(list) + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(list, object) private: static BOOST_PYTHON_DECL detail::new_non_null_reference call(object const&); diff --git a/include/boost/python/long.hpp b/include/boost/python/long.hpp index 3ba4f434..b0cc6417 100644 --- a/include/boost/python/long.hpp +++ b/include/boost/python/long.hpp @@ -31,7 +31,7 @@ class long_ : public object { } public: // implementation detail -- for internal use only - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(long_) + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(long_, object) private: static BOOST_PYTHON_DECL detail::new_non_null_reference call(object const&); diff --git a/include/boost/python/numeric.hpp b/include/boost/python/numeric.hpp new file mode 100644 index 00000000..1ef73439 --- /dev/null +++ b/include/boost/python/numeric.hpp @@ -0,0 +1,230 @@ +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. +#ifndef NUMARRAY_DWA2002922_HPP +# define NUMARRAY_DWA2002922_HPP + +# include +# include +# include +# include +# include +# include + +namespace boost { namespace python { namespace numeric { + +namespace aux +{ + struct BOOST_PYTHON_DECL array_base : object + { +# define BOOST_PP_LOCAL_MACRO(n) \ + array_base(BOOST_PP_ENUM_PARAMS(n, object const& x)); +# define BOOST_PP_LOCAL_LIMITS (1, 7) +# include BOOST_PP_LOCAL_ITERATE() + + object argmax(long axis=-1); + object argmin(long axis=-1); + object argsort(long axis=-1); + object astype(object const& type = object()); + void byteswap(); + object copy() const; + object diagonal(long offset = 0, long axis1 = 0, long axis2 = 1) const; + void info() const; + bool is_c_array() const; + bool isbyteswapped() const; + object new_(object type) const; + void sort(); + object trace(long offset = 0, long axis1 = 0, long axis2 = 1) const; + object type() const; + char typecode() const; + + object factory(object const& buffer=object() + , object const& type=object() + , object const& shape=object() + , bool copy = true + , bool savespace = false + , object typecode = object()); + + object getflat() const; + long getrank() const; + object getshape() const; + bool isaligned() const; + bool iscontiguous() const; + long itemsize() const; + long nelements() const; + object nonzero() const; + + void put(object const& indices, object const& values); + + void ravel(); + + object repeat(object const& repeats, long axis=0); + + void resize(object const& shape); + + void setflat(object const& flat); + void setshape(object const& shape); + + void swapaxes(long axis1, long axis2); + + object take(object const& sequence, long axis = 0) const; + + void tofile(object const& file) const; + + str tostring() const; + + void transpose(object const& axes = object()); + + object view() const; + + public: // implementation detail - do not touch. + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(array_base, object); + }; + + struct BOOST_PYTHON_DECL array_object_manager_traits + { + static bool check(PyObject* obj); + static detail::new_non_null_reference adopt(PyObject* obj); + }; +} // namespace aux + +class array : public aux::array_base +{ + typedef aux::array_base base; + public: + + object astype() { return base::astype(); } + + template + object astype(Type const& type_) + { + return base::astype(object(type_)); + } + + template + object new_(Type const& type_) const + { + return base::new_(object(type_)); + } + + template + void resize(Sequence const& x) + { + base::resize(object(x)); + } + +# define BOOST_PP_LOCAL_MACRO(n) \ + void resize(BOOST_PP_ENUM_PARAMS(n, long x)) \ + { \ + resize(make_tuple(BOOST_PP_ENUM_PARAMS(n, x))); \ + } +# define BOOST_PP_LOCAL_LIMITS (1, BOOST_PYTHON_MAX_ARITY) +# include BOOST_PP_LOCAL_ITERATE() + + template + void setshape(Sequence const& x) + { + base::setshape(object(x)); + } + +# define BOOST_PP_LOCAL_MACRO(n) \ + void setshape(BOOST_PP_ENUM_PARAMS(n, long x)) \ + { \ + setshape(make_tuple(BOOST_PP_ENUM_PARAMS(n, x))); \ + } +# define BOOST_PP_LOCAL_LIMITS (1, BOOST_PYTHON_MAX_ARITY) +# include BOOST_PP_LOCAL_ITERATE() + + template + void put(Indices const& indices, Values const& values) + { + base::put(object(indices), object(values)); + } + + template + object take(Sequence const& sequence, long axis = 0) + { + return base::take(object(sequence), axis); + } + + template + void tofile(File const& f) const + { + base::tofile(object(f)); + } + + object factory() + { + return base::factory(); + } + + template + object factory(Buffer const& buffer) + { + return base::factory(object(buffer)); + } + + template + object factory( + Buffer const& buffer + , Type const& type_) + { + return base::factory(object(buffer), object(type_)); + } + + template + object factory( + Buffer const& buffer + , Type const& type_ + , Shape const& shape + , bool copy = true + , bool savespace = false) + { + return base::factory(object(buffer), object(type_), object(shape), copy, savespace); + } + + template + object factory( + Buffer const& buffer + , Type const& type_ + , Shape const& shape + , bool copy + , bool savespace + , char typecode) + { + return base::factory(object(buffer), object(type_), object(shape), copy, savespace, object(typecode)); + } + +# define BOOST_PYTHON_ENUM_AS_OBJECT(z, n, x) object(BOOST_PP_CAT(x,n)) +# define BOOST_PP_LOCAL_MACRO(n) \ + template \ + explicit array(BOOST_PP_ENUM_BINARY_PARAMS(n, T, const& x)) \ + : array_base(BOOST_PP_ENUM(n, BOOST_PYTHON_ENUM_AS_OBJECT, x)) \ + {} +# define BOOST_PP_LOCAL_LIMITS (1, 7) +# include BOOST_PP_LOCAL_ITERATE() +# undef BOOST_PYTHON_AS_OBJECT + + static BOOST_PYTHON_DECL void set_module_and_type(char const* package_name = 0, char const* type_attribute_name = 0); + + public: // implementation detail -- for internal use only + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(array, array_base); +}; + +} // namespace boost::python::numeric + +namespace converter +{ + template <> + struct object_manager_traits< numeric::array > + : numeric::aux::array_object_manager_traits + { + BOOST_STATIC_CONSTANT(bool, is_specialized = true); + }; +} + +}} // namespace boost::python + +#endif // NUMARRAY_DWA2002922_HPP diff --git a/include/boost/python/object_core.hpp b/include/boost/python/object_core.hpp index ebdfeac1..2ee532a8 100755 --- a/include/boost/python/object_core.hpp +++ b/include/boost/python/object_core.hpp @@ -234,13 +234,13 @@ namespace api // Macros for forwarding constructors in classes derived from // object. Derived classes will usually want these as an // implementation detail -# define BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS_(derived) \ +# define BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS_(derived, base) \ inline explicit derived(python::detail::borrowed_reference p) \ - : object(p) {} \ + : base(p) {} \ inline explicit derived(python::detail::new_reference p) \ - : object(p) {} \ + : base(p) {} \ inline explicit derived(python::detail::new_non_null_reference p) \ - : object(p) {} + : base(p) {} # if !defined(BOOST_MSVC) || BOOST_MSVC > 1200 # define BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS_ @@ -253,9 +253,9 @@ namespace api // runtime failure into an ambiguity error at compile-time due to // the lack of partial ordering, or at least a link-time error if no // generalized template constructor is declared. -# define BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(derived) \ - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS_(derived) \ - template \ +# define BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(derived, base) \ + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS_(derived, base) \ + template \ explicit derived(extract const&); # endif diff --git a/include/boost/python/str.hpp b/include/boost/python/str.hpp index fb8bfe58..ffb3c362 100644 --- a/include/boost/python/str.hpp +++ b/include/boost/python/str.hpp @@ -352,7 +352,7 @@ class str : public object BOOST_PYTHON_DECL str upper() const; public: // implementation detail -- for internal use only - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(str) + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(str, object) private: static BOOST_PYTHON_DECL detail::new_reference call(object const&); diff --git a/include/boost/python/tuple.hpp b/include/boost/python/tuple.hpp index 2ab4ad73..77c5e67c 100644 --- a/include/boost/python/tuple.hpp +++ b/include/boost/python/tuple.hpp @@ -24,7 +24,7 @@ class tuple : public object } public: // implementation detail -- for internal use only - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(tuple) + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(tuple, object) private: static BOOST_PYTHON_DECL detail::new_reference call(object const&); diff --git a/src/numeric.cpp b/src/numeric.cpp new file mode 100644 index 00000000..7d06ec11 --- /dev/null +++ b/src/numeric.cpp @@ -0,0 +1,313 @@ +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. + +#include +#include +#include +#include +#include +#include + +namespace boost { namespace python { namespace numeric { + +namespace +{ + enum state_t { failed = -1, unknown, succeeded }; + state_t state = unknown; + std::string module_name; + std::string type_name; + + handle<> array_module; + object array_type; + object array_function; + + void throw_load_failure() + { + PyErr_Format( + PyExc_ImportError + , "No module named '%s' or its type '%s' did not follow the NumPy protocol" + , module_name.c_str(), type_name.c_str()); + throw_error_already_set(); + + } + + bool load(bool throw_on_error) + { + if (!state) + { + if (module_name.size() == 0) + { + module_name = "numarray"; + type_name = "NDArray"; + if (load(false)) + return true; + module_name = "Numeric"; + type_name = "ArrayType"; + } + + state = failed; + PyObject* module = ::PyImport_Import(object(module_name).ptr()); + if (module) + { + PyObject* type = ::PyObject_GetAttrString(module, const_cast(type_name.c_str())); + + if (type && PyType_Check(type)) + { + array_type = object(detail::new_non_null_reference(type)); + PyObject* function = ::PyObject_GetAttrString(module, const_cast("array")); + + if (function && PyCallable_Check(function)) + { + array_function = object(detail::new_reference(function)); + state = succeeded; + } + } + } + } + + if (state == succeeded) + return true; + + if (throw_on_error) + throw_load_failure(); + + PyErr_Clear(); + return false; + } + + object const& demand_array_function() + { + load(true); + return array_function; + } +} + +void array::set_module_and_type(char const* package_name, char const* type_attribute_name) +{ + state = unknown; + module_name = package_name ? package_name : "" ; + type_name = type_attribute_name ? type_attribute_name : "" ; +} + + +namespace aux +{ + bool array_object_manager_traits::check(PyObject* obj) + { + if (!load(false)) + return false; + return ::PyObject_IsInstance(obj, array_type.ptr()); + } + + python::detail::new_non_null_reference + array_object_manager_traits::adopt(PyObject* obj) + { + load(true); + return detail::new_non_null_reference( + pytype_check(downcast(array_type.ptr()), obj)); + } + + +# define BOOST_PYTHON_AS_OBJECT(z, n, _) object(x##n) +# define BOOST_PP_LOCAL_MACRO(n) \ + array_base::array_base(BOOST_PP_ENUM_PARAMS(n, object const& x)) \ + : object((load(true), array_function)(BOOST_PP_ENUM_PARAMS(n, x))) \ + {} +# define BOOST_PP_LOCAL_LIMITS (1, 6) +# include BOOST_PP_LOCAL_ITERATE() +# undef BOOST_PYTHON_AS_OBJECT + + array_base::array_base(BOOST_PP_ENUM_PARAMS(7, object const& x)) + : object((load(true), array_type)(BOOST_PP_ENUM_PARAMS(7, x))) + {} + + object array_base::argmax(long axis) + { + return attr("argmax")(axis); + } + + object array_base::argmin(long axis) + { + return attr("argmin")(axis); + } + + object array_base::argsort(long axis) + { + return attr("argsort")(axis); + } + + object array_base::astype(object const& type) + { + return attr("astype")(type); + } + + void array_base::byteswap() + { + attr("byteswap")(); + } + + object array_base::copy() const + { + return attr("copy")(); + } + + object array_base::diagonal(long offset, long axis1, long axis2) const + { + return attr("diagonal")(offset, axis1, axis2); + } + + void array_base::info() const + { + attr("info")(); + } + + bool array_base::is_c_array() const + { + return extract(attr("is_c_array")()); + } + + bool array_base::isbyteswapped() const + { + return extract(attr("isbyteswapped")()); + } + + object array_base::new_(object type) const + { + return attr("new")(type); + } + + void array_base::sort() + { + attr("sort")(); + } + + object array_base::trace(long offset, long axis1, long axis2) const + { + return attr("trace")(offset, axis1, axis2); + } + + object array_base::type() const + { + return attr("type")(); + } + + char array_base::typecode() const + { + return extract(attr("typecode")()); + } + + object array_base::factory(object const& buffer + , object const& type + , object const& shape + , bool copy + , bool savespace + , object typecode) + { + return attr("array")(buffer, type, shape, copy, savespace, typecode); + } + + object array_base::getflat() const + { + return attr("getflat")(); + } + + long array_base::getrank() const + { + return extract(attr("getrank")()); + } + + object array_base::getshape() const + { + return attr("getshape")(); + } + + bool array_base::isaligned() const + { + return extract(attr("isaligned")); + } + + bool array_base::iscontiguous() const + { + return extract(attr("isaligned")); + } + + long array_base::itemsize() const + { + return extract(attr("itemsize")); + } + + long array_base::nelements() const + { + return extract(attr("nelements")); + } + + object array_base::nonzero() const + { + return attr("nonzero")(); + } + + void array_base::put(object const& indices, object const& values) + { + attr("put")(indices, values); + } + + void array_base::ravel() + { + attr("ravel")(); + } + + object array_base::repeat(object const& repeats, long axis) + { + return attr("repeat")(repeats, axis); + } + + void array_base::resize(object const& shape) + { + attr("resize")(shape); + } + + void array_base::setflat(object const& flat) + { + attr("setflat")(flat); + } + + void array_base::setshape(object const& shape) + { + attr("setshape")(shape); + } + + void array_base::swapaxes(long axis1, long axis2) + { + attr("swapaxes")(axis1, axis2); + } + + object array_base::take(object const& sequence, long axis) const + { + return attr("take")(sequence, axis); + } + + void array_base::tofile(object const& file) const + { + attr("tofile")(file); + } + + str array_base::tostring() const + { + return str(attr("tostring")()); + } + + void array_base::transpose(object const& axes) + { + attr("transpose")(axes); + } + + object array_base::view() const + { + return attr("view")(); + } +} + +}}} // namespace boost::python::numeric diff --git a/test/Jamfile b/test/Jamfile index 0a6914f0..f8e3d631 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -54,6 +54,8 @@ rule bpl-test ( name ? : files * : requirements * ) boost-python-runtest $(name) : $(py) $(modules) ; } +bpl-test numpy ; + bpl-test enum ; bpl-test minimal ; bpl-test docstring ; diff --git a/test/numpy.cpp b/test/numpy.cpp new file mode 100644 index 00000000..36b9e070 --- /dev/null +++ b/test/numpy.cpp @@ -0,0 +1,105 @@ +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. + +#include +#include +#include +#include + +using namespace boost::python; + +// See if we can invoke array() from C++ +object new_array() +{ + return numeric::array( + make_tuple( + make_tuple(1,2,3) + , make_tuple(4,5,6) + , make_tuple(7,8,9) + ) + ); +} + +// test argument conversion +void take_array(numeric::array x) +{ +} + +// A separate function to invoke the info() member. Must happen +// outside any doctests since this prints directly to stdout and the +// result text includes the address of the 'self' array. +void info(numeric::array const& z) +{ + z.info(); +} + +// Tests which work on both Numeric and numarray array objects. Of +// course all of the operators "just work" since numeric::array +// inherits that behavior from object. +void exercise(numeric::array& y, object check) +{ + y[make_tuple(2,1)] = 3; + check(y); + check(y.astype('D')); + check(y.copy()); + check(y.typecode()); +} + +// numarray-specific tests. check is a callable object which we can +// use to record intermediate results, which are later compared with +// the results of corresponding python operations. +void exercise_numarray(numeric::array& y, object check) +{ + check(y.astype()); + + check(y.argmax()); + check(y.argmax(0)); + + check(y.argmin()); + check(y.argmin(0)); + + check(y.argsort()); + check(y.argsort(1)); + + y.byteswap(); + check(y); + + check(y.diagonal()); + check(y.diagonal(1)); + check(y.diagonal(0, 1)); + check(y.diagonal(0, 1, 0)); + + check(y.is_c_array()); + check(y.isbyteswapped()); + + check(y.trace()); + check(y.trace(1)); + check(y.trace(0, 1)); + check(y.trace(0, 1, 0)); + + check(y.new_('D')); + y.sort(); + check(y); + check(y.type()); + + check(y.factory(make_tuple(1.2, 3.4))); + check(y.factory(make_tuple(1.2, 3.4), "Double")); + check(y.factory(make_tuple(1.2, 3.4), "Double", make_tuple(1,2,1))); + check(y.factory(make_tuple(1.2, 3.4), "Double", make_tuple(2,1,1), false)); + check(y.factory(make_tuple(1.2, 3.4), "Double", make_tuple(2), true, true)); +} + +BOOST_PYTHON_MODULE_INIT(numpy_ext) +{ + def("new_array", new_array); + def("take_array", take_array); + def("exercise", exercise); + def("exercise_numarray", exercise_numarray); + def("set_module_and_type", &numeric::array::set_module_and_type); + def("info", info); +} + +#include "module_tail.cpp" diff --git a/test/numpy.py b/test/numpy.py new file mode 100644 index 00000000..ff0c6a8f --- /dev/null +++ b/test/numpy.py @@ -0,0 +1,175 @@ +def numeric_tests(): + ''' + >>> from numpy_ext import * + >>> x = new_array() + >>> x[1,1] = 0.0 + + >>> try: take_array(3) + ... except TypeError: pass + ... else: print 'expected a TypeError' + + >>> take_array(x) + + >>> print x + [[1 2 3] + [4 0 6] + [7 8 9]] + + >>> y = x.copy() + + + >>> p = _printer() + >>> check = p.check + >>> exercise(x, p) + >>> y[2,1] = 3 + >>> check(y); + + >>> check(y.astype('D')); + + >>> check(y.copy()); + + >>> check(y.typecode()); + + >>> p.results + [] + >>> del p + ''' + pass + +def _numarray_tests(): + ''' + >>> from numpy_ext import * + >>> x = new_array() + >>> y = x.copy() + >>> p = _printer() + >>> check = p.check + >>> exercise_numarray(x, p) + + >>> check(y.astype()); + + >>> check(y.argmax()); + >>> check(y.argmax(0)); + + >>> check(y.argmin()); + >>> check(y.argmin(0)); + + >>> check(y.argsort()); + >>> check(y.argsort(1)); + + >>> y.byteswap(); + >>> check(y); + + >>> check(y.diagonal()); + >>> check(y.diagonal(1)); + >>> check(y.diagonal(0, 1)); + >>> check(y.diagonal(0, 1, 0)); + + >>> check(y.is_c_array()); + >>> check(y.isbyteswapped()); + + >>> check(y.trace()); + >>> check(y.trace(1)); + >>> check(y.trace(0, 1)); + >>> check(y.trace(0, 1, 0)); + + >>> check(y.new('D')); + >>> y.sort(); + >>> check(y); + >>> check(y.type()); + + >>> check(y.array((1.2, 3.4))); + >>> check(y.array((1.2, 3.4), "Double")); + >>> check(y.array((1.2, 3.4), "Double", (1,2,1))); + >>> check(y.array((1.2, 3.4), "Double", (2,1,1), false)); + >>> check(y.array((1.2, 3.4), "Double", (2,), true, true)); + + >>> p.results + [] + >>> del p + ''' + pass + +false = 0; +true = 1; +class _printer(object): + def __init__(self): + self.results = []; + def __call__(self, *stuff): + self.results += [ str(x) for x in stuff ] + def check(self, x): + if self.results[0] == str(x): + del self.results[0] + else: + print ' Expected:\n %s\n but got:\n %s' % (x, self.results[0]) + +def _run(args = None): + import sys + import doctest + + if args is not None: + sys.argv = args + + # See which of the numeric modules are installed + has_numeric = 0 + try: + import Numeric + m = Numeric + has_numeric = 1 + except ImportError: pass + + has_numarray = 0 + try: + import numarray + m = numarray + has_numarray = 1 + except ImportError: pass + + # Bail if neither one is installed + if not (has_numeric or has_numarray): + return 0 + + # test the info routine outside the doctest. See numpy.cpp for an + # explanation + import numpy_ext + if (has_numarray): + numpy_ext.info(m.array((1,2,3))) + + failures = 0 + + # + # Run tests 4 different ways if both modules are installed, just + # to show that set_module_and_type() is working properly + # + + # run all the tests with default module search + print 'testing default extension module' + failures += doctest.testmod(sys.modules.get(__name__))[0] + + # test against Numeric if installed + if has_numeric: + print 'testing Numeric module explicitly' + numpy_ext.set_module_and_type('Numeric', 'ArrayType') + failures += doctest.testmod(sys.modules.get(__name__))[0] + + global __test__ + if has_numarray: + # Add the _numarray_tests to the list of things to test in + # this case. + __test__ = { 'numarray_tests':_numarray_tests, + 'numeric_tests': numeric_tests } + print 'testing numarray module explicitly' + numpy_ext.set_module_and_type('numarray', 'NDArray') + failures += doctest.testmod(sys.modules.get(__name__))[0] + + # see that we can go back to the default + del __test__ + print 'testing default module again' + numpy_ext.set_module_and_type('', '') + failures += doctest.testmod(sys.modules.get(__name__))[0] + + return failures + +if __name__ == '__main__': + print "running..." + import sys + sys.exit(_run())