2
0
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:
Dave Abrahams
2002-08-07 23:03:02 +00:00
parent 56e7b2a592
commit bd0257cbe5
11 changed files with 211 additions and 45 deletions

View File

@@ -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>();

View File

@@ -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

View File

@@ -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;
}
};

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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"

View File

@@ -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 */
};
}

View File

@@ -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
View 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
View 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])