diff --git a/include/boost/python/object/function.hpp b/include/boost/python/object/function.hpp index f5ed51c0..5ef0f827 100644 --- a/include/boost/python/object/function.hpp +++ b/include/boost/python/object/function.hpp @@ -40,6 +40,8 @@ struct BOOST_PYTHON_DECL function : PyObject object const& name() const; private: // helper functions + object signature(bool show_return_type=false) const; + object signatures(bool show_return_type=false) const; void argument_error(PyObject* args, PyObject* keywords) const; void add_overload(handle const&); diff --git a/src/object/function.cpp b/src/object/function.cpp index ac46000f..c397a07d 100644 --- a/src/object/function.cpp +++ b/src/object/function.cpp @@ -228,6 +228,58 @@ PyObject* function::call(PyObject* args, PyObject* keywords) const return 0; } +object function::signature(bool show_return_type) const +{ + py_function const& impl = m_fn; + + python::detail::signature_element const* return_type = impl.signature(); + python::detail::signature_element const* s = return_type + 1; + + list formal_params; + if (impl.max_arity() == 0) + formal_params.append("void"); + + for (unsigned n = 0; n < impl.max_arity(); ++n) + { + if (s[n].basename == 0) + { + formal_params.append("..."); + break; + } + + str param(s[n].basename); + if (s[n].lvalue) + param += " {lvalue}"; + + if (m_arg_names) // None or empty tuple will test false + { + object kv(m_arg_names[n]); + if (kv) + { + char const* const fmt = len(kv) > 1 ? " %s=%r" : " %s"; + param += fmt % kv; + } + } + + formal_params.append(param); + } + + if (show_return_type) + return "%s(%s) -> %s" % make_tuple( + m_name, str(", ").join(formal_params), return_type->basename); + return "%s(%s)" % make_tuple( + m_name, str(", ").join(formal_params)); +} + +object function::signatures(bool show_return_type) const +{ + list result; + for (function const* f = this; f; f = f->m_overloads.get()) { + result.append(f->signature(show_return_type)); + } + return result; +} + void function::argument_error(PyObject* args, PyObject* /*keywords*/) const { static handle<> exception( @@ -244,50 +296,7 @@ void function::argument_error(PyObject* args, PyObject* /*keywords*/) const } message += str(", ").join(actual_args); message += ")\ndid not match C++ signature:\n "; - - list signatures; - for (function const* f = this; f; f = f->m_overloads.get()) - { - py_function const& impl = f->m_fn; - - python::detail::signature_element const* s - = impl.signature() + 1; // skip over return type - - list formal_params; - if (impl.max_arity() == 0) - formal_params.append("void"); - - for (unsigned n = 0; n < impl.max_arity(); ++n) - { - if (s[n].basename == 0) - { - formal_params.append("..."); - break; - } - - str param(s[n].basename); - if (s[n].lvalue) - param += " {lvalue}"; - - if (f->m_arg_names) // None or empty tuple will test false - { - object kv(f->m_arg_names[n]); - if (kv) - { - char const* const fmt = len(kv) > 1 ? " %s=%r" : " %s"; - param += fmt % kv; - } - } - - formal_params.append(param); - } - - signatures.append( - "%s(%s)" % make_tuple(f->m_name, str(", ").join(formal_params)) - ); - } - - message += str("\n ").join(signatures); + message += str("\n ").join(signatures()); #if BOOST_PYTHON_DEBUG_ERROR_MESSAGES std::printf("\n--------\n%s\n--------\n", extract(message)()); @@ -391,6 +400,12 @@ namespace void function::add_to_namespace( object const& name_space, char const* name_, object const& attribute) +{ + add_to_namespace(name_space, name_, attribute, 0); +} + +void function::add_to_namespace( + object const& name_space, char const* name_, object const& attribute, char const* doc) { str const name(name_); PyObject* const ns = name_space.ptr(); @@ -463,16 +478,11 @@ void function::add_to_namespace( PyErr_Clear(); if (PyObject_SetAttr(ns, name.ptr(), attribute.ptr()) < 0) throw_error_already_set(); -} -void function::add_to_namespace( - object const& name_space, char const* name_, object const& attribute, char const* doc) -{ - add_to_namespace(name_space, name_, attribute); + object mutable_attribute(attribute); if (doc != 0) { // Accumulate documentation - object mutable_attribute(attribute); if ( PyObject_HasAttrString(mutable_attribute.ptr(), "__doc__") @@ -481,21 +491,35 @@ void function::add_to_namespace( mutable_attribute.attr("__doc__") += "\n\n"; mutable_attribute.attr("__doc__") += doc; } - else + else { mutable_attribute.attr("__doc__") = doc; + } + } + + { + if ( PyObject_HasAttrString(mutable_attribute.ptr(), "__doc__") + && mutable_attribute.attr("__doc__")) { + mutable_attribute.attr("__doc__") += "\n"; + } + else { + mutable_attribute.attr("__doc__") = ""; + } + function* f = downcast(attribute.ptr()); + mutable_attribute.attr("__doc__") += str("\n ").join(make_tuple( + "C++ signature:", f->signature(true))); } } BOOST_PYTHON_DECL void add_to_namespace( object const& name_space, char const* name, object const& attribute) { - function::add_to_namespace(name_space, name, attribute); + function::add_to_namespace(name_space, name, attribute, 0); } BOOST_PYTHON_DECL void add_to_namespace( object const& name_space, char const* name, object const& attribute, char const* doc) { - function::add_to_namespace(name_space, name, attribute, doc); + function::add_to_namespace(name_space, name, attribute, doc); } diff --git a/test/args.py b/test/args.py index 5426a663..cff6a7b8 100644 --- a/test/args.py +++ b/test/args.py @@ -85,21 +85,21 @@ >>> q.f1() (1, 4.25, 'wow') ->>> X.f.__doc__ -"This is X.f's docstring" +>>> X.f.__doc__.splitlines()[:2] +["This is X.f's docstring", 'C++ signature:'] >>> xfuncs = (X.inner0, X.inner1, X.inner2, X.inner3, X.inner4, X.inner5) >>> for f in xfuncs: ... print f(q,1).value(), ... print f(q, n = 1).value(), ... print f(q, n = 0).value(), -... print f.__doc__ -1 1 0 docstring -1 1 0 docstring -1 1 0 docstring -1 1 0 docstring -1 1 0 docstring -1 1 0 docstring +... print f.__doc__.splitlines()[:2] +1 1 0 ['docstring', 'C++ signature:'] +1 1 0 ['docstring', 'C++ signature:'] +1 1 0 ['docstring', 'C++ signature:'] +1 1 0 ['docstring', 'C++ signature:'] +1 1 0 ['docstring', 'C++ signature:'] +1 1 0 ['docstring', 'C++ signature:'] >>> x = X(a1 = 44, a0 = 22) >>> x.inner0(0).value() diff --git a/test/defaults.py b/test/defaults.py index 7320467c..6978231b 100644 --- a/test/defaults.py +++ b/test/defaults.py @@ -109,17 +109,18 @@ >>> x.get_state() 'Got exactly two arguments from constructor: string(Phoenix); bool(1); ' ->>> def printdoc(x): -... print x.__doc__ +>>> def selected_doc(obj, *args): +... doc = obj.__doc__.splitlines() +... return [doc[i] for i in args] ->>> printdoc(X.__init__) -doc of init +>>> selected_doc(X.__init__, 0, 2, 4, 6, 8, 9, 10, 12) +['C++ signature:', 'C++ signature:', 'C++ signature:', 'C++ signature:', '', 'doc of init', 'C++ signature:', 'C++ signature:'] ->>> printdoc(Y.__init__) -doc of Y init +>>> selected_doc(Y.__init__, 0, 1) +['doc of Y init', 'C++ signature:'] ->>> printdoc(X.bar2) -doc of X::bar2 +>>> selected_doc(X.bar2, 0, 2, 4, 6, 8, 9, 10) +['C++ signature:', 'C++ signature:', 'C++ signature:', 'C++ signature:', '', 'doc of X::bar2', 'C++ signature:'] """ def run(args = None): @@ -136,48 +137,3 @@ if __name__ == '__main__': status = run()[0] if (status == 0): print "Done." sys.exit(status) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/docstring.py b/test/docstring.py index 81294547..6e53348d 100644 --- a/test/docstring.py +++ b/test/docstring.py @@ -4,31 +4,24 @@ ''' >>> from docstring_ext import * ->>> def printdoc(x): -... print x.__doc__ +>>> def selected_doc(obj, *args): +... doc = obj.__doc__.splitlines() +... return [doc[i] for i in args] ->>> printdoc(X) -A simple class wrapper around a C++ int -includes some error-checking +>>> selected_doc(X.__init__, 0, 1, 2) +['this is the __init__ function', 'its documentation has two lines.', 'C++ signature:'] ->>> printdoc(X.__init__) -this is the __init__ function -its documentation has two lines. +>>> selected_doc(X.value, 0, 1, 3, 4, 5) +['gets the value of the object', 'C++ signature:', '', 'also gets the value of the object', 'C++ signature:'] ->>> printdoc(create) -creates a new X object +>>> selected_doc(create, 0, 1) +['creates a new X object', 'C++ signature:'] + +>>> selected_doc(fact, 0, 1) +['compute the factorial', 'C++ signature:'] ->>> printdoc(fact) -compute the factorial ''' -def check_double_string(): - """ - >>> assert check_double_string() == True - """ - from docstring_ext import X - return X.value.__doc__ == "gets the value of the object\n\nalso gets the value of the object" - def run(args = None): import sys import doctest