From 5e8d775b87dba5dd9350aa8dd1d4dc9850c95c5a Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Fri, 4 Oct 2002 21:34:32 +0000 Subject: [PATCH] Support for MinGW-2.0 [SVN r15719] --- include/boost/python/dict.hpp | 142 +-- include/boost/python/list.hpp | 97 +- include/boost/python/long.hpp | 36 +- include/boost/python/object/instance.hpp | 2 +- include/boost/python/str.hpp | 310 +++--- include/boost/python/tuple.hpp | 31 +- src/dict.cpp | 45 +- src/list.cpp | 38 +- src/long.cpp | 18 +- src/str.cpp | 214 ++-- src/tuple.cpp | 12 +- test/doctest.py | 1173 ---------------------- 12 files changed, 482 insertions(+), 1636 deletions(-) delete mode 100644 test/doctest.py diff --git a/include/boost/python/dict.hpp b/include/boost/python/dict.hpp index d78ccaf8..6bda6e2c 100644 --- a/include/boost/python/dict.hpp +++ b/include/boost/python/dict.hpp @@ -8,110 +8,126 @@ namespace boost { namespace python { -class dict : public object +class dict; + +namespace detail { - public: + struct BOOST_PYTHON_DECL dict_base : object + { + // D.clear() -> None. Remove all items from D. + void clear(); + + // D.copy() -> a shallow copy of D + dict copy(); + + // D.get(k[,d]) -> D[k] if D.has_key(k), else d. d defaults to None. + object get(object_cref k) const; + + object get(object_cref k, object_cref d) const; + + // D.has_key(k) -> 1 if D has a key k, else 0 + bool has_key(object_cref k) const; + + // D.items() -> list of D's (key, value) pairs, as 2-tuples + list items() const; + + // D.iteritems() -> an iterator over the (key, value) items of D + object iteritems() const; + + // D.iterkeys() -> an iterator over the keys of D + object iterkeys() const; + + // D.itervalues() -> an iterator over the values of D + object itervalues() const; + + // D.keys() -> list of D's keys + list keys() const; + + // D.popitem() -> (k, v), remove and return some (key, value) pair as a + // 2-tuple; but raise KeyError if D is empty + tuple popitem(); + + // D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if not D.has_key(k) + object setdefault(object_cref k); + + object setdefault(object_cref k, object_cref d); + + // D.update(E) -> None. Update D from E: for k in E.keys(): D[k] = E[k] + void update(object_cref E); + + // D.values() -> list of D's values + list values() const; + + protected: + // dict() -> new empty dictionary. + // dict(mapping) -> new dictionary initialized from a mapping object's + // (key, value) pairs. + // dict(seq) -> new dictionary initialized as if via: + dict_base(); // new dict + explicit dict_base(object_cref data); + + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(dict_base, object) + private: + static detail::new_reference call(object const&); + }; +} + +class dict : public detail::dict_base +{ + typedef detail::dict_base base; + public: // dict() -> new empty dictionary. // dict(mapping) -> new dictionary initialized from a mapping object's // (key, value) pairs. // dict(seq) -> new dictionary initialized as if via: - BOOST_PYTHON_DECL dict(); // new dict - explicit BOOST_PYTHON_DECL dict(object_cref data); + dict() {} // new dict template explicit dict(T const& data) - : object(dict::call(object(data))) + : base(object(data)) { } - // D.clear() -> None. Remove all items from D. - BOOST_PYTHON_DECL void clear(); - - // D.copy() -> a shallow copy of D - BOOST_PYTHON_DECL dict copy(); - - // D.get(k[,d]) -> D[k] if D.has_key(k), else d. d defaults to None. - BOOST_PYTHON_DECL object get(object_cref k) const; - template object get(T const& k) const { - return this->get(object(k)); + return base::get(object(k)); } - BOOST_PYTHON_DECL object get(object_cref k, object_cref d) const; - template object get(T1 const& k, T2 const& d) const { - return this->get(object(k),object(d)); + return base::get(object(k),object(d)); } - - // D.has_key(k) -> 1 if D has a key k, else 0 - BOOST_PYTHON_DECL bool has_key(object_cref k) const; - + template bool has_key(T const& k) const { - return this->has_key(object(k)); + return base::has_key(object(k)); } - - // D.items() -> list of D's (key, value) pairs, as 2-tuples - BOOST_PYTHON_DECL list items() const; - - // D.iteritems() -> an iterator over the (key, value) items of D - BOOST_PYTHON_DECL object iteritems() const; - - // D.iterkeys() -> an iterator over the keys of D - BOOST_PYTHON_DECL object iterkeys() const; - - // D.itervalues() -> an iterator over the values of D - BOOST_PYTHON_DECL object itervalues() const; - - // D.keys() -> list of D's keys - BOOST_PYTHON_DECL list keys() const; - - // D.popitem() -> (k, v), remove and return some (key, value) pair as a - // 2-tuple; but raise KeyError if D is empty - BOOST_PYTHON_DECL tuple popitem(); - - // D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if not D.has_key(k) - BOOST_PYTHON_DECL object setdefault(object_cref k); - + template object setdefault(T const& k) { - return this->setdefault(object(k)); + return base::setdefault(object(k)); } - - BOOST_PYTHON_DECL object setdefault(object_cref k, object_cref d); - + template object setdefault(T1 const& k, T2 const& d) { - return this->setdefault(object(k),object(d)); + return base::setdefault(object(k),object(d)); } - // D.update(E) -> None. Update D from E: for k in E.keys(): D[k] = E[k] - BOOST_PYTHON_DECL void update(object_cref E); - template void update(T const& E) { - this->update(object(E)); + base::update(object(E)); } - // D.values() -> list of D's values - BOOST_PYTHON_DECL list values() const; - public: // implementation detail -- for internal use only - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(dict, object) - - private: - static BOOST_PYTHON_DECL detail::new_reference call(object const&); + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(dict, base) }; - // // Converter Specializations // diff --git a/include/boost/python/list.hpp b/include/boost/python/list.hpp index e6696878..ce6cfab4 100644 --- a/include/boost/python/list.hpp +++ b/include/boost/python/list.hpp @@ -11,93 +11,116 @@ namespace boost { namespace python { -class list : public object +namespace detail { + struct BOOST_PYTHON_DECL list_base : object + { + void append(object_cref); // append object to end + + long count(object_cref value) const; // return number of occurrences of value + + void extend(object_cref sequence); // extend list by appending sequence elements + + long index(object_cref value) const; // return index of first occurrence of value + + void insert(int index, object_cref); // insert object before index + void insert(object const& index, object_cref); + + object pop(); // remove and return item at index (default last) + object pop(long index); + object pop(object const& index); + + void remove(object_cref value); // remove first occurrence of value + + void reverse(); // reverse *IN PLACE* + + void sort(); // sort *IN PLACE*; if given, cmpfunc(x, y) -> -1, 0, 1 + void sort(object_cref cmpfunc); + + + protected: + list_base(); // new list + explicit list_base(object_cref sequence); // new list initialized from sequence's items + + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(list_base, object) + private: + static detail::new_non_null_reference call(object const&); + }; +} + +class list : public detail::list_base +{ + typedef detail::list_base base; public: - BOOST_PYTHON_DECL list(); // new list - explicit BOOST_PYTHON_DECL list(object_cref sequence); // new list initialized from sequence's items + list() {} // new list template explicit list(T const& sequence) - : object(list::call(object(sequence))) + : base(object(sequence)) { } - BOOST_PYTHON_DECL void append(object_cref); // append object to end - template void append(T const& x) { - this->append(object(x)); + base::append(object(x)); } - BOOST_PYTHON_DECL long count(object_cref value) const; // return number of occurrences of value - template long count(T const& value) const { - return this->count(object(value)); + return base::count(object(value)); } - BOOST_PYTHON_DECL void extend(object_cref sequence); // extend list by appending sequence elements - template void extend(T const& x) { - this->extend(object(x)); + base::extend(object(x)); } - BOOST_PYTHON_DECL long index(object_cref value) const; // return index of first occurrence of value - template long index(T const& x) const { - return this->index(object(x)); + return base::index(object(x)); } - BOOST_PYTHON_DECL void insert(int index, object_cref); // insert object before index - BOOST_PYTHON_DECL void insert(object const& index, object_cref); - template void insert(int index, T const& x) // insert object before index { - this->insert(index, object(x)); + base::insert(index, object(x)); } template void insert(object const& index, T const& x) // insert object before index { - this->insert(index, object(x)); + base::insert(index, object(x)); } - - BOOST_PYTHON_DECL object pop(); // remove and return item at index (default last) - BOOST_PYTHON_DECL object pop(long index); - BOOST_PYTHON_DECL object pop(object const& index); - BOOST_PYTHON_DECL void remove(object_cref value); // remove first occurrence of value + object pop() { return base::pop(); } + object pop(long index) { return base::pop(index); } + template + object pop(T const& index) + { + return base::pop(object(index)); + } + template void remove(T const& value) { - this->remove(object(value)); + base::remove(object(value)); } + + void sort() { base::sort(); } - BOOST_PYTHON_DECL void reverse(); // reverse *IN PLACE* - - BOOST_PYTHON_DECL void sort(); // sort *IN PLACE*; if given, cmpfunc(x, y) -> -1, 0, 1 - BOOST_PYTHON_DECL void sort(object_cref cmpfunc); - template void sort(T const& value) { - this->sort(object(value)); + base::sort(object(value)); } public: // implementation detail -- for internal use only - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(list, object) - - private: - static BOOST_PYTHON_DECL detail::new_non_null_reference call(object const&); + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(list, base) }; // diff --git a/include/boost/python/long.hpp b/include/boost/python/long.hpp index b0cc6417..284296be 100644 --- a/include/boost/python/long.hpp +++ b/include/boost/python/long.hpp @@ -11,31 +11,43 @@ namespace boost { namespace python { -class long_ : public object +namespace detail { + struct BOOST_PYTHON_DECL long_base : object + { + protected: + long_base(); // new long_ + explicit long_base(object_cref rhs); + explicit long_base(object_cref rhs, object_cref base); + + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(long_base, object) + + private: + static detail::new_non_null_reference call(object const&); + static detail::new_non_null_reference call(object const&, object const&); + }; +} + +class long_ : public detail::long_base +{ + typedef detail::long_base base; public: - BOOST_PYTHON_DECL long_(); // new long_ - explicit BOOST_PYTHON_DECL long_(object_cref rhs); + long_() {} // new long_ template explicit long_(T const& rhs) - : object(long_::call(object(rhs))) + : base(object(rhs)) { } - explicit BOOST_PYTHON_DECL long_(object_cref rhs, object_cref base); - template explicit long_(T const& rhs, U const& base) - : object(long_::call(object(rhs), object(base))) + : base(object(rhs), object(base)) { } - public: // implementation detail -- for internal use only - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(long_, object) - private: - static BOOST_PYTHON_DECL detail::new_non_null_reference call(object const&); - static BOOST_PYTHON_DECL detail::new_non_null_reference call(object const&, object const&); + public: // implementation detail -- for internal use only + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(long_, base) }; // diff --git a/include/boost/python/object/instance.hpp b/include/boost/python/object/instance.hpp index 4cddcc99..b258a2a0 100644 --- a/include/boost/python/object/instance.hpp +++ b/include/boost/python/object/instance.hpp @@ -12,7 +12,7 @@ namespace boost { namespace python { - struct instance_holder; + struct BOOST_PYTHON_DECL instance_holder; }} // namespace boost::python namespace boost { namespace python { namespace objects { diff --git a/include/boost/python/str.hpp b/include/boost/python/str.hpp index ffb3c362..285d7153 100644 --- a/include/boost/python/str.hpp +++ b/include/boost/python/str.hpp @@ -15,347 +15,367 @@ namespace boost { namespace python { -class str : public object -{ - public: - BOOST_PYTHON_DECL str(); // new str - - BOOST_PYTHON_DECL str(const char* s); // new str - explicit BOOST_PYTHON_DECL str(object_cref other); +class str; +namespace detail +{ + struct BOOST_PYTHON_DECL str_base : object + { + str capitalize() const; + + str center(object_cref width) const; + + long count(object_cref sub) const; + + long count(object_cref sub, object_cref start) const; + + long count(object_cref sub, object_cref start, object_cref end) const; + + object decode() const; + object decode(object_cref encoding) const; + + object decode(object_cref encoding, object_cref errors) const; + + object encode() const; + object encode(object_cref encoding) const; + object encode(object_cref encoding, object_cref errors) const; + + bool endswith(object_cref suffix) const; + + bool endswith(object_cref suffix, object_cref start) const; + bool endswith(object_cref suffix, object_cref start, object_cref end) const; + + str expandtabs() const; + str expandtabs(object_cref tabsize) const; + + long find(object_cref sub) const; + long find(object_cref sub, object_cref start) const; + + long find(object_cref sub, object_cref start, object_cref end) const; + + long index(object_cref sub) const; + + long index(object_cref sub, object_cref start) const; + long index(object_cref sub, object_cref start, object_cref end) const; + + bool isalnum() const; + bool isalpha() const; + bool isdigit() const; + bool islower() const; + bool isspace() const; + bool istitle() const; + bool isupper() const; + + str join(object_cref sequence) const; + + str ljust(object_cref width) const; + str lower() const; + str lstrip() const; + + str replace(object_cref old, object_cref new_) const; + str replace(object_cref old, object_cref new_, object_cref maxsplit) const; + long rfind(object_cref sub) const; + + long rfind(object_cref sub, object_cref start) const; + + long rfind(object_cref sub, object_cref start, object_cref end) const; + long rindex(object_cref sub) const; + long rindex(object_cref sub, object_cref start) const; + + + long rindex(object_cref sub, object_cref start, object_cref end) const; + + str rjust(object_cref width) const; + + str rstrip() const; + + list split() const; + list split(object_cref sep) const; + + list split(object_cref sep, object_cref maxsplit) const; + + + list splitlines() const; + list splitlines(object_cref keepends) const; + + bool startswith(object_cref prefix) const; + + + bool startswith(object_cref prefix, object_cref start) const; + bool startswith(object_cref prefix, object_cref start, object_cref end) const; + + str strip() const; + str swapcase() const; + str title() const; + + str translate(object_cref table) const; + + str translate(object_cref table, object_cref deletechars) const; + + + str upper() const; + + protected: + str_base(); // new str + + str_base(const char* s); // new str + explicit str_base(object_cref other); + + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(str_base, object) + private: + static new_reference call(object const&); + }; +} + + +class str : public detail::str_base +{ + typedef detail::str_base base; + public: + str() {} // new str + + str(const char* s) : str_base(s) {} // new str + template explicit str(T const& other) - : object(str::call(object(other))) + : str_base(object(other)) { } - BOOST_PYTHON_DECL str capitalize() const ; - - BOOST_PYTHON_DECL str center(object_cref width) const ; - template str center(T const& width) const { - return this->center(object(width)); + return base::center(object(width)); } - BOOST_PYTHON_DECL long count(object_cref sub) const; - template long count(T const& sub) const { - return this->count(object(sub)); + return base::count(object(sub)); } - BOOST_PYTHON_DECL long count(object_cref sub, object_cref start) const; - template long count(T1 const& sub,T2 const& start) const { - return this->count(object(sub), object(start)); + return base::count(object(sub), object(start)); } - BOOST_PYTHON_DECL long count(object_cref sub, object_cref start, object_cref end) const; - template long count(T1 const& sub,T2 const& start, T3 const& end) const { - return this->count(object(sub), object(start)); + return base::count(object(sub), object(start)); } - BOOST_PYTHON_DECL object decode() const; - BOOST_PYTHON_DECL object decode(object_cref encoding) const; - + object decode() const { return base::decode(); } + template object decode(T const& encoding) const { - return this->decode(object(encoding)); + return base::decode(object(encoding)); } - BOOST_PYTHON_DECL object decode(object_cref encoding, object_cref errors) const; - template object decode(T1 const& encoding, T2 const& errors) const { - return this->decode(object(encoding),object(errors)); + return base::decode(object(encoding),object(errors)); } - BOOST_PYTHON_DECL object encode() const; - BOOST_PYTHON_DECL object encode(object_cref encoding) const; + object encode() const { return base::encode(); } template object encode(T const& encoding) const { - return this->encode(object(encoding)); + return base::encode(object(encoding)); } - BOOST_PYTHON_DECL object encode(object_cref encoding, object_cref errors) const; - template object encode(T1 const& encoding, T2 const& errors) const { - return this->encode(object(encoding),object(errors)); + return base::encode(object(encoding),object(errors)); } - BOOST_PYTHON_DECL bool endswith(object_cref suffix) const; - template bool endswith(T const& suffix) const { - return this->endswith(object(suffix)); + return base::endswith(object(suffix)); } - BOOST_PYTHON_DECL bool endswith(object_cref suffix, object_cref start) const; - template bool endswith(T1 const& suffix, T2 const& start) const { - return this->endswith(object(suffix), object(start)); + return base::endswith(object(suffix), object(start)); } - BOOST_PYTHON_DECL bool endswith(object_cref suffix, object_cref start, object_cref end) const; - template bool endswith(T1 const& suffix, T2 const& start, T3 const& end) const { - return this->endswith(object(suffix), object(start), object(end)); + return base::endswith(object(suffix), object(start), object(end)); } - BOOST_PYTHON_DECL str expandtabs() const; - BOOST_PYTHON_DECL str expandtabs(object_cref tabsize) const; + str expandtabs() const { return base::expandtabs(); } template str expandtabs(T const& tabsize) const { - return this->expandtabs(object(tabsize)); + return base::expandtabs(object(tabsize)); } - BOOST_PYTHON_DECL long find(object_cref sub) const; - template long find(T const& sub) const { - return this->find(object(sub)); + return base::find(object(sub)); } - BOOST_PYTHON_DECL long find(object_cref sub, object_cref start) const; - template long find(T1 const& sub, T2 const& start) const { - return this->find(object(sub), object(start)); + return base::find(object(sub), object(start)); } - BOOST_PYTHON_DECL long find(object_cref sub, object_cref start, object_cref end) const; - template long find(T1 const& sub, T2 const& start, T3 const& end) const { - return this->find(object(sub), object(start), object(end)); + return base::find(object(sub), object(start), object(end)); } - BOOST_PYTHON_DECL long index(object_cref sub) const; - template long index(T const& sub) const { - return this->index(object(sub)); + return base::index(object(sub)); } - BOOST_PYTHON_DECL long index(object_cref sub, object_cref start) const; - template long index(T1 const& sub, T2 const& start) const { - return this->index(object(sub), object(start)); + return base::index(object(sub), object(start)); } - BOOST_PYTHON_DECL long index(object_cref sub, object_cref start, object_cref end) const; - template long index(T1 const& sub, T2 const& start, T3 const& end) const { - return this->index(object(sub), object(start), object(end)); + return base::index(object(sub), object(start), object(end)); } - BOOST_PYTHON_DECL bool isalnum() const; - BOOST_PYTHON_DECL bool isalpha() const; - BOOST_PYTHON_DECL bool isdigit() const; - BOOST_PYTHON_DECL bool islower() const; - BOOST_PYTHON_DECL bool isspace() const; - BOOST_PYTHON_DECL bool istitle() const; - BOOST_PYTHON_DECL bool isupper() const; - - BOOST_PYTHON_DECL str join(object_cref sequence) const; - template str join(T const& sequence) const { - return this->join(object(sequence)); + return base::join(object(sequence)); } - BOOST_PYTHON_DECL str ljust(object_cref width) const; - template str ljust(T const& width) const { - return this->ljust(object(width)); + return base::ljust(object(width)); } - BOOST_PYTHON_DECL str lower() const; - BOOST_PYTHON_DECL str lstrip() const; - - BOOST_PYTHON_DECL str replace(object_cref old, object_cref new_) const ; - template str replace(T1 const& old, T2 const& new_) const { - return this->replace(object(old),object(new_)); + return base::replace(object(old),object(new_)); } - BOOST_PYTHON_DECL str replace(object_cref old, object_cref new_, object_cref maxsplit) const ; - template str replace(T1 const& old, T2 const& new_, T3 const& maxsplit) const { - return this->replace(object(old),object(new_),object(maxsplit)); + return base::replace(object(old),object(new_), object(maxsplit)); } - BOOST_PYTHON_DECL long rfind(object_cref sub) const; - template long rfind(T const& sub) const { - return this->rfind(object(sub)); + return base::rfind(object(sub)); } - BOOST_PYTHON_DECL long rfind(object_cref sub, object_cref start) const; - template long rfind(T1 const& sub, T2 const& start) const { - return this->rfind(object(sub), object(start)); + return base::rfind(object(sub), object(start)); } - BOOST_PYTHON_DECL long rfind(object_cref sub, object_cref start, object_cref end) const; - template long rfind(T1 const& sub, T2 const& start, T3 const& end) const { - return this->rfind(object(sub), object(start), object(end)); + return base::rfind(object(sub), object(start), object(end)); } - BOOST_PYTHON_DECL long rindex(object_cref sub) const; - template long rindex(T const& sub) const { - return this->rindex(object(sub)); + return base::rindex(object(sub)); } - BOOST_PYTHON_DECL long rindex(object_cref sub, object_cref start) const; - template long rindex(T1 const& sub, T2 const& start) const { - return this->rindex(object(sub), object(start)); + return base::rindex(object(sub), object(start)); } - BOOST_PYTHON_DECL long rindex(object_cref sub, object_cref start, object_cref end) const; - template long rindex(T1 const& sub, T2 const& start, T3 const& end) const { - return this->rindex(object(sub), object(start), object(end)); + return base::rindex(object(sub), object(start), object(end)); } - BOOST_PYTHON_DECL str rjust(object_cref width) const; - template str rjust(T const& width) const { - return this->rjust(object(width)); + return base::rjust(object(width)); } - BOOST_PYTHON_DECL str rstrip() const; - - BOOST_PYTHON_DECL list split() const; - BOOST_PYTHON_DECL list split(object_cref sep) const; + list split() const { return base::split(); } template list split(T const& sep) const { - return this->split(object(sep)); + return base::split(object(sep)); } - BOOST_PYTHON_DECL list split(object_cref sep, object_cref maxsplit) const; - template list split(T1 const& sep, T2 const& maxsplit) const { - return this->split(object(sep), object(maxsplit)); + return base::split(object(sep), object(maxsplit)); } - BOOST_PYTHON_DECL list splitlines() const; - BOOST_PYTHON_DECL list splitlines(object_cref keepends) const; + list splitlines() const { return base::splitlines(); } template list splitlines(T const& keepends) const { - return this->splitlines(object(keepends)); + return base::splitlines(object(keepends)); } - BOOST_PYTHON_DECL bool startswith(object_cref prefix) const ; - template bool startswith(T const& prefix) const { - return this->startswith(object(prefix)); + return base::startswith(object(prefix)); } - BOOST_PYTHON_DECL bool startswith(object_cref prefix, object_cref start) const ; - template bool startswidth(T1 const& prefix, T2 const& start) const { - return this->startswidth(object(prefix), object(start)); + return base::startswidth(object(prefix), object(start)); } - BOOST_PYTHON_DECL bool startswith(object_cref prefix, object_cref start, object_cref end) const ; - template bool startswidth(T1 const& prefix, T2 const& start, T3 const& end) const { - return this->startswidth(object(prefix), object(start), object(end)); + return base::startswidth(object(prefix), object(start), object(end)); } - BOOST_PYTHON_DECL str strip() const ; - BOOST_PYTHON_DECL str swapcase() const ; - BOOST_PYTHON_DECL str title() const ; - - BOOST_PYTHON_DECL str translate(object_cref table) const; - template str translate(T const& table) const { - return this->translate(object(table)); + return base::translate(object(table)); } - BOOST_PYTHON_DECL str translate(object_cref table, object_cref deletechars) const; - template str translate(T1 const& table, T2 const& deletechars) const { - return this->translate(object(table), object(deletechars)); + return base::translate(object(table), object(deletechars)); } - BOOST_PYTHON_DECL str upper() const; - public: // implementation detail -- for internal use only - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(str, object) - - private: - static BOOST_PYTHON_DECL detail::new_reference call(object const&); + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(str, base) }; // diff --git a/include/boost/python/tuple.hpp b/include/boost/python/tuple.hpp index 77c5e67c..5fe4cb3b 100644 --- a/include/boost/python/tuple.hpp +++ b/include/boost/python/tuple.hpp @@ -8,26 +8,35 @@ namespace boost { namespace python { -class tuple : public object +namespace detail { - public: - // tuple() -> an empty tuple - BOOST_PYTHON_DECL tuple(); + struct BOOST_PYTHON_DECL tuple_base : object + { + protected: + tuple_base(); + tuple_base(object_cref sequence); + + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(tuple_base, object) - // tuple(sequence) -> tuple initialized from sequence's items - BOOST_PYTHON_DECL tuple(object_cref sequence); + private: + static detail::new_reference call(object const&); + }; +} + +class tuple : public detail::tuple_base +{ + typedef detail::tuple_base base; + public: + tuple() {} template explicit tuple(T const& sequence) - : object(tuple::call(object(sequence))) + : tuple_base(object(sequence)) { } public: // implementation detail -- for internal use only - BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(tuple, object) - - private: - static BOOST_PYTHON_DECL detail::new_reference call(object const&); + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(tuple, base) }; // diff --git a/src/dict.cpp b/src/dict.cpp index e24145d0..847d2182 100644 --- a/src/dict.cpp +++ b/src/dict.cpp @@ -1,8 +1,7 @@ #include #include -namespace boost { namespace python { - +namespace boost { namespace python { namespace detail { namespace { // When returning list objects from methods, it may turn out that the @@ -18,28 +17,28 @@ namespace } // No PyDict_CheckExact; roll our own. - inline bool check_exact(dict const* p) + inline bool check_exact(dict_base const* p) { return p->ptr()->ob_type == &PyDict_Type; } } -detail::new_reference dict::call(object const& arg_) +detail::new_reference dict_base::call(object const& arg_) { return (detail::new_reference)PyObject_CallFunction( (PyObject*)&PyDict_Type, "(O)", arg_.ptr()); } -dict::dict() +dict_base::dict_base() : object(detail::new_reference(PyDict_New())) {} -dict::dict(object_cref data) - : object(dict::call(data)) +dict_base::dict_base(object_cref data) + : object(call(data)) {} -void dict::clear() +void dict_base::clear() { if (check_exact(this)) PyDict_Clear(this->ptr()); @@ -47,7 +46,7 @@ void dict::clear() this->attr("clear")(); } -dict dict::copy() +dict dict_base::copy() { if (check_exact(this)) { @@ -62,7 +61,7 @@ dict dict::copy() } } -object dict::get(object_cref k) const +object dict_base::get(object_cref k) const { if (check_exact(this)) { @@ -75,17 +74,17 @@ object dict::get(object_cref k) const } } -object dict::get(object_cref k, object_cref d) const +object dict_base::get(object_cref k, object_cref d) const { return this->attr("get")(k,d); } -bool dict::has_key(object_cref k) const +bool dict_base::has_key(object_cref k) const { return extract(this->attr("has_key")(k)); } -list dict::items() const +list dict_base::items() const { if (check_exact(this)) { @@ -98,22 +97,22 @@ list dict::items() const } } -object dict::iteritems() const +object dict_base::iteritems() const { return this->attr("iteritems")(); } -object dict::iterkeys() const +object dict_base::iterkeys() const { return this->attr("iterkeys")(); } -object dict::itervalues() const +object dict_base::itervalues() const { return this->attr("itervalues")(); } -list dict::keys() const +list dict_base::keys() const { if (check_exact(this)) { @@ -126,24 +125,24 @@ list dict::keys() const } } -tuple dict::popitem() +tuple dict_base::popitem() { return tuple(detail::borrowed_reference( this->attr("popitem")().ptr() )); } -object dict::setdefault(object_cref k) +object dict_base::setdefault(object_cref k) { return this->attr("setdefault")(k); } -object dict::setdefault(object_cref k, object_cref d) +object dict_base::setdefault(object_cref k, object_cref d) { return this->attr("setdefault")(k,d); } -void dict::update(object_cref other) +void dict_base::update(object_cref other) { if (check_exact(this)) { @@ -156,7 +155,7 @@ void dict::update(object_cref other) } } -list dict::values() const +list dict_base::values() const { if (check_exact(this)) { @@ -169,4 +168,4 @@ list dict::values() const } } -}} // namespace boost::python +}}} // namespace boost::python diff --git a/src/list.cpp b/src/list.cpp index 1f434320..aab01bc3 100644 --- a/src/list.cpp +++ b/src/list.cpp @@ -5,9 +5,9 @@ // to its suitability for any purpose. #include -namespace boost { namespace python { +namespace boost { namespace python { namespace detail { -detail::new_non_null_reference list::call(object const& arg_) +detail::new_non_null_reference list_base::call(object const& arg_) { return (detail::new_non_null_reference) (expect_non_null)( @@ -16,15 +16,15 @@ detail::new_non_null_reference list::call(object const& arg_) arg_.ptr())); } -list::list() +list_base::list_base() : object(detail::new_reference(PyList_New(0))) {} -list::list(object_cref sequence) - : object(list::call(sequence)) +list_base::list_base(object_cref sequence) + : object(list_base::call(sequence)) {} -void list::append(object_cref x) +void list_base::append(object_cref x) { if (PyList_CheckExact(this->ptr())) { @@ -37,7 +37,7 @@ void list::append(object_cref x) } } -long list::count(object_cref value) const +long list_base::count(object_cref value) const { object result_obj(this->attr("count")(value)); long result = PyInt_AsLong(result_obj.ptr()); @@ -46,12 +46,12 @@ long list::count(object_cref value) const return result; } -void list::extend(object_cref sequence) +void list_base::extend(object_cref sequence) { this->attr("extend")(sequence); } -long list::index(object_cref value) const +long list_base::index(object_cref value) const { object result_obj(this->attr("index")(value)); long result = PyInt_AsLong(result_obj.ptr()); @@ -60,7 +60,7 @@ long list::index(object_cref value) const return result; } -void list::insert(int index, object_cref item) +void list_base::insert(int index, object_cref item) { if (PyList_CheckExact(this->ptr())) { @@ -73,7 +73,7 @@ void list::insert(int index, object_cref item) } } -void list::insert(object const& index, object_cref x) +void list_base::insert(object const& index, object_cref x) { long index_ = PyInt_AsLong(index.ptr()); if (index_ == -1 && PyErr_Occurred()) @@ -81,27 +81,27 @@ void list::insert(object const& index, object_cref x) this->insert(index_, x); } -object list::pop() +object list_base::pop() { return this->attr("pop")(); } -object list::pop(long index) +object list_base::pop(long index) { return this->pop(object(index)); } -object list::pop(object const& index) +object list_base::pop(object const& index) { return this->attr("pop")(index); } -void list::remove(object_cref value) +void list_base::remove(object_cref value) { this->attr("remove")(value); } -void list::reverse() +void list_base::reverse() { if (PyList_CheckExact(this->ptr())) { @@ -114,7 +114,7 @@ void list::reverse() } } -void list::sort() +void list_base::sort() { if (PyList_CheckExact(this->ptr())) { @@ -127,9 +127,9 @@ void list::sort() } } -void list::sort(object_cref cmpfunc) +void list_base::sort(object_cref cmpfunc) { this->attr("sort")(cmpfunc); } -}} // namespace boost::python +}}} // namespace boost::python diff --git a/src/long.cpp b/src/long.cpp index e66d4bdd..bfc09257 100644 --- a/src/long.cpp +++ b/src/long.cpp @@ -5,36 +5,36 @@ // to its suitability for any purpose. #include -namespace boost { namespace python { +namespace boost { namespace python { namespace detail { -detail::new_non_null_reference long_::call(object const& arg_) +new_non_null_reference long_base::call(object const& arg_) { return (detail::new_non_null_reference)PyObject_CallFunction( (PyObject*)&PyLong_Type, "(O)", arg_.ptr()); } -detail::new_non_null_reference long_::call(object const& arg_, object const& base) +new_non_null_reference long_base::call(object const& arg_, object const& base) { return (detail::new_non_null_reference)PyObject_CallFunction( (PyObject*)&PyLong_Type, "(OO)", arg_.ptr(), base.ptr()); } -long_::long_() +long_base::long_base() : object( detail::new_reference( PyObject_CallFunction((PyObject*)&PyLong_Type, "()")) ) {} -long_::long_(object_cref arg) - : object(long_::call(arg)) +long_base::long_base(object_cref arg) + : object(long_base::call(arg)) {} -long_::long_(object_cref arg, object_cref base) - : object(long_::call(arg, base)) +long_base::long_base(object_cref arg, object_cref base) + : object(long_base::call(arg, base)) {} -}} // namespace boost::python +}}} // namespace boost::python diff --git a/src/str.cpp b/src/str.cpp index fff18221..a8a2383f 100644 --- a/src/str.cpp +++ b/src/str.cpp @@ -1,99 +1,98 @@ #include #include -namespace boost { namespace python { +namespace boost { namespace python { namespace detail { -detail::new_reference str::call(object const& arg_) +detail::new_reference str_base::call(object const& arg_) { return (detail::new_reference)PyObject_CallFunction( (PyObject*)&PyString_Type, "(O)", arg_.ptr()); } -str::str() +str_base::str_base() : object(detail::new_reference(PyString_FromString(""))) {} -str::str(const char* s) +str_base::str_base(const char* s) : object(detail::new_reference(PyString_FromString(s))) {} -str::str(object_cref other) - : object(str::call(other)) +str_base::str_base(object_cref other) + : object(str_base::call(other)) {} namespace { - // When returning str objects from methods, it may turn out that the - // derived class is returning something else, perhaps something not - // even derived from str. Since it is generally harmless for a - // Boost.Python wrapper object to hold an object of a different - // type, and because calling str() with an object may in fact - // perform a conversion, the least-bad alternative is to assume that - // we have a Python str object and stuff it into the str result. - str assume_str(object const& o) + new_reference new_attr_reference(object const* obj, char const* name) { - return str(detail::borrowed_reference(o.ptr())); + return new_reference(incref(object(obj->attr(name)).ptr())); } } -str str::capitalize() const -{ - return assume_str(this->attr("capitalize")()); + +#define BOOST_PYTHON_FORMAT_OBJECT(z, n, data) "O" +#define BOOST_PYTHON_OBJECT_PTR(z, n, data) , x##n .ptr() + +#define BOOST_PYTHON_DEFINE_STR_METHOD(name, arity) \ +str str_base:: name ( BOOST_PP_ENUM_PARAMS(arity, object_cref x) ) const \ +{ \ + return str(new_reference( \ + expect_non_null( \ + PyObject_CallMethod( \ + this->ptr(), #name, \ + "(" BOOST_PP_REPEAT(arity, BOOST_PYTHON_FORMAT_OBJECT, _) ")" \ + BOOST_PP_REPEAT_1(arity, BOOST_PYTHON_OBJECT_PTR, _))))); \ } -str str::center(object_cref width) const -{ - return assume_str( - this->attr("center")(width) - ); -} +BOOST_PYTHON_DEFINE_STR_METHOD(capitalize, 0) +BOOST_PYTHON_DEFINE_STR_METHOD(center, 1) -long str::count(object_cref sub) const +long str_base::count(object_cref sub) const { return extract(this->attr("count")(sub)); } -long str::count(object_cref sub, object_cref start) const +long str_base::count(object_cref sub, object_cref start) const { return extract(this->attr("count")(sub,start)); } -long str::count(object_cref sub, object_cref start, object_cref end) const +long str_base::count(object_cref sub, object_cref start, object_cref end) const { return extract(this->attr("count")(sub,start,end)); } -object str::decode() const +object str_base::decode() const { return this->attr("decode")(); } -object str::decode(object_cref encoding) const +object str_base::decode(object_cref encoding) const { return this->attr("decode")(encoding); } -object str::decode(object_cref encoding, object_cref errors) const +object str_base::decode(object_cref encoding, object_cref errors) const { return this->attr("decode")(encoding,errors); } -object str::encode() const +object str_base::encode() const { return this->attr("encode")(); } -object str::encode(object_cref encoding) const +object str_base::encode(object_cref encoding) const { return this->attr("encode")(encoding); } -object str::encode(object_cref encoding, object_cref errors) const +object str_base::encode(object_cref encoding, object_cref errors) const { return this->attr("encode")(encoding,errors); } -bool str::endswith(object_cref suffix) const +bool str_base::endswith(object_cref suffix) const { bool result = PyInt_AsLong(this->attr("endswith")(suffix).ptr()); if (PyErr_Occurred()) @@ -101,17 +100,10 @@ bool str::endswith(object_cref suffix) const return result; } -str str::expandtabs() const -{ - return assume_str(this->attr("expandtabs")()); -} +BOOST_PYTHON_DEFINE_STR_METHOD(expandtabs, 0) +BOOST_PYTHON_DEFINE_STR_METHOD(expandtabs, 1) -str str::expandtabs(object_cref tabsize) const -{ - return assume_str(this->attr("expandtabs")(tabsize)); -} - -long str::find(object_cref sub) const +long str_base::find(object_cref sub) const { long result = PyInt_AsLong(this->attr("find")(sub).ptr()); if (PyErr_Occurred()) @@ -119,7 +111,7 @@ long str::find(object_cref sub) const return result; } -long str::find(object_cref sub, object_cref start) const +long str_base::find(object_cref sub, object_cref start) const { long result = PyInt_AsLong(this->attr("find")(sub,start).ptr()); if (PyErr_Occurred()) @@ -127,7 +119,7 @@ long str::find(object_cref sub, object_cref start) const return result; } -long str::find(object_cref sub, object_cref start, object_cref end) const +long str_base::find(object_cref sub, object_cref start, object_cref end) const { long result = PyInt_AsLong(this->attr("find")(sub,start,end).ptr()); if (PyErr_Occurred()) @@ -135,7 +127,7 @@ long str::find(object_cref sub, object_cref start, object_cref end) const return result; } -long str::index(object_cref sub) const +long str_base::index(object_cref sub) const { long result = PyInt_AsLong(this->attr("index")(sub).ptr()); if (PyErr_Occurred()) @@ -143,7 +135,7 @@ long str::index(object_cref sub) const return result; } -long str::index(object_cref sub, object_cref start) const +long str_base::index(object_cref sub, object_cref start) const { long result = PyInt_AsLong(this->attr("index")(sub,start).ptr()); if (PyErr_Occurred()) @@ -151,7 +143,7 @@ long str::index(object_cref sub, object_cref start) const return result; } -long str::index(object_cref sub, object_cref start, object_cref end) const +long str_base::index(object_cref sub, object_cref start, object_cref end) const { long result = PyInt_AsLong(this->attr("index")(sub,start,end).ptr()); if (PyErr_Occurred()) @@ -159,7 +151,7 @@ long str::index(object_cref sub, object_cref start, object_cref end) const return result; } -bool str::isalnum() const +bool str_base::isalnum() const { bool result = PyInt_AsLong(this->attr("isalnum")().ptr()); if (PyErr_Occurred()) @@ -167,7 +159,7 @@ bool str::isalnum() const return result; } -bool str::isalpha() const +bool str_base::isalpha() const { bool result = PyInt_AsLong(this->attr("isalpha")().ptr()); if (PyErr_Occurred()) @@ -175,7 +167,7 @@ bool str::isalpha() const return result; } -bool str::isdigit() const +bool str_base::isdigit() const { bool result = PyInt_AsLong(this->attr("isdigit")().ptr()); if (PyErr_Occurred()) @@ -183,7 +175,7 @@ bool str::isdigit() const return result; } -bool str::islower() const +bool str_base::islower() const { bool result = PyInt_AsLong(this->attr("islower")().ptr()); if (PyErr_Occurred()) @@ -191,7 +183,7 @@ bool str::islower() const return result; } -bool str::isspace() const +bool str_base::isspace() const { bool result = PyInt_AsLong(this->attr("isspace")().ptr()); if (PyErr_Occurred()) @@ -199,7 +191,7 @@ bool str::isspace() const return result; } -bool str::istitle() const +bool str_base::istitle() const { bool result = PyInt_AsLong(this->attr("istitle")().ptr()); if (PyErr_Occurred()) @@ -207,7 +199,7 @@ bool str::istitle() const return result; } -bool str::isupper() const +bool str_base::isupper() const { bool result = PyInt_AsLong(this->attr("isupper")().ptr()); if (PyErr_Occurred()) @@ -215,36 +207,14 @@ bool str::isupper() const return result; } -str str::join(object_cref sequence) const -{ - return assume_str(this->attr("join")(sequence)); -} +BOOST_PYTHON_DEFINE_STR_METHOD(join, 1) +BOOST_PYTHON_DEFINE_STR_METHOD(ljust, 1) +BOOST_PYTHON_DEFINE_STR_METHOD(lower, 0) +BOOST_PYTHON_DEFINE_STR_METHOD(lstrip, 0) +BOOST_PYTHON_DEFINE_STR_METHOD(replace, 2) +BOOST_PYTHON_DEFINE_STR_METHOD(replace, 3) -str str::ljust(object_cref width) const -{ - return assume_str(this->attr("ljust")(width)); -} - -str str::lower() const -{ - return assume_str(this->attr("lower")()); -} - -str str::lstrip() const -{ - return assume_str(this->attr("lstrip")()); -} - -str str::replace(object_cref old, object_cref new_) const -{ - return assume_str(this->attr("replace")(old,new_)); -} - -str str::replace(object_cref old, object_cref new_, object_cref maxsplit) const { - return assume_str(this->attr("replace")(old,new_,maxsplit)); -} - -long str::rfind(object_cref sub) const +long str_base::rfind(object_cref sub) const { long result = PyInt_AsLong(this->attr("rfind")(sub).ptr()); if (PyErr_Occurred()) @@ -252,7 +222,7 @@ long str::rfind(object_cref sub) const return result; } -long str::rfind(object_cref sub, object_cref start) const +long str_base::rfind(object_cref sub, object_cref start) const { long result = PyInt_AsLong(this->attr("rfind")(sub,start).ptr()); if (PyErr_Occurred()) @@ -260,7 +230,7 @@ long str::rfind(object_cref sub, object_cref start) const return result; } -long str::rfind(object_cref sub, object_cref start, object_cref end) const +long str_base::rfind(object_cref sub, object_cref start, object_cref end) const { long result = PyInt_AsLong(this->attr("rfind")(sub,start,end).ptr()); if (PyErr_Occurred()) @@ -268,7 +238,7 @@ long str::rfind(object_cref sub, object_cref start, object_cref end) const return result; } -long str::rindex(object_cref sub) const +long str_base::rindex(object_cref sub) const { long result = PyInt_AsLong(this->attr("rindex")(sub).ptr()); if (PyErr_Occurred()) @@ -276,7 +246,7 @@ long str::rindex(object_cref sub) const return result; } -long str::rindex(object_cref sub, object_cref start) const +long str_base::rindex(object_cref sub, object_cref start) const { long result = PyInt_AsLong(this->attr("rindex")(sub,start).ptr()); if (PyErr_Occurred()) @@ -284,7 +254,7 @@ long str::rindex(object_cref sub, object_cref start) const return result; } -long str::rindex(object_cref sub, object_cref start, object_cref end) const +long str_base::rindex(object_cref sub, object_cref start, object_cref end) const { long result = PyInt_AsLong(this->attr("rindex")(sub,start,end).ptr()); if (PyErr_Occurred()) @@ -292,42 +262,35 @@ long str::rindex(object_cref sub, object_cref start, object_cref end) const return result; } -str str::rjust(object_cref width) const -{ - return assume_str(this->attr("rjust")(width)); -} +BOOST_PYTHON_DEFINE_STR_METHOD(rjust, 1) +BOOST_PYTHON_DEFINE_STR_METHOD(rstrip, 0) -str str::rstrip() const -{ - return assume_str(this->attr("rstrip")()); -} - -list str::split() const +list str_base::split() const { return list(this->attr("split")()); } -list str::split(object_cref sep) const +list str_base::split(object_cref sep) const { return list(this->attr("split")(sep)); } -list str::split(object_cref sep, object_cref maxsplit) const +list str_base::split(object_cref sep, object_cref maxsplit) const { return list(this->attr("split")(sep,maxsplit)); } -list str::splitlines() const +list str_base::splitlines() const { return list(this->attr("splitlines")()); } -list str::splitlines(object_cref keepends) const +list str_base::splitlines(object_cref keepends) const { return list(this->attr("splitlines")(keepends)); } -bool str::startswith(object_cref prefix) const +bool str_base::startswith(object_cref prefix) const { bool result = PyInt_AsLong(this->attr("startswith")(prefix).ptr()); if (PyErr_Occurred()) @@ -335,7 +298,7 @@ bool str::startswith(object_cref prefix) const return result; } -bool str::startswith(object_cref prefix, object_cref start) const +bool str_base::startswith(object_cref prefix, object_cref start) const { bool result = PyInt_AsLong(this->attr("startswith")(prefix,start).ptr()); if (PyErr_Occurred()) @@ -343,7 +306,7 @@ bool str::startswith(object_cref prefix, object_cref start) const return result; } -bool str::startswith(object_cref prefix, object_cref start, object_cref end) const +bool str_base::startswith(object_cref prefix, object_cref start, object_cref end) const { bool result = PyInt_AsLong(this->attr("startswith")(prefix,start,end).ptr()); if (PyErr_Occurred()) @@ -351,34 +314,11 @@ bool str::startswith(object_cref prefix, object_cref start, object_cref end) con return result; } -str str::strip() const -{ - return assume_str(this->attr("strip")()); -} +BOOST_PYTHON_DEFINE_STR_METHOD(strip, 0) +BOOST_PYTHON_DEFINE_STR_METHOD(swapcase, 0) +BOOST_PYTHON_DEFINE_STR_METHOD(title, 0) +BOOST_PYTHON_DEFINE_STR_METHOD(translate, 1) +BOOST_PYTHON_DEFINE_STR_METHOD(translate, 2) +BOOST_PYTHON_DEFINE_STR_METHOD(upper, 0) -str str::swapcase() const -{ - return assume_str(this->attr("swapcase")()); -} - -str str::title() const -{ - return assume_str(this->attr("title")()); -} - -str str::translate(object_cref table) const -{ - return assume_str(this->attr("translate")(table)); -} - -str str::translate(object_cref table, object_cref deletechars) const -{ - return assume_str(this->attr("translate")(table,deletechars)); -} - -str str::upper() const -{ - return assume_str(this->attr("upper")()); -} - -}} // namespace boost::python +}}} // namespace boost::python diff --git a/src/tuple.cpp b/src/tuple.cpp index 16b8e773..2762ab27 100644 --- a/src/tuple.cpp +++ b/src/tuple.cpp @@ -1,20 +1,20 @@ #include -namespace boost { namespace python { +namespace boost { namespace python { namespace detail { -detail::new_reference tuple::call(object const& arg_) +detail::new_reference tuple_base::call(object const& arg_) { return (detail::new_reference)PyObject_CallFunction( (PyObject*)&PyTuple_Type, "(O)", arg_.ptr()); } -tuple::tuple() +tuple_base::tuple_base() : object(detail::new_reference(PyTuple_New(0))) {} -tuple::tuple(object_cref sequence) - : object(tuple::call(sequence)) +tuple_base::tuple_base(object_cref sequence) + : object(call(sequence)) {} -}} // namespace boost::python +}}} // namespace boost::python diff --git a/test/doctest.py b/test/doctest.py deleted file mode 100644 index 2829f1e6..00000000 --- a/test/doctest.py +++ /dev/null @@ -1,1173 +0,0 @@ -# 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()