2
0
mirror of https://github.com/boostorg/python.git synced 2026-01-20 16:52:15 +00:00

automatic addition of C++ signatures to doc strings

[SVN r32290]
This commit is contained in:
Ralf W. Grosse-Kunstleve
2006-01-12 00:32:29 +00:00
parent efcd2833f1
commit 1cacefc226
5 changed files with 109 additions and 134 deletions

View File

@@ -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<function> const&);

View File

@@ -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<const char*>(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<function>(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);
}

View File

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

View File

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

View File

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