From bd0b01c049ccb91a003a793eb13d45c061ea9fe6 Mon Sep 17 00:00:00 2001 From: nobody Date: Mon, 30 Sep 2002 03:35:54 +0000 Subject: [PATCH] This commit was manufactured by cvs2svn to create branch 'RC_1_29_0'. [SVN r15564] --- include/boost/python/args_fwd.hpp | 41 +++ .../python/detail/make_keyword_range_fn.hpp | 45 +++ include/boost/python/detail/scope.hpp | 15 + include/boost/python/numeric.hpp | 231 +++++++++++++ src/numeric.cpp | 313 ++++++++++++++++++ test/args.cpp | 81 +++++ test/args.py | 161 +++++++++ test/numpy.cpp | 105 ++++++ test/numpy.py | 175 ++++++++++ 9 files changed, 1167 insertions(+) create mode 100644 include/boost/python/args_fwd.hpp create mode 100644 include/boost/python/detail/make_keyword_range_fn.hpp create mode 100644 include/boost/python/detail/scope.hpp create mode 100644 include/boost/python/numeric.hpp create mode 100644 src/numeric.cpp create mode 100644 test/args.cpp create mode 100644 test/args.py create mode 100644 test/numpy.cpp create mode 100644 test/numpy.py diff --git a/include/boost/python/args_fwd.hpp b/include/boost/python/args_fwd.hpp new file mode 100644 index 00000000..b8ca0c7d --- /dev/null +++ b/include/boost/python/args_fwd.hpp @@ -0,0 +1,41 @@ +// 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 ARGS_FWD_DWA2002927_HPP +# define ARGS_FWD_DWA2002927_HPP + +# include +# include +# include + +namespace boost { namespace python { + +namespace detail +{ + struct keyword; + template struct keywords; + + typedef std::pair keyword_range; + + template <> + struct keywords<0> + { + BOOST_STATIC_CONSTANT(std::size_t, size = 0); + static keyword_range range() { return keyword_range(); } + }; + + namespace error + { + template + struct more_keywords_than_function_arguments + { + typedef char too_many_keywords[keywords > function_args ? -1 : 1]; + }; + } +} + +}} // namespace boost::python + +#endif // ARGS_FWD_DWA2002927_HPP diff --git a/include/boost/python/detail/make_keyword_range_fn.hpp b/include/boost/python/detail/make_keyword_range_fn.hpp new file mode 100644 index 00000000..e807f158 --- /dev/null +++ b/include/boost/python/detail/make_keyword_range_fn.hpp @@ -0,0 +1,45 @@ +// 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 MAKE_KEYWORD_RANGE_FN_DWA2002927_HPP +# define MAKE_KEYWORD_RANGE_FN_DWA2002927_HPP + +# include +# include +# include +# include + +namespace boost { namespace python { namespace detail { + +template +object make_keyword_range_function(F f, Policies const& policies, keyword_range const& kw) +{ + enum { n_arguments = detail::arg_tuple_size::value }; + return objects::function_object( + ::boost::bind(detail::caller(), f, _1, _2, policies) + , n_arguments + , kw); +} + +template +object make_keyword_range_constructor( + Policies const& policies + , detail::keyword_range const& kw + , HolderGenerator* = 0 + , ArgList* = 0) +{ + enum { nargs = mpl::size::value }; + + return objects::function_object( + ::boost::bind(detail::caller(), + objects::make_holder + ::template apply::execute + , _1, _2, policies) + , nargs + 1, kw); +} + +}}} // namespace boost::python::detail + +#endif // MAKE_KEYWORD_RANGE_FN_DWA2002927_HPP diff --git a/include/boost/python/detail/scope.hpp b/include/boost/python/detail/scope.hpp new file mode 100644 index 00000000..f8ece8f9 --- /dev/null +++ b/include/boost/python/detail/scope.hpp @@ -0,0 +1,15 @@ +// 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 SCOPE_DWA2002927_HPP +# define SCOPE_DWA2002927_HPP + +namespace boost { namespace python { namespace detail { + +void BOOST_PYTHON_DECL scope_setattr_doc(char const* name, object const& obj, char const* doc); + +}}} // namespace boost::python::detail + +#endif // SCOPE_DWA2002927_HPP diff --git a/include/boost/python/numeric.hpp b/include/boost/python/numeric.hpp new file mode 100644 index 00000000..c7563b09 --- /dev/null +++ b/include/boost/python/numeric.hpp @@ -0,0 +1,231 @@ +// 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 +# 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_Z(1, 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_Z(1, n, long x)) \ + { \ + resize(make_tuple(BOOST_PP_ENUM_PARAMS_Z(1, 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_Z(1, n, long x)) \ + { \ + setshape(make_tuple(BOOST_PP_ENUM_PARAMS_Z(1, 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_Z(1, n, T, const& x)) \ + : base(BOOST_PP_ENUM_1(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, 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/src/numeric.cpp b/src/numeric.cpp new file mode 100644 index 00000000..7d275ff0 --- /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; + handle<> array_type; + handle<> 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 = handle<>(type); + PyObject* function = ::PyObject_GetAttrString(module, const_cast("array")); + + if (function && PyCallable_Check(function)) + { + array_function = handle<>(function); + state = succeeded; + } + } + } + } + + if (state == succeeded) + return true; + + if (throw_on_error) + throw_load_failure(); + + PyErr_Clear(); + return false; + } + + object demand_array_function() + { + load(true); + return object(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.get()); + } + + 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.get()), 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(demand_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(demand_array_function()(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/args.cpp b/test/args.cpp new file mode 100644 index 00000000..ee79fcfa --- /dev/null +++ b/test/args.cpp @@ -0,0 +1,81 @@ +// 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 +#include "test_class.hpp" + +using namespace boost::python; + +tuple f(int x = 1, double y = 4.25, char const* z = "wow") +{ + return make_tuple(x, y, z); +} + +BOOST_PYTHON_FUNCTION_OVERLOADS(f_overloads, f, 0, 3) + +typedef test_class<> Y; + +struct X +{ + X(int a0 = 0, int a1 = 1) : inner0(a0), inner1(a1) {} + tuple f(int x = 1, double y = 4.25, char const* z = "wow") + { + return make_tuple(x, y, z); + } + + Y const& inner(bool n) const { return n ? inner1 : inner0; } + + Y inner0; + Y inner1; +}; + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(X_f_overloads, X::f, 0, 3) + +BOOST_PYTHON_MODULE_INIT(args_ext) +{ + def("f", f, args("x", "y", "z") + , "This is f's docstring" + ); + +#if defined(BOOST_MSVC) && BOOST_MSVC <= 1200 + // MSVC6 gives a fatal error LNK1179: invalid or corrupt file: + // duplicate comdat error if we try to re-use the exact type of f + // here, so substitute long for int. + tuple (*f)(long,double,char const*) = 0; +#endif + def("f1", f, f_overloads("f1's docstring", args("x", "y", "z"))); + def("f2", f, f_overloads(args("x", "y", "z"))); + def("f3", f, f_overloads(args("x", "y", "z"), "f3's docstring")); + + class_("Y", init(args("value"), "Y's docstring")) + .def("value", &Y::value) + ; + + class_("X", "This is X's docstring") + .def(init >(args("a0", "a1"))) + .def("f", &X::f + , "This is X.f's docstring" + , args("x", "y", "z")) + + // Just to prove that all the different argument combinations work + .def("inner0", &X::inner, return_internal_reference<>(), args("n"), "docstring") + .def("inner1", &X::inner, return_internal_reference<>(), "docstring", args("n")) + + .def("inner2", &X::inner, args("n"), return_internal_reference<>(), "docstring") + .def("inner3", &X::inner, "docstring", return_internal_reference<>(), args("n")) + + .def("inner4", &X::inner, args("n"), "docstring", return_internal_reference<>()) + .def("inner5", &X::inner, "docstring", args("n"), return_internal_reference<>()) + + .def("f1", &X::f, X_f_overloads(args("x", "y", "z"))) + ; + + def("inner", &X::inner, "docstring", args("self", "n"), return_internal_reference<>()); +} diff --git a/test/args.py b/test/args.py new file mode 100644 index 00000000..665fae41 --- /dev/null +++ b/test/args.py @@ -0,0 +1,161 @@ +""" +>>> from args_ext import * + +>>> f(x= 1, y = 3, z = 'hello') +(1, 3.0, 'hello') + +>>> f(z = 'hello', x = 3, y = 2.5) +(3, 2.5, 'hello') + +>>> f(1, z = 'hi', y = 3) +(1, 3.0, 'hi') + +>>> try: f(1, 2, 'hello', bar = 'baz') +... except TypeError: pass +... else: print 'expected an exception: unknown keyword' + + + Exercise the functions using default stubs + +>>> f1(z = 'nix', y = .125, x = 2) +(2, 0.125, 'nix') +>>> f1(y = .125, x = 2) +(2, 0.125, 'wow') +>>> f1(x = 2) +(2, 4.25, 'wow') +>>> f1() +(1, 4.25, 'wow') + +>>> f2(z = 'nix', y = .125, x = 2) +(2, 0.125, 'nix') +>>> f2(y = .125, x = 2) +(2, 0.125, 'wow') +>>> f2(x = 2) +(2, 4.25, 'wow') +>>> f2() +(1, 4.25, 'wow') + +>>> f3(z = 'nix', y = .125, x = 2) +(2, 0.125, 'nix') +>>> f3(y = .125, x = 2) +(2, 0.125, 'wow') +>>> f3(x = 2) +(2, 4.25, 'wow') +>>> f3() +(1, 4.25, 'wow') + + Member function tests + +>>> q = X() +>>> q.f(x= 1, y = 3, z = 'hello') +(1, 3.0, 'hello') + +>>> q.f(z = 'hello', x = 3, y = 2.5) +(3, 2.5, 'hello') + +>>> q.f(1, z = 'hi', y = 3) +(1, 3.0, 'hi') + +>>> try: q.f(1, 2, 'hello', bar = 'baz') +... except TypeError: pass +... else: print 'expected an exception: unknown keyword' + + Exercise member functions using default stubs + +>>> q.f1(z = 'nix', y = .125, x = 2) +(2, 0.125, 'nix') +>>> q.f1(y = .125, x = 2) +(2, 0.125, 'wow') +>>> q.f1(x = 2) +(2, 4.25, 'wow') +>>> q.f1() +(1, 4.25, 'wow') + +>>> X.f.__doc__ +"This is X.f's docstring" + +>>> xfuncs = (X.inner0, X.inner1, X.inner2, X.inner3, X.inner4, X.inner5) +>>> for f in xfuncs: +... print f(q,1).value(), +... print f(q, n = 1).value(), +... print f(q, n = 0).value(), +... print f.__doc__ +1 1 0 docstring +1 1 0 docstring +1 1 0 docstring +1 1 0 docstring +1 1 0 docstring +1 1 0 docstring + +>>> x = X(a1 = 44, a0 = 22) +>>> x.inner0(0).value() +22 +>>> x.inner0(1).value() +44 + +>>> x = X(a0 = 7) +>>> x.inner0(0).value() +7 +>>> x.inner0(1).value() +1 + +>>> inner(n = 1, self = q).value() +1 +""" +def run(args = None): + import sys + import doctest + + if args is not None: + sys.argv = args + return doctest.testmod(sys.modules.get(__name__)) + +if __name__ == '__main__': + print "running..." + import sys + sys.exit(run()[0]) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..b4c3ec62 --- /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] + del __test__ + + # see that we can go back to the default + 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())