mirror of
https://github.com/boostorg/python.git
synced 2026-01-27 19:12:16 +00:00
Full docstring support
[SVN r14734]
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
# include <boost/python/object/pickle_support.hpp>
|
||||
# include <boost/python/make_function.hpp>
|
||||
# include <boost/python/object/add_to_namespace.hpp>
|
||||
# include <boost/python/detail/def_helper.hpp>
|
||||
|
||||
namespace boost { namespace python {
|
||||
|
||||
@@ -57,7 +58,7 @@ namespace detail
|
||||
template <class T, class Holder>
|
||||
static inline void register_copy_constructor(mpl::bool_t<false> const&, Holder*, object const&, T* = 0)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@@ -93,7 +94,7 @@ class class_ : public objects::class_base
|
||||
// Construct with the class name. [ Would have used a default
|
||||
// argument but gcc-2.95.2 choked on typeid(T).name() as a default
|
||||
// parameter value]
|
||||
class_(char const* name);
|
||||
class_(char const* name, char const* doc = 0);
|
||||
|
||||
|
||||
// Wrap a member function or a non-member function which can take
|
||||
@@ -102,27 +103,25 @@ class class_ : public objects::class_base
|
||||
template <class F>
|
||||
self& def(char const* name, F f)
|
||||
{
|
||||
this->def_impl(name, f, 0, &f);
|
||||
this->def_impl(name, f, default_call_policies(), 0, &f);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class Fn, class CallPolicy>
|
||||
self& def(char const* name, Fn fn, CallPolicy policy)
|
||||
template <class Fn, class CallPolicyOrDoc>
|
||||
self& def(char const* name, Fn fn, CallPolicyOrDoc const& policy_or_doc, char const* doc = 0)
|
||||
{
|
||||
return this->def(name
|
||||
, boost::python::make_function(
|
||||
// This bit of nastiness casts F to a member function of T if possible.
|
||||
detail::member_function_cast<T,Fn>::stage1(fn).stage2((T*)0).stage3(fn)
|
||||
, policy)
|
||||
);
|
||||
typedef detail::def_helper<CallPolicyOrDoc> helper;
|
||||
|
||||
this->def_impl(
|
||||
name, fn, helper::get_policy(policy_or_doc), helper::get_doc(policy_or_doc, doc), &fn);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <detail::operator_id id, class L, class R>
|
||||
self& def(detail::operator_<id,L,R> const& op)
|
||||
{
|
||||
typedef detail::operator_<id,L,R> op_t;
|
||||
// Use function::add_to_namespace to achieve overloading if
|
||||
// appropriate.
|
||||
return this->def(op.name(), &op_t::template apply<T>::execute);
|
||||
}
|
||||
|
||||
@@ -139,15 +138,19 @@ class class_ : public objects::class_base
|
||||
);
|
||||
}
|
||||
|
||||
template <class Args, class CallPolicy>
|
||||
self& def_init(Args const&, CallPolicy policy)
|
||||
template <class Args, class CallPolicyOrDoc>
|
||||
self& def_init(Args const&, CallPolicyOrDoc const& policy_or_doc, char const* doc = 0)
|
||||
{
|
||||
return this->def("__init__",
|
||||
typedef detail::def_helper<CallPolicyOrDoc> helper;
|
||||
|
||||
return this->def(
|
||||
"__init__",
|
||||
python::make_constructor<Args>(
|
||||
policy
|
||||
helper::get_policy(policy_or_doc)
|
||||
// Using runtime type selection works around a CWPro7 bug.
|
||||
, objects::select_holder<T,held_type>((held_type*)0).get()
|
||||
)
|
||||
, helper::get_doc(policy_or_doc, doc)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -201,22 +204,26 @@ class class_ : public objects::class_base
|
||||
|
||||
private: // helper functions
|
||||
|
||||
template <class F>
|
||||
inline void def_impl(char const* name, F const& f, char const* doc, ...)
|
||||
template <class Fn, class Policies>
|
||||
inline void def_impl(char const* name, Fn fn, Policies const& policies
|
||||
, char const* doc, ...)
|
||||
{
|
||||
objects::add_to_namespace(
|
||||
*this, name, make_function(
|
||||
*this, name,
|
||||
make_function(
|
||||
// This bit of nastiness casts F to a member function of T if possible.
|
||||
detail::member_function_cast<T,F>::stage1(f).stage2((T*)0).stage3(f))
|
||||
detail::member_function_cast<T,Fn>::stage1(fn).stage2((T*)0).stage3(fn)
|
||||
, policies)
|
||||
, doc);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
inline void def_impl(char const* name, F const& f, char const* doc, object const volatile*)
|
||||
inline void def_impl(char const* name, F f, default_call_policies const&
|
||||
, char const* doc, object const*)
|
||||
{
|
||||
objects::add_to_namespace(*this, name, f, doc);
|
||||
}
|
||||
|
||||
|
||||
private: // types
|
||||
typedef objects::class_id class_id;
|
||||
|
||||
@@ -266,8 +273,8 @@ inline class_<T,X1,X2,X3>::class_()
|
||||
}
|
||||
|
||||
template <class T, class X1, class X2, class X3>
|
||||
inline class_<T,X1,X2,X3>::class_(char const* name)
|
||||
: base(name, id_vector::size, id_vector().ids)
|
||||
inline class_<T,X1,X2,X3>::class_(char const* name, char const* doc)
|
||||
: base(name, id_vector::size, id_vector().ids, doc)
|
||||
{
|
||||
// register converters
|
||||
objects::register_class_from_python<T,bases>();
|
||||
|
||||
@@ -15,7 +15,7 @@ class BOOST_PYTHON_DECL module_base
|
||||
{
|
||||
public:
|
||||
// Create a module. REQUIRES: only one module is created per module.
|
||||
module_base(const char* name);
|
||||
module_base(char const* name, char const* doc = 0);
|
||||
~module_base();
|
||||
|
||||
// Add elements to the module
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
# include <boost/python/detail/module_base.hpp>
|
||||
# include <boost/python/module_init.hpp>
|
||||
# include <boost/python/object_core.hpp>
|
||||
# include <boost/python/detail/def_helper.hpp>
|
||||
|
||||
namespace boost { namespace python {
|
||||
|
||||
@@ -21,8 +22,8 @@ class module : public detail::module_base
|
||||
public:
|
||||
typedef detail::module_base base;
|
||||
|
||||
module(const char* name)
|
||||
: base(name) {}
|
||||
module(char const* name, char const* doc = 0)
|
||||
: base(name, doc) {}
|
||||
|
||||
// Add elements to the module
|
||||
template <class T>
|
||||
@@ -42,17 +43,22 @@ class module : public detail::module_base
|
||||
}
|
||||
|
||||
template <class Fn>
|
||||
module& def(char const* name, Fn fn, char const* doc = 0)
|
||||
module& def(char const* name, Fn fn)
|
||||
{
|
||||
this->setattr_doc(name, boost::python::make_function(fn), doc);
|
||||
this->setattr_doc(
|
||||
name, boost::python::make_function(fn), 0);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template <class Fn, class ResultHandler>
|
||||
module& def(char const* name, Fn fn, ResultHandler handler, char const* doc = 0)
|
||||
template <class Fn, class CallPolicyOrDoc>
|
||||
module& def(char const* name, Fn fn, CallPolicyOrDoc const& policy_or_doc, char const* doc = 0)
|
||||
{
|
||||
this->setattr_doc(name, boost::python::make_function(fn, handler), doc);
|
||||
typedef detail::def_helper<CallPolicyOrDoc> helper;
|
||||
|
||||
this->setattr_doc(
|
||||
name, boost::python::make_function(fn, helper::get_policy(policy_or_doc)),
|
||||
helper::get_doc(policy_or_doc, doc));
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -33,6 +33,7 @@ struct BOOST_PYTHON_DECL class_base : python::api::object
|
||||
, std::size_t num_types // A list of class_ids. The first is the type
|
||||
, class_id const*const types // this is wrapping. The rest are the types of
|
||||
// any bases.
|
||||
, char const* doc = 0 // Docstring, if any.
|
||||
);
|
||||
|
||||
// Retrieve the underlying object
|
||||
|
||||
@@ -34,6 +34,8 @@ struct BOOST_PYTHON_DECL function : PyObject
|
||||
object const& doc() const;
|
||||
void doc(object const& x);
|
||||
|
||||
object const& name() const;
|
||||
|
||||
private: // helper functions
|
||||
void argument_error(PyObject* args, PyObject* keywords) const;
|
||||
void add_overload(handle<function> const&);
|
||||
@@ -43,6 +45,7 @@ struct BOOST_PYTHON_DECL function : PyObject
|
||||
unsigned m_min_args;
|
||||
unsigned m_max_args;
|
||||
handle<function> m_overloads;
|
||||
object m_name;
|
||||
object m_doc;
|
||||
};
|
||||
|
||||
@@ -59,6 +62,11 @@ inline void function::doc(object const& x)
|
||||
this->m_doc = x;
|
||||
}
|
||||
|
||||
inline object const& function::name() const
|
||||
{
|
||||
return this->m_name;
|
||||
}
|
||||
|
||||
}}} // namespace boost::python::objects
|
||||
|
||||
#endif // FUNCTION_DWA20011214_HPP
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
namespace boost { namespace python { namespace detail {
|
||||
|
||||
module_base::module_base(const char* name)
|
||||
module_base::module_base(char const* name, char const* doc)
|
||||
: m_module(
|
||||
allow_null(python::borrowed(
|
||||
scope().ptr()
|
||||
)))
|
||||
{
|
||||
if (doc != 0)
|
||||
scope().attr("__doc__") = doc;
|
||||
}
|
||||
|
||||
module_base::~module_base()
|
||||
|
||||
@@ -279,7 +279,7 @@ namespace objects
|
||||
}
|
||||
|
||||
class_base::class_base(
|
||||
char const* name, std::size_t num_types, class_id const* const types)
|
||||
char const* name, std::size_t num_types, class_id const* const types, char const* doc)
|
||||
: object(new_class(name, num_types, types))
|
||||
{
|
||||
// Insert the new class object in the registry
|
||||
@@ -288,6 +288,9 @@ namespace objects
|
||||
|
||||
// Class object is leaked, for now
|
||||
converters.class_object = (PyTypeObject*)incref(this->ptr());
|
||||
|
||||
if (doc)
|
||||
this->attr("__doc__") = doc;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
|
||||
@@ -24,6 +24,7 @@ function::function(py_function const& implementation, unsigned min_args, unsigne
|
||||
, m_max_args(std::max(max_args,min_args))
|
||||
{
|
||||
PyObject* p = this;
|
||||
::PyType_Ready(&function_type);
|
||||
PyObject_INIT(p, &function_type);
|
||||
}
|
||||
|
||||
@@ -162,6 +163,7 @@ void function::add_to_namespace(
|
||||
|
||||
if (attribute.ptr()->ob_type == &function_type)
|
||||
{
|
||||
function* new_func = downcast<function>(attribute.ptr());
|
||||
PyObject* dict = 0;
|
||||
|
||||
if (PyClass_Check(ns))
|
||||
@@ -180,17 +182,24 @@ void function::add_to_namespace(
|
||||
allow_null(downcast<function>(::PyObject_GetItem(dict, name.ptr())))
|
||||
);
|
||||
|
||||
if (existing.get())
|
||||
if (existing)
|
||||
{
|
||||
if (existing->ob_type == &function_type)
|
||||
static_cast<function*>(attribute.ptr())->add_overload(existing);
|
||||
new_func->add_overload(existing);
|
||||
}
|
||||
// Binary operators need an additional overload which returns NotImplemented
|
||||
else if (is_binary_operator(name_))
|
||||
{
|
||||
static_cast<function*>(attribute.ptr())->add_overload(
|
||||
not_implemented_function());
|
||||
// Binary operators need an additional overload which
|
||||
// returns NotImplemented, so that Python will try the
|
||||
// lxxx functions on the other operand. We add this
|
||||
// overload already when no overloads for the operator
|
||||
// already exist.
|
||||
new_func->add_overload(not_implemented_function());
|
||||
}
|
||||
|
||||
// A function is named the first time it is added to a namespace.
|
||||
if (new_func->name().ptr() == Py_None)
|
||||
new_func->m_name = name;
|
||||
}
|
||||
|
||||
// The PyObject_GetAttrString() call above left an active error
|
||||
@@ -259,21 +268,38 @@ extern "C"
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// Here we're using the function's tp_getset rather than its
|
||||
// tp_members to set up __doc__ and __name__, because tp_members
|
||||
// really depends on having a POD object type (it relies on
|
||||
// offsets). It might make sense to reformulate function as a POD
|
||||
// at some point, but this is much more expedient.
|
||||
//
|
||||
static PyObject* function_get_doc(PyObject* op, void*)
|
||||
{
|
||||
function* f = downcast<function>(op);
|
||||
return incref(f->doc().ptr());
|
||||
return python::incref(f->doc().ptr());
|
||||
}
|
||||
|
||||
static int function_set_doc(PyObject* op, PyObject* doc)
|
||||
static int function_set_doc(PyObject* op, PyObject* doc, void*)
|
||||
{
|
||||
function* f = downcast<function>(op);
|
||||
f->doc(doc ? object(python::detail::borrowed_reference(doc)) : object());
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject* function_get_name(PyObject* op, void*)
|
||||
{
|
||||
function* f = downcast<function>(op);
|
||||
if (f->name().ptr() == Py_None)
|
||||
return PyString_InternFromString("<unnamed Boost.Python function>");
|
||||
else
|
||||
return python::incref(f->name().ptr());
|
||||
}
|
||||
|
||||
static PyGetSetDef function_getsetlist[] = {
|
||||
{"__doc__", function_get_doc, (setter)function_set_doc},
|
||||
{"__name__", function_get_name, 0 },
|
||||
{"__doc__", function_get_doc, function_set_doc},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ rule bpl-test ( name ? : files * )
|
||||
}
|
||||
|
||||
bpl-test minimal ;
|
||||
bpl-test docstring ;
|
||||
bpl-test pearu1 : test_cltree.py cltree.cpp ;
|
||||
bpl-test try : newtest.py m1.cpp m2.cpp ;
|
||||
bpl-test builtin_converters : test_builtin_converters.py test_builtin_converters.cpp ;
|
||||
|
||||
61
test/docstring.cpp
Normal file
61
test/docstring.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
// 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 <string>
|
||||
#include <boost/python/operators.hpp>
|
||||
#include <boost/python/class.hpp>
|
||||
#include <boost/python/module.hpp>
|
||||
#include <boost/python/manage_new_object.hpp>
|
||||
#include "test_class.hpp"
|
||||
|
||||
// Just use math.h here; trying to use std::pow() causes too much
|
||||
// trouble for non-conforming compilers and libraries.
|
||||
#include <math.h>
|
||||
|
||||
using namespace boost::python;
|
||||
|
||||
typedef test_class<> X;
|
||||
|
||||
X* create(int x)
|
||||
{
|
||||
return new X(x);
|
||||
}
|
||||
|
||||
unsigned long fact(unsigned long n)
|
||||
{
|
||||
return n <= 1 ? n : n * fact(n - 1);
|
||||
}
|
||||
|
||||
BOOST_PYTHON_MODULE_INIT(docstring_ext)
|
||||
{
|
||||
module("docstring_ext",
|
||||
|
||||
"A simple test module for documentation strings\n"
|
||||
"Exercised by docstring.py"
|
||||
)
|
||||
|
||||
.add(
|
||||
class_<X>("X",
|
||||
"A simple class wrapper around a C++ int\n"
|
||||
"includes some error-checking"
|
||||
)
|
||||
|
||||
.def_init(args<int>(),
|
||||
"this is the __init__ function\n"
|
||||
"its documentation has two lines."
|
||||
)
|
||||
|
||||
.def("value", &X::value,
|
||||
"gets the value of the object")
|
||||
)
|
||||
|
||||
.def("create", create, return_value_policy<manage_new_object>(),
|
||||
"creates a new X object")
|
||||
|
||||
.def("fact", fact, "compute the factorial")
|
||||
;
|
||||
}
|
||||
|
||||
#include "module_tail.cpp"
|
||||
51
test/docstring.py
Normal file
51
test/docstring.py
Normal file
@@ -0,0 +1,51 @@
|
||||
'''
|
||||
>>> from docstring_ext import *
|
||||
|
||||
>>> def printdoc(x):
|
||||
... print x.__doc__
|
||||
|
||||
>>> printdoc(X)
|
||||
A simple class wrapper around a C++ int
|
||||
includes some error-checking
|
||||
|
||||
>>> printdoc(X.__init__)
|
||||
this is the __init__ function
|
||||
its documentation has two lines.
|
||||
|
||||
>>> printdoc(X.value)
|
||||
gets the value of the object
|
||||
|
||||
>>> printdoc(create)
|
||||
creates a new X object
|
||||
|
||||
>>> printdoc(fact)
|
||||
compute the factorial
|
||||
'''
|
||||
|
||||
def run(args = None):
|
||||
import sys
|
||||
import doctest
|
||||
|
||||
if args is not None:
|
||||
sys.argv = args
|
||||
|
||||
import docstring_ext
|
||||
|
||||
result = doctest.testmod(sys.modules.get(__name__))
|
||||
|
||||
try:
|
||||
print 'printing module help:'
|
||||
help(docstring_ext)
|
||||
except object, x:
|
||||
print '********* failed **********'
|
||||
print x
|
||||
result = list(result)
|
||||
result[0] += 1
|
||||
return tuple(result)
|
||||
|
||||
return result
|
||||
|
||||
if __name__ == '__main__':
|
||||
print "running..."
|
||||
import sys
|
||||
sys.exit(run()[0])
|
||||
Reference in New Issue
Block a user