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:
@@ -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&);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
18
test/args.py
18
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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user