Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emit complete type information in generated docstrings #417

Merged
merged 8 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions include/boost/python/class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,21 +372,23 @@ class class_ : public objects::class_base
{
typedef typename api::is_object_operators<F>::type is_obj_or_proxy;

return this->make_fn_impl(
return objects::add_doc(
this->make_fn_impl(
detail::unwrap_wrapper((W*)0)
, f, is_obj_or_proxy(), (char*)0, detail::is_data_member_pointer<F>()
);
), NULL);
}

template <class F>
object make_setter(F f)
{
typedef typename api::is_object_operators<F>::type is_obj_or_proxy;

return this->make_fn_impl(
return objects::add_doc(
this->make_fn_impl(
detail::unwrap_wrapper((W*)0)
, f, is_obj_or_proxy(), (int*)0, detail::is_data_member_pointer<F>()
);
), NULL);
}

template <class T, class F>
Expand Down
8 changes: 7 additions & 1 deletion include/boost/python/converter/pytype_function.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# include <boost/python/converter/registered.hpp>
# include <boost/python/detail/unwind_type.hpp>
# include <boost/python/detail/type_traits.hpp>

# include <boost/python/back_reference.hpp>

namespace boost { namespace python {

Expand Down Expand Up @@ -46,6 +46,12 @@ inline python::type_info unwind_type_id_(boost::type<T>* = 0, mpl::false_ * =0)
return boost::python::detail::unwind_type<unwind_type_id_helper, T> ();
}

template <class T>
inline python::type_info unwind_type_id_(boost::type<back_reference<T> >* = 0, mpl::false_ * =0)
{
return boost::python::detail::unwind_type<unwind_type_id_helper, T> ();
}

inline python::type_info unwind_type_id_(boost::type<void>* = 0, mpl::true_* =0)
{
return type_id<void>();
Expand Down
2 changes: 2 additions & 0 deletions include/boost/python/object/add_to_namespace.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ BOOST_PYTHON_DECL void add_to_namespace(
BOOST_PYTHON_DECL void add_to_namespace(
object const& name_space, char const* name, object const& attribute, char const* doc);

BOOST_PYTHON_DECL object const& add_doc(object const& attribute, char const* doc);

}}} // namespace boost::python::objects

#endif // ADD_TO_NAMESPACE_DWA200286_HPP
5 changes: 5 additions & 0 deletions include/boost/python/object/function.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,17 @@ struct BOOST_PYTHON_DECL function : PyObject
static void add_to_namespace(
object const& name_space, char const* name, object const& attribute, char const* doc);

static object const& add_doc(object const& attribute, char const* doc);

object const& doc() const;
void doc(object const& x);

object const& name() const;

object const& get_namespace() const { return m_namespace; }

object const& get_module() const { return m_module; }

