diff --git a/include/boost/python/arg_from_python.hpp b/include/boost/python/arg_from_python.hpp new file mode 100755 index 00000000..1d033acd --- /dev/null +++ b/include/boost/python/arg_from_python.hpp @@ -0,0 +1,52 @@ +// 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 ARG_FROM_PYTHON_DWA2002128_HPP +# define ARG_FROM_PYTHON_DWA2002128_HPP + +# include + +namespace boost { namespace python { + +template +struct arg_from_python + : converter::select_arg_from_python::type +{ + typedef typename converter::select_arg_from_python::type base; + arg_from_python(PyObject*); +}; + +// specialization for PyObject* +template <> +struct arg_from_python +{ + typedef PyObject* result_type; + + arg_from_python(PyObject*) {} + bool convertible() const { return true; } + PyObject* operator()(PyObject* source) const { return source; } +}; + +template <> +struct arg_from_python +{ + typedef PyObject* const& result_type; + arg_from_python(PyObject*) {} + bool convertible() const { return true; } + PyObject*const& operator()(PyObject*const& source) const { return source; } +}; + +// +// implementations +// +template +inline arg_from_python::arg_from_python(PyObject* source) + : base(source) +{ +} + +}} // namespace boost::python + +#endif // ARG_FROM_PYTHON_DWA2002128_HPP diff --git a/include/boost/python/base_type_traits.hpp b/include/boost/python/base_type_traits.hpp new file mode 100755 index 00000000..cae9ebc2 --- /dev/null +++ b/include/boost/python/base_type_traits.hpp @@ -0,0 +1,36 @@ +// 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 BASE_TYPE_TRAITS_DWA2002614_HPP +# define BASE_TYPE_TRAITS_DWA2002614_HPP + +namespace boost { namespace python { + +namespace detail +{ + struct unspecialized {}; +} + +// Derive from unspecialized so we can detect whether traits are +// specialized +template struct base_type_traits + : detail::unspecialized +{}; + +template <> +struct base_type_traits +{ + typedef PyObject type; +}; + +template <> +struct base_type_traits +{ + typedef PyObject type; +}; + +}} // namespace boost::python + +#endif // BASE_TYPE_TRAITS_DWA2002614_HPP diff --git a/include/boost/python/class_fwd.hpp b/include/boost/python/class_fwd.hpp new file mode 100644 index 00000000..87f3ba32 --- /dev/null +++ b/include/boost/python/class_fwd.hpp @@ -0,0 +1,30 @@ +// 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 CLASS_FWD_DWA200222_HPP +# define CLASS_FWD_DWA200222_HPP +# include +# include +# include + +namespace boost { namespace python { + +namespace detail +{ + struct empty_list; +} + +template < + class T // class being wrapped + // arbitrarily-ordered optional arguments. Full qualification needed for MSVC6 + , class X1 = ::boost::python::detail::not_specified + , class X2 = ::boost::python::detail::not_specified + , class X3 = ::boost::python::detail::not_specified + > +class class_; + +}} // namespace boost::python + +#endif // CLASS_FWD_DWA200222_HPP diff --git a/include/boost/python/converter/pointer_type_id.hpp b/include/boost/python/converter/pointer_type_id.hpp new file mode 100644 index 00000000..01e55977 --- /dev/null +++ b/include/boost/python/converter/pointer_type_id.hpp @@ -0,0 +1,69 @@ +// 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 POINTER_TYPE_ID_DWA2002222_HPP +# define POINTER_TYPE_ID_DWA2002222_HPP + +# include +# include + +namespace boost { namespace python { namespace converter { + +namespace detail +{ + template + struct pointer_typeid_select + { + template + static inline type_info execute(T*(*)() = 0) + { + return type_id(); + } + }; + + template <> + struct pointer_typeid_select + { + template + static inline type_info execute(T* const volatile&(*)() = 0) + { + return type_id(); + } + + template + static inline type_info execute(T*volatile&(*)() = 0) + { + return type_id(); + } + + template + static inline type_info execute(T*const&(*)() = 0) + { + return type_id(); + } + + template + static inline type_info execute(T*&(*)() = 0) + { + return type_id(); + } + }; +} + +// Usage: pointer_type_id() +// +// Returns a type_info associated with the type pointed +// to by T, which may be a pointer or a reference to a pointer. +template +type_info pointer_type_id(T(*)() = 0) +{ + return detail::pointer_typeid_select< + is_reference::value + >::execute((T(*)())0); +} + +}}} // namespace boost::python::converter + +#endif // POINTER_TYPE_ID_DWA2002222_HPP diff --git a/include/boost/python/detail/config.hpp b/include/boost/python/detail/config.hpp new file mode 100644 index 00000000..2a9ec35e --- /dev/null +++ b/include/boost/python/detail/config.hpp @@ -0,0 +1,109 @@ +// (C) Copyright David Abrahams 2000. 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. +// +// The author gratefully acknowleges the support of Dragon Systems, Inc., in +// producing this work. + +// Revision History: +// 04 Mar 01 Some fixes so it will compile with Intel C++ (Dave Abrahams) + +#ifndef CONFIG_DWA052200_H_ +# define CONFIG_DWA052200_H_ + +# include + +# ifdef BOOST_NO_OPERATORS_IN_NAMESPACE + // A gcc bug forces some symbols into the global namespace +# define BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE +# define BOOST_PYTHON_END_CONVERSION_NAMESPACE +# define BOOST_PYTHON_CONVERSION +# define BOOST_PYTHON_IMPORT_CONVERSION(x) using ::x +# else +# define BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE namespace boost { namespace python { +# define BOOST_PYTHON_END_CONVERSION_NAMESPACE }} // namespace boost::python +# define BOOST_PYTHON_CONVERSION boost::python +# define BOOST_PYTHON_IMPORT_CONVERSION(x) void never_defined() // so we can follow the macro with a ';' +# endif + +# if defined(BOOST_MSVC) +# if _MSC_VER <= 1200 +# define BOOST_MSVC6_OR_EARLIER 1 +# endif + +# pragma warning (disable : 4786) + +# elif defined(__ICL) && __ICL < 600 // Intel C++ 5 + +# pragma warning(disable: 985) // identifier was truncated in debug information + +# endif + +// The STLport puts all of the standard 'C' library names in std (as far as the +// user is concerned), but without it you need a fix if you're using MSVC or +// Intel C++ +# if defined(BOOST_MSVC_STD_ITERATOR) +# define BOOST_CSTD_ +# else +# define BOOST_CSTD_ std +# endif + +/***************************************************************************** + * + * Set up dll import/export options: + * + ****************************************************************************/ + +// backwards compatibility: +#ifdef BOOST_PYTHON_STATIC_LIB +# define BOOST_PYTHON_STATIC_LINK +# elif !defined(BOOST_PYTHON_DYNAMIC_LIB) +# define BOOST_PYTHON_DYNAMIC_LIB +#endif + +#if defined(__MWERKS__) \ + || (defined(__DECCXX_VER) && __DECCXX_VER <= 60590002) \ + || (defined(__sgi) && defined(_COMPILER_VERSION) && _COMPILER_VERSION <= 730) +# define BOOST_PYTHON_NO_TEMPLATE_EXPORT +#endif + +#if defined(BOOST_PYTHON_DYNAMIC_LIB) && defined(_WIN32) +# if defined(BOOST_PYTHON_SOURCE) +# define BOOST_PYTHON_DECL __declspec(dllexport) +# define BOOST_PYTHON_BUILD_DLL +# else +# define BOOST_PYTHON_DECL __declspec(dllimport) +# endif + +// MinGW, at least, has some problems exporting template instantiations +# if defined(__GNUC__) && __GNUC__ < 3 && !defined(__CYGWIN__) +# define BOOST_PYTHON_NO_TEMPLATE_EXPORT +# endif + +#endif + +#ifndef BOOST_PYTHON_DECL +# define BOOST_PYTHON_DECL +#endif + +#ifndef BOOST_PYTHON_EXPORT +# define BOOST_PYTHON_EXPORT extern +#endif + +#if !defined(BOOST_PYTHON_NO_TEMPLATE_EXPORT) +# define BOOST_PYTHON_EXPORT_CLASS_TEMPLATE(instantiation) BOOST_PYTHON_EXPORT template class BOOST_PYTHON_DECL instantiation +#else +# define BOOST_PYTHON_EXPORT_CLASS_TEMPLATE(instantiation) struct ThIsTyPeNeVeRuSeD +#endif + +#if defined(__sgi) && defined(_COMPILER_VERSION) && _COMPILER_VERSION <= 730 +// Work around a compiler bug. +// boost::python::detail::function has to be seen by the compiler before the +// boost::function class template. +namespace boost { namespace python { namespace detail { +class function; +}}} +#endif + +#endif // CONFIG_DWA052200_H_ diff --git a/include/boost/python/detail/convertible.hpp b/include/boost/python/detail/convertible.hpp new file mode 100755 index 00000000..4ab58d94 --- /dev/null +++ b/include/boost/python/detail/convertible.hpp @@ -0,0 +1,39 @@ +// 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 CONVERTIBLE_DWA2002614_HPP +# define CONVERTIBLE_DWA2002614_HPP + +# if defined(__EDG_VERSION__) && __EDG_VERSION__ <= 241 +# include +# include +# endif + +// Supplies a runtime is_convertible check which can be used with tag +// dispatching to work around the Metrowerks Pro7 limitation with boost::is_convertible +namespace boost { namespace python { namespace detail { + +typedef char* yes_convertible; +typedef int* no_convertible; + +template +struct convertible +{ +# if !defined(__EDG_VERSION__) || __EDG_VERSION__ > 241 || __EDG_VERSION__ == 238 + static inline no_convertible check(...) { return 0; } + static inline yes_convertible check(Target) { return 0; } +# else + template + static inline typename mpl::select_type< + is_convertible::value + , yes_convertible + , no_convertible + >::type check(X const&) { return 0; } +# endif +}; + +}}} // namespace boost::python::detail + +#endif // CONVERTIBLE_DWA2002614_HPP diff --git a/include/boost/python/detail/msvc_typeinfo.hpp b/include/boost/python/detail/msvc_typeinfo.hpp new file mode 100644 index 00000000..21e47389 --- /dev/null +++ b/include/boost/python/detail/msvc_typeinfo.hpp @@ -0,0 +1,100 @@ +// 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 MSVC_TYPEINFO_DWA200222_HPP +# define MSVC_TYPEINFO_DWA200222_HPP + +#include +#include +#include +#include +// +// Fix for MSVC's broken typeid() implementation which doesn't strip +// decoration. This fix doesn't handle cv-qualified array types. It +// could probably be done, but I haven't figured it out yet. +// + +# if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 || defined(BOOST_INTEL_CXX_VERSION) && BOOST_INTEL_CXX_VERSION <= 600 + +namespace boost { namespace python { namespace detail { + +typedef std::type_info const& typeinfo; + +templatestruct value_id_accessor; + +template<> +struct value_id_accessor<0> +{ + template + static typeinfo get(T*) { return typeid(T); } +}; + +template<> +struct value_id_accessor<1> +{ + template + static typeinfo get(T const*) { return typeid(T); } +}; + +template<> +struct value_id_accessor<2> +{ + template + static typeinfo get(T volatile*) { return typeid(T); } +}; + +template<> +struct value_id_accessor<3> +{ + template + static typeinfo get(T const volatile*) { return typeid(T); } +}; + +template struct bool_t{}; + +template +inline typeinfo typeid_nonref(boost::type* = 0) +{ + bool const c = is_const::value; + bool const v = is_volatile::value; + return value_id_accessor<(2 * v + c)>::get((T*)0); +} + +template +inline typeinfo typeid_ref(T&(*)()) +{ + return typeid_nonref(); +} + +template +inline typeinfo array_ref_typeid(bool_t, bool_t, boost::type* = 0) +{ + return typeid_ref((T&(*)())0); +} + +template +inline typeinfo array_ref_typeid(bool_t, bool_t, boost::type* = 0) +{ + return typeid_ref((T(*)())0); +} + +template +inline typeinfo array_ref_typeid(bool_t, bool_t, boost::type* = 0) +{ + return typeid_ref((T&(*)())0); +} + +template +inline typeinfo msvc_typeid(boost::type* = 0) +{ + typedef bool_t::value> array_tag; + typedef bool_t::value> ref_tag; + return array_ref_typeid(array_tag(), ref_tag(), (boost::type*)0); +} + +}}} // namespace boost::python::detail + +# endif // BOOST_MSVC +#endif // MSVC_TYPEINFO_DWA200222_HPP diff --git a/include/boost/python/detail/operator_id.hpp b/include/boost/python/detail/operator_id.hpp new file mode 100755 index 00000000..8edb310b --- /dev/null +++ b/include/boost/python/detail/operator_id.hpp @@ -0,0 +1,55 @@ +// 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 OPERATOR_ID_DWA2002531_HPP +# define OPERATOR_ID_DWA2002531_HPP + +namespace boost { namespace python { namespace detail { + +enum operator_id +{ + op_add, + op_sub, + op_mul, + op_div, + op_mod, + op_divmod, + op_pow, + op_lshift, + op_rshift, + op_and, + op_xor, + op_or, + op_neg, + op_pos, + op_abs, + op_invert, + op_int, + op_long, + op_float, + op_str, + op_cmp, + op_gt, + op_ge, + op_lt, + op_le, + op_eq, + op_ne, + op_iadd, + op_isub, + op_imul, + op_idiv, + op_imod, + op_ilshift, + op_irshift, + op_iand, + op_ixor, + op_ior, + op_complex +}; + +}}} // namespace boost::python::detail + +#endif // OPERATOR_ID_DWA2002531_HPP diff --git a/include/boost/python/detail/pointee.hpp b/include/boost/python/detail/pointee.hpp new file mode 100644 index 00000000..2af1535f --- /dev/null +++ b/include/boost/python/detail/pointee.hpp @@ -0,0 +1,36 @@ +// 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 POINTEE_DWA2002323_HPP +# define POINTEE_DWA2002323_HPP + +# include + +namespace boost { namespace python { namespace detail { + +template +struct pointee_impl +{ + template struct apply : remove_pointer {}; +}; + +template <> +struct pointee_impl +{ + template struct apply + { + typedef typename T::element_type type; + }; +}; + +template +struct pointee + : pointee_impl::value>::template apply +{ +}; + +}}} // namespace boost::python::detail + +#endif // POINTEE_DWA2002323_HPP diff --git a/include/boost/python/detail/unwind_type.hpp b/include/boost/python/detail/unwind_type.hpp new file mode 100644 index 00000000..878b6cc1 --- /dev/null +++ b/include/boost/python/detail/unwind_type.hpp @@ -0,0 +1,151 @@ +// 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 UNWIND_TYPE_DWA200222_HPP +# define UNWIND_TYPE_DWA200222_HPP + +# include +# include +# include + +namespace boost { namespace python { namespace detail { + +template +inline typename Generator::result_type +unwind_type_cv(U* p, cv_unqualified, Generator* = 0) +{ + return Generator::execute(p); +} + +template +inline typename Generator::result_type +unwind_type_cv(U const* p, const_, Generator* = 0) +{ + return unwind_type(const_cast(p), (Generator*)0); +} + +template +inline typename Generator::result_type +unwind_type_cv(U volatile* p, volatile_, Generator* = 0) +{ + return unwind_type(const_cast(p), (Generator*)0); +} + +template +inline typename Generator::result_type +unwind_type_cv(U const volatile* p, const_volatile_, Generator* = 0) +{ + return unwind_type(const_cast(p), (Generator*)0); +} + +template +inline typename Generator::result_type +unwind_ptr_type(U* p, Generator* = 0) +{ + typedef typename cv_category::type tag; + return unwind_type_cv(p, tag()); +} + +template +struct unwind_helper +{ + template + static typename Generator::result_type + execute(U p, Generator* = 0) + { + return unwind_ptr_type(p, (Generator*)0); + } +}; + +template <> +struct unwind_helper +{ + template + static typename Generator::result_type + execute(U& p, Generator* = 0) + { + return unwind_ptr_type(&p, (Generator*)0); + } +}; + +template +inline typename Generator::result_type +unwind_type(U const& p, Generator* = 0) +{ + return unwind_helper::value>::execute(p, (Generator*)0); +} + +enum { direct_ = 0, pointer_ = 1, reference_ = 2, reference_to_pointer_ = 3 }; +template struct unwind_helper2; + +template <> +struct unwind_helper2 +{ + template + static typename Generator::result_type + execute(U(*)(), Generator* = 0) + { + return unwind_ptr_type((U*)0, (Generator*)0); + } +}; + +template <> +struct unwind_helper2 +{ + template + static typename Generator::result_type + execute(U*(*)(), Generator* = 0) + { + return unwind_ptr_type((U*)0, (Generator*)0); + } +}; + +template <> +struct unwind_helper2 +{ + template + static typename Generator::result_type + execute(U&(*)(), Generator* = 0) + { + return unwind_ptr_type((U*)0, (Generator*)0); + } +}; + +template <> +struct unwind_helper2 +{ + template + static typename Generator::result_type + execute(U&(*)(), Generator* = 0) + { + return unwind_ptr_type(U(0), (Generator*)0); + } +}; + +// Call this one with both template parameters explicitly specified +// and no function arguments: +// +// return unwind_type(); +// +// Doesn't work if T is an array type; we could handle that case, but +// why bother? +template +inline typename Generator::result_type +unwind_type(boost::type*p = 0, Generator* = 0) +{ + BOOST_STATIC_CONSTANT(int, indirection + = (is_pointer::value ? pointer_ : 0) + + (is_reference_to_pointer::value + ? reference_to_pointer_ + : is_reference::value + ? reference_ + : 0)); + + return unwind_helper2::execute((U(*)())0,(Generator*)0); +} + +}}} // namespace boost::python::detail + +#endif // UNWIND_TYPE_DWA200222_HPP diff --git a/include/boost/python/handle_fwd.hpp b/include/boost/python/handle_fwd.hpp new file mode 100755 index 00000000..afcce163 --- /dev/null +++ b/include/boost/python/handle_fwd.hpp @@ -0,0 +1,17 @@ +// 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 HANDLE_FWD_DWA2002615_HPP +# define HANDLE_FWD_DWA2002615_HPP + +# include + +namespace boost { namespace python { + +template class handle; + +}} // namespace boost::python + +#endif // HANDLE_FWD_DWA2002615_HPP diff --git a/include/boost/python/implicit.hpp b/include/boost/python/implicit.hpp new file mode 100644 index 00000000..a6e73eba --- /dev/null +++ b/include/boost/python/implicit.hpp @@ -0,0 +1,28 @@ +// 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 IMPLICIT_DWA2002325_HPP +# define IMPLICIT_DWA2002325_HPP +# include +# include +# include +# include + +namespace boost { namespace python { + +template +void implicitly_convertible(boost::type* = 0, boost::type* = 0) +{ + typedef converter::implicit functions; + + converter::registry::push_back( + &functions::convertible + , &functions::construct + , type_id()); +} + +}} // namespace boost::python + +#endif // IMPLICIT_DWA2002325_HPP diff --git a/include/boost/python/lvalue_from_pytype.hpp b/include/boost/python/lvalue_from_pytype.hpp new file mode 100755 index 00000000..ae01a6bd --- /dev/null +++ b/include/boost/python/lvalue_from_pytype.hpp @@ -0,0 +1,104 @@ +// 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 LVALUE_FROM_PYTYPE_DWA2002130_HPP +# define LVALUE_FROM_PYTYPE_DWA2002130_HPP + +# include +# include +# include + +namespace boost { namespace python { + +namespace detail +{ + // Given a pointer-to-function of 1 parameter returning a reference + // type, return the type_id of the function's return type. + template + inline type_info extractor_type_id(T&(*)(U)) + { + return type_id(); + } + + // A function generator whose static execute() function is an lvalue + // from_python converter using the given Extractor. U is expected to + // be the actual type of the PyObject instance from which the result + // is being extracted. + template + struct normalized_extractor + { + static inline void* execute(PyObject* op) + { + typedef typename boost::add_reference::type param; + return &Extractor::execute( + boost::python::detail::void_ptr_to_reference( + op, (param(*)())0 ) + ); + } + }; + + // Given an Extractor type and a pointer to its execute function, + // return a new object whose static execute function does the same + // job but is a conforming lvalue from_python conversion function. + // + // usage: normalize(&Extractor::execute) + template + inline normalized_extractor + normalize(T(*)(U), Extractor* = 0) + { + return normalized_extractor(); + } +} + +// An Extractor which extracts the given member from a Python object +// whose instances are stored as InstanceType. +template +struct extract_member +{ + static MemberType& execute(InstanceType& c) + { + (void)c.ob_type; // static assertion + return c.*member; + } +}; + +// An Extractor which simply extracts the entire python object +// instance of InstanceType. +template +struct extract_identity +{ + static InstanceType& execute(InstanceType& c) + { + (void)c.ob_type; // static assertion + return c; + } +}; + +// Registers a from_python conversion which extracts lvalues using +// Extractor's static execute function from Python objects whose type +// object is python_type. +template +struct lvalue_from_pytype +{ + lvalue_from_pytype() + { + converter::registry::insert( + &extract, detail::extractor_type_id(&Extractor::execute)); + } + private: + static void* extract(PyObject* op) + { + return PyObject_TypeCheck(op, const_cast(python_type)) + ? const_cast( + static_cast( + detail::normalize(&Extractor::execute).execute(op))) + : 0 + ; + } +}; + +}} // namespace boost::python + +#endif // LVALUE_FROM_PYTYPE_DWA2002130_HPP diff --git a/include/boost/python/manage_new_object.hpp b/include/boost/python/manage_new_object.hpp new file mode 100644 index 00000000..e19035b7 --- /dev/null +++ b/include/boost/python/manage_new_object.hpp @@ -0,0 +1,40 @@ +// 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 MANAGE_NEW_OBJECT_DWA200222_HPP +# define MANAGE_NEW_OBJECT_DWA200222_HPP +# include +# include +# include +# include + +namespace boost { namespace python { + +namespace detail +{ + template + struct manage_new_object_requires_a_pointer_return_type +# if defined(__GNUC__) && __GNUC__ >= 3 || defined(__EDG__) + {} +# endif + ; +} + +struct manage_new_object +{ + template + struct apply + { + typedef typename mpl::select_type< + boost::is_pointer::value + , to_python_indirect + , detail::manage_new_object_requires_a_pointer_return_type + >::type type; + }; +}; + +}} // namespace boost::python + +#endif // MANAGE_NEW_OBJECT_DWA200222_HPP diff --git a/include/boost/python/module_builder.hpp b/include/boost/python/module_builder.hpp new file mode 100644 index 00000000..222b28d7 --- /dev/null +++ b/include/boost/python/module_builder.hpp @@ -0,0 +1,76 @@ +// (C) Copyright David Abrahams 2000. 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. +// +// The author gratefully acknowleges the support of Dragon Systems, Inc., in +// producing this work. + +#ifndef MODULE_DWA051000_H_ +# define MODULE_DWA051000_H_ + +# include +# include +# include +# include +# include + +namespace boost { namespace python { + +class BOOST_PYTHON_DECL module_builder_base +{ + public: + // Create a module. REQUIRES: only one module_builder is created per module. + module_builder_base(const char* name); + ~module_builder_base(); + + // Add elements to the module + void add(detail::function* x, const char* name); + void add(PyTypeObject* x, const char* name = 0); + void add(ref x, const char*name); + + // Return true iff a module is currently being built. + static bool initializing(); + + // Return the name of the module currently being built. + // REQUIRES: initializing() == true + static string name(); + + // Return a pointer to the Python module object being built + PyObject* module() const; + + private: + PyObject* m_module; + static PyMethodDef initial_methods[1]; +}; + +class module_builder : public module_builder_base +{ + public: + module_builder(const char* name) + : module_builder_base(name) {} + + template + void def_raw(Fn fn, const char* name) + { + add(detail::new_raw_arguments_function(fn), name); + } + + template + void def(Fn fn, const char* name) + { + add(detail::new_wrapped_function(fn), name); + } +}; + +// +// inline implementations +// +inline PyObject* module_builder_base::module() const +{ + return m_module; +} + +}} // namespace boost::python + +#endif diff --git a/include/boost/python/object/class_converters.hpp b/include/boost/python/object/class_converters.hpp new file mode 100644 index 00000000..14e79a9d --- /dev/null +++ b/include/boost/python/object/class_converters.hpp @@ -0,0 +1,90 @@ +// 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 CLASS_CONVERTERS_DWA2002119_HPP +# define CLASS_CONVERTERS_DWA2002119_HPP + +# include +# include +# include +# include +# include +# include +# include + +namespace boost { namespace python { namespace objects { + +////////////////////////////////////////////////////////////////////// +// +// register_base_of - +// A BinaryMetaFunction object which registers a single base +// class of T, and the corresponding cast(s) +// + + +// register_downcast/do_nothing - +// Helpers for register_base_of<> which take care of registering +// down-casts +template +struct register_downcast +{ + static void execute() + { + register_conversion(true); + } +}; + +struct do_nothing +{ + static void execute() { } +}; + +// Here's where the real work gets done: +template +struct register_base_of +{ + // Ignored is needed because mpl::for_each is still actually + // accumulate. We're not using any state so it just sits there. + template + struct apply + { + typedef void type; // 'type' needs to be defined for the same reasons + + // Here's the runtime part: + static void execute() + { + // Register the Base class + register_dynamic_id(); + // Register the up-cast + register_conversion(false); + + // Register the down-cast, if appropriate. + mpl::select_type< + is_polymorphic::value + , register_downcast + , do_nothing + >::type::execute(); + } + }; +}; + +// Brings into existence all converters associated with a class Bases +// is expected to be an mpl sequence of base types. +template +inline void register_class_from_python(Derived* = 0, Bases* = 0) +{ + // cause the static registration to be instantiated. + python::detail::force_instantiate(instance_finder::registration); + + // register all up/downcasts here + register_dynamic_id(); + + // register each base in the sequence + mpl::for_each >::execute(); +} + +}}} // namespace boost::python::object + +#endif // CLASS_CONVERTERS_DWA2002119_HPP diff --git a/include/boost/python/object/find_instance.hpp b/include/boost/python/object/find_instance.hpp new file mode 100644 index 00000000..44c79550 --- /dev/null +++ b/include/boost/python/object/find_instance.hpp @@ -0,0 +1,40 @@ +// 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 FIND_INSTANCE_DWA2002312_HPP +# define FIND_INSTANCE_DWA2002312_HPP + +# include +# include + +namespace boost { namespace python { namespace objects { + +// Given a type_id, find the instance data which corresponds to it, or +// return 0 in case no such type is held. +BOOST_PYTHON_DECL void* find_instance_impl(PyObject*, type_info); + +// This produces a function with the right signature for use in from_python conversions +template +struct instance_finder +{ + instance_finder() + { + converter::registry::insert(&execute, python::type_id()); + } + + static instance_finder const registration; + private: + static inline void* execute(PyObject* p) + { + return find_instance_impl(p, python::type_id()); + } +}; + +template +instance_finder const instance_finder::registration; + +}}} // namespace boost::python::objects + +#endif // FIND_INSTANCE_DWA2002312_HPP diff --git a/include/boost/python/object/forward.hpp b/include/boost/python/object/forward.hpp new file mode 100644 index 00000000..cc23e94c --- /dev/null +++ b/include/boost/python/object/forward.hpp @@ -0,0 +1,108 @@ +// Copyright David Abrahams 2001. 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 FORWARD_DWA20011215_HPP +# define FORWARD_DWA20011215_HPP + +# include +# include +# include +# include +# include + +namespace boost { namespace python { namespace objects { + +template +struct reference_to_value +{ + typedef typename add_reference::type>::type reference; + + reference_to_value(reference x) : m_value(x) {} + operator reference() const { return m_value; } + private: + reference m_value; +}; + +// A little metaprogram which selects the type to pass through an +// intermediate forwarding function when the destination argument type +// is T. +template +struct forward + : mpl::select_type< + is_scalar::value + , T + , reference_to_value > +{ +}; + +# ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION +template +class unforward +{ + public: + typedef typename unwrap_reference::type& type; +}; + +template +class unforward > +{ + public: + typedef T type; +}; +# else // no partial specialization + +namespace detail +{ + typedef char (&yes_reference_to_value_t)[1]; + typedef char (&no_reference_to_value_t)[2]; + + no_reference_to_value_t is_reference_to_value_test(...); + + template + yes_reference_to_value_t is_reference_to_value_test(boost::type< reference_to_value >); + + template + struct unforwarder + { + template + struct apply + { + typedef typename unwrap_reference::type& type; + }; + }; + + template<> + struct unforwarder + { + template + struct apply + { + typedef typename T::reference type; + }; + }; + + template + class is_reference_to_value + { + public: + BOOST_STATIC_CONSTANT( + bool, value = ( + sizeof(is_reference_to_value_test(boost::type())) + == sizeof(yes_reference_to_value_t))); + }; +} + +template +class unforward + : public detail::unforwarder< + detail::is_reference_to_value::value + >::template apply +{}; + +# endif // BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + +}}} // namespace boost::python::objects + +#endif // FORWARD_DWA20011215_HPP diff --git a/include/boost/python/object/inheritance.hpp b/include/boost/python/object/inheritance.hpp new file mode 100644 index 00000000..5129e35e --- /dev/null +++ b/include/boost/python/object/inheritance.hpp @@ -0,0 +1,167 @@ +// 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 INHERITANCE_DWA200216_HPP +# define INHERITANCE_DWA200216_HPP + +# include +# include +# include +# include + +namespace boost { namespace python { namespace objects { + +typedef type_info class_id; +using python::type_id; + +// Types used to get address and id of most derived type +typedef std::pair dynamic_id_t; +typedef dynamic_id_t (*dynamic_id_function)(void*); + +BOOST_PYTHON_DECL void register_dynamic_id_aux( + class_id static_id, dynamic_id_function get_dynamic_id); + +BOOST_PYTHON_DECL void add_cast( + class_id src_t, class_id dst_t, void* (*cast)(void*), bool polymorphic); + +BOOST_PYTHON_DECL void* find_static_type(void* p, class_id src, class_id dst); + +BOOST_PYTHON_DECL void* find_dynamic_type(void* p, class_id src, class_id dst); + +// is_polymorphic test from John Maddock +template +struct is_polymorphic +{ + struct d1 : public T + { + d1(); + char padding[256]; + }; + struct d2 : public T + { + d2(); + virtual ~d2(); + virtual void foo(); + char padding[256]; + }; + BOOST_STATIC_CONSTANT(bool, value = (sizeof(d2) == sizeof(d1))); +}; + +// +// a generator with an execute() function which, given a source type +// and a pointer to an object of that type, returns its most-derived +// /reachable/ type identifier and object pointer. +// + +// first, the case where T has virtual functions +template +struct polymorphic_id_generator +{ + static dynamic_id_t execute(void* p_) + { + T* p = static_cast(p_); + return std::make_pair(dynamic_cast(p), class_id(typeid(*p))); + } +}; + +// now, the non-polymorphic case. +template +struct non_polymorphic_id_generator +{ + static dynamic_id_t execute(void* p_) + { + return std::make_pair(p_, python::type_id()); + } +}; + +// Now the generalized selector +template +struct dynamic_id_generator +{ + typedef typename mpl::select_type< + is_polymorphic::value + , polymorphic_id_generator + , non_polymorphic_id_generator >::type type; +}; + +// Register the dynamic id function for T with the type-conversion +// system. +template +void register_dynamic_id(T* = 0) +{ + typedef typename dynamic_id_generator::type generator; + register_dynamic_id_aux( + python::type_id(), &generator::execute); +} + +// +// a generator with an execute() function which, given a void* +// pointing to an object of type Source will attempt to convert it to +// an object of type Target. +// + +template +struct dynamic_cast_generator +{ + static void* execute(void* source) + { + return dynamic_cast( + static_cast(source)); + } + +}; + +template +struct implicit_cast_generator +{ + static void* execute(void* source) + { + Target* result = static_cast(source); + return result; + } +}; + +template +struct cast_generator +{ + // CWPro7 will return false sometimes, but that's OK since we can + // always cast up with dynamic_cast<> + BOOST_STATIC_CONSTANT( + bool, is_upcast = ( + is_base_and_derived::value + )); + + typedef typename mpl::select_type< + is_upcast +# if defined(__MWERKS__) && __MWERKS__ <= 0x2406 + // grab a few more implicit_cast cases for CodeWarrior + || !is_polymorphic::value + || !is_polymorphic::value +# endif + , implicit_cast_generator + , dynamic_cast_generator + >::type type; +}; + +template +inline void register_conversion( + // We need this parameter because CWPro7 can't determine + // which is the base reliably. + bool is_downcast = !cast_generator::is_upcast + + // These parameters shouldn't be used, they're an MSVC bug workaround + , Source* = 0, Target* = 0) +{ + typedef typename cast_generator::type generator; + + add_cast(python::type_id() + , python::type_id() + , &generator::execute + , is_downcast); +} + +}}} // namespace boost::python::object + +#endif // INHERITANCE_DWA200216_HPP diff --git a/include/boost/python/object_attributes.hpp b/include/boost/python/object_attributes.hpp new file mode 100755 index 00000000..4d3c2a7d --- /dev/null +++ b/include/boost/python/object_attributes.hpp @@ -0,0 +1,67 @@ +// 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 OBJECT_ATTRIBUTES_DWA2002615_HPP +# define OBJECT_ATTRIBUTES_DWA2002615_HPP + +# include +# include +# include + +namespace boost { namespace python { namespace api { + +struct const_attribute_policies +{ + typedef char const* key_type; + static object get(object const& target, char const* key); +}; + +struct attribute_policies : const_attribute_policies +{ + static object const& set(object const& target, char const* key, object const& value); + static void del(object const&target, char const* key); +}; + +// +// implementation +// +template +inline object_attribute object_operators::attr(char const* name) +{ + object_cref2 x = *static_cast(this); + return object_attribute(x, name); +} + +template +inline const_object_attribute object_operators::attr(char const* name) const +{ + object_cref2 x = *static_cast(this); + return const_object_attribute(x, name); +} + +inline object const_attribute_policies::get(object const& target, char const* key) +{ + return python::getattr(target, key); +} + +inline object const& attribute_policies::set( + object const& target + , char const* key + , object const& value) +{ + python::setattr(target, key, value); + return value; +} + +inline void attribute_policies::del( + object const& target + , char const* key) +{ + python::delattr(target, key); +} + +}}} // namespace boost::python::api + +#endif // OBJECT_ATTRIBUTES_DWA2002615_HPP diff --git a/include/boost/python/object_items.hpp b/include/boost/python/object_items.hpp new file mode 100755 index 00000000..436a671f --- /dev/null +++ b/include/boost/python/object_items.hpp @@ -0,0 +1,88 @@ +// 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 OBJECT_ITEMS_DWA2002615_HPP +# define OBJECT_ITEMS_DWA2002615_HPP + +# include +# include +# include + +namespace boost { namespace python { namespace api { + +struct const_item_policies +{ + typedef object key_type; + static object get(object const& target, object const& key); +}; + +struct item_policies : const_item_policies +{ + static object const& set(object const& target, object const& key, object const& value); + static void del(object const& target, object const& key); +}; + +// +// implementation +// +template +inline object_item +object_operators::operator[](object_cref key) +{ + object_cref2 x = *static_cast(this); + return object_item(x, key); +} + +template +inline const_object_item +object_operators::operator[](object_cref key) const +{ + object_cref2 x = *static_cast(this); + return const_object_item(x, key); +} + +# if !defined(BOOST_MSVC) || BOOST_MSVC > 1300 +template +template +inline const_object_item +object_operators::operator[](T const& key) const +{ + return (*this)[object(key)]; +} + +template +template +inline object_item +object_operators::operator[](T const& key) +{ + return (*this)[object(key)]; +} +# endif + + +inline object const_item_policies::get(object const& target, object const& key) +{ + return getitem(target, key); +} + +inline object const& item_policies::set( + object const& target + , object const& key + , object const& value) +{ + setitem(target, key, value); + return value; +} + +inline void item_policies::del( + object const& target + , object const& key) +{ + delitem(target, key); +} + +}}} // namespace boost::python::api + +#endif // OBJECT_ITEMS_DWA2002615_HPP diff --git a/include/boost/python/operators.hpp b/include/boost/python/operators.hpp new file mode 100644 index 00000000..df401f85 --- /dev/null +++ b/include/boost/python/operators.hpp @@ -0,0 +1,555 @@ +// (C) Copyright Ullrich Koethe and David Abrahams 2000-2001. 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. +// +// The authors gratefully acknowlege the support of Dragon Systems, Inc., in +// producing this work. +// +// Revision History: +// 23 Jan 2001 - Another stupid typo fix by Ralf W. Grosse-Kunstleve (David Abrahams) +// 20 Jan 2001 - Added a fix from Ralf W. Grosse-Kunstleve (David Abrahams) +#ifndef OPERATORS_UK112000_H_ +# define OPERATORS_UK112000_H_ +# ifdef BOOST_PYTHON_V2 + +# include + +# else + +# include +# include + +// When STLport is used with native streams, _STL::ostringstream().str() is not +// _STL::string, but std::string. This confuses to_python(), so we'll use +// strstream instead. Also, GCC 2.95.2 doesn't have sstream. +# if defined(__SGI_STL_PORT) ? defined(__SGI_STL_OWN_IOSTREAMS) : (!defined(__GNUC__) || __GNUC__ > 2) +# define BOOST_PYTHON_USE_SSTREAM +# endif + +# if defined(BOOST_PYTHON_USE_SSTREAM) +# include +# else +# include +# endif + +namespace boost { namespace python { + +BOOST_PYTHON_DECL tuple standard_coerce(ref l, ref r); + +namespace detail { + + // helper class for automatic operand type detection + // during operator wrapping. + struct auto_operand {}; +} + +// Define operator ids that can be or'ed together +// (boost::python::op_add | boost::python::op_sub | boost::python::op_mul). +// This allows to wrap several operators in one line. +enum operator_id +{ + op_add = 0x1, + op_sub = 0x2, + op_mul = 0x4, + op_div = 0x8, + op_mod = 0x10, + op_divmod =0x20, + op_pow = 0x40, + op_lshift = 0x80, + op_rshift = 0x100, + op_and = 0x200, + op_xor = 0x400, + op_or = 0x800, + op_neg = 0x1000, + op_pos = 0x2000, + op_abs = 0x4000, + op_invert = 0x8000, + op_int = 0x10000, + op_long = 0x20000, + op_float = 0x40000, + op_str = 0x80000, + op_cmp = 0x100000, + op_gt = 0x200000, + op_ge = 0x400000, + op_lt = 0x800000, + op_le = 0x1000000, + op_eq = 0x2000000, + op_ne = 0x4000000 +}; + +// Wrap the operators given by "which". Usage: +// foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub)>()); +template +struct operators {}; + +// Wrap heterogeneous operators with given left operand type. Usage: +// foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub)>(), +// boost::python::left_operand()); +template +struct left_operand {}; + +// Wrap heterogeneous operators with given right operand type. Usage: +// foo_class.def(boost::python::operators<(boost::python::op_add | boost::python::op_sub)>(), +// boost::python::right_operand()); +template +struct right_operand {}; + +namespace detail +{ + template + struct operand_select + { + template + struct wrapped + { + typedef Specified type; + }; + }; + + template <> + struct operand_select + { + template + struct wrapped + { + typedef const wrapped_type& type; + }; + }; + + template struct define_operator; + + // Base class which grants access to extension_class_base::add_method() to its derived classes + struct add_operator_base + { + protected: + static inline void add_method(extension_class_base* target, function* method, const char* name) + { target->add_method(method, name); } + }; + +// +// choose_op, choose_unary_op, and choose_rop +// +// These templates use "poor man's partial specialization" to generate the +// appropriate add_method() call (if any) for a given operator and argument set. +// +// Usage: +// choose_op<(which & op_add)>::template args::add(ext_class); +// +// (see extension_class<>::def_operators() for more examples). +// + template + struct choose_op + { + template + struct args : add_operator_base + { + static inline void add(extension_class_base* target) + { + typedef define_operator def_op; + add_method(target, + new typename def_op::template operator_function(), + def_op::name()); + } + + }; + }; + + // specialization for 0 has no effect + template <> + struct choose_op<0> + { + template + struct args + { + static inline void add(extension_class_base*) + { + } + + }; + }; + + template + struct choose_unary_op + { + template + struct args : add_operator_base + { + static inline void add(extension_class_base* target) + { + typedef define_operator def_op; + add_method(target, + new typename def_op::template operator_function(), + def_op::name()); + } + + }; + }; + + // specialization for 0 has no effect + template <> + struct choose_unary_op<0> + { + template + struct args + { + static inline void add(extension_class_base*) + { + } + + }; + }; + + template + struct choose_rop + { + template + struct args : add_operator_base + { + static inline void add(extension_class_base* target) + { + typedef define_operator def_op; + add_method(target, + new typename def_op::template roperator_function(), + def_op::rname()); + } + + }; + }; + + // specialization for 0 has no effect + template <> + struct choose_rop<0> + { + template + struct args + { + static inline void add(extension_class_base*) + { + } + + }; + }; + + +// Fully specialize define_operator for all operators defined in operator_id above. +// Every specialization defines one function object for normal operator calls and one +// for operator calls with operands reversed ("__r*__" function variants). +// Specializations for most operators follow a standard pattern: execute the expression +// that uses the operator in question. This standard pattern is realized by the following +// macros so that the actual specialization can be done by just calling a macro. +# define PY_DEFINE_BINARY_OPERATORS(id, oper) \ + template <> \ + struct define_operator \ + { \ + template \ + struct operator_function : function \ + { \ + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const \ + { \ + tuple args(ref(arguments, ref::increment_count)); \ + \ + return BOOST_PYTHON_CONVERSION::to_python( \ + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) oper \ + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type())); \ + } \ + \ + const char* description() const \ + { return "__" #id "__"; } \ + }; \ + \ + template \ + struct roperator_function : function \ + { \ + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const \ + { \ + tuple args(ref(arguments, ref::increment_count)); \ + \ + return BOOST_PYTHON_CONVERSION::to_python( \ + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()) oper \ + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type())); \ + } \ + \ + const char* description() const \ + { return "__r" #id "__"; } \ + \ + }; \ + \ + static const char * name() { return "__" #id "__"; } \ + static const char * rname() { return "__r" #id "__"; } \ + } + +# define PY_DEFINE_UNARY_OPERATORS(id, oper) \ + template <> \ + struct define_operator \ + { \ + template \ + struct operator_function : function \ + { \ + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const \ + { \ + tuple args(ref(arguments, ref::increment_count)); \ + \ + return BOOST_PYTHON_CONVERSION::to_python( \ + oper(BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()))); \ + } \ + \ + const char* description() const \ + { return "__" #id "__"; } \ + }; \ + \ + static const char * name() { return "__" #id "__"; } \ + } + + PY_DEFINE_BINARY_OPERATORS(add, +); + PY_DEFINE_BINARY_OPERATORS(sub, -); + PY_DEFINE_BINARY_OPERATORS(mul, *); + PY_DEFINE_BINARY_OPERATORS(div, /); + PY_DEFINE_BINARY_OPERATORS(mod, %); + PY_DEFINE_BINARY_OPERATORS(lshift, <<); + PY_DEFINE_BINARY_OPERATORS(rshift, >>); + PY_DEFINE_BINARY_OPERATORS(and, &); + PY_DEFINE_BINARY_OPERATORS(xor, ^); + PY_DEFINE_BINARY_OPERATORS(or, |); + PY_DEFINE_BINARY_OPERATORS(gt, >); + PY_DEFINE_BINARY_OPERATORS(ge, >=); + PY_DEFINE_BINARY_OPERATORS(lt, <); + PY_DEFINE_BINARY_OPERATORS(le, <=); + PY_DEFINE_BINARY_OPERATORS(eq, ==); + PY_DEFINE_BINARY_OPERATORS(ne, !=); + + PY_DEFINE_UNARY_OPERATORS(neg, -); + PY_DEFINE_UNARY_OPERATORS(pos, +); + PY_DEFINE_UNARY_OPERATORS(abs, abs); + PY_DEFINE_UNARY_OPERATORS(invert, ~); + PY_DEFINE_UNARY_OPERATORS(int, long); + PY_DEFINE_UNARY_OPERATORS(long, PyLong_FromLong); + PY_DEFINE_UNARY_OPERATORS(float, double); + +# undef PY_DEFINE_BINARY_OPERATORS +# undef PY_DEFINE_UNARY_OPERATORS + +// Some operators need special treatment, e.g. because there is no corresponding +// expression in C++. These are specialized manually. + +// pow(): Manual specialization needed because an error message is required if this +// function is called with three arguments. The "power modulo" operator is not +// supported by define_operator, but can be wrapped manually (see special.html). + template <> + struct define_operator + { + template + struct operator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const + { + tuple args(ref(arguments, ref::increment_count)); + + if (args.size() == 3 && args[2]->ob_type != Py_None->ob_type) + { + PyErr_SetString(PyExc_TypeError, "expected 2 arguments, got 3"); + throw_argument_error(); + } + + return BOOST_PYTHON_CONVERSION::to_python( + pow(BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()), + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()))); + } + + const char* description() const + { return "__pow__"; } + + }; + + template + struct roperator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const + { + tuple args(ref(arguments, ref::increment_count)); + + if (args.size() == 3 && args[2]->ob_type != Py_None->ob_type) + { + PyErr_SetString(PyExc_TypeError, "bad operand type(s) for pow()"); + throw_argument_error(); + } + + return BOOST_PYTHON_CONVERSION::to_python( + pow(BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()), + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()))); + } + + const char* description() const + { return "__rpow__"; } + + }; + + static const char * name() { return "__pow__"; } + static const char * rname() { return "__rpow__"; } + }; + +// divmod(): Manual specialization needed because we must actually call two operators and +// return a tuple containing both results + template <> + struct define_operator + { + template + struct operator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const + { + tuple args(ref(arguments, ref::increment_count)); + PyObject * res = PyTuple_New(2); + + PyTuple_SET_ITEM(res, 0, + BOOST_PYTHON_CONVERSION::to_python( + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) / + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()))); + PyTuple_SET_ITEM(res, 1, + BOOST_PYTHON_CONVERSION::to_python( + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) % + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()))); + + return res; + } + + const char* description() const + { return "__divmod__"; } + + }; + + template + struct roperator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const + { + tuple args(ref(arguments, ref::increment_count)); + PyObject * res = PyTuple_New(2); + + PyTuple_SET_ITEM(res, 0, + BOOST_PYTHON_CONVERSION::to_python( + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()) / + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()))); + PyTuple_SET_ITEM(res, 1, + BOOST_PYTHON_CONVERSION::to_python( + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()) % + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()))); + + return res; + } + + const char* description() const + { return "__rdivmod__"; } + + }; + + static const char * name() { return "__divmod__"; } + static const char * rname() { return "__rdivmod__"; } + }; + +// cmp(): Manual specialization needed because there is no three-way compare in C++. +// It is implemented by two one-way comparisons with operators reversed in the second. + template <> + struct define_operator + { + template + struct operator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const + { + tuple args(ref(arguments, ref::increment_count)); + + return BOOST_PYTHON_CONVERSION::to_python( + (BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) < + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type())) ? + - 1 : + (BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()) < + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type())) ? + 1 : + 0) ; + } + + const char* description() const + { return "__cmp__"; } + + }; + + template + struct roperator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject* /* keywords */) const + { + tuple args(ref(arguments, ref::increment_count)); + + return BOOST_PYTHON_CONVERSION::to_python( + (BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type()) < + BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type())) ? + - 1 : + (BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) < + BOOST_PYTHON_CONVERSION::from_python(args[1].get(), boost::python::type())) ? + 1 : + 0) ; + } + + const char* description() const + { return "__rcmp__"; } + + }; + + static const char * name() { return "__cmp__"; } + static const char * rname() { return "__rcmp__"; } + }; + +# ifndef BOOST_PYTHON_USE_SSTREAM + class unfreezer { + public: + unfreezer(std::ostrstream& s) : m_stream(s) {} + ~unfreezer() { m_stream.freeze(false); } + private: + std::ostrstream& m_stream; + }; +# endif + +// str(): Manual specialization needed because the string conversion does not follow +// the standard pattern relized by the macros. + template <> + struct define_operator + { + template + struct operator_function : function + { + PyObject* do_call(PyObject* arguments, PyObject*) const + { + tuple args(ref(arguments, ref::increment_count)); + +// When STLport is used with native streams, _STL::ostringstream().str() is not +// _STL::string, but std::string. +# ifdef BOOST_PYTHON_USE_SSTREAM + std::ostringstream s; + s << BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()); + return BOOST_PYTHON_CONVERSION::to_python(s.str()); +# else + std::ostrstream s; + s << BOOST_PYTHON_CONVERSION::from_python(args[0].get(), boost::python::type()) << char(); + auto unfreezer unfreeze(s); + return BOOST_PYTHON_CONVERSION::to_python(const_cast(s.str())); +# endif + } + + const char* description() const + { return "__str__"; } + + }; + + static const char * name() { return "__str__"; } + }; + + +} // namespace detail + +}} // namespace boost::python + +# undef BOOST_PYTHON_USE_SSTREAM +# endif +#endif /* OPERATORS_UK112000_H_ */ diff --git a/include/boost/python/other.hpp b/include/boost/python/other.hpp new file mode 100755 index 00000000..0142da34 --- /dev/null +++ b/include/boost/python/other.hpp @@ -0,0 +1,113 @@ +#ifndef OTHER_DWA20020601_HPP +# define OTHER_DWA20020601_HPP +// 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. + +# if _MSC_VER+0 >= 1020 +# pragma once +# endif + +# include + +namespace boost { namespace python { + +template struct other +{ + typedef T type; +}; + +# ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION +namespace detail +{ + template + class is_other + { + public: + BOOST_STATIC_CONSTANT(bool, value = false); + }; + + template + class is_other > + { + public: + BOOST_STATIC_CONSTANT(bool, value = true); + }; + + template + class unwrap_other + { + public: + typedef T type; + }; + + template + class unwrap_other > + { + public: + typedef T type; + }; +} +# else // no partial specialization + +}} // namespace boost::python + +#include + +namespace boost { namespace python { + +namespace detail +{ + typedef char (&yes_other_t)[1]; + typedef char (&no_other_t)[2]; + + no_other_t is_other_test(...); + + template + yes_other_t is_other_test(type< other >); + + template + struct other_unwrapper + { + template + struct apply + { + typedef T type; + }; + }; + + template<> + struct other_unwrapper + { + template + struct apply + { + typedef typename T::type type; + }; + }; + + template + class is_other + { + public: + BOOST_STATIC_CONSTANT( + bool, value = ( + sizeof(detail::is_other_test(type())) + == sizeof(detail::yes_other_t))); + }; + + template + class unwrap_other + : public detail::other_unwrapper< + is_other::value + >::template apply + {}; +} + +# endif // BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + +}} // namespace boost::python + +#endif // #ifndef OTHER_DWA20020601_HPP diff --git a/include/boost/python/pointee.hpp b/include/boost/python/pointee.hpp new file mode 100644 index 00000000..52d65d67 --- /dev/null +++ b/include/boost/python/pointee.hpp @@ -0,0 +1,39 @@ +// 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 POINTEE_DWA2002323_HPP +# define POINTEE_DWA2002323_HPP + +# include + +namespace boost { namespace python { + +namespace detail +{ + template + struct pointee_impl + { + template struct apply : remove_pointer {}; + }; + + template <> + struct pointee_impl + { + template struct apply + { + typedef typename T::element_type type; + }; + }; +} + +template +struct pointee + : detail::pointee_impl::value>::template apply +{ +}; + +}} // namespace boost::python::detail + +#endif // POINTEE_DWA2002323_HPP diff --git a/include/boost/python/proxy.hpp b/include/boost/python/proxy.hpp new file mode 100755 index 00000000..1818fa27 --- /dev/null +++ b/include/boost/python/proxy.hpp @@ -0,0 +1,101 @@ +// 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 PROXY_DWA2002615_HPP +# define PROXY_DWA2002615_HPP +# include +# include + +namespace boost { namespace python { namespace api { + +template +class proxy : public object_operators > +{ + typedef typename Policies::key_type key_type; + +# if !defined(BOOST_MSVC) || BOOST_MSVC > 1200 + typedef proxy const& assignment_self; +# else + typedef proxy assignment_self; +# endif + public: + proxy(object const& target, key_type const& key); + operator object() const; + + // to support a[b] = c[d] + proxy const& operator=(assignment_self) const; + + template + inline proxy const& operator=(T const& rhs) const + { + Policies::set(m_target, m_key, object(rhs)); + return *this; + } + + public: // implementation detail + void del() const; + + private: + object m_target; + key_type m_key; +}; + + +template +inline void del(proxy const& x) +{ + x.del(); +} + +// +// implementation +// + +template +inline proxy::proxy(object const& target, key_type const& key) + : m_target(target), m_key(key) +{} + +template +inline proxy::operator object() const +{ + return Policies::get(m_target, m_key); +} + +// to support a[b] = c[d] +template +inline proxy const& proxy::operator=(typename proxy::assignment_self rhs) const +{ + return *this = python::object(rhs); +} + +# define BOOST_PYTHON_PROXY_INPLACE(op) \ +template \ +proxy const& operator op(proxy const& lhs, R const& rhs) \ +{ \ + object old(lhs); \ + return lhs = (old op rhs); \ +} +BOOST_PYTHON_PROXY_INPLACE(+=) +BOOST_PYTHON_PROXY_INPLACE(-=) +BOOST_PYTHON_PROXY_INPLACE(*=) +BOOST_PYTHON_PROXY_INPLACE(/=) +BOOST_PYTHON_PROXY_INPLACE(%=) +BOOST_PYTHON_PROXY_INPLACE(<<=) +BOOST_PYTHON_PROXY_INPLACE(>>=) +BOOST_PYTHON_PROXY_INPLACE(&=) +BOOST_PYTHON_PROXY_INPLACE(^=) +BOOST_PYTHON_PROXY_INPLACE(|=) +# undef BOOST_PYTHON_PROXY_INPLACE + +template +inline void proxy::del() const +{ + Policies::del(m_target, m_key); +} + +}}} // namespace boost::python::api + +#endif // PROXY_DWA2002615_HPP diff --git a/include/boost/python/ptr.hpp b/include/boost/python/ptr.hpp new file mode 100644 index 00000000..5f8ad2d0 --- /dev/null +++ b/include/boost/python/ptr.hpp @@ -0,0 +1,127 @@ +#ifndef PTR_DWA20020601_HPP +# define PTR_DWA20020601_HPP +// 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. +// +// Based on boost/ref.hpp, thus: +// Copyright (C) 1999, 2000 Jaakko Järvi (jaakko.jarvi@cs.utu.fi) +// Copyright (C) 2001 Peter Dimov + +# if _MSC_VER+0 >= 1020 +# pragma once +# endif + +# include + +namespace boost { namespace python { + +template class pointer_wrapper +{ + public: + typedef Ptr type; + + explicit pointer_wrapper(Ptr x): p_(x) {} + operator Ptr() const { return p_; } + Ptr get() const { return p_; } + private: + Ptr p_; +}; + +template +inline pointer_wrapper ptr(T t) +{ + return pointer_wrapper(t); +} + +# ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION +template +class is_pointer_wrapper +{ + public: + BOOST_STATIC_CONSTANT(bool, value = false); +}; + +template +class is_pointer_wrapper > +{ + public: + BOOST_STATIC_CONSTANT(bool, value = true); +}; + +template +class unwrap_pointer +{ + public: + typedef T type; +}; + +template +class unwrap_pointer > +{ + public: + typedef T type; +}; +# else // no partial specialization + +}} // namespace boost::python + +#include + +namespace boost { namespace python { + +namespace detail +{ + typedef char (&yes_pointer_wrapper_t)[1]; + typedef char (&no_pointer_wrapper_t)[2]; + + no_pointer_wrapper_t is_pointer_wrapper_test(...); + + template + yes_pointer_wrapper_t is_pointer_wrapper_test(boost::type< pointer_wrapper >); + + template + struct pointer_unwrapper + { + template + struct apply + { + typedef T type; + }; + }; + + template<> + struct pointer_unwrapper + { + template + struct apply + { + typedef typename T::type type; + }; + }; +} + +template +class is_pointer_wrapper +{ + public: + BOOST_STATIC_CONSTANT( + bool, value = ( + sizeof(detail::is_pointer_wrapper_test(boost::type())) + == sizeof(detail::yes_pointer_wrapper_t))); +}; + +template +class unwrap_pointer + : public detail::pointer_unwrapper< + is_pointer_wrapper::value + >::template apply +{}; + +# endif // BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + +}} // namespace boost::python + +#endif // #ifndef PTR_DWA20020601_HPP diff --git a/include/boost/python/refcount.hpp b/include/boost/python/refcount.hpp new file mode 100755 index 00000000..dce6b266 --- /dev/null +++ b/include/boost/python/refcount.hpp @@ -0,0 +1,42 @@ +// 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 REFCOUNT_DWA2002615_HPP +# define REFCOUNT_DWA2002615_HPP + +# include +# include + +namespace boost { namespace python { + +template +inline T* incref(T* p) +{ + Py_INCREF(python::upcast(p)); + return p; +} + +template +inline T* xincref(T* p) +{ + Py_XINCREF(python::upcast(p)); + return p; +} + +template +inline void decref(T* p) +{ + Py_DECREF(python::upcast(p)); +} + +template +inline void xdecref(T* p) +{ + Py_XDECREF(python::upcast(p)); +} + +}} // namespace boost::python + +#endif // REFCOUNT_DWA2002615_HPP diff --git a/include/boost/python/reference.hpp b/include/boost/python/reference.hpp new file mode 100644 index 00000000..f070f114 --- /dev/null +++ b/include/boost/python/reference.hpp @@ -0,0 +1,236 @@ +// (C) Copyright David Abrahams 2000. 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. +// +// The author gratefully acknowleges the support of Dragon Systems, Inc., in +// producing this work. + +#ifndef PYPTR_DWA050400_H_ +# define PYPTR_DWA050400_H_ + +# ifdef BOOST_PYTHON_V2 + +# error obsolete + +# else + +# include +# include +# include +# include +# include +# include +# include +# include +# include + +# include + +BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE + +template +struct py_ptr_conversions : Base +{ + inline friend T from_python(PyObject* x, boost::python::type) + { return T(boost::python::downcast(x).get(), T::increment_count); } + + inline friend T from_python(PyObject* x, boost::python::type) + { return T(boost::python::downcast(x).get(), T::increment_count); } + + inline friend PyObject* to_python(T x) + { return boost::python::as_object(x.release()); } + +}; + +BOOST_PYTHON_END_CONVERSION_NAMESPACE + +namespace boost { namespace python { + +BOOST_PYTHON_IMPORT_CONVERSION(py_ptr_conversions); + +template +class reference + : public py_ptr_conversions, T> +{ +public: + typedef T value_type; + + reference(const reference& rhs) + : m_p(rhs.m_p) + { + assert(m_p == 0 || m_p->ob_refcnt > 0); + Py_XINCREF(m_p); + } + + reference() : m_p(0) {} + + // These are two ways of spelling the same thing, that we need to increment + // the reference count on the pointer when we're initialized. + enum increment_count_t { increment_count }; + + enum allow_null { null_ok }; + + template + explicit reference(T2* x) + : m_p(expect_non_null(x)) + { + assert(m_p->ob_refcnt > 0); + } + + template + reference(T2* x, increment_count_t) + : m_p(expect_non_null(x)) + { + assert(m_p->ob_refcnt > 0); + Py_INCREF(m_p); + } + + template + reference(T2* x, allow_null) + : m_p(x) + { + assert(m_p == 0 || m_p->ob_refcnt > 0); + } + + template + reference(T2* x, allow_null, increment_count_t) + : m_p(x) + { + assert(m_p == 0 || m_p->ob_refcnt > 0); + Py_XINCREF(m_p); + } + + template + reference(T2* x, increment_count_t, allow_null) + : m_p(x) + { + assert(m_p == 0 || m_p->ob_refcnt > 0); + Py_XINCREF(m_p); + } + + reference& operator=(const reference& rhs) + { + assert(rhs.m_p == 0 || rhs.m_p->ob_refcnt > 0); + Py_XINCREF(static_cast(rhs.m_p)); + assert(m_p == 0 || m_p->ob_refcnt > 0); + Py_XDECREF(m_p); + m_p = rhs.m_p; + return *this; + } + + ~reference() + { + assert(m_p == 0 || m_p->ob_refcnt > 0); + Py_XDECREF(m_p); + } + + T& operator*() const + { + assert(m_p == 0 || m_p->ob_refcnt > 0); + return *m_p; + } + + // MSVC doesn't like boost::dereferencable unless T has a default + // constructor, so operator-> must be defined by hand :( + T* operator->() const + { + assert(m_p == 0 || m_p->ob_refcnt > 0); + return &**this; + } + + T* get() const + { + assert(m_p == 0 || m_p->ob_refcnt > 0); + return m_p; + } + + T* release() + { + assert(m_p == 0 || m_p->ob_refcnt > 0); + T* p = m_p; + m_p = 0; + return p; + } + + void reset() + { + assert(m_p == 0 || m_p->ob_refcnt > 0); + Py_XDECREF(m_p); + m_p = 0; + } + + template + void reset(T2* x) + { + assert(m_p == 0 || m_p->ob_refcnt > 0); + Py_XDECREF(m_p); + m_p = expect_non_null(x); + assert(m_p == 0 || m_p->ob_refcnt > 0); + } + + template + void reset(T2* x, increment_count_t) + { + assert(m_p == 0 || m_p->ob_refcnt > 0); + Py_XINCREF(x); + Py_XDECREF(m_p); + m_p = expect_non_null(x); + assert(m_p->ob_refcnt > 0); + } + + template + void reset(T2* x, allow_null) + { + assert(m_p == 0 || m_p->ob_refcnt > 0); + Py_XDECREF(m_p); + m_p = x; + assert(m_p == 0 || m_p->ob_refcnt > 0); + } + + template + void reset(T2* x, allow_null, increment_count_t) + { + assert(m_p == 0 || m_p->ob_refcnt > 0); + Py_XINCREF(x); + Py_XDECREF(m_p); + m_p = x; + assert(m_p == 0 || m_p->ob_refcnt > 0); + } + + template + void reset(T2* x, increment_count_t, allow_null) + { + assert(m_p == 0 || m_p->ob_refcnt > 0); + Py_XINCREF(x); + Py_XDECREF(m_p); + m_p = x; + assert(m_p == 0 || m_p->ob_refcnt > 0); + } + +#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) +private: + template friend class shared_ptr; +#endif + + inline PyObject* object() const + { + return as_object(m_p); + } + + T* m_p; +}; + +typedef reference ref; + +template +ref make_ref(const T& x) +{ + return ref(to_python(x)); +} + +}} // namespace boost::python + +#endif // BOOST_PYTHON_V2 + +#endif // PYPTR_DWA050400_H_ diff --git a/test/back_reference.py b/test/back_reference.py new file mode 100644 index 00000000..21d5d1c0 --- /dev/null +++ b/test/back_reference.py @@ -0,0 +1,30 @@ +''' +>>> from back_reference_ext import * +>>> y = Y(3) +>>> z = Z(4) +>>> x_instances() +2 +>>> y2 = copy_Y(y) +>>> x_instances() +3 +>>> z2 = copy_Z(z) +>>> x_instances() +4 +>>> y_identity(y) is y +1 +>>> y_equality(y, y) +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/borrowed.cpp b/test/borrowed.cpp new file mode 100755 index 00000000..0f8d3112 --- /dev/null +++ b/test/borrowed.cpp @@ -0,0 +1,34 @@ +// 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 + +using namespace boost::python; + +template +void assert_borrowed_ptr(T const& x) +{ + BOOST_STATIC_ASSERT(boost::python::detail::is_borrowed_ptr::value); +} + +template +void assert_not_borrowed_ptr(T const& x) +{ + BOOST_STATIC_ASSERT(!boost::python::detail::is_borrowed_ptr::value); +} + +int main() +{ + assert_borrowed_ptr(borrowed((PyObject*)0)); + assert_borrowed_ptr(borrowed((PyTypeObject*)0)); + assert_borrowed_ptr((detail::borrowed const*)0); + assert_borrowed_ptr((detail::borrowed volatile*)0); + assert_borrowed_ptr((detail::borrowed const volatile*)0); + assert_not_borrowed_ptr((PyObject*)0); + assert_not_borrowed_ptr(0); + return 0; +} diff --git a/test/callbacks.py b/test/callbacks.py new file mode 100644 index 00000000..937961fd --- /dev/null +++ b/test/callbacks.py @@ -0,0 +1,133 @@ +''' +>>> from callbacks_ext import * + +>>> def double(x): +... return x + x +... +>>> apply_int_int(double, 42) +84 +>>> apply_void_int(double, 42) + +>>> def identity(x): +... return x + +Once we have array conversion support, this test will fail. Er, +succeed: + +>>> try: apply_to_string_literal(identity) +... except: pass # expected +... else: print 'expected an exception!' + +>>> x = apply_X_X(identity, X(42)) +>>> x.value() +42 +>>> x_count() +1 +>>> del x +>>> x_count() +0 + +>>> def increment(x): +... x.set(x.value() + 1) +... +>>> x = X(42) +>>> apply_void_X_ref(increment, x) +>>> x.value() +43 + +>>> apply_void_X_cref(increment, x) +>>> x.value() # const-ness is not respected, sorry! +44 + +>>> last_x = 1 +>>> def decrement(x): +... global last_x +... last_x = x +... if x is not None: +... x.set(x.value() - 1) + +>>> apply_void_X_ptr(decrement, x) +>>> x.value() +43 +>>> last_x.value() +43 +>>> increment(last_x) +>>> x.value() +44 +>>> last_x.value() +44 + +>>> apply_void_X_ptr(decrement, None) +>>> assert last_x is None +>>> x.value() +44 + +>>> last_x = 1 +>>> apply_void_X_deep_ptr(decrement, None) +>>> assert last_x is None +>>> x.value() +44 + +>>> apply_void_X_deep_ptr(decrement, x) +>>> x.value() +44 +>>> last_x.value() +43 + +>>> y = apply_X_ref_handle(identity, x) +>>> assert y.value() == x.value() +>>> increment(x) +>>> assert y.value() == x.value() + +>>> y = apply_X_ptr_handle_cref(identity, x) +>>> assert y.value() == x.value() +>>> increment(x) +>>> assert y.value() == x.value() + +>>> y = apply_X_ptr_handle_cref(identity, None) +>>> y + +>>> def new_x(ignored): +... return X(666) +... +>>> try: apply_X_ref_handle(new_x, 1) +... except ReferenceError: pass +... else: print 'no error' + +>>> try: apply_X_ptr_handle_cref(new_x, 1) +... except ReferenceError: pass +... else: print 'no error' + +>>> try: apply_cstring_cstring(identity, 'hello') +... except ReferenceError: pass +... else: print 'no error' + +>>> apply_char_char(identity, 'x') +'x' + +>>> apply_cstring_pyobject(identity, 'hello') +'hello' + +>>> apply_cstring_pyobject(identity, None) + + +>>> apply_char_char(identity, 'x') +'x' + +>>> assert apply_to_own_type(identity) is type(identity) + +>>> assert apply_object_object(identity, identity) is identity +''' + +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/comprehensive.py b/test/comprehensive.py new file mode 100644 index 00000000..f64ed661 --- /dev/null +++ b/test/comprehensive.py @@ -0,0 +1,1281 @@ +r''' +// (C) Copyright David Abrahams 2000. 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. +// +// The author gratefully acknowleges the support of Dragon Systems, Inc., in +// producing this work. + +// Revision History: +// 2001 Nov 01 Python 2.2 pickle problems fixed (rwgk) +// 04 Mar 01 Changed name of extension module so it would work with DebugPython, +// fixed exception message checking to work with Python 2.0 +// (Dave Abrahams) + +Automatic checking of the number and type of arguments. Foo's constructor takes +a single long parameter. + + >>> try: + ... ext = Foo() + ... except TypeError, err: + ... assert re.match(r'function .* exactly 1 argument;? \(?0 given\)?', + ... str(err)) + ... else: + ... print 'no exception' + + >>> try: ext = Foo('foo') + ... except TypeError, err: + ... assert_integer_expected(err) + ... else: + ... print 'no exception' + + >>> ext = Foo(1) + +Call a virtual function. This call takes a trip into C++ where +FooCallback::add_len() looks up the Python "add_len" attribute and finds the +wrapper for FooCallback::default_add_len(), which in turn calls Foo::add_len(). + + >>> ext.add_len('hello') + 6 + >>> ext.set(3) + >>> ext.add_len('hello') + 8 + +Call a pure virtual function which should have been overridden, but was not. + + >>> ext.call_pure() + Traceback (innermost last): + File "", line 1, in ? + AttributeError: pure + +We can subclass Foo. + + >>> class Subclass(Foo): + ... def __init__(self, seq): + ... Foo.__init__(self, len(seq)) + ... + ... def pure(self): + ... return 'not pure anymore!' + ... + ... def get(self): + ... return Foo.add_len(self, '') + ... + ... def add_len(self, s): + ... print 'called add_len()' + ... return self.get() + len(s) + ... + >>> b = Subclass('yippee') + >>> b.get() + 6 + >>> b.mumble() + 'mumble' + >>> b.call_pure() + 'not pure anymore!' + +None corresponds to a NULL pointer or smart pointer + >>> f = foo_factory(1) + >>> f.add_len('xxx') + 1000 + >>> foo_factory(0) is None + 1 + >>> foo_ptr_is_null(None) + 1 + >>> foo_ptr_is_null(f) + 0 + >>> foo_shared_ptr_is_null(None) + 1 + >>> foo_shared_ptr_is_null(f) + 0 + +If no __init__ function is defined, the one from the base class takes effect, just +like in a Python class. + + >>> class DemonstrateInitPassthru(Foo): pass + ... + >>> q = DemonstrateInitPassthru(1) + >>> q.add_len("x") + 2 + +If we don't initialize the base class, we'll get a RuntimeError when we try to +use its methods. The test illustrates the kind of error to expect. + + >>> class BadSubclass(Foo): + ... def __init__(self): pass + ... + >>> barf = BadSubclass() + >>> barf.set(4) + Traceback (innermost last): + ... + RuntimeError: __init__ function for extension class 'Foo' was never called. + +Here we are tesing that the simple definition procedure used in the C++ demo +file for classes without any virtual functions actually worked. + + >>> bar = Bar(3, 4) + >>> bar.first() + 3 + >>> bar.second() + 4 + >>> baz = Baz() + +We can actually return the wrapped classes by value + + >>> baz.pass_bar(bar).first() + 3 + >>> bar.pass_baz(baz) is baz # A copy of the return value is made. + 0 + >>> type(bar.pass_baz(baz)) is type(baz) + 1 + +And, yes, we can multiply inherit from these classes. + + >>> class MISubclass(Subclass, Bar): + ... def __init__(self, s): + ... Subclass.__init__(self, s) + ... Bar.__init__(self, 0, len(s)) + ... + >>> mi = MISubclass('xx') + >>> mi.first() + 0 + >>> mi.second() + 2 + >>> mi.mumble() + 'mumble' + +We can even mulitply inherit from built-in Python classes, even if they are +first in the list of bases + + >>> class RealPythonClass: + ... def real_python_method(self): + ... print 'RealPythonClass.real_python_method()' + ... def other_first(self, other): + ... return other.first() + + >>> class MISubclass2(RealPythonClass, Bar): + ... def new_method(self): + ... print 'MISubclass2.new_method()' + ... bound_function = RealPythonClass().other_first + ... + >>> mi2 = MISubclass2(7, 8) + >>> mi2.first() # we can call inherited member functions from Bar + 7 + >>> mi2.real_python_method() # we can call inherited member functions from RealPythonClass + RealPythonClass.real_python_method() + + >>> mi2.new_method() # we can call methods on the common derived class + MISubclass2.new_method() + + We can call unbound methods from the base class accessed through the derived class + >>> MISubclass2.real_python_method(mi2) + RealPythonClass.real_python_method() + + We have not interfered with ordinary python bound methods + >>> MISubclass2.bound_function(mi2) + 7 + >>> mi2.bound_function() + 7 + +Any object whose class is derived from Bar can be passed to a function expecting +a Bar parameter: + + >>> baz.pass_bar(mi).first() + 0 + +But objects not derived from Bar cannot: + + >>> baz.pass_bar(baz) + Traceback (innermost last): + ... + TypeError: extension class 'Baz' is not convertible into 'Bar'. + +The clone function on Baz returns a smart pointer; we wrap it into an +extension_instance and make it look just like any other Baz obj. + + >>> baz_clone = baz.clone() + >>> baz_clone.pass_bar(mi).first() + 0 + +Functions expecting an std::auto_ptr parameter will not accept a raw Baz + + >>> try: baz.eat_baz(Baz()) + ... except RuntimeError, err: + ... assert re.match("Object of extension class 'Baz' does not wrap <.*>.", + ... str(err)) + ... else: + ... print 'no exception' + +We can pass std::auto_ptr where it is expected + + >>> baz.eat_baz(baz_clone) + +And if the auto_ptr has given up ownership? + + # MSVC6 ships with an outdated auto_ptr that doesn't get zeroed out when it + # gives up ownership. If you are using MSVC6 without the new Dinkumware + # library, SGI STL or the STLport, expect this test to crash unless you put + # --broken-auto-ptr on the command line. + >>> if not '--broken-auto-ptr' in sys.argv: + ... try: baz_clone.clone() + ... except RuntimeError, err: + ... assert re.match('Converting from python, pointer or smart pointer to <.*> is NULL.', str(err)) + ... else: + ... print 'no exeption' + +Polymorphism also works: + + >>> polymorphic_foo = baz.create_foo() + >>> polymorphic_foo.call_pure() + 'this was never pure!' + >>> baz.get_foo_value(polymorphic_foo) + 1000 + +Simple nested class test: + >>> foo_a = Foo.Foo_A() + >>> foo_a.mumble() + 'mumble a' + >>> foo_b = Foo.Foo_B() + >>> foo_b.mumble() + 'mumble b' + +Pickling tests: + + >>> world.__module__ + 'boost_python_test' + >>> world.__safe_for_unpickling__ + 1 + >>> world.__reduce__() + 'world' + >>> reduced = world('Hello').__reduce__() + >>> reduced[0] == world + 1 + >>> reduced[1:] + (('Hello',), (0,)) + >>> import StringIO + >>> import cPickle + >>> pickle = cPickle + >>> for number in (24, 42): + ... wd = world('California') + ... wd.set_secret_number(number) + ... # Dump it out and read it back in. + ... f = StringIO.StringIO() + ... pickle.dump(wd, f) + ... f = StringIO.StringIO(f.getvalue()) + ... wl = pickle.load(f) + ... # + ... print wd.greet(), wd.get_secret_number() + ... print wl.greet(), wl.get_secret_number() + ... + Hello from California! 24 + Hello from California! 24 + Hello from California! 42 + Hello from California! 0 + +Pickle safety measures: + >>> r=Rational(3, 4) + >>> r + Rational(3, 4) + >>> try: s=pickle.dumps(r) + ... except RuntimeError, err: print err[0] + ... + Incomplete pickle support (__dict_defines_state__ not set) + >>> r=myrational(3, 4) + >>> r + Rational(3, 4) + >>> s=pickle.dumps(r) + >>> u=pickle.loads(s) + + >>> w = myworld() + >>> w.greet() + 'Hello from anywhere!' + >>> w.__dict__ + {'x': 1} + >>> try: s=pickle.dumps(w) + ... except RuntimeError, err: print err[0] + ... + Incomplete pickle support (__getstate_manages_dict__ not set) + + >>> w = myunsafeworld() + >>> w.greet() + 'Hello from anywhere!' + >>> w.__dict__ + {'x': 1} + >>> s=pickle.dumps(w) + +Special member attributes. Tests courtesy of Barry Scott + + >>> class DerivedFromFoo(Foo): + ... def __init__(self): + ... Foo.__init__( self, 1 ) + ... def fred(self): + ... 'Docs for DerivedFromFoo.fred' + ... print 'Barry.fred' + ... def __del__(self): + ... print 'Deleting DerivedFromFoo' + + >>> class Base: + ... i_am_base = 'yes' + ... def fred(self): + ... 'Docs for Base.fred' + ... pass + + + >>> class DerivedFromBase(Base): + ... i_am_derived_from_base = 'yes' + ... def fred(self): + ... 'Docs for DerivedFromBase.fred' + ... pass + + >>> dir(DerivedFromFoo) + ['__del__', '__doc__', '__init__', '__module__', 'fred'] + + >>> df = DerivedFromFoo() + >>> df.__dict__ + {} + >>> df.fred.__doc__ + 'Docs for DerivedFromFoo.fred' + + >>> db = DerivedFromBase() + >>> db.__dict__ + {} + >>> db.fred.__doc__ + 'Docs for DerivedFromBase.fred' + + >>> import sys + >>> if not sys.__dict__.has_key('version_info') or \ + ... sys.version_info[0] < 2 or ( sys.version_info[0] == 2 and + ... sys.version_info[1] < 2 ): + ... assert dir(df) == [] + ... assert dir(db) == [] + ... assert dir(DerivedFromBase) == [ + ... '__doc__', '__module__', 'fred', 'i_am_derived_from_base'] + ... else: + ... assert dir(df) == [ + ... 'Foo_A', 'Foo_B', '__del__', '__doc__', '__init__', '__module__', 'add_len', + ... 'call_add_len', 'call_pure', 'fred', 'mumble', 'set'] + ... assert dir(db) == ['__doc__', '__module__', 'fred' + ... , 'i_am_base', 'i_am_derived_from_base'] + ... assert dir(DerivedFromBase) == [ + ... '__doc__', '__module__', 'fred', 'i_am_base', 'i_am_derived_from_base'] + +Special member functions in action + >>> del df + Deleting DerivedFromFoo + + # force method table sharing + >>> class DerivedFromStringMap(StringMap): pass + ... + + >>> m = StringMap() + +__getitem__() + >>> m[1] + Traceback (innermost last): + File "", line 1, in ? + KeyError: 1 + +__setitem__() + + >>> m[1] = 'hello' + +__getitem__() + >>> m[1] + 'hello' + +__delitem__() + >>> del m[1] + >>> m[1] # prove that it's gone + Traceback (innermost last): + File "", line 1, in ? + KeyError: 1 + +__delitem__() + >>> del m[2] + Traceback (innermost last): + File "", line 1, in ? + KeyError: 2 + +__length__() + >>> len(m) + 0 + >>> m[3] = 'farther' + >>> len(m) + 1 + +Check for sequence/mapping confusion: + >>> for x in m: + ... print x + ... + Traceback (innermost last): + File "", line 1, in ? + KeyError: 0 + +Check for the ability to pass a non-const reference as a constructor parameter + >>> x = Fubar(Foo(1)) + +Some simple overloading tests: + >>> r = Range(3) + >>> print str(r) + (3, 3) + >>> r.start + 3 + >>> r.finish + 3 + >>> r.__len__() + 0 + >>> r.__len__(4) + >>> r.finish + 7 + >>> try: r = Range('yikes') + ... except TypeError, e: + ... assert re.match( + ... 'No overloaded functions match [(]Range, str[a-z]*[)]\. Candidates are:\n.*\n.*', + ... str(e)) + ... else: print 'no exception' + +Sequence tests: + >>> len(Range(3, 10)) + 7 + + >>> map(lambda x:x, Range(3, 10)) + [3, 4, 5, 6, 7, 8, 9] + + >>> map(lambda x:x, Range(3, 10)[-2:]) + [8, 9] + + >>> map(lambda x:x, Range(3, 10)[:-4]) + [3, 4, 5] + + >>> map(lambda x:x, Range(3, 10)[4:]) + [7, 8, 9] + + >>> map(lambda x:x, Range(3, 10)[4:100]) + [7, 8, 9] + + >>> map(lambda x:x, Range(3, 10)[20:]) + [] + + >>> map(lambda x:x, Range(3, 10)[0:4]) + [3, 4, 5, 6] + +Numeric tests: + >>> x = Rational(2,3) + >>> y = Rational(1,4) + >>> print x + y + 11/12 + >>> print x - y + 5/12 + >>> print x * y + 1/6 + >>> print x / y + 8/3 + >>> print x + 1 # testing coercion + 5/3 + >>> print 1 + x # coercion the other way + 5/3 + +delete non-existent attribute: + del m.foobar + Traceback (innermost last): + File "", line 1, in ? + AttributeError: delete non-existing obj attribute + +Testing __getattr__ and __getattr__: + + >>> n = IntPair(1, 2) + >>> n.first + 1 + >>> n.second + 2 + >>> n.third + Traceback (innermost last): + File "", line 1, in ? + AttributeError: third + +Testing __setattr__ and __setattr__: + >>> n.first = 33 # N.B __setattr__first sets first to + >>> n.first # the negative of its argument. + -33 + >>> n.second = 66 + >>> n.second + 66 + +Testing __delattr__ and __delattr__: + >>> del n.first + Traceback (innermost last): + File "", line 1, in ? + AttributeError: first can't be deleted! + >>> del n.second + Traceback (innermost last): + File "", line 1, in ? + AttributeError: Attributes can't be deleted! + >>> del n.third + Traceback (innermost last): + File "", line 1, in ? + AttributeError: Attributes can't be deleted! + + # Now show that we can override it. + + >>> class IntTriple(IntPair): + ... def __getattr__(self, s): + ... if s in ['first', 'second']: + ... return IntPair.__getattr__(self, s) + ... elif s == 'third': + ... return 3 + ... else: + ... raise AttributeError(s) + ... + ... # Also show that __setattr__ is supported + ... def __setattr__(self, name, value): + ... raise AttributeError('no writable attributes') + ... + >>> p = IntTriple(0, 1) + >>> p.first + 0 + >>> p.second + 1 + >>> p.third + 3 + >>> p.bax + Traceback (innermost last): + File "", line 1, in ? + AttributeError: bax + >>> p.third = 'yes' + Traceback (innermost last): + File "", line 1, in ? + AttributeError: no writable attributes + >>> del p.third + Traceback (innermost last): + File "", line 1, in ? + AttributeError: Attributes can't be deleted! + +demonstrate def_readonly, def_read_write: + >>> sp = StringPair("hello", "world") + >>> sp.first # first is read-only + 'hello' + >>> first_string(sp) # prove that we're not just looking in sp's __dict__ + 'hello' + >>> sp.first = 'hi' # we're not allowed to change it + Traceback (innermost last): + File "", line 1, in ? + AttributeError: 'first' attribute is read-only + >>> first_string(sp) # prove that it hasn't changed + 'hello' + + >>> sp.second # second is read/write + 'world' + >>> second_string(sp) + 'world' + >>> sp.second = 'universe' # set the second attribute + >>> sp.second + 'universe' + >>> second_string(sp) # this proves we didn't just set it in sp's __dict__ + 'universe' + +some __str__ and __repr__ tests: + >>> sp + ('hello', 'universe') + >>> repr(sp) + "('hello', 'universe')" + >>> str(sp) + "('hello', 'universe')" + + Range has a __str__ function but not a __repr__ function + >>> range = Range(5, 20) + >>> str(range) + '(5, 20)' + >>> assert re.match('', repr(range)) + +__hash__ and __cmp__ tests: + # Range has both __hash__ and __cmp__, thus is hashable + >>> colors = { Range(3,4): 'blue', Range(7,9): 'red' } + >>> colors[Range(3,4)] + 'blue' + + # StringPair has only __cmp__ + >>> { StringPair('yo', 'eddy'): 1 } + Traceback (innermost last): + File "", line 1, in ? + TypeError: unhashable type + + # But it can be sorted + >>> stringpairs = [ StringPair('yo', 'eddy'), StringPair('yo', 'betty'), sp ] + >>> stringpairs.sort() + >>> stringpairs + [('hello', 'universe'), ('yo', 'betty'), ('yo', 'eddy')] + +make_pair is a global function in the module. + + >>> couple = make_pair(3,12) + >>> couple.first + 3 + >>> couple.second + 12 + +Testing __call__: + >>> couple2 = make_pair(3, 7) + >>> comparator = CompareIntPair() + >>> comparator(couple, couple) + 0 + >>> comparator(couple, couple2) + 0 + >>> comparator(couple2, couple) + 1 + +Testing overloaded free functions + >>> overloaded() + 'Hello world!' + >>> overloaded(1) + 1 + >>> overloaded('foo') + 'foo' + >>> overloaded(1,2) + 3 + >>> overloaded(1,2,3) + 6 + >>> overloaded(1,2,3,4) + 10 + >>> overloaded(1,2,3,4,5) + 15 + >>> try: overloaded(1, 'foo') + ... except TypeError, err: + ... assert re.match("No overloaded functions match \(int, str[a-z]*\)\. Candidates are:", + ... str(err)) + ... else: + ... print 'no exception' + +Testing overloaded constructors + + >>> over = OverloadTest() + >>> over.getX() + 1000 + >>> over = OverloadTest(1) + >>> over.getX() + 1 + >>> over = OverloadTest(1,1) + >>> over.getX() + 2 + >>> over = OverloadTest(1,1,1) + >>> over.getX() + 3 + >>> over = OverloadTest(1,1,1,1) + >>> over.getX() + 4 + >>> over = OverloadTest(1,1,1,1,1) + >>> over.getX() + 5 + >>> over = OverloadTest(over) + >>> over.getX() + 5 + >>> try: over = OverloadTest(1, 'foo') + ... except TypeError, err: + ... assert re.match("No overloaded functions match \(OverloadTest, int, str[a-z]*\)\. Candidates are:", + ... str(err)) + ... else: + ... print 'no exception' + +Testing overloaded methods + + >>> over.setX(3) + >>> over.overloaded() + 3 + >>> over.overloaded(1) + 1 + >>> over.overloaded(1,1) + 2 + >>> over.overloaded(1,1,1) + 3 + >>> over.overloaded(1,1,1,1) + 4 + >>> over.overloaded(1,1,1,1,1) + 5 + >>> try: over.overloaded(1,'foo') + ... except TypeError, err: + ... assert re.match("No overloaded functions match \(OverloadTest, int, str[a-z]*\)\. Candidates are:", + ... str(err)) + ... else: + ... print 'no exception' + +Testing base class conversions + + >>> testUpcast(over) + Traceback (innermost last): + TypeError: extension class 'OverloadTest' is not convertible into 'Base'. + >>> der1 = Derived1(333) + >>> der1.x() + 333 + >>> testUpcast(der1) + 333 + >>> der1 = derived1Factory(1000) + >>> testDowncast1(der1) + 1000 + >>> testDowncast2(der1) + Traceback (innermost last): + TypeError: extension class 'Base' is not convertible into 'Derived2'. + >>> der2 = Derived2(444) + >>> der2.x() + 444 + >>> testUpcast(der2) + 444 + >>> der2 = derived2Factory(1111) + >>> testDowncast2(der2) + Traceback (innermost last): + TypeError: extension class 'Base' is not convertible into 'Derived2'. + +Testing interaction between callbacks, base declarations, and overloading +- testCallback() calls callback() (within C++) +- callback() is overloaded (in the wrapped class CallbackTest) +- callback() is redefined in RedefineCallback (overloading is simulated by type casing) +- testCallback() should use the redefined callback() + + >>> c = CallbackTest() + >>> c.testCallback(1) + 2 + + >>> try: c.testCallback('foo') + ... except TypeError, err: assert_integer_expected(err) + ... else: print 'no exception' + + >>> c.callback(1) + 2 + >>> c.callback('foo') + 'foo 1' + + >>> import types + >>> class RedefineCallback(CallbackTest): + ... def callback(self, x): + ... if type(x) is types.IntType: + ... return x - 2 + ... else: + ... return CallbackTest.callback(self,x) + ... + >>> r = RedefineCallback() + >>> r.callback(1) + -1 + >>> r.callback('foo') + 'foo 1' + + >>> try: r.testCallback('foo') + ... except TypeError, err: assert_integer_expected(err) + ... else: print 'no exception' + + >>> r.testCallback(1) + -1 + >>> testCallback(r, 1) + -1 + +Regression test for a reference-counting bug thanks to Mark Evans +() + >>> sizelist([]) + 0.0 + >>> sizelist([1, 2, 4]) + 3.0 + +And another for doubles + >>> vector_double().push_back(3.0) + +Tests for method lookup in the context of inheritance +Set up the tests + + >>> a1 = A1() + >>> a2 = A2() + >>> b1 = B1() + >>> b2 = B2() + >>> pa1_a1 = factoryA1asA1() + >>> pb1_a1 = factoryB1asA1() + >>> pb2_a1 = factoryB2asA1() + >>> pc_a1 = factoryCasA1() + >>> pa2_a2 = factoryA2asA2() + >>> pb1_a2 = factoryB1asA2() + >>> pb1_b1 = factoryB1asB1() + >>> pc_b1 = factoryCasB1() + >>> class DA1(A1): + ... def overrideA1(self): + ... return 'DA1.overrideA1' + ... + >>> da1 = DA1() + >>> class DB1(B1): + ... def overrideA1(self): + ... return 'DB1.overrideA1' + ... def overrideB1(self): + ... return 'DB1.overrideB1' + ... + >>> db1 = DB1() + >>> class DB2(B2): pass + ... + >>> db2 = DB2() + +test overrideA1 + + >>> a1.overrideA1() + 'A1::overrideA1' + >>> b1.overrideA1() + 'B1::overrideA1' + >>> b2.overrideA1() + 'B2::overrideA1' + >>> da1.overrideA1() + 'DA1.overrideA1' + >>> db1.overrideA1() + 'DB1.overrideA1' + >>> pa1_a1.overrideA1() + 'A1::overrideA1' + >>> pb1_a1.overrideA1() + 'B1::overrideA1' + >>> pb2_a1.overrideA1() + 'B2::overrideA1' + >>> pb1_b1.overrideA1() + 'B1::overrideA1' + >>> pc_a1.overrideA1() + 'B1::overrideA1' + >>> pc_b1.overrideA1() + 'B1::overrideA1' + +test call_overrideA1 + + >>> call_overrideA1(a1) + 'A1::overrideA1' + >>> call_overrideA1(b1) + 'B1::overrideA1' + >>> call_overrideA1(b2) + 'B2::overrideA1' + >>> call_overrideA1(da1) + 'DA1.overrideA1' + >>> call_overrideA1(db1) + 'DB1.overrideA1' + >>> call_overrideA1(pa1_a1) + 'A1::overrideA1' + >>> call_overrideA1(pb1_a1) + 'B1::overrideA1' + >>> call_overrideA1(pb2_a1) + 'B2::overrideA1' + >>> call_overrideA1(pb1_b1) + 'B1::overrideA1' + >>> call_overrideA1(pc_a1) + 'B1::overrideA1' + >>> call_overrideA1(pc_b1) + 'B1::overrideA1' + +test inheritA1 + + >>> a1.inheritA1() + 'A1::inheritA1' + >>> b1.inheritA1() + 'A1::inheritA1' + >>> b2.inheritA1() + 'A1::inheritA1' + >>> da1.inheritA1() + 'A1::inheritA1' + >>> db1.inheritA1() + 'A1::inheritA1' + >>> pa1_a1.inheritA1() + 'A1::inheritA1' + >>> pb1_a1.inheritA1() + 'A1::inheritA1' + >>> pb2_a1.inheritA1() + 'A1::inheritA1' + >>> pb1_b1.inheritA1() + 'A1::inheritA1' + >>> pc_a1.inheritA1() + 'A1::inheritA1' + >>> pc_b1.inheritA1() + 'A1::inheritA1' + +test call_inheritA1 + + >>> call_inheritA1(a1) + 'A1::inheritA1' + >>> call_inheritA1(b1) + 'A1::inheritA1' + >>> call_inheritA1(b2) + 'A1::inheritA1' + >>> call_inheritA1(da1) + 'A1::inheritA1' + >>> call_inheritA1(db1) + 'A1::inheritA1' + >>> call_inheritA1(pa1_a1) + 'A1::inheritA1' + >>> call_inheritA1(pb1_a1) + 'A1::inheritA1' + >>> call_inheritA1(pb2_a1) + 'A1::inheritA1' + >>> call_inheritA1(pb1_b1) + 'A1::inheritA1' + >>> call_inheritA1(pc_a1) + 'A1::inheritA1' + >>> call_inheritA1(pc_b1) + 'A1::inheritA1' + +test inheritA2 + + >>> a2.inheritA2() + 'A2::inheritA2' + >>> b1.inheritA2() + 'A2::inheritA2' + >>> b2.inheritA2() + 'A2::inheritA2' + >>> db1.inheritA2() + 'A2::inheritA2' + >>> pa2_a2.inheritA2() + 'A2::inheritA2' + >>> pb1_a2.inheritA2() + 'A2::inheritA2' + >>> pb1_b1.inheritA2() + 'A2::inheritA2' + +test overrideB1 + + >>> b1.overrideB1() + 'B1::overrideB1' + >>> db1.overrideB1() + 'DB1.overrideB1' + >>> pb1_b1.overrideB1() + 'B1::overrideB1' + >>> pc_b1.overrideB1() + 'C::overrideB1' + +test call_overrideB1 + + >>> call_overrideB1(b1) + 'B1::overrideB1' + >>> call_overrideB1(db1) + 'DB1.overrideB1' + >>> call_overrideB1(pb1_a1) + 'B1::overrideB1' + >>> call_overrideB1(pc_a1) + 'C::overrideB1' + >>> call_overrideB1(pb1_b1) + 'B1::overrideB1' + >>> call_overrideB1(pc_b1) + 'C::overrideB1' + +test inheritB2 + + >>> b2.inheritB2() + 'B2::inheritB2' + >>> db2.inheritB2() + 'B2::inheritB2' + +========= test the new def_raw() feature ========== + + >>> r = RawTest(1) + >>> raw(r,1,third=1,fourth=1) + 4 + >>> r.raw(1,third=1,fourth=1) + 4 + >>> raw(r,1,third=1,f=1) + Traceback (innermost last): + KeyError: fourth + >>> raw(r,1,third=1) + Traceback (innermost last): + TypeError: wrong number of arguments + >>> raw(r,1) + Traceback (innermost last): + TypeError: wrong number of arguments + >>> raw() + Traceback (innermost last): + TypeError: wrong number of arguments + >>> raw1(1,second=1) + 2 + >>> raw1(1) + 1 + >>> raw1(second=1) + 1 + >>> raw1() + 0 + >>> raw2(1,second=1) + 2 + >>> raw2(1) + 1 + >>> raw2(second=1) + 1 + >>> raw2() + 0 + +========= test export of operators ========== + + >>> i = Int(2) + >>> j = i+i + >>> j.i() + 4 + >>> j = i-i + >>> j.i() + 0 + >>> j = i*i + >>> j.i() + 4 + >>> i>> cmp(i,i) + 0 + >>> k = Int(5) + >>> j = divmod(k, i) + >>> j[0].i() + 2 + >>> j[1].i() + 1 + >>> j = pow(i, k) + >>> j.i() + 32 + >>> j = pow(i, k, k) + >>> j.i() + 2 + >>> j = -i + >>> j.i() + -2 + >>> str(i) + '2' + >>> try: j = i/i + ... except TypeError, err: + ... assert re.match(r'(bad|unsupported) operand type\(s\) for /', + ... str(err)) + ... else: print 'no exception' + + >>> j = abs(i) + Traceback (innermost last): + TypeError: bad operand type for abs() + >>> j = i+1 + >>> j.i() + 3 + >>> j = i-1 + >>> j.i() + 1 + >>> j = i*1 + >>> j.i() + 2 + >>> i<1 + 0 + >>> cmp(i,1) + 1 + >>> j = pow(i, 5) + >>> j.i() + 32 + >>> j = pow(i, 5, k) + Traceback (innermost last): + TypeError: bad operand type(s) for pow() + >>> j = pow(i, 5, 5) + Traceback (innermost last): + TypeError: bad operand type(s) for pow() + >>> j = i/1 + Traceback (innermost last): + TypeError: bad operand type(s) for / + >>> j = 1+i + >>> j.i() + 3 + >>> j = 1-i + >>> j.i() + -1 + >>> j = 1*i + >>> j.i() + 2 + >>> 1>> cmp(1,i) + -1 + >>> j = 1/i + Traceback (innermost last): + TypeError: bad operand type(s) for / + >>> pow(1,i) + Traceback (innermost last): + TypeError: bad operand type(s) for pow() + +Test operator export to a subclass + + # force method table sharing + >>> class IntDerived1(Int): pass + ... + + >>> class IntDerived(Int): + ... def __init__(self, i): + ... Int.__init__(self, i) + ... def __str__(self): + ... return 'IntDerived: ' + str(self.i()) + ... + >>> f = IntDerived(3) + >>> str(f) + 'IntDerived: 3' + >>> j = f * f + >>> j.i() + 9 + >>> j = f * i + >>> j.i() + 6 + >>> j = f * 5 + >>> j.i() + 15 + >>> j = i * f + >>> j.i() + 6 + >>> j = 5 * f + >>> j.i() + 15 + + +========= Prove that the "phantom base class" issue is resolved ========== + + >>> assert pa1_a1.__class__ == A1 + >>> assert pb1_a1.__class__ == A1 + >>> assert pb2_a1.__class__ == A1 + >>> assert pc_a1.__class__ == A1 + >>> assert pa2_a2.__class__ == A2 + >>> assert pb1_a2.__class__ == A2 + >>> assert pb1_b1.__class__ == B1 + >>> assert pc_b1.__class__ == B1 + >>> assert A1 in B1.__bases__ + >>> assert A2 in B1.__bases__ + >>> assert A1 in B2.__bases__ + >>> assert A2 in B2.__bases__ + >>> assert A1 in DA1.__bases__ + >>> assert B1 in DB1.__bases__ + >>> assert B2 in DB2.__bases__ + +=============================================================== +test methodologies for wrapping functions that return a pointer + + >>> get_record().value + 1234 + + In this methodology, the referent is copied + >>> get_record() == get_record() + 0 + +======== Enums and non-method class attributes ============== + >>> eo = EnumOwner(EnumOwner.one, EnumOwner.two) + >>> eo.first + 1 + >>> eo.second + 2 + >>> eo.first = EnumOwner.three + >>> eo.second = EnumOwner.one + >>> eo.first + 3 + >>> eo.second + 1 + +======== test [plain] char converters ============== + >>> get_plain_char() + 'x' + >>> use_plain_char('a') + 'aaa' + >>> use_const_plain_char('b') + 'bbbbb' + +======== test std::complex converters ============== + >>> c = dpolar(3, 5) + >>> type(c) + + >>> '%.3g' % (dreal(c)) + '0.851' + >>> '%.3g' % (dimag(c)) + '-2.88' + >>> '%.3g' % (freal(c)) + '0.851' + >>> '%.3g' % (fimag(c)) + '-2.88' + >>> c = fpolar(7, 13) + >>> type(c) + + >>> '%.3g' % (fimag(c)) + '2.94' + >>> '%.3g' % (freal(c)) + '6.35' + >>> '%.3g' % (dimag(c)) + '2.94' + >>> '%.3g' % (dreal(c)) + '6.35' + >>> '%.3g' % (dreal(3)) + '3' + >>> '%.3g' % (dreal(3L)) + '3' + >>> '%.3g' % (dreal(3.)) + '3' + >>> '%.3g' % (freal(3)) + '3' + >>> '%.3g' % (freal(3L)) + '3' + >>> '%.3g' % (freal(3.)) + '3' + +''' +#' + +__test__ = {} +import sys + +# Inplace ops only exist in python 2.1 or later. +if sys.hexversion >= 0x02010000: + __test__['inplacetests'] = r''' + >>> ii = Int(1) + >>> ii += Int(2) + >>> ii.i() + 3 + >>> ii -= Int(1) + >>> ii.i() + 2 + >>> ii *= Int(3) + >>> ii.i() + 6 + >>> ii /= Int(2) + >>> ii.i() + 3 + >>> ii <<= Int(2) + >>> ii.i() + 12 + >>> ii >>= Int(1) + >>> ii.i() + 6 + >>> ii &= Int(5) + >>> ii.i() + 4 + >>> ii |= Int(9) + >>> ii.i() + 13 + >>> ii ^= Int(7) + >>> ii.i() + 10 + >>> ii %= Int(4) + >>> ii.i() + 2 + >>> ii **= Int(3) + >>> ii.i() + 8 + >>> ii.j() + 11 +''' + +from boost_python_test import * + +# pickle requires these derived classes to be +# at the global scope of the module + +class myrational(Rational): + __dict_defines_state__ = 1 # this is a lie but good enough for testing. + +class myworld(world): + def __init__(self): + world.__init__(self, 'anywhere') + self.x = 1 + +class myunsafeworld(myworld): + __getstate_manages_dict__ = 1 # this is a lie but good enough for testing. + + +def assert_integer_expected(err): + """Handle a common error report which appears differently in Python 1.5.x and 2.0""" + assert isinstance(err, TypeError) + message = str(err) + assert (message == "illegal argument type for built-in operation" + or message == "an integer is required") + +import string +import re +import sys + +def run(args = None): + if args is not None: + sys.argv = args + + import doctest, comprehensive + return doctest.testmod(comprehensive) + +if __name__ == '__main__': + sys.exit(run()[0]) diff --git a/test/doctest.py b/test/doctest.py new file mode 100644 index 00000000..2829f1e6 --- /dev/null +++ b/test/doctest.py @@ -0,0 +1,1173 @@ +# Module doctest. +# Released to the public domain 16-Jan-2001, +# by Tim Peters (tim.one@home.com). + +# Provided as-is; use at your own risk; no warranty; no promises; enjoy! + +"""Module doctest -- a framework for running examples in docstrings. + +NORMAL USAGE + +In normal use, end each module M with: + +def _test(): + import doctest, M # replace M with your module's name + return doctest.testmod(M) # ditto + +if __name__ == "__main__": + _test() + +Then running the module as a script will cause the examples in the +docstrings to get executed and verified: + +python M.py + +This won't display anything unless an example fails, in which case the +failing example(s) and the cause(s) of the failure(s) are printed to stdout +(why not stderr? because stderr is a lame hack <0.2 wink>), and the final +line of output is "Test failed.". + +Run it with the -v switch instead: + +python M.py -v + +and a detailed report of all examples tried is printed to stdout, along +with assorted summaries at the end. + +You can force verbose mode by passing "verbose=1" to testmod, or prohibit +it by passing "verbose=0". In either of those cases, sys.argv is not +examined by testmod. + +In any case, testmod returns a 2-tuple of ints (f, t), where f is the +number of docstring examples that failed and t is the total number of +docstring examples attempted. + + +WHICH DOCSTRINGS ARE EXAMINED? + ++ M.__doc__. + ++ f.__doc__ for all functions f in M.__dict__.values(), except those + with private names and those defined in other modules. + ++ C.__doc__ for all classes C in M.__dict__.values(), except those with + private names and those defined in other modules. + ++ If M.__test__ exists and "is true", it must be a dict, and + each entry maps a (string) name to a function object, class object, or + string. Function and class object docstrings found from M.__test__ + are searched even if the name is private, and strings are searched + directly as if they were docstrings. In output, a key K in M.__test__ + appears with name + .__test__.K + +Any classes found are recursively searched similarly, to test docstrings in +their contained methods and nested classes. Private names reached from M's +globals are skipped, but all names reached from M.__test__ are searched. + +By default, a name is considered to be private if it begins with an +underscore (like "_my_func") but doesn't both begin and end with (at least) +two underscores (like "__init__"). You can change the default by passing +your own "isprivate" function to testmod. + +If you want to test docstrings in objects with private names too, stuff +them into an M.__test__ dict, or see ADVANCED USAGE below (e.g., pass your +own isprivate function to Tester's constructor, or call the rundoc method +of a Tester instance). + +WHAT'S THE EXECUTION CONTEXT? + +By default, each time testmod finds a docstring to test, it uses a *copy* +of M's globals (so that running tests on a module doesn't change the +module's real globals, and so that one test in M can't leave behind crumbs +that accidentally allow another test to work). This means examples can +freely use any names defined at top-level in M. It also means that sloppy +imports (see above) can cause examples in external docstrings to use +globals inappropriate for them. + +You can force use of your own dict as the execution context by passing +"globs=your_dict" to testmod instead. Presumably this would be a copy of +M.__dict__ merged with the globals from other imported modules. + + +WHAT IF I WANT TO TEST A WHOLE PACKAGE? + +Piece o' cake, provided the modules do their testing from docstrings. +Here's the test.py I use for the world's most elaborate Rational/ +floating-base-conversion pkg (which I'll distribute some day): + +from Rational import Cvt +from Rational import Format +from Rational import machprec +from Rational import Rat +from Rational import Round +from Rational import utils + +modules = (Cvt, + Format, + machprec, + Rat, + Round, + utils) + +def _test(): + import doctest + import sys + verbose = "-v" in sys.argv + for mod in modules: + doctest.testmod(mod, verbose=verbose, report=0) + doctest.master.summarize() + +if __name__ == "__main__": + _test() + +IOW, it just runs testmod on all the pkg modules. testmod remembers the +names and outcomes (# of failures, # of tries) for each item it's seen, and +passing "report=0" prevents it from printing a summary in verbose mode. +Instead, the summary is delayed until all modules have been tested, and +then "doctest.master.summarize()" forces the summary at the end. + +So this is very nice in practice: each module can be tested individually +with almost no work beyond writing up docstring examples, and collections +of modules can be tested too as a unit with no more work than the above. + + +WHAT ABOUT EXCEPTIONS? + +No problem, as long as the only output generated by the example is the +traceback itself. For example: + + >>> [1, 2, 3].remove(42) + Traceback (most recent call last): + File "", line 1, in ? + ValueError: list.remove(x): x not in list + >>> + +Note that only the exception type and value are compared (specifically, +only the last line in the traceback). + + +ADVANCED USAGE + +doctest.testmod() captures the testing policy I find most useful most +often. You may want other policies. + +testmod() actually creates a local instance of class doctest.Tester, runs +appropriate methods of that class, and merges the results into global +Tester instance doctest.master. + +You can create your own instances of doctest.Tester, and so build your own +policies, or even run methods of doctest.master directly. See +doctest.Tester.__doc__ for details. + + +SO WHAT DOES A DOCSTRING EXAMPLE LOOK LIKE ALREADY!? + +Oh ya. It's easy! In most cases a copy-and-paste of an interactive +console session works fine -- just make sure the leading whitespace is +rigidly consistent (you can mix tabs and spaces if you're too lazy to do it +right, but doctest is not in the business of guessing what you think a tab +means). + + >>> # comments are ignored + >>> x = 12 + >>> x + 12 + >>> if x == 13: + ... print "yes" + ... else: + ... print "no" + ... print "NO" + ... print "NO!!!" + ... + no + NO + NO!!! + >>> + +Any expected output must immediately follow the final ">>>" or "..." line +containing the code, and the expected output (if any) extends to the next +">>>" or all-whitespace line. That's it. + +Bummers: + ++ Expected output cannot contain an all-whitespace line, since such a line + is taken to signal the end of expected output. + ++ Output to stdout is captured, but not output to stderr (exception + tracebacks are captured via a different means). + ++ If you continue a line via backslashing in an interactive session, or for + any other reason use a backslash, you need to double the backslash in the + docstring version. This is simply because you're in a string, and so the + backslash must be escaped for it to survive intact. Like: + +>>> if "yes" == \\ +... "y" + \\ +... "es": # in the source code you'll see the doubled backslashes +... print 'yes' +yes + +The starting column doesn't matter: + +>>> assert "Easy!" + >>> import math + >>> math.floor(1.9) + 1.0 + +and as many leading whitespace characters are stripped from the expected +output as appeared in the initial ">>>" line that triggered it. + +If you execute this very file, the examples above will be found and +executed, leading to this output in verbose mode: + +Running doctest.__doc__ +Trying: [1, 2, 3].remove(42) +Expecting: +Traceback (most recent call last): + File "", line 1, in ? +ValueError: list.remove(x): x not in list +ok +Trying: x = 12 +Expecting: nothing +ok +Trying: x +Expecting: 12 +ok +Trying: +if x == 13: + print "yes" +else: + print "no" + print "NO" + print "NO!!!" +Expecting: +no +NO +NO!!! +ok +... and a bunch more like that, with this summary at the end: + +5 items had no tests: + doctest.Tester.__init__ + doctest.Tester.run__test__ + doctest.Tester.summarize + doctest.run_docstring_examples + doctest.testmod +12 items passed all tests: + 8 tests in doctest + 6 tests in doctest.Tester + 10 tests in doctest.Tester.merge + 14 tests in doctest.Tester.rundict + 3 tests in doctest.Tester.rundoc + 3 tests in doctest.Tester.runstring + 2 tests in doctest.__test__._TestClass + 2 tests in doctest.__test__._TestClass.__init__ + 2 tests in doctest.__test__._TestClass.get + 1 tests in doctest.__test__._TestClass.square + 2 tests in doctest.__test__.string + 7 tests in doctest.is_private +60 tests in 17 items. +60 passed and 0 failed. +Test passed. +""" + +__all__ = [ + 'testmod', + 'run_docstring_examples', + 'is_private', + 'Tester', +] + +import __future__ + +import re +PS1 = ">>>" +PS2 = "..." +_isPS1 = re.compile(r"(\s*)" + re.escape(PS1)).match +_isPS2 = re.compile(r"(\s*)" + re.escape(PS2)).match +_isEmpty = re.compile(r"\s*$").match +_isComment = re.compile(r"\s*#").match +del re + +from types import StringTypes as _StringTypes + +from inspect import isclass as _isclass +from inspect import isfunction as _isfunction +from inspect import ismodule as _ismodule +from inspect import classify_class_attrs as _classify_class_attrs + +# Extract interactive examples from a string. Return a list of triples, +# (source, outcome, lineno). "source" is the source code, and ends +# with a newline iff the source spans more than one line. "outcome" is +# the expected output if any, else an empty string. When not empty, +# outcome always ends with a newline. "lineno" is the line number, +# 0-based wrt the start of the string, of the first source line. + +def _extract_examples(s): + isPS1, isPS2 = _isPS1, _isPS2 + isEmpty, isComment = _isEmpty, _isComment + examples = [] + lines = s.split("\n") + i, n = 0, len(lines) + while i < n: + line = lines[i] + i = i + 1 + m = isPS1(line) + if m is None: + continue + j = m.end(0) # beyond the prompt + if isEmpty(line, j) or isComment(line, j): + # a bare prompt or comment -- not interesting + continue + lineno = i - 1 + if line[j] != " ": + raise ValueError("line " + `lineno` + " of docstring lacks " + "blank after " + PS1 + ": " + line) + j = j + 1 + blanks = m.group(1) + nblanks = len(blanks) + # suck up this and following PS2 lines + source = [] + while 1: + source.append(line[j:]) + line = lines[i] + m = isPS2(line) + if m: + if m.group(1) != blanks: + raise ValueError("inconsistent leading whitespace " + "in line " + `i` + " of docstring: " + line) + i = i + 1 + else: + break + if len(source) == 1: + source = source[0] + else: + # get rid of useless null line from trailing empty "..." + if source[-1] == "": + del source[-1] + source = "\n".join(source) + "\n" + # suck up response + if isPS1(line) or isEmpty(line): + expect = "" + else: + expect = [] + while 1: + if line[:nblanks] != blanks: + raise ValueError("inconsistent leading whitespace " + "in line " + `i` + " of docstring: " + line) + expect.append(line[nblanks:]) + i = i + 1 + line = lines[i] + if isPS1(line) or isEmpty(line): + break + expect = "\n".join(expect) + "\n" + examples.append( (source, expect, lineno) ) + return examples + +# Capture stdout when running examples. + +class _SpoofOut: + def __init__(self): + self.clear() + def write(self, s): + self.buf.append(s) + def get(self): + guts = "".join(self.buf) + # If anything at all was written, make sure there's a trailing + # newline. There's no way for the expected output to indicate + # that a trailing newline is missing. + if guts and not guts.endswith("\n"): + guts = guts + "\n" + # Prevent softspace from screwing up the next test case, in + # case they used print with a trailing comma in an example. + if hasattr(self, "softspace"): + del self.softspace + return guts + def clear(self): + self.buf = [] + if hasattr(self, "softspace"): + del self.softspace + def flush(self): + # JPython calls flush + pass + +# Display some tag-and-msg pairs nicely, keeping the tag and its msg +# on the same line when that makes sense. + +def _tag_out(printer, *tag_msg_pairs): + for tag, msg in tag_msg_pairs: + printer(tag + ":") + msg_has_nl = msg[-1:] == "\n" + msg_has_two_nl = msg_has_nl and \ + msg.find("\n") < len(msg) - 1 + if len(tag) + len(msg) < 76 and not msg_has_two_nl: + printer(" ") + else: + printer("\n") + printer(msg) + if not msg_has_nl: + printer("\n") + +# Run list of examples, in context globs. "out" can be used to display +# stuff to "the real" stdout, and fakeout is an instance of _SpoofOut +# that captures the examples' std output. Return (#failures, #tries). + +def _run_examples_inner(out, fakeout, examples, globs, verbose, name, + compileflags): + import sys, traceback + OK, BOOM, FAIL = range(3) + NADA = "nothing" + stderr = _SpoofOut() + failures = 0 + for source, want, lineno in examples: + if verbose: + _tag_out(out, ("Trying", source), + ("Expecting", want or NADA)) + fakeout.clear() + try: + exec compile(source, "", "single", + compileflags, 1) in globs + got = fakeout.get() + state = OK + except: + # See whether the exception was expected. + if want.find("Traceback (innermost last):\n") == 0 or \ + want.find("Traceback (most recent call last):\n") == 0: + # Only compare exception type and value - the rest of + # the traceback isn't necessary. + want = want.split('\n')[-2] + '\n' + exc_type, exc_val = sys.exc_info()[:2] + got = traceback.format_exception_only(exc_type, exc_val)[-1] + state = OK + else: + # unexpected exception + stderr.clear() + traceback.print_exc(file=stderr) + state = BOOM + + if state == OK: + if got == want: + if verbose: + out("ok\n") + continue + state = FAIL + + assert state in (FAIL, BOOM) + failures = failures + 1 + out("*" * 65 + "\n") + _tag_out(out, ("Failure in example", source)) + out("from line #" + `lineno` + " of " + name + "\n") + if state == FAIL: + _tag_out(out, ("Expected", want or NADA), ("Got", got)) + else: + assert state == BOOM + _tag_out(out, ("Exception raised", stderr.get())) + + return failures, len(examples) + +# Get the future-flags associated with the future features that have been +# imported into globs. + +def _extract_future_flags(globs): + flags = 0 + for fname in __future__.all_feature_names: + feature = globs.get(fname, None) + if feature is getattr(__future__, fname): + flags |= feature.compiler_flag + return flags + +# Run list of examples, in a shallow copy of context (dict) globs. +# Return (#failures, #tries). + +def _run_examples(examples, globs, verbose, name, compileflags): + import sys + saveout = sys.stdout + globs = globs.copy() + try: + sys.stdout = fakeout = _SpoofOut() + x = _run_examples_inner(saveout.write, fakeout, examples, + globs, verbose, name, compileflags) + finally: + sys.stdout = saveout + # While Python gc can clean up most cycles on its own, it doesn't + # chase frame objects. This is especially irksome when running + # generator tests that raise exceptions, because a named generator- + # iterator gets an entry in globs, and the generator-iterator + # object's frame's traceback info points back to globs. This is + # easy to break just by clearing the namespace. This can also + # help to break other kinds of cycles, and even for cycles that + # gc can break itself it's better to break them ASAP. + globs.clear() + return x + +def run_docstring_examples(f, globs, verbose=0, name="NoName", + compileflags=None): + """f, globs, verbose=0, name="NoName" -> run examples from f.__doc__. + + Use (a shallow copy of) dict globs as the globals for execution. + Return (#failures, #tries). + + If optional arg verbose is true, print stuff even if there are no + failures. + Use string name in failure msgs. + """ + + try: + doc = f.__doc__ + if not doc: + # docstring empty or None + return 0, 0 + # just in case CT invents a doc object that has to be forced + # to look like a string <0.9 wink> + doc = str(doc) + except: + return 0, 0 + + e = _extract_examples(doc) + if not e: + return 0, 0 + if compileflags is None: + compileflags = _extract_future_flags(globs) + return _run_examples(e, globs, verbose, name, compileflags) + +def is_private(prefix, base): + """prefix, base -> true iff name prefix + "." + base is "private". + + Prefix may be an empty string, and base does not contain a period. + Prefix is ignored (although functions you write conforming to this + protocol may make use of it). + Return true iff base begins with an (at least one) underscore, but + does not both begin and end with (at least) two underscores. + + >>> is_private("a.b", "my_func") + 0 + >>> is_private("____", "_my_func") + 1 + >>> is_private("someclass", "__init__") + 0 + >>> is_private("sometypo", "__init_") + 1 + >>> is_private("x.y.z", "_") + 1 + >>> is_private("_x.y.z", "__") + 0 + >>> is_private("", "") # senseless but consistent + 0 + """ + + return base[:1] == "_" and not base[:2] == "__" == base[-2:] + +# Determine if a class of function was defined in the given module. + +def _from_module(module, object): + if _isfunction(object): + return module.__dict__ is object.func_globals + if _isclass(object): + return module.__name__ == object.__module__ + raise ValueError("object must be a class or function") + +class Tester: + """Class Tester -- runs docstring examples and accumulates stats. + +In normal use, function doctest.testmod() hides all this from you, +so use that if you can. Create your own instances of Tester to do +fancier things. + +Methods: + runstring(s, name) + Search string s for examples to run; use name for logging. + Return (#failures, #tries). + + rundoc(object, name=None) + Search object.__doc__ for examples to run; use name (or + object.__name__) for logging. Return (#failures, #tries). + + rundict(d, name, module=None) + Search for examples in docstrings in all of d.values(); use name + for logging. Exclude functions and classes not defined in module + if specified. Return (#failures, #tries). + + run__test__(d, name) + Treat dict d like module.__test__. Return (#failures, #tries). + + summarize(verbose=None) + Display summary of testing results, to stdout. Return + (#failures, #tries). + + merge(other) + Merge in the test results from Tester instance "other". + +>>> from doctest import Tester +>>> t = Tester(globs={'x': 42}, verbose=0) +>>> t.runstring(r''' +... >>> x = x * 2 +... >>> print x +... 42 +... ''', 'XYZ') +***************************************************************** +Failure in example: print x +from line #2 of XYZ +Expected: 42 +Got: 84 +(1, 2) +>>> t.runstring(">>> x = x * 2\\n>>> print x\\n84\\n", 'example2') +(0, 2) +>>> t.summarize() +***************************************************************** +1 items had failures: + 1 of 2 in XYZ +***Test Failed*** 1 failures. +(1, 4) +>>> t.summarize(verbose=1) +1 items passed all tests: + 2 tests in example2 +***************************************************************** +1 items had failures: + 1 of 2 in XYZ +4 tests in 2 items. +3 passed and 1 failed. +***Test Failed*** 1 failures. +(1, 4) +>>> +""" + + def __init__(self, mod=None, globs=None, verbose=None, + isprivate=None): + """mod=None, globs=None, verbose=None, isprivate=None + +See doctest.__doc__ for an overview. + +Optional keyword arg "mod" is a module, whose globals are used for +executing examples. If not specified, globs must be specified. + +Optional keyword arg "globs" gives a dict to be used as the globals +when executing examples; if not specified, use the globals from +module mod. + +In either case, a copy of the dict is used for each docstring +examined. + +Optional keyword arg "verbose" prints lots of stuff if true, only +failures if false; by default, it's true iff "-v" is in sys.argv. + +Optional keyword arg "isprivate" specifies a function used to determine +whether a name is private. The default function is doctest.is_private; +see its docs for details. +""" + + if mod is None and globs is None: + raise TypeError("Tester.__init__: must specify mod or globs") + if mod is not None and not _ismodule(mod): + raise TypeError("Tester.__init__: mod must be a module; " + + `mod`) + if globs is None: + globs = mod.__dict__ + self.globs = globs + + if verbose is None: + import sys + verbose = "-v" in sys.argv + self.verbose = verbose + + if isprivate is None: + isprivate = is_private + self.isprivate = isprivate + + self.name2ft = {} # map name to (#failures, #trials) pair + + self.compileflags = _extract_future_flags(globs) + + def runstring(self, s, name): + """ + s, name -> search string s for examples to run, logging as name. + + Use string name as the key for logging the outcome. + Return (#failures, #examples). + + >>> t = Tester(globs={}, verbose=1) + >>> test = r''' + ... # just an example + ... >>> x = 1 + 2 + ... >>> x + ... 3 + ... ''' + >>> t.runstring(test, "Example") + Running string Example + Trying: x = 1 + 2 + Expecting: nothing + ok + Trying: x + Expecting: 3 + ok + 0 of 2 examples failed in string Example + (0, 2) + """ + + if self.verbose: + print "Running string", name + f = t = 0 + e = _extract_examples(s) + if e: + f, t = _run_examples(e, self.globs, self.verbose, name, + self.compileflags) + if self.verbose: + print f, "of", t, "examples failed in string", name + self.__record_outcome(name, f, t) + return f, t + + def rundoc(self, object, name=None): + """ + object, name=None -> search object.__doc__ for examples to run. + + Use optional string name as the key for logging the outcome; + by default use object.__name__. + Return (#failures, #examples). + If object is a class object, search recursively for method + docstrings too. + object.__doc__ is examined regardless of name, but if object is + a class, whether private names reached from object are searched + depends on the constructor's "isprivate" argument. + + >>> t = Tester(globs={}, verbose=0) + >>> def _f(): + ... '''Trivial docstring example. + ... >>> assert 2 == 2 + ... ''' + ... return 32 + ... + >>> t.rundoc(_f) # expect 0 failures in 1 example + (0, 1) + """ + + if name is None: + try: + name = object.__name__ + except AttributeError: + raise ValueError("Tester.rundoc: name must be given " + "when object.__name__ doesn't exist; " + `object`) + if self.verbose: + print "Running", name + ".__doc__" + f, t = run_docstring_examples(object, self.globs, self.verbose, name, + self.compileflags) + if self.verbose: + print f, "of", t, "examples failed in", name + ".__doc__" + self.__record_outcome(name, f, t) + if _isclass(object): + # In 2.2, class and static methods complicate life. Build + # a dict "that works", by hook or by crook. + d = {} + for tag, kind, homecls, value in _classify_class_attrs(object): + + if homecls is not object: + # Only look at names defined immediately by the class. + continue + + elif self.isprivate(name, tag): + continue + + elif kind == "method": + # value is already a function + d[tag] = value + + elif kind == "static method": + # value isn't a function, but getattr reveals one + d[tag] = getattr(object, tag) + + elif kind == "class method": + # Hmm. A classmethod object doesn't seem to reveal + # enough. But getattr turns it into a bound method, + # and from there .im_func retrieves the underlying + # function. + d[tag] = getattr(object, tag).im_func + + elif kind == "property": + # The methods implementing the property have their + # own docstrings -- but the property may have one too. + if value.__doc__ is not None: + d[tag] = str(value.__doc__) + + elif kind == "data": + # Grab nested classes. + if _isclass(value): + d[tag] = value + + else: + raise ValueError("teach doctest about %r" % kind) + + f2, t2 = self.run__test__(d, name) + f += f2 + t += t2 + + return f, t + + def rundict(self, d, name, module=None): + """ + d, name, module=None -> search for docstring examples in d.values(). + + For k, v in d.items() such that v is a function or class, + do self.rundoc(v, name + "." + k). Whether this includes + objects with private names depends on the constructor's + "isprivate" argument. If module is specified, functions and + classes that are not defined in module are excluded. + Return aggregate (#failures, #examples). + + Build and populate two modules with sample functions to test that + exclusion of external functions and classes works. + + >>> import new + >>> m1 = new.module('_m1') + >>> m2 = new.module('_m2') + >>> test_data = \""" + ... def _f(): + ... '''>>> assert 1 == 1 + ... ''' + ... def g(): + ... '''>>> assert 2 != 1 + ... ''' + ... class H: + ... '''>>> assert 2 > 1 + ... ''' + ... def bar(self): + ... '''>>> assert 1 < 2 + ... ''' + ... \""" + >>> exec test_data in m1.__dict__ + >>> exec test_data in m2.__dict__ + >>> m1.__dict__.update({"f2": m2._f, "g2": m2.g, "h2": m2.H}) + + Tests that objects outside m1 are excluded: + + >>> t = Tester(globs={}, verbose=0) + >>> t.rundict(m1.__dict__, "rundict_test", m1) # _f, f2 and g2 and h2 skipped + (0, 3) + + Again, but with a custom isprivate function allowing _f: + + >>> t = Tester(globs={}, verbose=0, isprivate=lambda x,y: 0) + >>> t.rundict(m1.__dict__, "rundict_test_pvt", m1) # Only f2, g2 and h2 skipped + (0, 4) + + And once more, not excluding stuff outside m1: + + >>> t = Tester(globs={}, verbose=0, isprivate=lambda x,y: 0) + >>> t.rundict(m1.__dict__, "rundict_test_pvt") # None are skipped. + (0, 8) + + The exclusion of objects from outside the designated module is + meant to be invoked automagically by testmod. + + >>> testmod(m1) + (0, 3) + + """ + + if not hasattr(d, "items"): + raise TypeError("Tester.rundict: d must support .items(); " + + `d`) + f = t = 0 + # Run the tests by alpha order of names, for consistency in + # verbose-mode output. + names = d.keys() + names.sort() + for thisname in names: + value = d[thisname] + if _isfunction(value) or _isclass(value): + if module and not _from_module(module, value): + continue + f2, t2 = self.__runone(value, name + "." + thisname) + f = f + f2 + t = t + t2 + return f, t + + def run__test__(self, d, name): + """d, name -> Treat dict d like module.__test__. + + Return (#failures, #tries). + See testmod.__doc__ for details. + """ + + failures = tries = 0 + prefix = name + "." + savepvt = self.isprivate + try: + self.isprivate = lambda *args: 0 + # Run the tests by alpha order of names, for consistency in + # verbose-mode output. + keys = d.keys() + keys.sort() + for k in keys: + v = d[k] + thisname = prefix + k + if type(v) in _StringTypes: + f, t = self.runstring(v, thisname) + elif _isfunction(v) or _isclass(v): + f, t = self.rundoc(v, thisname) + else: + raise TypeError("Tester.run__test__: values in " + "dict must be strings, functions " + "or classes; " + `v`) + failures = failures + f + tries = tries + t + finally: + self.isprivate = savepvt + return failures, tries + + def summarize(self, verbose=None): + """ + verbose=None -> summarize results, return (#failures, #tests). + + Print summary of test results to stdout. + Optional arg 'verbose' controls how wordy this is. By + default, use the verbose setting established by the + constructor. + """ + + if verbose is None: + verbose = self.verbose + notests = [] + passed = [] + failed = [] + totalt = totalf = 0 + for x in self.name2ft.items(): + name, (f, t) = x + assert f <= t + totalt = totalt + t + totalf = totalf + f + if t == 0: + notests.append(name) + elif f == 0: + passed.append( (name, t) ) + else: + failed.append(x) + if verbose: + if notests: + print len(notests), "items had no tests:" + notests.sort() + for thing in notests: + print " ", thing + if passed: + print len(passed), "items passed all tests:" + passed.sort() + for thing, count in passed: + print " %3d tests in %s" % (count, thing) + if failed: + print "*" * 65 + print len(failed), "items had failures:" + failed.sort() + for thing, (f, t) in failed: + print " %3d of %3d in %s" % (f, t, thing) + if verbose: + print totalt, "tests in", len(self.name2ft), "items." + print totalt - totalf, "passed and", totalf, "failed." + if totalf: + print "***Test Failed***", totalf, "failures." + elif verbose: + print "Test passed." + return totalf, totalt + + def merge(self, other): + """ + other -> merge in test results from the other Tester instance. + + If self and other both have a test result for something + with the same name, the (#failures, #tests) results are + summed, and a warning is printed to stdout. + + >>> from doctest import Tester + >>> t1 = Tester(globs={}, verbose=0) + >>> t1.runstring(''' + ... >>> x = 12 + ... >>> print x + ... 12 + ... ''', "t1example") + (0, 2) + >>> + >>> t2 = Tester(globs={}, verbose=0) + >>> t2.runstring(''' + ... >>> x = 13 + ... >>> print x + ... 13 + ... ''', "t2example") + (0, 2) + >>> common = ">>> assert 1 + 2 == 3\\n" + >>> t1.runstring(common, "common") + (0, 1) + >>> t2.runstring(common, "common") + (0, 1) + >>> t1.merge(t2) + *** Tester.merge: 'common' in both testers; summing outcomes. + >>> t1.summarize(1) + 3 items passed all tests: + 2 tests in common + 2 tests in t1example + 2 tests in t2example + 6 tests in 3 items. + 6 passed and 0 failed. + Test passed. + (0, 6) + >>> + """ + + d = self.name2ft + for name, (f, t) in other.name2ft.items(): + if d.has_key(name): + print "*** Tester.merge: '" + name + "' in both" \ + " testers; summing outcomes." + f2, t2 = d[name] + f = f + f2 + t = t + t2 + d[name] = f, t + + def __record_outcome(self, name, f, t): + if self.name2ft.has_key(name): + print "*** Warning: '" + name + "' was tested before;", \ + "summing outcomes." + f2, t2 = self.name2ft[name] + f = f + f2 + t = t + t2 + self.name2ft[name] = f, t + + def __runone(self, target, name): + if "." in name: + i = name.rindex(".") + prefix, base = name[:i], name[i+1:] + else: + prefix, base = "", base + if self.isprivate(prefix, base): + return 0, 0 + return self.rundoc(target, name) + +master = None + +def testmod(m, name=None, globs=None, verbose=None, isprivate=None, + report=1): + """m, name=None, globs=None, verbose=None, isprivate=None, report=1 + + Test examples in docstrings in functions and classes reachable from + module m, starting with m.__doc__. Private names are skipped. + + Also test examples reachable from dict m.__test__ if it exists and is + not None. m.__dict__ maps names to functions, classes and strings; + function and class docstrings are tested even if the name is private; + strings are tested directly, as if they were docstrings. + + Return (#failures, #tests). + + See doctest.__doc__ for an overview. + + Optional keyword arg "name" gives the name of the module; by default + use m.__name__. + + Optional keyword arg "globs" gives a dict to be used as the globals + when executing examples; by default, use m.__dict__. A copy of this + dict is actually used for each docstring, so that each docstring's + examples start with a clean slate. + + Optional keyword arg "verbose" prints lots of stuff if true, prints + only failures if false; by default, it's true iff "-v" is in sys.argv. + + Optional keyword arg "isprivate" specifies a function used to + determine whether a name is private. The default function is + doctest.is_private; see its docs for details. + + Optional keyword arg "report" prints a summary at the end when true, + else prints nothing at the end. In verbose mode, the summary is + detailed, else very brief (in fact, empty if all tests passed). + + Advanced tomfoolery: testmod runs methods of a local instance of + class doctest.Tester, then merges the results into (or creates) + global Tester instance doctest.master. Methods of doctest.master + can be called directly too, if you want to do something unusual. + Passing report=0 to testmod is especially useful then, to delay + displaying a summary. Invoke doctest.master.summarize(verbose) + when you're done fiddling. + """ + + global master + + if not _ismodule(m): + raise TypeError("testmod: module required; " + `m`) + if name is None: + name = m.__name__ + tester = Tester(m, globs=globs, verbose=verbose, isprivate=isprivate) + failures, tries = tester.rundoc(m, name) + f, t = tester.rundict(m.__dict__, name, m) + failures = failures + f + tries = tries + t + if hasattr(m, "__test__"): + testdict = m.__test__ + if testdict: + if not hasattr(testdict, "items"): + raise TypeError("testmod: module.__test__ must support " + ".items(); " + `testdict`) + f, t = tester.run__test__(testdict, name + ".__test__") + failures = failures + f + tries = tries + t + if report: + tester.summarize() + if master is None: + master = tester + else: + master.merge(tester) + return failures, tries + +class _TestClass: + """ + A pointless class, for sanity-checking of docstring testing. + + Methods: + square() + get() + + >>> _TestClass(13).get() + _TestClass(-12).get() + 1 + >>> hex(_TestClass(13).square().get()) + '0xa9' + """ + + def __init__(self, val): + """val -> _TestClass object with associated value val. + + >>> t = _TestClass(123) + >>> print t.get() + 123 + """ + + self.val = val + + def square(self): + """square() -> square TestClass's associated value + + >>> _TestClass(13).square().get() + 169 + """ + + self.val = self.val ** 2 + return self + + def get(self): + """get() -> return TestClass's associated value. + + >>> x = _TestClass(-42) + >>> print x.get() + -42 + """ + + return self.val + +__test__ = {"_TestClass": _TestClass, + "string": r""" + Example of a string object, searched as-is. + >>> x = 1; y = 2 + >>> x + y, x * y + (3, 2) + """ + } + +def _test(): + import doctest + return doctest.testmod(doctest) + +if __name__ == "__main__": + _test() diff --git a/test/if_else.cpp b/test/if_else.cpp new file mode 100644 index 00000000..e2b50f28 --- /dev/null +++ b/test/if_else.cpp @@ -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. +#include +#include +#include + + typedef char c1; + typedef char c2[2]; + typedef char c3[3]; + typedef char c4[4]; + +template +struct choose +{ + typedef typename boost::python::detail::if_< + (sizeof(c1) == size) + >::template then< + c1 + >::template elif< + (sizeof(c2) == size) + >::template then< + c2 + >::template elif< + (sizeof(c3) == size) + >::template then< + c3 + >::template elif< + (sizeof(c4) == size) + >::template then< + c4 + >::template else_::type type; +}; + +int main() +{ + BOOST_STATIC_ASSERT((boost::is_same::type,c1>::value)); + BOOST_STATIC_ASSERT((boost::is_same::type,c2>::value)); + BOOST_STATIC_ASSERT((boost::is_same::type,c3>::value)); + BOOST_STATIC_ASSERT((boost::is_same::type,c4>::value)); + BOOST_STATIC_ASSERT((boost::is_same::type,void*>::value)); + return 0; +} diff --git a/test/indirect_traits_test.cpp b/test/indirect_traits_test.cpp new file mode 100644 index 00000000..7160b8eb --- /dev/null +++ b/test/indirect_traits_test.cpp @@ -0,0 +1,80 @@ +//#include +#include +#include + +//#define print(expr) printf("%s ==> %s\n", #expr, expr) + +// not all the compilers can handle an incomplete class type here. +struct X {}; + +int main() +{ +using namespace boost::python::detail; + +#if 0 // not yet supported + assert(is_reference_to_function::value); + assert(!is_reference_to_function::value); +#endif + + assert(!is_pointer_to_function::value); + assert(is_pointer_to_function::value); + + assert(is_reference_to_pointer::value); + assert(is_reference_to_pointer::value); + assert(is_reference_to_pointer::value); + assert(is_reference_to_pointer::value); + + assert(!is_reference_to_pointer::value); + assert(!is_reference_to_pointer::value); + assert(!is_reference_to_pointer::value); + + assert(!is_reference_to_const::value); + assert(is_reference_to_const::value); + assert(!is_reference_to_const::value); + assert(is_reference_to_const::value); + + assert(!is_reference_to_const::value); + assert(!is_reference_to_const::value); + assert(!is_reference_to_const::value); + + assert(is_reference_to_non_const::value); + assert(!is_reference_to_non_const::value); + assert(is_reference_to_non_const::value); + assert(!is_reference_to_non_const::value); + + assert(!is_reference_to_non_const::value); + assert(!is_reference_to_non_const::value); + assert(!is_reference_to_non_const::value); + + assert(!is_reference_to_volatile::value); + assert(!is_reference_to_volatile::value); + assert(is_reference_to_volatile::value); + assert(is_reference_to_volatile::value); + + assert(!is_reference_to_volatile::value); + assert(!is_reference_to_volatile::value); + assert(!is_reference_to_volatile::value); + + assert(!is_reference_to_class::value); + assert(!is_reference_to_class::value); + assert(!is_reference_to_class::value); + + assert(!is_reference_to_class::value); + assert(is_reference_to_class::value); + assert(is_reference_to_class::value); + assert(is_reference_to_class::value); + assert(is_reference_to_class::value); + + assert(!is_pointer_to_class::value); + assert(!is_pointer_to_class::value); + assert(!is_pointer_to_class::value); + + assert(!is_pointer_to_class::value); + assert(!is_pointer_to_class::value); + assert(is_pointer_to_class::value); + assert(is_pointer_to_class::value); + assert(is_pointer_to_class::value); + assert(is_pointer_to_class::value); + + return 0; +} diff --git a/test/iterator.py b/test/iterator.py new file mode 100644 index 00000000..2f2f8e02 --- /dev/null +++ b/test/iterator.py @@ -0,0 +1,72 @@ +''' +>>> from iterator_ext import * +>>> from input_iterator import * +>>> x = list_int() +>>> x.push_back(1) +>>> x.back() +1 +>>> x.push_back(3) +>>> x.push_back(5) +>>> for y in x: +... print y +1 +3 +5 +>>> z = range(x) +>>> for y in z: +... print y +1 +3 +5 + + Range2 wraps a transform_iterator which doubles the elements it + traverses. This proves we can wrap input iterators + +>>> z2 = range2(x) +>>> for y in z2: +... print y +2 +6 +10 + +>>> l2 = two_lists() +>>> for y in l2.primes: +... print y +2 +3 +5 +7 +11 +13 +>>> for y in l2.evens: +... print y +2 +4 +6 +8 +10 +12 +>>> ll = list_list() +>>> ll.push_back(x) +>>> x.push_back(7) +>>> ll.push_back(x) +>>> for a in ll: +... for b in a: +... print b, +... print +... +1 3 5 +1 3 5 7 +''' +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/minimal.py b/test/minimal.py new file mode 100644 index 00000000..fe5788f5 --- /dev/null +++ b/test/minimal.py @@ -0,0 +1,2 @@ +import minimal_ext + diff --git a/test/multi_arg_constructor.py b/test/multi_arg_constructor.py new file mode 100644 index 00000000..fb062e8a --- /dev/null +++ b/test/multi_arg_constructor.py @@ -0,0 +1,16 @@ +''' +>>> from multi_arg_constructor_ext import * +>>> a = A(1.0, 2, 3, 4, 5, 6, 7.0, 8.1, 9.3) +''' +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/object.py b/test/object.py new file mode 100644 index 00000000..02da5ce8 --- /dev/null +++ b/test/object.py @@ -0,0 +1,126 @@ +''' +>>> from object_ext import * +>>> def print1(x): +... print x +>>> call_object_3(print1) +3 +>>> message() +'hello, world!' +>>> number() +42 + +>>> test('hi') +1 +>>> test(None) +0 +>>> test_not('hi') +0 +>>> test_not(0) +1 + + Attributes + +>>> class X: pass +... +>>> x = X() + +>>> try: obj_getattr(x, 'foo') +... except AttributeError: pass +... else: print 'expected an exception' + +>>> obj_setattr(x, 'foo', 1) +>>> x.foo +1 +>>> obj_getattr(x, 'foo') +1 +>>> obj_const_getattr(x, 'foo') +1 +>>> obj_setattr42(x, 'foo') +>>> x.foo +42 +>>> obj_moveattr(x, 'foo', 'bar') +>>> x.bar +42 +>>> test_attr(x, 'foo') +1 +>>> test_not_attr(x, 'foo') +0 +>>> x.foo = None +>>> test_attr(x, 'foo') +0 +>>> test_not_attr(x, 'foo') +1 + + Items + +>>> d = {} +>>> obj_setitem(d, 'foo', 1) +>>> d['foo'] +1 +>>> obj_getitem(d, 'foo') +1 +>>> obj_const_getitem(d, 'foo') +1 +>>> obj_setitem42(d, 'foo') +>>> obj_getitem(d, 'foo') +42 +>>> d['foo'] +42 +>>> obj_moveitem(d, 'foo', 'bar') +>>> d['bar'] +42 +>>> obj_moveitem2(d, 'bar', d, 'baz') +>>> d['baz'] +42 +>>> test_item(d, 'foo') +1 +>>> test_not_item(d, 'foo') +0 +>>> d['foo'] = None +>>> test_item(d, 'foo') +0 +>>> test_not_item(d, 'foo') +1 + + Slices + +>>> assert check_string_slice() + + Operators + + +>>> assert check_binary_operators() + +>>> class X: pass +... +>>> assert check_inplace(range(3), X()) + + + Now make sure that object is actually managing reference counts + +>>> import weakref +>>> class Z: pass +... +>>> z = Z() +>>> def death(r): print 'death' +... +>>> r = weakref.ref(z, death) +>>> z.foo = 1 +>>> obj_getattr(z, 'foo') +1 +>>> del z +death +''' + +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/object_fail1.cpp b/test/object_fail1.cpp new file mode 100755 index 00000000..3b09e71d --- /dev/null +++ b/test/object_fail1.cpp @@ -0,0 +1,12 @@ +// 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 + +int f(boost::python::object const& x) +{ + x._("hello") = 1; + return 0; +} diff --git a/test/operators.py b/test/operators.py new file mode 100644 index 00000000..58ffb5eb --- /dev/null +++ b/test/operators.py @@ -0,0 +1,86 @@ +''' +>>> from operators_ext import * +>>> x = X(42) +>>> x.value() +42 +>>> y = x - X(5) +>>> y.value() +37 +>>> y = x - 4 +>>> y.value() +38 +>>> y = 3 - x +>>> y.value() +-39 +>>> (-y).value() +39 + +>>> abs(y).value() +39 + +>>> x < 10 +0 +>>> x < 43 +1 + +>>> 10 < x +1 +>>> 43 < x +0 + +>>> x < y +0 +>>> y < x +1 + + ------ +>>> x > 10 +1 +>>> x > 43 +0 + +>>> 10 > x +0 +>>> 43 > x +1 + +>>> x > y +1 +>>> y > x +0 + +>>> y = x - 5 +>>> x -= y +>>> x.value() +5 +>>> str(x) +'5' + +>>> z = Z(10) +>>> int(z) +10 +>>> float(z) +10.0 +>>> complex(z) +(10+0j) + +>>> pow(2,x) +32 +>>> pow(x,2).value() +25 +>>> pow(X(2),x).value() +32 +''' + +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/pointee.cpp b/test/pointee.cpp new file mode 100644 index 00000000..44836b05 --- /dev/null +++ b/test/pointee.cpp @@ -0,0 +1,35 @@ +// 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 + +struct A; + +int main() +{ + BOOST_STATIC_ASSERT( + (boost::is_same< + boost::python::pointee >::type + , char** + >::value)); + + BOOST_STATIC_ASSERT( + (boost::is_same< + boost::python::pointee >::type + , A>::value)); + +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + BOOST_STATIC_ASSERT( + (boost::is_same< + boost::python::pointee::type + , char + >::value)); +#endif + return 0; +} diff --git a/test/pointer_type_id_test.cpp b/test/pointer_type_id_test.cpp new file mode 100644 index 00000000..e3314e0b --- /dev/null +++ b/test/pointer_type_id_test.cpp @@ -0,0 +1,39 @@ +#include +#include +#include + +int main() +{ + using namespace boost::python::converter; + + boost::python::type_info x + = boost::python::type_id(); + + + assert(pointer_type_id() == x); + assert(pointer_type_id() == x); + assert(pointer_type_id() == x); + assert(pointer_type_id() == x); + + assert(pointer_type_id() == x); + assert(pointer_type_id() == x); + assert(pointer_type_id() == x); + assert(pointer_type_id() == x); + + assert(pointer_type_id() == x); + assert(pointer_type_id() == x); + assert(pointer_type_id() == x); + assert(pointer_type_id() == x); + + assert(pointer_type_id() == x); + assert(pointer_type_id() == x); + assert(pointer_type_id() == x); + assert(pointer_type_id() == x); + + assert(pointer_type_id() == x); + assert(pointer_type_id() == x); + assert(pointer_type_id() == x); + assert(pointer_type_id() == x); + + return 0; +} diff --git a/test/raw_pyobject_fail1.cpp b/test/raw_pyobject_fail1.cpp new file mode 100755 index 00000000..8fe73d68 --- /dev/null +++ b/test/raw_pyobject_fail1.cpp @@ -0,0 +1,12 @@ +// 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 + +int main() +{ + boost::python::converter::arg_to_python x(0); + return 0; +} diff --git a/test/raw_pyobject_fail2.cpp b/test/raw_pyobject_fail2.cpp new file mode 100755 index 00000000..7a0e79d9 --- /dev/null +++ b/test/raw_pyobject_fail2.cpp @@ -0,0 +1,14 @@ +// 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 + +struct X : PyObject {}; + +int main() +{ + boost::python::converter::arg_to_python x(0); + return 0; +} diff --git a/test/result.cpp b/test/result.cpp new file mode 100755 index 00000000..19b1cf41 --- /dev/null +++ b/test/result.cpp @@ -0,0 +1,112 @@ +// 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 + +using boost::python::detail::result; +using boost::type; + +void expect_int(type*) {} +void expect_string(type*) {} + +struct X {}; + +int main() +{ + // Test the usage which works for functions, member functions, and data members + expect_int( + result((int(*)())0) + ); + + expect_int( + result((int(*)(char))0) + ); + + expect_int( + result((int(X::*)())0) + ); + + expect_int( + result((int(X::*)(char))0) + ); + + expect_int( + result((int(X::*))0) + ); + + expect_string( + result((char*(*)())0) + ); + + expect_string( + result((char*(*)(char))0) + ); + + expect_string( + result((char*(X::*)())0) + ); + + expect_string( + result((char*(X::*)(char))0) + ); + + expect_string( + result((char*(X::*))0) + ); + + // Show that we can use the general version that works for + // AdaptableFunctions + expect_int( + result((int(*)())0,0) + ); + + expect_int( + result((int(*)(char))0,0) + ); + + expect_int( + result((int(X::*)())0,0) + ); + + expect_int( + result((int(X::*)(char))0,0) + ); + + expect_int( + result((int(X::*))0,0) + ); + + expect_int( + result(std::plus(),0) + ); + + expect_string( + result((char*(*)())0,0) + ); + + expect_string( + result((char*(*)(char))0,0) + ); + + expect_string( + result((char*(X::*)())0,0) + ); + + expect_string( + result((char*(X::*)(char))0,0) + ); + + expect_string( + result((char*(X::*))0,0) + ); + + expect_string( + result(std::plus(),0) + ); + + return 0; +} diff --git a/test/select_from_python_test.cpp b/test/select_from_python_test.cpp new file mode 100644 index 00000000..34018781 --- /dev/null +++ b/test/select_from_python_test.cpp @@ -0,0 +1,157 @@ +#include +#include +#include + +// gcc 2.95.x and MIPSpro 7.3.1.3 linker seem to demand this definition +#if ((defined(__GNUC__) && __GNUC__ < 3)) \ + || (defined(__sgi) && defined(__EDG_VERSION__) && (__EDG_VERSION__ == 238)) +namespace boost { namespace python { +BOOST_PYTHON_DECL bool handle_exception_impl(function0) +{ + return true; +} +}} +#endif + +int result; + +#define ASSERT_SAME(T1,T2) \ + if (!is_same< T1, T2 >::value) { \ + std::cout << "*********************\n"; \ + std::cout << python::type_id< T1 >() << " != " << python::type_id< T2 >() << "\n"; \ + std::cout << "*********************\n"; \ + result = 1; \ + } + +int main() +{ + using namespace boost::python::converter; + using namespace boost; + + + ASSERT_SAME( + select_arg_from_python::type, arg_rvalue_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, arg_rvalue_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, arg_rvalue_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, arg_rvalue_from_python + ); + + + + ASSERT_SAME( + select_arg_from_python::type, pointer_arg_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, pointer_arg_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, pointer_arg_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, pointer_arg_from_python + ); + + + + + ASSERT_SAME( + select_arg_from_python::type, reference_arg_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, arg_rvalue_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, reference_arg_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, reference_arg_from_python + ); + + + + ASSERT_SAME( + select_arg_from_python::type, reference_arg_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, reference_arg_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, reference_arg_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, reference_arg_from_python + ); + + + + ASSERT_SAME( + select_arg_from_python::type, pointer_cref_arg_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, pointer_cref_arg_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, pointer_cref_arg_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, pointer_cref_arg_from_python + ); + + + + ASSERT_SAME( + select_arg_from_python::type, reference_arg_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, reference_arg_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, reference_arg_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, reference_arg_from_python + ); + + + + ASSERT_SAME( + select_arg_from_python::type, reference_arg_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, reference_arg_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, reference_arg_from_python + ); + + ASSERT_SAME( + select_arg_from_python::type, reference_arg_from_python + ); + return result; +} diff --git a/test/upcast.cpp b/test/upcast.cpp new file mode 100755 index 00000000..b02d2534 --- /dev/null +++ b/test/upcast.cpp @@ -0,0 +1,19 @@ +// 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 + +struct X { long x; }; +struct Y : X, PyObject {}; + +int main() +{ + PyTypeObject o; + Y y; + assert(&boost::python::upcast(&o)->ob_refcnt == &o.ob_refcnt); + assert(&boost::python::upcast(&y)->ob_refcnt == &y.ob_refcnt); + return 0; +} +