2
0
mirror of https://github.com/boostorg/python.git synced 2026-01-23 05:42:30 +00:00

object operator support

[SVN r14168]
This commit is contained in:
Dave Abrahams
2002-06-18 13:49:09 +00:00
parent 87bda9e124
commit 66f2cd81a8
15 changed files with 771 additions and 432 deletions

View File

@@ -28,6 +28,7 @@ dll bpl
src/converter/callback.cpp
src/object/iterator.cpp
src/object_protocol.cpp
src/object_operators.cpp
:
$(BOOST_PYTHON_V2_PROPERTIES)
<define>BOOST_PYTHON_SOURCE

View File

@@ -12,11 +12,11 @@
# include <boost/mpl/select_type.hpp>
# include <boost/python/detail/indirect_traits.hpp>
namespace boost { namespace python {
namespace boost { namespace python { namespace api {
class object;
}}
}}}
namespace boost { namespace python { namespace converter {
@@ -142,7 +142,7 @@ inline T* get_managed_object(python::detail::borrowed<T> const volatile* p)
// forward declaration needed because name lookup is bound by the
// definition context.
PyObject* get_managed_object(python::object const&);
PyObject* get_managed_object(python::api::object const&);
}}} // namespace boost::python::converter

View File

@@ -9,6 +9,7 @@
# include <boost/python/object_core.hpp>
# include <boost/python/object_attributes.hpp>
# include <boost/python/object_items.hpp>
# include <boost/python/object_operators.hpp>
namespace boost { namespace python {

View File

@@ -10,51 +10,48 @@
# include <boost/python/object_core.hpp>
# include <boost/python/object_protocol.hpp>
namespace boost { namespace python {
namespace boost { namespace python { namespace api {
namespace detail
struct const_attribute_policies
{
struct const_attribute_policies
{
static object get(object const& target, object const& key);
};
static object get(object const& target, object const& key);
};
struct attribute_policies : const_attribute_policies
{
static object const& set(object const& target, object const& key, object const& value);
};
}
struct attribute_policies : const_attribute_policies
{
static object const& set(object const& target, object const& key, object const& value);
};
//
// implementation
//
inline object_attribute object::_(char const* name)
inline object_attribute object::attr(char const* name)
{
return object_attribute(*this, object(name));
}
inline const_object_attribute object::_(char const* name) const
inline const_object_attribute object::attr(char const* name) const
{
return const_object_attribute(*this, object(name));
}
namespace detail
{
inline object const_attribute_policies::get(object const& target, object const& key)
{
return python::getattr(target, key);
}
inline object const& attribute_policies::set(
object const& target
, object const& key
, object const& value)
{
python::setattr(target, key, value);
return value;
}
inline object const_attribute_policies::get(object const& target, object const& key)
{
return python::getattr(target, key);
}
inline object const& attribute_policies::set(
object const& target
, object const& key
, object const& value)
{
python::setattr(target, key, value);
return value;
}
} // namespace api
namespace converter
{
// These specializations are a lie; the proxies do not directly
@@ -64,13 +61,13 @@ namespace converter
// actually managing the object during the duration of the
// conversion.
template <>
struct is_object_manager<object_attribute>
struct is_object_manager<api::object_attribute>
{
BOOST_STATIC_CONSTANT(bool, value = true);
};
template <>
struct is_object_manager<const_object_attribute>
struct is_object_manager<api::const_object_attribute>
{
BOOST_STATIC_CONSTANT(bool, value = true);
};

View File

@@ -12,122 +12,147 @@
namespace boost { namespace python {
// Put this in an inner namespace so that the generalized operators won't take over
namespace api
{
// This file contains the definition of the object class and enough to
// construct/copy it, but not enough to do operations like
// attribute/item access or addition.
template <class Policies> class proxy;
namespace detail
{
template <class Policies> class proxy;
struct const_attribute_policies;
struct attribute_policies;
struct const_item_policies;
struct item_policies;
}
typedef proxy<detail::const_attribute_policies> const_object_attribute;
typedef proxy<detail::attribute_policies> object_attribute;
typedef proxy<detail::const_item_policies> const_object_item;
typedef proxy<detail::item_policies> object_item;
class object
{
typedef proxy<const_attribute_policies> const_object_attribute;
typedef proxy<attribute_policies> object_attribute;
typedef proxy<const_item_policies> const_object_item;
typedef proxy<item_policies> object_item;
// A way to turn a conrete type T into a type dependent on U. This
// keeps conforming compilers from complaining about returning an
// incomplete T from a template member function (which must be
// defined in the class body to keep MSVC happy).
template <class T, class U>
struct dependent
{
typedef T type;
};
class object
{
# if !defined(BOOST_MSVC) || BOOST_MSVC > 1200
typedef object const& self_cref;
typedef object const& self_cref;
# else
typedef object self_cref;
typedef object self_cref;
# endif
public:
public:
# ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
// copy constructor without NULL checking, for efficiency
object(object const&);
// copy constructor without NULL checking, for efficiency
object(object const&);
# endif
// explicit conversion from any C++ object to Python
template <class T>
explicit object(T const& x)
: m_ptr(
python::borrowed(
python::allow_null( // null check is already done
converter::arg_to_python<T>(x).get())
)
)
{
}
// explicit conversion from any C++ object to Python
template <class T>
explicit object(T const& x)
: m_ptr(
python::borrowed(
python::allow_null( // null check is already done
converter::arg_to_python<T>(x).get())
)
)
{
}
// capture this case explicitly to handle string
// literals. Otherwise, they get deduced as char[N]const& above
// and conversion fails at runtime.
explicit object(char const* x)
: m_ptr(
python::borrowed(
python::allow_null( // null check is already done
converter::arg_to_python<char const*>(x).get())
)
)
{
// capture this case explicitly to handle string
// literals. Otherwise, they get deduced as char[N]const& above
// and conversion fails at runtime.
explicit object(char const* x)
: m_ptr(
python::borrowed(
python::allow_null( // null check is already done
converter::arg_to_python<char const*>(x).get())
)
)
{
}
}
// Throw error_already_set() if the handle is null.
explicit object(handle<> const&);
// Throw error_already_set() if the handle is null.
explicit object(handle<> const&);
// Attribute access via x._("attribute_name")
const_object_attribute _(char const*) const;
object_attribute _(char const*);
// Attribute access via x.attr("attribute_name")
const_object_attribute attr(char const*) const;
object_attribute attr(char const*);
object operator()() const
{
return object(call<handle<> >(m_ptr.get()));
}
object operator()() const
{
return object(call<handle<> >(m_ptr.get()));
}
# ifndef BOOST_PYTHON_GENERATE_CODE
# include <boost/python/preprocessed/object_call.hpp>
# endif
# define BOOST_PYTHON_OBJECT_CALL(nargs,ignored) \
template <BOOST_PP_ENUM_PARAMS(nargs, class A)> \
object operator()(BOOST_PYTHON_ENUM_PARAMS2(nargs, (A,const& a))) const \
{ \
return object(call<handle<> >(&**this, BOOST_PP_ENUM_PARAMS(nargs, a))); \
}
template <BOOST_PP_ENUM_PARAMS(nargs, class A)> \
object operator()(BOOST_PYTHON_ENUM_PARAMS2(nargs, (A,const& a))) const \
{ \
return object(call<handle<> >(&**this, BOOST_PP_ENUM_PARAMS(nargs, a))); \
}
BOOST_PYTHON_REPEAT_ARITY_2ND(BOOST_PYTHON_OBJECT_CALL, ignored)
BOOST_PYTHON_REPEAT_ARITY_2ND(BOOST_PYTHON_OBJECT_CALL, ignored)
// truth value testing
typedef handle<> (object::*bool_type);
operator bool_type() const;
bool operator!() const; // needed for vc6
// truth value testing
typedef handle<> (object::*bool_type);
operator bool_type() const;
bool operator!() const; // needed for vc6
// item access
const_object_item operator[](self_cref) const;
object_item operator[](self_cref);
// item access
const_object_item operator[](self_cref) const;
object_item operator[](self_cref);
template <class T>
const_object_item operator[](T const& key) const
{
return (*this)[object(key)];
}
template <class T>
# if BOOST_MSVC != 1300
typename dependent<const_object_item,T>::type
# else
const_object_item
# endif
operator[](T const& key) const
{
return (*this)[object(key)];
}
template <class T>
object_item operator[](T const& key)
{
return (*this)[object(key)];
}
template <class T>
# if BOOST_MSVC != 1300
typename dependent<object_item,T>::type
# else
object_item
# endif
operator[](T const& key)
{
return (*this)[object(key)];
}
// Underlying object access
PyObject* operator->() const;
PyObject& operator*() const;
// Underlying object access
PyObject* operator->() const;
PyObject& operator*() const;
public: // implementation detail -- for internal use only
object(null_ok<detail::borrowed<PyObject> >*);
object(detail::borrowed<null_ok<PyObject> >*);
object(detail::borrowed<PyObject>*);
class new_pyobject_reference;
object(new_pyobject_reference*);
public: // implementation detail -- for internal use only
object(null_ok<detail::borrowed<PyObject> >*);
object(detail::borrowed<null_ok<PyObject> >*);
object(detail::borrowed<PyObject>*);
class new_pyobject_reference;
object(new_pyobject_reference*);
private:
handle<> m_ptr;
};
private:
handle<> m_ptr;
};
}
using api::object;
//
// Converter Specializations

View File

@@ -10,20 +10,17 @@
# include <boost/python/object_core.hpp>
# include <boost/python/object_protocol.hpp>
namespace boost { namespace python {
namespace boost { namespace python { namespace api {
namespace detail
struct const_item_policies
{
struct const_item_policies
{
static object get(object const& target, object const& key);
};
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);
};
}
struct item_policies : const_item_policies
{
static object const& set(object const& target, object const& key, object const& value);
};
//
// implementation
@@ -38,23 +35,23 @@ inline const_object_item object::operator[](object::self_cref key) const
return const_object_item(*this, key);
}
namespace detail
{
inline object const_item_policies::get(object const& target, object const& key)
{
return python::getitem(target, key);
}
inline object const& item_policies::set(
object const& target
, object const& key
, object const& value)
{
python::setitem(target, key, value);
return value;
}
inline object const_item_policies::get(object const& target, object const& key)
{
return python::getitem(target, key);
}
inline object const& item_policies::set(
object const& target
, object const& key
, object const& value)
{
python::setitem(target, key, value);
return value;
}
} // namespace api
namespace converter
{
// These specializations are a lie; the proxies do not directly
@@ -64,13 +61,13 @@ namespace converter
// actually managing the object during the duration of the
// conversion.
template <>
struct is_object_manager<object_item>
struct is_object_manager<api::object_item>
{
BOOST_STATIC_CONSTANT(bool, value = true);
};
template <>
struct is_object_manager<const_object_item>
struct is_object_manager<api::const_object_item>
{
BOOST_STATIC_CONSTANT(bool, value = true);
};

View File

@@ -0,0 +1,68 @@
// 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_OPERATORS_DWA2002617_HPP
# define OBJECT_OPERATORS_DWA2002617_HPP
# include <boost/python/object_core.hpp>
namespace boost { namespace python { namespace api {
# define BOOST_PYTHON_COMPARE_OP(op, opid) \
template <class L, class R> \
bool operator op(L const& l, R const& r) \
{ \
return PyObject_RichCompareBool(&*object(l), &*object(r), opid); \
}
BOOST_PYTHON_COMPARE_OP(>, Py_GT)
BOOST_PYTHON_COMPARE_OP(>=, Py_GE)
BOOST_PYTHON_COMPARE_OP(<, Py_LT)
BOOST_PYTHON_COMPARE_OP(<=, Py_LE)
BOOST_PYTHON_COMPARE_OP(==, Py_EQ)
BOOST_PYTHON_COMPARE_OP(!=, Py_NE)
# undef BOOST_PYTHON_COMPARE_OP
# define BOOST_PYTHON_BINARY_OPERATOR(op) \
BOOST_PYTHON_DECL object operator op(object const& l, object const& r); \
template <class L, class R> \
object operator op(L const& l, R const& r) \
{ \
return object(l) op object(r); \
}
BOOST_PYTHON_BINARY_OPERATOR(+)
BOOST_PYTHON_BINARY_OPERATOR(-)
BOOST_PYTHON_BINARY_OPERATOR(*)
BOOST_PYTHON_BINARY_OPERATOR(/)
BOOST_PYTHON_BINARY_OPERATOR(%)
BOOST_PYTHON_BINARY_OPERATOR(<<)
BOOST_PYTHON_BINARY_OPERATOR(>>)
BOOST_PYTHON_BINARY_OPERATOR(&)
BOOST_PYTHON_BINARY_OPERATOR(^)
BOOST_PYTHON_BINARY_OPERATOR(|)
# undef BOOST_PYTHON_BINARY_OPERATOR
# define BOOST_PYTHON_INPLACE_OPERATOR(op) \
BOOST_PYTHON_DECL object& operator op(object& l, object const& r); \
template <class R> \
object& operator op(object& l, R const& r) \
{ \
return l op object(r); \
}
BOOST_PYTHON_INPLACE_OPERATOR(+=)
BOOST_PYTHON_INPLACE_OPERATOR(-=)
BOOST_PYTHON_INPLACE_OPERATOR(*=)
BOOST_PYTHON_INPLACE_OPERATOR(/=)
BOOST_PYTHON_INPLACE_OPERATOR(%=)
BOOST_PYTHON_INPLACE_OPERATOR(<<=)
BOOST_PYTHON_INPLACE_OPERATOR(>>=)
BOOST_PYTHON_INPLACE_OPERATOR(&=)
BOOST_PYTHON_INPLACE_OPERATOR(^=)
BOOST_PYTHON_INPLACE_OPERATOR(|=)
# undef BOOST_PYTHON_INPLACE_OPERATOR
}}} // namespace boost::python
#endif // OBJECT_OPERATORS_DWA2002617_HPP

View File

@@ -8,12 +8,20 @@
namespace boost { namespace python {
class object;
namespace api
{
class object;
BOOST_PYTHON_DECL object getattr(object const& target, object const& key);
BOOST_PYTHON_DECL void setattr(object const& target, object const& key, object const& value);
BOOST_PYTHON_DECL object getitem(object const& target, object const& key);
BOOST_PYTHON_DECL void setitem(object const& target, object const& key, object const& value);
BOOST_PYTHON_DECL object getattr(object const& target, object const& key);
BOOST_PYTHON_DECL void setattr(object const& target, object const& key, object const& value);
BOOST_PYTHON_DECL object getitem(object const& target, object const& key);
BOOST_PYTHON_DECL void setitem(object const& target, object const& key, object const& value);
}
using api::getattr;
using api::setattr;
using api::getitem;
using api::setitem;
}} // namespace boost::python

View File

@@ -196,9 +196,12 @@ BOOST_PYTHON_BINARY_OPERATOR(lt, gt, <)
BOOST_PYTHON_BINARY_OPERATOR(le, ge, <=)
BOOST_PYTHON_BINARY_OPERATOR(eq, eq, ==)
BOOST_PYTHON_BINARY_OPERATOR(ne, ne, !=)
# undef BOOST_PYTHON_BINARY_OPERATOR
// pow isn't an operator in C++; handle it specially.
BOOST_PYTHON_BINARY_OPERATION(pow, rpow, pow(l,r))
# undef BOOST_PYTHON_BINARY_OPERATION
namespace self_ns
{
# ifndef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP
@@ -300,6 +303,7 @@ namespace self_ns \
return detail::operator_<detail::op_##id>(); \
} \
}
# undef BOOST_PYTHON_INPLACE_OPERATOR
BOOST_PYTHON_UNARY_OPERATOR(neg, -, operator-)
BOOST_PYTHON_UNARY_OPERATOR(pos, +, operator+)
@@ -310,7 +314,8 @@ BOOST_PYTHON_UNARY_OPERATOR(long, PyLong_FromLong, long_)
BOOST_PYTHON_UNARY_OPERATOR(float, double, float_)
BOOST_PYTHON_UNARY_OPERATOR(complex, std::complex<double>, complex_)
BOOST_PYTHON_UNARY_OPERATOR(str, lexical_cast<std::string>, str)
# undef BOOST_PYTHON_UNARY_OPERATOR
}} // namespace boost::python
# ifdef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP

View File

@@ -6,10 +6,9 @@
#ifndef PROXY_DWA2002615_HPP
# define PROXY_DWA2002615_HPP
# include <boost/python/object_core.hpp>
# include <boost/python/object_operators.hpp>
namespace boost { namespace python {
class object;
namespace boost { namespace python { namespace api {
template <class Policies>
class proxy
@@ -23,37 +22,15 @@ class proxy
operator object() const;
// to support a[b] = c[d]
proxy& operator=(copy_ctor_self);
proxy const& operator=(copy_ctor_self) const;
template <class T>
inline proxy& operator=(T const& rhs)
inline proxy const& operator=(T const& rhs) const
{
Policies::set(m_target, m_key, python::object(rhs));
return *this;
}
# define BOOST_PYTHON_PROXY_ASSIGN_DECL(op) \
object operator op (object const&); \
\
template <class T> \
object operator op (T const& rhs) \
{ \
return *this op python::object(rhs); \
}
// BOOST_PYTHON_PROXY_ASSIGN_DECL(=)
BOOST_PYTHON_PROXY_ASSIGN_DECL(+=)
BOOST_PYTHON_PROXY_ASSIGN_DECL(-=)
BOOST_PYTHON_PROXY_ASSIGN_DECL(*=)
BOOST_PYTHON_PROXY_ASSIGN_DECL(/=)
BOOST_PYTHON_PROXY_ASSIGN_DECL(%=)
BOOST_PYTHON_PROXY_ASSIGN_DECL(<<=)
BOOST_PYTHON_PROXY_ASSIGN_DECL(>>=)
BOOST_PYTHON_PROXY_ASSIGN_DECL(&=)
BOOST_PYTHON_PROXY_ASSIGN_DECL(^=)
BOOST_PYTHON_PROXY_ASSIGN_DECL(|=)
# undef BOOST_PYTHON_PROXY_ASSIGN_DECL
// truth value testing
operator object::bool_type() const;
bool operator!() const; // needed for vc6
@@ -84,33 +61,29 @@ inline proxy<Policies>::operator object() const
// to support a[b] = c[d]
template <class Policies>
inline proxy<Policies>& proxy<Policies>::operator=(typename proxy::copy_ctor_self rhs)
inline proxy<Policies> const& proxy<Policies>::operator=(typename proxy::copy_ctor_self rhs) const
{
return *this = python::object(rhs);
}
# define BOOST_PYTHON_PROXY_ASSIGN_DEF(op) \
template <class Policies> \
inline object proxy<Policies>::operator op(object const& other) \
{ \
return Policies::set( \
m_target, m_key \
, Policies::get(m_target,m_key) op other); \
}
BOOST_PYTHON_PROXY_ASSIGN_DEF(+=)
BOOST_PYTHON_PROXY_ASSIGN_DEF(-=)
BOOST_PYTHON_PROXY_ASSIGN_DEF(*=)
BOOST_PYTHON_PROXY_ASSIGN_DEF(/=)
BOOST_PYTHON_PROXY_ASSIGN_DEF(%=)
BOOST_PYTHON_PROXY_ASSIGN_DEF(<<=)
BOOST_PYTHON_PROXY_ASSIGN_DEF(>>=)
BOOST_PYTHON_PROXY_ASSIGN_DEF(&=)
BOOST_PYTHON_PROXY_ASSIGN_DEF(^=)
BOOST_PYTHON_PROXY_ASSIGN_DEF(|=)
# undef BOOST_PYTHON_PROXY_ASSIGN_DEF
# define BOOST_PYTHON_PROXY_INPLACE(op) \
template <class Policies, class R> \
proxy<Policies> const& operator op(proxy<Policies> const& lhs, R const& other) \
{ \
object old(lhs); \
return lhs = (old op other); \
}
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 <class Policies>
inline proxy<Policies>::operator object::bool_type() const
@@ -124,6 +97,6 @@ inline bool proxy<Policies>::operator!() const
return !python::object(*this);
}
}} // namespace boost::python
}}} // namespace boost::python::api
#endif // PROXY_DWA2002615_HPP

51
src/object_operators.cpp Normal file
View File

@@ -0,0 +1,51 @@
// 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 <boost/python/object_operators.hpp>
namespace boost { namespace python { namespace api {
#define BOOST_PYTHON_BINARY_OPERATOR(op, name) \
BOOST_PYTHON_DECL object operator op(object const& l, object const& r) \
{ \
return object( \
(object::new_pyobject_reference*) \
PyNumber_##name(&*l, &*r)); \
}
BOOST_PYTHON_BINARY_OPERATOR(+, Add)
BOOST_PYTHON_BINARY_OPERATOR(-, Subtract)
BOOST_PYTHON_BINARY_OPERATOR(*, Multiply)
BOOST_PYTHON_BINARY_OPERATOR(/, Divide)
BOOST_PYTHON_BINARY_OPERATOR(%, Remainder)
BOOST_PYTHON_BINARY_OPERATOR(<<, Lshift)
BOOST_PYTHON_BINARY_OPERATOR(>>, Rshift)
BOOST_PYTHON_BINARY_OPERATOR(&, And)
BOOST_PYTHON_BINARY_OPERATOR(^, Xor)
BOOST_PYTHON_BINARY_OPERATOR(|, Or)
#undef BOOST_PYTHON_BINARY_OPERATOR
#define BOOST_PYTHON_INPLACE_OPERATOR(op, name) \
BOOST_PYTHON_DECL object& operator op##=(object& l, object const& r) \
{ \
return l = object( \
(object::new_pyobject_reference*) \
PyNumber_InPlace##name(&*l, &*r)); \
}
BOOST_PYTHON_INPLACE_OPERATOR(+, Add)
BOOST_PYTHON_INPLACE_OPERATOR(-, Subtract)
BOOST_PYTHON_INPLACE_OPERATOR(*, Multiply)
BOOST_PYTHON_INPLACE_OPERATOR(/, Divide)
BOOST_PYTHON_INPLACE_OPERATOR(%, Remainder)
BOOST_PYTHON_INPLACE_OPERATOR(<<, Lshift)
BOOST_PYTHON_INPLACE_OPERATOR(>>, Rshift)
BOOST_PYTHON_INPLACE_OPERATOR(&, And)
BOOST_PYTHON_INPLACE_OPERATOR(^, Xor)
BOOST_PYTHON_INPLACE_OPERATOR(|, Or)
#undef BOOST_PYTHON_INPLACE_OPERATOR
}}} // namespace boost::python

View File

@@ -8,28 +8,28 @@
#include <boost/python/errors.hpp>
#include <boost/python/object.hpp>
namespace boost { namespace python {
namespace boost { namespace python { namespace api {
object getattr(object const& target, object const& key)
BOOST_PYTHON_DECL object getattr(object const& target, object const& key)
{
return object((object::new_pyobject_reference*)PyObject_GetAttr(&*target, &*key));
}
void setattr(object const& target, object const& key, object const& value)
BOOST_PYTHON_DECL void setattr(object const& target, object const& key, object const& value)
{
if (PyObject_SetAttr(&*target, &*key, &*value) == -1)
throw_error_already_set();
}
object getitem(object const& target, object const& key)
BOOST_PYTHON_DECL object getitem(object const& target, object const& key)
{
return object((object::new_pyobject_reference*)PyObject_GetItem(&*target, &*key));
}
void setitem(object const& target, object const& key, object const& value)
BOOST_PYTHON_DECL void setitem(object const& target, object const& key, object const& value)
{
if (PyObject_SetItem(&*target, &*key, &*value) == -1)
throw_error_already_set();
}
}} // namespace boost::python
}}} // namespace boost::python::api

View File

@@ -1,10 +1,10 @@
# Module doctest version 0.9.4
# Released to the public domain 27-Mar-1999,
# by Tim Peters (tim_one@email.msn.com).
# 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_builder doctest -- a framework for running examples in docstrings.
"""Module doctest -- a framework for running examples in docstrings.
NORMAL USAGE
@@ -20,16 +20,16 @@ if __name__ == "__main__":
Then running the module as a script will cause the examples in the
docstrings to get executed and verified:
python M.python
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.".
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.python -v
python M.py -v
and a detailed report of all examples tried is printed to stdout, along
with assorted summaries at the end.
@@ -48,71 +48,52 @@ WHICH DOCSTRINGS ARE EXAMINED?
+ M.__doc__.
+ f.__doc__ for all functions f in M.__dict__.values(), except those
with private names.
with private names and those defined in other modules.
+ C.__doc__ for all classes C in M.__dict__.values(), except those with
private names.
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__
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
<name of M>.__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.
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.
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 obj).
Warning: imports can cause trouble; e.g., if you do
from XYZ import XYZclass
then XYZclass is a name in M.__dict__ too, and doctest has no way to
know that XYZclass wasn't *defined* in M. So it may try to execute the
examples in XYZclass's docstring, and those in turn may require a
different set of globals to work correctly. I prefer to do "import *"-
friendly imports, a la
import XYY
_XYZclass = XYZ.XYZclass
del XYZ
and then the leading underscore stops testmod from going nuts. You may
prefer the method in the next section.
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.
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.
"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.python I use for the world's most elaborate Rational/
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
@@ -141,11 +122,10 @@ 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.
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
@@ -157,10 +137,10 @@ WHAT ABOUT EXCEPTIONS?
No problem, as long as the only output generated by the example is the
traceback itself. For example:
>>> 1/0
Traceback (innermost last):
>>> [1, 2, 3].remove(42)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ZeroDivisionError: integer division or modulo
ValueError: list.remove(x): x not in list
>>>
Note that only the exception type and value are compared (specifically,
@@ -172,22 +152,22 @@ ADVANCED USAGE
doctest.testmod() captures the testing policy I find most useful most
often. You may want other policies.
testmod() actually creates a local obj of class doctest.Tester,
runs appropriate methods of that class, and merges the results into
global Tester obj doctest.master.
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
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).
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
@@ -205,23 +185,22 @@ you think a tab means).
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.
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.
+ 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 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" + \\
@@ -243,11 +222,11 @@ 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/0
Trying: [1, 2, 3].remove(42)
Expecting:
Traceback (innermost last):
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ZeroDivisionError: integer division or modulo
ValueError: list.remove(x): x not in list
ok
Trying: x = 12
Expecting: nothing
@@ -279,7 +258,7 @@ ok
8 tests in doctest
6 tests in doctest.Tester
10 tests in doctest.Tester.merge
7 tests in doctest.Tester.rundict
14 tests in doctest.Tester.rundict
3 tests in doctest.Tester.rundoc
3 tests in doctest.Tester.runstring
2 tests in doctest.__test__._TestClass
@@ -288,79 +267,19 @@ ok
1 tests in doctest.__test__._TestClass.square
2 tests in doctest.__test__.string
7 tests in doctest.is_private
53 tests in 17 items.
53 passed and 0 failed.
60 tests in 17 items.
60 passed and 0 failed.
Test passed.
"""
# 0,0,1 06-Mar-1999
# initial version posted
# 0,0,2 06-Mar-1999
# loosened parsing:
# cater to stinkin' tabs
# don't insist on a blank after PS2 prefix
# so trailing "... " line from a compound stmt no longer
# breaks if the file gets whitespace-trimmed
# better error msgs for inconsistent leading whitespace
# 0,9,1 08-Mar-1999
# exposed the Tester class and added client methods
# plus docstring examples of their use (eww - head-twisting!)
# fixed logic error in reporting total # of tests & failures
# added __test__ support to testmod (a pale reflection of Christian
# Tismer's vision ...)
# removed the "deep" argument; fiddle __test__ instead
# simplified endcase logic for extracting tests, and running them.
# before, if no output was expected but some was produced
# anyway via an eval'ed result, the discrepancy wasn't caught
# made TestClass private and used __test__ to get at it
# many doc updates
# speed _SpoofOut for long expected outputs
# 0,9,2 09-Mar-1999
# throw out comments from examples, enabling use of the much simpler
# exec compile(... "single") ...
# for simulating the runtime; that barfs on comment-only lines
# used the traceback module to do a much better job of reporting
# exceptions
# run __doc__ values thru str(), "just in case"
# privateness of names now determined by an overridable "isprivate"
# function
# by default a name now considered to be private iff it begins with
# an underscore but doesn't both begin & end with two of 'em; so
# e.g. class_t.__init__ etc are searched now -- as they always
# should have been
# 0,9,3 18-Mar-1999
# added .flush stub to _SpoofOut (JPython buglet diagnosed by
# Hugh Emberson)
# repaired ridiculous docs about backslashes in examples
# minor internal changes
# changed source to Unix line-end conventions
# moved __test__ logic into new Tester.run__test__ method
# 0,9,4 27-Mar-1999
# report item name and line # in failing examples
# 0,9,5 29-Jun-1999
# allow straightforward exceptions in examples - thanks to Mark Hammond!
# 0,9,5,1 31-Mar-2000
# break cyclic references to functions which are defined in docstrings,
# avoiding cyclic trash
# 0,9,5,2 11-Apr-2000
# made module argument to testmod optional; it runs testmod on the __main__
# module in that case.
__all__ = [
'testmod',
'run_docstring_examples',
'is_private',
'Tester',
]
__version__ = 0, 9, 5
import types
_FunctionType = types.FunctionType
_ClassType = types.ClassType
_ModuleType = types.ModuleType
_StringType = types.StringType
del types
import string
_string_find = string.find
_string_join = string.join
_string_split = string.split
_string_rindex = string.rindex
del string
import __future__
import re
PS1 = ">>>"
@@ -371,6 +290,13 @@ _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
@@ -382,7 +308,7 @@ def _extract_examples(s):
isPS1, isPS2 = _isPS1, _isPS2
isEmpty, isComment = _isEmpty, _isComment
examples = []
lines = _string_split(s, "\n")
lines = s.split("\n")
i, n = 0, len(lines)
while i < n:
line = lines[i]
@@ -420,7 +346,7 @@ def _extract_examples(s):
# get rid of useless null line from trailing empty "..."
if source[-1] == "":
del source[-1]
source = _string_join(source, "\n") + "\n"
source = "\n".join(source) + "\n"
# suck up response
if isPS1(line) or isEmpty(line):
expect = ""
@@ -435,7 +361,7 @@ def _extract_examples(s):
line = lines[i]
if isPS1(line) or isEmpty(line):
break
expect = _string_join(expect, "\n") + "\n"
expect = "\n".join(expect) + "\n"
examples.append( (source, expect, lineno) )
return examples
@@ -447,9 +373,21 @@ class _SpoofOut:
def write(self, s):
self.buf.append(s)
def get(self):
return _string_join(self.buf, "")
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
@@ -462,7 +400,7 @@ def _tag_out(printer, *tag_msg_pairs):
printer(tag + ":")
msg_has_nl = msg[-1:] == "\n"
msg_has_two_nl = msg_has_nl and \
_string_find(msg, "\n") < len(msg) - 1
msg.find("\n") < len(msg) - 1
if len(tag) + len(msg) < 76 and not msg_has_two_nl:
printer(" ")
else:
@@ -472,10 +410,11 @@ def _tag_out(printer, *tag_msg_pairs):
printer("\n")
# Run list of examples, in context globs. "out" can be used to display
# stuff to "the real" stdout, and fakeout is an obj of _SpoofOut
# 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):
def _run_examples_inner(out, fakeout, examples, globs, verbose, name,
compileflags):
import sys, traceback
OK, BOOM, FAIL = range(3)
NADA = "nothing"
@@ -487,17 +426,19 @@ def _run_examples_inner(out, fakeout, examples, globs, verbose, name):
("Expecting", want or NADA))
fakeout.clear()
try:
exec compile(source, "<string>", "single") in globs
exec compile(source, "<string>", "single",
compileflags, 1) in globs
got = fakeout.get()
state = OK
except:
# See whether the exception was expected.
if _string_find(want, "Traceback (innermost last):\n") == 0:
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 = _string_split(want, '\n')[-2] + '\n'
exc_type, exc_val, exc_tb = sys.exc_info()
got = traceback.format_exception_only(exc_type, exc_val)[0]
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
@@ -522,25 +463,49 @@ def _run_examples_inner(out, fakeout, examples, globs, verbose, name):
else:
assert state == BOOM
_tag_out(out, ("Exception raised", stderr.get()))
return failures, len(examples)
# Run list of examples, in context globs. Return (#failures, #tries).
# Get the future-flags associated with the future features that have been
# imported into globs.
def _run_examples(examples, globs, verbose, name):
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)
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"):
def run_docstring_examples(f, globs, verbose=0, name="NoName",
compileflags=None):
"""f, globs, verbose=0, name="NoName" -> run examples from f.__doc__.
Use dict globs as the globals for execution.
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
@@ -562,7 +527,9 @@ def run_docstring_examples(f, globs, verbose=0, name="NoName"):
e = _extract_examples(doc)
if not e:
return 0, 0
return _run_examples(e, globs, verbose, name)
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".
@@ -591,8 +558,17 @@ def is_private(prefix, base):
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_t Tester -- runs docstring examples and accumulates stats.
"""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
@@ -607,9 +583,10 @@ Methods:
Search object.__doc__ for examples to run; use name (or
object.__name__) for logging. Return (#failures, #tries).
rundict(d, name)
rundict(d, name, module=None)
Search for examples in docstrings in all of d.values(); use name
for logging. Return (#failures, #tries).
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).
@@ -619,7 +596,7 @@ Methods:
(#failures, #tries).
merge(other)
Merge in the test results from Tester obj "other".
Merge in the test results from Tester instance "other".
>>> from doctest import Tester
>>> t = Tester(globs={'x': 42}, verbose=0)
@@ -637,6 +614,7 @@ Got: 84
>>> 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.
@@ -644,6 +622,7 @@ Got: 84
>>> 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.
@@ -679,7 +658,7 @@ 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 type(mod) is not _ModuleType:
if mod is not None and not _ismodule(mod):
raise TypeError("Tester.__init__: mod must be a module; " +
`mod`)
if globs is None:
@@ -697,6 +676,8 @@ see its docs for details.
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.
@@ -728,9 +709,8 @@ see its docs for details.
f = t = 0
e = _extract_examples(s)
if e:
globs = self.globs.copy()
f, t = _run_examples(e, globs, self.verbose, name)
globs.clear() # DWA - break cyclic references to functions defined in docstrings
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)
@@ -768,51 +748,133 @@ see its docs for details.
"when object.__name__ doesn't exist; " + `object`)
if self.verbose:
print "Running", name + ".__doc__"
globs = self.globs.copy()
f, t = run_docstring_examples(object, globs,
self.verbose, name)
globs.clear() # DWA - break cyclic references to functions defined in docstrings
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 type(object) is _ClassType:
f2, t2 = self.rundict(object.__dict__, name)
f = f + f2
t = t + t2
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):
def rundict(self, d, name, module=None):
"""
d. name -> search for docstring examples in all of d.values().
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.
"isprivate" argument. If module is specified, functions and
classes that are not defined in module are excluded.
Return aggregate (#failures, #examples).
>>> def _f():
... '''>>> assert 1 == 1
... '''
>>> def g():
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
... '''
>>> d = {"_f": _f, "g": g}
... 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(d, "rundict_test") # _f is skipped
(0, 1)
>>> 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(d, "rundict_test_pvt") # both are searched
(0, 2)
>>> 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
for thisname, value in d.items():
if type(value) in (_FunctionType, _ClassType):
# 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
@@ -830,11 +892,16 @@ see its docs for details.
savepvt = self.isprivate
try:
self.isprivate = lambda *args: 0
for k, v in d.items():
# 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) is _StringType:
if type(v) in _StringTypes:
f, t = self.runstring(v, thisname)
elif type(v) in (_FunctionType, _ClassType):
elif _isfunction(v) or _isclass(v):
f, t = self.rundoc(v, thisname)
else:
raise TypeError("Tester.run__test__: values in "
@@ -885,6 +952,7 @@ see its docs for details.
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:
@@ -900,7 +968,7 @@ see its docs for details.
def merge(self, other):
"""
other -> merge in test results from the other Tester obj.
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
@@ -962,7 +1030,7 @@ see its docs for details.
def __runone(self, target, name):
if "." in name:
i = _string_rindex(name, ".")
i = name.rindex(".")
prefix, base = name[:i], name[i+1:]
else:
prefix, base = "", base
@@ -972,9 +1040,9 @@ see its docs for details.
master = None
def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None,
def testmod(m, name=None, globs=None, verbose=None, isprivate=None,
report=1):
"""m=None, 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.
@@ -1007,10 +1075,10 @@ def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None,
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 obj of
Advanced tomfoolery: testmod runs methods of a local instance of
class doctest.Tester, then merges the results into (or creates)
global Tester obj doctest.master. Methods of doctest.master
can be called directly too, if you want to do something unusual.
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.
@@ -1018,20 +1086,13 @@ def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None,
global master
if m is None:
import sys
# DWA - m will still be None if this wasn't invoked from the command
# line, in which case the following TypeError is about as good an error
# as we should expect
m = sys.modules.get('__main__')
if type(m) is not _ModuleType:
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)
f, t = tester.rundict(m.__dict__, name, m)
failures = failures + f
tries = tries + t
if hasattr(m, "__test__"):

View File

@@ -25,27 +25,27 @@ object number()
object obj_getattr(object x, char const* name)
{
return x._(name);
return x.attr(name);
}
object obj_const_getattr(object const& x, char const* name)
{
return x._(name);
return x.attr(name);
}
void obj_setattr(object x, char const* name, object value)
{
x._(name) = value;
x.attr(name) = value;
}
void obj_setattr42(object x, char const* name)
{
x._(name) = 42;
x.attr(name) = 42;
}
void obj_moveattr(object& x, char const* src, char const* dst)
{
x._(dst) = x._(src);
x.attr(dst) = x.attr(src);
}
object obj_getitem(object x, object key)
@@ -95,12 +95,12 @@ bool test_not(object y)
bool test_attr(object y, char* name)
{
return y._(name);
return y.attr(name);
}
bool test_not_attr(object y, char* name)
{
return !y._(name);
return !y.attr(name);
}
bool test_item(object y, object key)
@@ -113,20 +113,161 @@ bool test_not_item(object y, object key)
return !y[key];
}
bool check_binary_operators()
{
int y;
object x(3);
#define TEST_BINARY(op) \
for (y = 1; y < 6; ++y) \
{ \
if ((x op y) != (3 op y)) \
return false; \
} \
for (y = 1; y < 6; ++y) \
{ \
if ((y op x) != (y op 3)) \
return false; \
} \
for (y = 1; y < 6; ++y) \
{ \
object oy(y); \
if ((oy op x) != (oy op 3)) \
return false; \
}
TEST_BINARY(>)
TEST_BINARY(>=)
TEST_BINARY(<)
TEST_BINARY(<=)
TEST_BINARY(==)
TEST_BINARY(!=)
TEST_BINARY(+)
TEST_BINARY(-)
TEST_BINARY(*)
TEST_BINARY(/)
TEST_BINARY(%)
TEST_BINARY(<<)
TEST_BINARY(>>)
TEST_BINARY(&)
TEST_BINARY(^)
TEST_BINARY(|)
return true;
}
bool check_inplace(object l, object o)
{
int y;
#define TEST_INPLACE(op) \
for (y = 1; y < 6; ++y) \
{ \
object x(666); \
x op##= y; \
if (x != (666 op y)) \
return false; \
} \
for (y = 1; y < 6; ++y) \
{ \
object x(666); \
x op##= object(y); \
if (!(x == (666 op y))) \
return false; \
}
TEST_INPLACE(+)
TEST_INPLACE(-)
TEST_INPLACE(*)
TEST_INPLACE(/)
TEST_INPLACE(%)
TEST_INPLACE(<<)
TEST_INPLACE(>>)
TEST_INPLACE(&)
TEST_INPLACE(^)
TEST_INPLACE(|)
l += l;
for (y = 0; y < 6; ++y)
{
if (l[y] != y % 3)
return false;
}
#define TEST_ITEM_INPLACE(index, op, n, r1, r2) \
l[index] op##= n; \
if (l[index] != r1) \
return false; \
l[index] op##= object(n); \
if (!(l[index] == r2)) \
return false;
TEST_ITEM_INPLACE(0,+,7,7,14)
TEST_ITEM_INPLACE(1,-,2,-1,-3)
TEST_ITEM_INPLACE(2,*,3,6,18)
TEST_ITEM_INPLACE(2,/,2,9,4)
TEST_ITEM_INPLACE(0,%,4,2,2)
l[0] += 1;
TEST_ITEM_INPLACE(0,<<,2,12,48)
TEST_ITEM_INPLACE(0,>>,1,24,12)
l[4] = 15;
TEST_ITEM_INPLACE(4,&,(16+4+1),5,5)
TEST_ITEM_INPLACE(0,^,1,13,12)
TEST_ITEM_INPLACE(0,|,1,13,13)
o.attr("x0") = 0;
o.attr("x1") = 1;
o.attr("x2") = 2;
o.attr("x3") = 0;
o.attr("x4") = 1;
#define TEST_ATTR_INPLACE(index, op, n, r1, r2) \
o.attr("x" #index) op##= n; \
if (o.attr("x" #index) != r1) \
return false; \
o.attr("x" #index) op##= object(n); \
if (o.attr("x" #index) != r2) \
return false;
TEST_ATTR_INPLACE(0,+,7,7,14)
TEST_ATTR_INPLACE(1,-,2,-1,-3)
TEST_ATTR_INPLACE(2,*,3,6,18)
TEST_ATTR_INPLACE(2,/,2,9,4)
TEST_ATTR_INPLACE(0,%,4,2,2)
o.attr("x0") += 1;
TEST_ATTR_INPLACE(0,<<,2,12,48)
TEST_ATTR_INPLACE(0,>>,1,24,12)
o.attr("x4") = 15;
TEST_ATTR_INPLACE(4,&,(16+4+1),5,5)
TEST_ATTR_INPLACE(0,^,1,13,12)
TEST_ATTR_INPLACE(0,|,1,13,13)
if (l[0] != o.attr("x0"))
return false;
if (l[1] != o.attr("x1"))
return false;
if (l[2] != o.attr("x2"))
return false;
if (l[3] != o.attr("x3"))
return false;
if (l[4] != o.attr("x4"))
return false;
return true;
}
BOOST_PYTHON_MODULE_INIT(object_ext)
{
module("object_ext")
.def("call_object_3", call_object_3)
.def("message", message)
.def("number", number)
.def("obj_getattr", obj_getattr)
.def("obj_const_getattr", obj_const_getattr)
.def("obj_setattr", obj_setattr)
.def("obj_setattr42", obj_setattr42)
.def("obj_moveattr", obj_moveattr)
.def("obj_getitem", obj_getitem)
.def("obj_getitem3", obj_getitem)
.def("obj_const_getitem", obj_const_getitem)
@@ -134,7 +275,7 @@ BOOST_PYTHON_MODULE_INIT(object_ext)
.def("obj_setitem42", obj_setitem42)
.def("obj_moveitem", obj_moveitem)
.def("obj_moveitem2", obj_moveitem2)
.def("test", test)
.def("test_not", test_not)
@@ -143,6 +284,9 @@ BOOST_PYTHON_MODULE_INIT(object_ext)
.def("test_item", test_item)
.def("test_not_item", test_not_item)
.def("check_binary_operators", check_binary_operators)
.def("check_inplace", check_inplace)
;
}

View File

@@ -79,6 +79,14 @@
0
>>> test_not_item(d, 'foo')
1
Operators
>>> assert check_binary_operators()
>>> class X: pass
...
>>> assert check_inplace(range(3), X())
'''
def run(args = None):