mirror of
https://github.com/boostorg/python.git
synced 2026-01-19 16:32:16 +00:00
343 lines
11 KiB
C++
343 lines
11 KiB
C++
// Copyright Nikolay Mladenov 2007.
|
|
// Distributed under the Boost Software License, Version 1.0. (See
|
|
// accompanying file LICENSE_1_0.txt or copy at
|
|
// http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
#include <boost/python/converter/registrations.hpp>
|
|
#include <boost/python/object/function_doc_signature.hpp>
|
|
#include <boost/python/errors.hpp>
|
|
#include <boost/python/str.hpp>
|
|
#include <boost/python/args.hpp>
|
|
#include <boost/python/tuple.hpp>
|
|
|
|
#include <boost/python/detail/signature.hpp>
|
|
|
|
|
|
#include <vector>
|
|
|
|
namespace boost { namespace python { namespace objects {
|
|
|
|
bool function_doc_signature_generator::arity_cmp( function const *f1, function const *f2 )
|
|
{
|
|
return f1->m_fn.max_arity() < f2->m_fn.max_arity();
|
|
}
|
|
|
|
bool function_doc_signature_generator::are_seq_overloads( function const *f1, function const *f2 , bool check_docs)
|
|
{
|
|
py_function const & impl1 = f1->m_fn;
|
|
py_function const & impl2 = f2->m_fn;
|
|
|
|
//the number of parameters differs by 1
|
|
if (impl2.max_arity()-impl1.max_arity() != 1)
|
|
return false;
|
|
|
|
// if check docs then f1 shold not have docstring or have the same docstring as f2
|
|
if (check_docs && f2->doc() != f1->doc() && f1->doc())
|
|
return false;
|
|
|
|
python::detail::signature_element const* s1 = impl1.signature();
|
|
python::detail::signature_element const* s2 = impl2.signature();
|
|
|
|
unsigned size = impl1.max_arity()+1;
|
|
|
|
for (unsigned i = 0; i != size; ++i)
|
|
{
|
|
//check if the argument types are the same
|
|
if (s1[i].basename != s2[i].basename)
|
|
return false;
|
|
|
|
//return type
|
|
if (!i) continue;
|
|
|
|
//check if the argument default values are the same
|
|
bool f1_has_names = bool(f1->m_arg_names);
|
|
bool f2_has_names = bool(f2->m_arg_names);
|
|
if ( f1_has_names && f2_has_names && f2->m_arg_names[i-1]!=f1->m_arg_names[i-1]
|
|
|| f1_has_names && !f2_has_names
|
|
|| !f1_has_names && f2_has_names && f2->m_arg_names[i-1]!=python::object()
|
|
)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::vector<function const*> function_doc_signature_generator::flatten(function const *f)
|
|
{
|
|
object name = f->name();
|
|
|
|
std::vector<function const*> res;
|
|
|
|
while (f) {
|
|
|
|
//this if takes out the not_implemented_function
|
|
if (f->name() == name)
|
|
res.push_back(f);
|
|
|
|
f=f->m_overloads.get();
|
|
}
|
|
|
|
//std::sort(res.begin(),res.end(), &arity_cmp);
|
|
|
|
return res;
|
|
}
|
|
std::vector<function const*> function_doc_signature_generator::split_seq_overloads( const std::vector<function const *> &funcs, bool split_on_doc_change)
|
|
{
|
|
std::vector<function const*> res;
|
|
|
|
std::vector<function const*>::const_iterator fi = funcs.begin();
|
|
|
|
function const * last = *fi;
|
|
|
|
while (++fi != funcs.end()){
|
|
|
|
//check if fi starts a new chain of overloads
|
|
if (!are_seq_overloads( last, *fi, split_on_doc_change ))
|
|
res.push_back(last);
|
|
|
|
last = *fi;
|
|
}
|
|
|
|
if (last)
|
|
res.push_back(last);
|
|
|
|
return res;
|
|
}
|
|
|
|
str function_doc_signature_generator::raw_function_pretty_signature(function const *f, size_t n_overloads, bool cpp_types )
|
|
{
|
|
str res("object");
|
|
|
|
res = str("%s %s(%s)" % make_tuple( res, f->m_name, str("tuple args, dict kwds")) );
|
|
|
|
return res;
|
|
}
|
|
|
|
const char * function_doc_signature_generator::py_type_str(const python::detail::signature_element &s)
|
|
{
|
|
if (s.basename==std::string("void")){
|
|
static const char * none = "None";
|
|
return none;
|
|
}
|
|
|
|
PyTypeObject const * py_type = s.pytype_f?s.pytype_f():0;
|
|
if ( py_type )
|
|
return py_type->tp_name;
|
|
else{
|
|
static const char * object = "object";
|
|
return object;
|
|
}
|
|
}
|
|
|
|
str function_doc_signature_generator::parameter_string(py_function const &f, size_t n, object arg_names, bool cpp_types)
|
|
{
|
|
str param;
|
|
|
|
python::detail::signature_element const * s = f.signature();
|
|
if (cpp_types)
|
|
{
|
|
if(!n)
|
|
s = &f.get_return_type();
|
|
if (s[n].basename == 0)
|
|
{
|
|
return str("...");
|
|
}
|
|
|
|
param = str(s[n].basename);
|
|
|
|
if (s[n].lvalue)
|
|
param += " {lvalue}";
|
|
|
|
}
|
|
else
|
|
{
|
|
if (n) //we are processing an argument and trying to come up with a name for it
|
|
{
|
|
object kv;
|
|
if ( arg_names && (kv = arg_names[n-1]) )
|
|
param = str( " (%s)%s" % make_tuple(py_type_str(s[n]),kv[0]) );
|
|
else
|
|
param = str(" (%s)%s%d" % make_tuple(py_type_str(s[n]),"arg", n) );
|
|
}
|
|
else //we are processing the return type
|
|
param = py_type_str(f.get_return_type());
|
|
}
|
|
|
|
//an argument - check for default value and append it
|
|
if(n && arg_names)
|
|
{
|
|
object kv(arg_names[n-1]);
|
|
if (kv && len(kv) == 2)
|
|
{
|
|
param = str("%s=%r" % make_tuple(param, kv[1]));
|
|
}
|
|
}
|
|
return param;
|
|
}
|
|
|
|
str function_doc_signature_generator::pretty_signature(function const *f, size_t n_overloads, bool cpp_types )
|
|
{
|
|
py_function
|
|
const& impl = f->m_fn;
|
|
;
|
|
|
|
|
|
unsigned arity = impl.max_arity();
|
|
|
|
if(arity == unsigned(-1))// is this the proper raw function test?
|
|
{
|
|
return raw_function_pretty_signature(f,n_overloads,cpp_types);
|
|
}
|
|
|
|
list formal_params;
|
|
|
|
size_t n_extra_default_args=0;
|
|
|
|
for (unsigned n = 0; n <= arity; ++n)
|
|
{
|
|
str param;
|
|
|
|
formal_params.append(
|
|
parameter_string(impl, n, f->m_arg_names, cpp_types)
|
|
);
|
|
|
|
// find all the arguments with default values preceeding the arity-n_overloads
|
|
if (n && f->m_arg_names)
|
|
{
|
|
object kv(f->m_arg_names[n-1]);
|
|
|
|
if (kv && len(kv) == 2)
|
|
{
|
|
//default argument preceeding the arity-n_overloads
|
|
if( n <= arity-n_overloads)
|
|
++n_extra_default_args;
|
|
}
|
|
else
|
|
//argument without default, preceeding the arity-n_overloads
|
|
if( n <= arity-n_overloads)
|
|
n_extra_default_args = 0;
|
|
}
|
|
}
|
|
|
|
n_overloads+=n_extra_default_args;
|
|
|
|
if (!arity && cpp_types)
|
|
formal_params.append("void");
|
|
|
|
str ret_type (formal_params.pop(0));
|
|
if (cpp_types )
|
|
{
|
|
return str(
|
|
"%s %s(%s%s%s%s)"
|
|
% make_tuple
|
|
( ret_type
|
|
, f->m_name
|
|
, str(",").join(formal_params.slice(0,arity-n_overloads))
|
|
, n_overloads ? (n_overloads!=arity?str(" [,"):str("[ ")) : str()
|
|
, str(" [,").join(formal_params.slice(arity-n_overloads,arity))
|
|
, std::string(n_overloads,']')
|
|
));
|
|
}else{
|
|
return str(
|
|
"%s(%s%s%s%s) -> %s"
|
|
% make_tuple
|
|
( f->m_name
|
|
, str(",").join(formal_params.slice(0,arity-n_overloads))
|
|
, n_overloads ? (n_overloads!=arity?str(" [,"):str("[ ")) : str()
|
|
, str(" [,").join(formal_params.slice(arity-n_overloads,arity))
|
|
, std::string(n_overloads,']')
|
|
, ret_type
|
|
));
|
|
}
|
|
|
|
return str(
|
|
"%s %s(%s%s%s%s) %s"
|
|
% make_tuple
|
|
( cpp_types?ret_type:str("")
|
|
, f->m_name
|
|
, str(",").join(formal_params.slice(0,arity-n_overloads))
|
|
, n_overloads ? (n_overloads!=arity?str(" [,"):str("[ ")) : str()
|
|
, str(" [,").join(formal_params.slice(arity-n_overloads,arity))
|
|
, std::string(n_overloads,']')
|
|
, cpp_types?str(""):ret_type
|
|
));
|
|
|
|
}
|
|
|
|
namespace detail {
|
|
char py_signature_tag[] = "PY signature :";
|
|
char cpp_signature_tag[] = "C++ signature :";
|
|
}
|
|
|
|
list function_doc_signature_generator::function_doc_signatures( function const * f)
|
|
{
|
|
list signatures;
|
|
std::vector<function const*> funcs = flatten( f);
|
|
std::vector<function const*> split_funcs = split_seq_overloads( funcs, true);
|
|
std::vector<function const*>::const_iterator sfi=split_funcs.begin(), fi;
|
|
size_t n_overloads=0;
|
|
for (fi=funcs.begin(); fi!=funcs.end(); ++fi)
|
|
{
|
|
if(*sfi == *fi){
|
|
if((*fi)->doc())
|
|
{
|
|
str func_doc = str((*fi)->doc());
|
|
|
|
int doc_len = len(func_doc);
|
|
|
|
bool show_py_signature = doc_len >= int(sizeof(detail::py_signature_tag)/sizeof(char)-1)
|
|
&& str(detail::py_signature_tag) == func_doc.slice(0, int(sizeof(detail::py_signature_tag)/sizeof(char))-1);
|
|
if(show_py_signature)
|
|
{
|
|
func_doc = str(func_doc.slice(int(sizeof(detail::py_signature_tag)/sizeof(char))-1, _));
|
|
doc_len = len(func_doc);
|
|
}
|
|
|
|
bool show_cpp_signature = doc_len >= int(sizeof(detail::cpp_signature_tag)/sizeof(char)-1)
|
|
&& str(detail::cpp_signature_tag) == func_doc.slice( 1-int(sizeof(detail::cpp_signature_tag)/sizeof(char)), _);
|
|
|
|
if(show_cpp_signature)
|
|
{
|
|
func_doc = str(func_doc.slice(_, 1-int(sizeof(detail::cpp_signature_tag)/sizeof(char))));
|
|
doc_len = len(func_doc);
|
|
}
|
|
|
|
str res="\n";
|
|
str pad = "\n";
|
|
|
|
if(show_py_signature)
|
|
{
|
|
str sig = pretty_signature(*fi, n_overloads,false);
|
|
res+=sig;
|
|
if(doc_len || show_cpp_signature )res+=" :";
|
|
pad+= str(" ");
|
|
}
|
|
|
|
if(doc_len)
|
|
{
|
|
if(show_py_signature)
|
|
res+=pad;
|
|
res+= pad.join(func_doc.split("\n"));
|
|
}
|
|
|
|
if( show_cpp_signature)
|
|
{
|
|
if(len(res)>1)
|
|
res+="\n"+pad;
|
|
res+=detail::cpp_signature_tag+pad+" "+pretty_signature(*fi, n_overloads,true);
|
|
}
|
|
|
|
signatures.append(res);
|
|
}
|
|
++sfi;
|
|
n_overloads = 0;
|
|
}else
|
|
++n_overloads ;
|
|
}
|
|
|
|
return signatures;
|
|
}
|
|
|
|
|
|
}}}
|
|
|