// 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) // boost::python::make_tuple below are for gcc 4.4 -std=c++0x compatibility // (Intel C++ 10 and 11 with -std=c++0x don't need the full qualification). #include #include #include #include #include #include #include #include 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_doc_signature_generator::flatten(function const *f) { object name = f->name(); std::vector 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_doc_signature_generator::split_seq_overloads( const std::vector &funcs, bool split_on_doc_change) { std::vector res; std::vector::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)" % boost::python::make_tuple // workaround, see top ( 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" % boost::python::make_tuple // workaround, see top ( 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" % boost::python::make_tuple // workaround, see top ( 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 funcs = flatten( f); std::vector split_funcs = split_seq_overloads( funcs, true); std::vector::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; } }}}