private: // helper functions
object signature(bool show_return_type=false) const;
object signatures(bool show_return_type=false) const;
Expand All @@ -53,6 +57,7 @@ struct BOOST_PYTHON_DECL function : PyObject
handle<function> m_overloads;
object m_name;
object m_namespace;
object m_module;
object m_doc;
object m_arg_names;
unsigned m_nkeyword_values;
Expand Down
4 changes: 2 additions & 2 deletions include/boost/python/object/function_doc_signature.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@
namespace boost { namespace python { namespace objects {

class function_doc_signature_generator{
static const char * py_type_str(const python::detail::signature_element &s);
static str py_type_str(const python::detail::signature_element &s, const object& current_module_name);
static bool arity_cmp( function const *f1, function const *f2 );
static bool are_seq_overloads( function const *f1, function const *f2 , bool check_docs);
static std::vector<function const*> flatten(function const *f);
static std::vector<function const*> split_seq_overloads( const std::vector<function const *> &funcs, bool split_on_doc_change);
static str raw_function_pretty_signature(function const *f, size_t n_overloads, bool cpp_types = false);
static str parameter_string(py_function const &f, size_t n, object arg_names, bool cpp_types);
static str parameter_string(py_function const &f, size_t n, object arg_names, const object& module_name, bool cpp_types);
static str pretty_signature(function const *f, size_t n_overloads, bool cpp_types = false);

public:
Expand Down
1 change: 1 addition & 0 deletions include/boost/python/pure_virtual.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ namespace detail
, make_function(
detail::nullary_function_adaptor<void(*)()>(pure_virtual_called)
, default_call_policies()
, options.keywords()
, detail::error_signature<held_type>(detail::get_signature(m_pmf))
)
);
Expand Down
13 changes: 13 additions & 0 deletions src/object/class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,16 @@ namespace objects
);
}

str qualname(const char *name)
{
#if PY_VERSION_HEX >= 0x03030000
if (PyObject_HasAttrString(scope().ptr(), "__qualname__")) {
return str("%s.%s" % make_tuple(scope().attr("__qualname__"), name));
}
#endif
return str(name);
}

namespace
{
// Find a registered class object corresponding to id. Return a
Expand Down Expand Up @@ -564,6 +574,9 @@ namespace objects

object m = module_prefix();
if (m) d["__module__"] = m;
#if PY_VERSION_HEX >= 0x03030000
d["__qualname__"] = qualname(name);
#endif

if (doc != 0)
d["__doc__"] = doc;
Expand Down
14 changes: 12 additions & 2 deletions src/object/enum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ extern "C"
if (!self->name)
{
return
#if PY_VERSION_HEX >= 0x03000000
#if PY_VERSION_HEX >= 0x03030000
PyUnicode_FromFormat("%S.%S(%ld)", mod, ((PyHeapTypeObject*)(self_->ob_type))->ht_qualname, PyLong_AsLong(self_));
#elif PY_VERSION_HEX >= 0x03000000
PyUnicode_FromFormat("%S.%s(%ld)", mod, self_->ob_type->tp_name, PyLong_AsLong(self_));
#else
PyString_FromFormat("%s.%s(%ld)", PyString_AsString(mod), self_->ob_type->tp_name, PyInt_AS_LONG(self_));
Expand All @@ -62,7 +64,9 @@ extern "C"
return 0;

return
#if PY_VERSION_HEX >= 0x03000000
#if PY_VERSION_HEX >= 0x03030000
PyUnicode_FromFormat("%S.%S.%S", mod, ((PyHeapTypeObject*)(self_->ob_type))->ht_qualname, name);
#elif PY_VERSION_HEX >= 0x03000000
PyUnicode_FromFormat("%S.%s.%S", mod, self_->ob_type->tp_name, name);
#else
PyString_FromFormat("%s.%s.%s",
Expand Down Expand Up @@ -145,6 +149,7 @@ static PyTypeObject enum_type_object = {
};

object module_prefix();
object qualname(const char *name);

namespace
{
Expand Down Expand Up @@ -175,6 +180,11 @@ namespace
object module_name = module_prefix();
if (module_name)
d["__module__"] = module_name;
#if PY_VERSION_HEX >= 0x03030000
object q = qualname(name);
if (q)
d["__qualname__"] = q;
#endif
if (doc)
d["__doc__"] = doc;

Expand Down
65 changes: 45 additions & 20 deletions src/object/function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,30 @@ namespace detail
extern char cpp_signature_tag[];
}

object const& function::add_doc(object const& attribute, char const* doc)
{
str _doc;

if (docstring_options::show_py_signatures_)
{
_doc += str(const_cast<const char*>(detail::py_signature_tag));
}
if (doc != 0 && docstring_options::show_user_defined_)
_doc += doc;

if (docstring_options::show_cpp_signatures_)
{
_doc += str(const_cast<const char*>(detail::cpp_signature_tag));
}
if(_doc)
{
object mutable_attribute(attribute);
mutable_attribute.attr("__doc__")= _doc;
}

return attribute;
}

void function::add_to_namespace(
object const& name_space, char const* name_, object const& attribute, char const* doc)
{
Expand Down Expand Up @@ -489,11 +513,24 @@ void function::add_to_namespace(

assert(!PyErr_Occurred());
handle<> name_space_name(
allow_null(::PyObject_GetAttrString(name_space.ptr(), const_cast<char*>("__name__"))));
allow_null(::PyObject_GetAttrString(name_space.ptr(), const_cast<char*>(
#if PY_VERSION_HEX < 0x03030000
"__name__"
#else
"__qualname__"
#endif
))));
PyErr_Clear();

if (name_space_name)
new_func->m_namespace = object(name_space_name);

object module_name(
PyObject_IsInstance(name_space.ptr(), upcast<PyObject>(&PyModule_Type))
? object(name_space.attr("__name__"))
: api::getattr(name_space, "__module__", str())
);
new_func->m_module = module_name;
}

if (PyObject_SetAttr(ns, name.ptr(), attribute.ptr()) < 0)
Expand Down Expand Up @@ -532,24 +569,7 @@ void function::add_to_namespace(
"C++ signature:", f->signature(true)));
}
*/
str _doc;

if (docstring_options::show_py_signatures_)
{
_doc += str(const_cast<const char*>(detail::py_signature_tag));
}
if (doc != 0 && docstring_options::show_user_defined_)
_doc += doc;

if (docstring_options::show_cpp_signatures_)
{
_doc += str(const_cast<const char*>(detail::cpp_signature_tag));
}
if(_doc)
{
object mutable_attribute(attribute);
mutable_attribute.attr("__doc__")= _doc;
}
add_doc(attribute, doc);
}

BOOST_PYTHON_DECL void add_to_namespace(
Expand All @@ -564,6 +584,11 @@ BOOST_PYTHON_DECL void add_to_namespace(
function::add_to_namespace(name_space, name, attribute, doc);
}

BOOST_PYTHON_DECL object const& add_doc(object const& attribute, char const* doc)
{
return function::add_doc(attribute, doc);
}


namespace
{
Expand Down Expand Up @@ -670,7 +695,7 @@ extern "C"
static PyObject* function_get_module(PyObject* op, void*)
{
function* f = downcast<function>(op);
object const& ns = f->get_namespace();
object const& ns = f->get_module();
if (!ns.is_none()) {
return python::incref(ns.ptr());
}
Expand Down
46 changes: 35 additions & 11 deletions src/object/function_doc_signature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,23 +114,47 @@ namespace boost { namespace python { namespace objects {
return res;
}

const char * function_doc_signature_generator::py_type_str(const python::detail::signature_element &s)
static str get_qualname(const PyTypeObject *py_type)
{
# if PY_VERSION_HEX >= 0x03030000
if ( py_type->tp_flags & Py_TPFLAGS_HEAPTYPE )
return str(handle<>(borrowed(((PyHeapTypeObject*)(py_type))->ht_qualname)));
# endif
return str(py_type->tp_name);
}

str function_doc_signature_generator::py_type_str(const python::detail::signature_element &s, const object &current_module_name)
{
if (s.basename==std::string("void")){
static const char * none = "None";
return none;
return str(none);
}

PyTypeObject const * py_type = s.pytype_f?s.pytype_f():0;
if ( py_type )
return py_type->tp_name;
else{
if ( py_type ) {
str name(get_qualname(py_type));
if ( py_type->tp_flags & Py_TPFLAGS_HEAPTYPE ) {
// Qualify the type name if it is defined in a different module.
PyObject *type_module_name = PyDict_GetItemString(py_type->tp_dict, "__module__");
if (
type_module_name
&& PyObject_RichCompareBool(
type_module_name,
current_module_name.ptr(),
Py_NE
) != 0
) {
return str("%s.%s" % make_tuple(handle<>(borrowed(type_module_name)), name));
}
}
return name;
} else {
static const char * object = "object";
return object;
return str(object);
}
}

str function_doc_signature_generator::parameter_string(py_function const &f, size_t n, object arg_names, bool cpp_types)
str function_doc_signature_generator::parameter_string(py_function const &f, size_t n, object arg_names, const object& current_module_name, bool cpp_types)
{
str param;

Expand All @@ -156,12 +180,12 @@ namespace boost { namespace python { namespace objects {
{
object kv;
if ( arg_names && (kv = arg_names[n-1]) )
param = str( " (%s)%s" % make_tuple(py_type_str(s[n]),kv[0]) );
param = str( " (%s)%s" % make_tuple(py_type_str(s[n], current_module_name),kv[0]) );
else
param = str(" (%s)%s%d" % make_tuple(py_type_str(s[n]),"arg", n) );
param = str(" (%s)%s%d" % make_tuple(py_type_str(s[n], current_module_name),"arg", n) );
}
else //we are processing the return type
param = py_type_str(f.get_return_type());
param = py_type_str(f.get_return_type(), current_module_name);
}

//an argument - check for default value and append it
Expand Down Expand Up @@ -199,7 +223,7 @@ namespace boost { namespace python { namespace objects {
str param;

formal_params.append(
parameter_string(impl, n, f->m_arg_names, cpp_types)
parameter_string(impl, n, f->m_arg_names, f->get_module(), cpp_types)
);

// find all the arguments with default values preceeding the arity-n_overloads
Expand Down
10 changes: 10 additions & 0 deletions src/slice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,14 @@ slice_base::step() const
((PySliceObject*)this->ptr())->step));
}

static struct register_slice_pytype_ptr
{
register_slice_pytype_ptr()
{
const_cast<converter::registration &>(
converter::registry::lookup(boost::python::type_id<boost::python::slice>())
).m_class_object = &PySlice_Type;
}
}register_slice_pytype_ptr_;

} } } // !namespace boost::python::detail
7 changes: 7 additions & 0 deletions test/map_indexing_suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,13 @@
... i.data()
4

#####################################################################
# Test signature...
#####################################################################

>>> AMap.__iter__.__doc__.strip().split("\\n")[0]
'__iter__( (AMap)arg1) -> __main__.iterator :'

#####################################################################
# END....
#####################################################################
Expand Down
Loading