1 // Copyright Nikolay Mladenov 2007. 2 // Distributed under the Boost Software License, Version 1.0. (See 3 // accompanying file LICENSE_1_0.txt or copy at 4 // http://www.boost.org/LICENSE_1_0.txt) 5 6 // boost::python::make_tuple below are for gcc 4.4 -std=c++0x compatibility 7 // (Intel C++ 10 and 11 with -std=c++0x don't need the full qualification). 8 9 #include <boost/python/converter/registrations.hpp> 10 #include <boost/python/object/function_doc_signature.hpp> 11 #include <boost/python/errors.hpp> 12 #include <boost/python/str.hpp> 13 #include <boost/python/args.hpp> 14 #include <boost/python/tuple.hpp> 15 16 #include <boost/python/detail/signature.hpp> 17 18 #include <vector> 19 20 namespace boost { namespace python { namespace objects { 21 arity_cmp(function const * f1,function const * f2)22 bool function_doc_signature_generator::arity_cmp( function const *f1, function const *f2 ) 23 { 24 return f1->m_fn.max_arity() < f2->m_fn.max_arity(); 25 } 26 are_seq_overloads(function const * f1,function const * f2,bool check_docs)27 bool function_doc_signature_generator::are_seq_overloads( function const *f1, function const *f2 , bool check_docs) 28 { 29 py_function const & impl1 = f1->m_fn; 30 py_function const & impl2 = f2->m_fn; 31 32 //the number of parameters differs by 1 33 if (impl2.max_arity()-impl1.max_arity() != 1) 34 return false; 35 36 // if check docs then f1 shold not have docstring or have the same docstring as f2 37 if (check_docs && f2->doc() != f1->doc() && f1->doc()) 38 return false; 39 40 python::detail::signature_element const* s1 = impl1.signature(); 41 python::detail::signature_element const* s2 = impl2.signature(); 42 43 unsigned size = impl1.max_arity()+1; 44 45 for (unsigned i = 0; i != size; ++i) 46 { 47 //check if the argument types are the same 48 if (s1[i].basename != s2[i].basename) 49 return false; 50 51 //return type 52 if (!i) continue; 53 54 //check if the argument default values are the same 55 bool f1_has_names = bool(f1->m_arg_names); 56 bool f2_has_names = bool(f2->m_arg_names); 57 if ( (f1_has_names && f2_has_names && f2->m_arg_names[i-1]!=f1->m_arg_names[i-1]) 58 || (f1_has_names && !f2_has_names) 59 || (!f1_has_names && f2_has_names && f2->m_arg_names[i-1]!=python::object()) 60 ) 61 return false; 62 } 63 return true; 64 } 65 flatten(function const * f)66 std::vector<function const*> function_doc_signature_generator::flatten(function const *f) 67 { 68 object name = f->name(); 69 70 std::vector<function const*> res; 71 72 while (f) { 73 74 //this if takes out the not_implemented_function 75 if (f->name() == name) 76 res.push_back(f); 77 78 f=f->m_overloads.get(); 79 } 80 81 //std::sort(res.begin(),res.end(), &arity_cmp); 82 83 return res; 84 } split_seq_overloads(const std::vector<function const * > & funcs,bool split_on_doc_change)85 std::vector<function const*> function_doc_signature_generator::split_seq_overloads( const std::vector<function const *> &funcs, bool split_on_doc_change) 86 { 87 std::vector<function const*> res; 88 89 std::vector<function const*>::const_iterator fi = funcs.begin(); 90 91 function const * last = *fi; 92 93 while (++fi != funcs.end()){ 94 95 //check if fi starts a new chain of overloads 96 if (!are_seq_overloads( last, *fi, split_on_doc_change )) 97 res.push_back(last); 98 99 last = *fi; 100 } 101 102 if (last) 103 res.push_back(last); 104 105 return res; 106 } 107 raw_function_pretty_signature(function const * f,size_t n_overloads,bool cpp_types)108 str function_doc_signature_generator::raw_function_pretty_signature(function const *f, size_t n_overloads, bool cpp_types ) 109 { 110 str res("object"); 111 112 res = str("%s %s(%s)" % make_tuple( res, f->m_name, str("tuple args, dict kwds")) ); 113 114 return res; 115 } 116 py_type_str(const python::detail::signature_element & s)117 const char * function_doc_signature_generator::py_type_str(const python::detail::signature_element &s) 118 { 119 if (s.basename==std::string("void")){ 120 static const char * none = "None"; 121 return none; 122 } 123 124 PyTypeObject const * py_type = s.pytype_f?s.pytype_f():0; 125 if ( py_type ) 126 return py_type->tp_name; 127 else{ 128 static const char * object = "object"; 129 return object; 130 } 131 } 132 parameter_string(py_function const & f,size_t n,object arg_names,bool cpp_types)133 str function_doc_signature_generator::parameter_string(py_function const &f, size_t n, object arg_names, bool cpp_types) 134 { 135 str param; 136 137 python::detail::signature_element const * s = f.signature(); 138 if (cpp_types) 139 { 140 if(!n) 141 s = &f.get_return_type(); 142 if (s[n].basename == 0) 143 { 144 return str("..."); 145 } 146 147 param = str(s[n].basename); 148 149 if (s[n].lvalue) 150 param += " {lvalue}"; 151 152 } 153 else 154 { 155 if (n) //we are processing an argument and trying to come up with a name for it 156 { 157 object kv; 158 if ( arg_names && (kv = arg_names[n-1]) ) 159 param = str( " (%s)%s" % make_tuple(py_type_str(s[n]),kv[0]) ); 160 else 161 param = str(" (%s)%s%d" % make_tuple(py_type_str(s[n]),"arg", n) ); 162 } 163 else //we are processing the return type 164 param = py_type_str(f.get_return_type()); 165 } 166 167 //an argument - check for default value and append it 168 if(n && arg_names) 169 { 170 object kv(arg_names[n-1]); 171 if (kv && len(kv) == 2) 172 { 173 param = str("%s=%r" % make_tuple(param, kv[1])); 174 } 175 } 176 return param; 177 } 178 pretty_signature(function const * f,size_t n_overloads,bool cpp_types)179 str function_doc_signature_generator::pretty_signature(function const *f, size_t n_overloads, bool cpp_types ) 180 { 181 py_function 182 const& impl = f->m_fn; 183 ; 184 185 186 unsigned arity = impl.max_arity(); 187 188 if(arity == unsigned(-1))// is this the proper raw function test? 189 { 190 return raw_function_pretty_signature(f,n_overloads,cpp_types); 191 } 192 193 list formal_params; 194 195 size_t n_extra_default_args=0; 196 197 for (unsigned n = 0; n <= arity; ++n) 198 { 199 str param; 200 201 formal_params.append( 202 parameter_string(impl, n, f->m_arg_names, cpp_types) 203 ); 204 205 // find all the arguments with default values preceeding the arity-n_overloads 206 if (n && f->m_arg_names) 207 { 208 object kv(f->m_arg_names[n-1]); 209 210 if (kv && len(kv) == 2) 211 { 212 //default argument preceeding the arity-n_overloads 213 if( n <= arity-n_overloads) 214 ++n_extra_default_args; 215 } 216 else 217 //argument without default, preceeding the arity-n_overloads 218 if( n <= arity-n_overloads) 219 n_extra_default_args = 0; 220 } 221 } 222 223 n_overloads+=n_extra_default_args; 224 225 if (!arity && cpp_types) 226 formal_params.append("void"); 227 228 str ret_type (formal_params.pop(0)); 229 if (cpp_types ) 230 { 231 return str( 232 "%s %s(%s%s%s%s)" 233 % boost::python::make_tuple // workaround, see top 234 ( ret_type 235 , f->m_name 236 , str(",").join(formal_params.slice(0,arity-n_overloads)) 237 , n_overloads ? (n_overloads!=arity?str(" [,"):str("[ ")) : str() 238 , str(" [,").join(formal_params.slice(arity-n_overloads,arity)) 239 , std::string(n_overloads,']') 240 )); 241 }else{ 242 return str( 243 "%s(%s%s%s%s) -> %s" 244 % boost::python::make_tuple // workaround, see top 245 ( f->m_name 246 , str(",").join(formal_params.slice(0,arity-n_overloads)) 247 , n_overloads ? (n_overloads!=arity?str(" [,"):str("[ ")) : str() 248 , str(" [,").join(formal_params.slice(arity-n_overloads,arity)) 249 , std::string(n_overloads,']') 250 , ret_type 251 )); 252 } 253 254 return str( 255 "%s %s(%s%s%s%s) %s" 256 % boost::python::make_tuple // workaround, see top 257 ( cpp_types?ret_type:str("") 258 , f->m_name 259 , str(",").join(formal_params.slice(0,arity-n_overloads)) 260 , n_overloads ? (n_overloads!=arity?str(" [,"):str("[ ")) : str() 261 , str(" [,").join(formal_params.slice(arity-n_overloads,arity)) 262 , std::string(n_overloads,']') 263 , cpp_types?str(""):ret_type 264 )); 265 266 } 267 268 namespace detail { 269 char py_signature_tag[] = "PY signature :"; 270 char cpp_signature_tag[] = "C++ signature :"; 271 } 272 function_doc_signatures(function const * f)273 list function_doc_signature_generator::function_doc_signatures( function const * f) 274 { 275 list signatures; 276 std::vector<function const*> funcs = flatten( f); 277 std::vector<function const*> split_funcs = split_seq_overloads( funcs, true); 278 std::vector<function const*>::const_iterator sfi=split_funcs.begin(), fi; 279 size_t n_overloads=0; 280 for (fi=funcs.begin(); fi!=funcs.end(); ++fi) 281 { 282 if(*sfi == *fi){ 283 if((*fi)->doc()) 284 { 285 str func_doc = str((*fi)->doc()); 286 287 int doc_len = len(func_doc); 288 289 bool show_py_signature = doc_len >= int(sizeof(detail::py_signature_tag)/sizeof(char)-1) 290 && str(detail::py_signature_tag) == func_doc.slice(0, int(sizeof(detail::py_signature_tag)/sizeof(char))-1); 291 if(show_py_signature) 292 { 293 func_doc = str(func_doc.slice(int(sizeof(detail::py_signature_tag)/sizeof(char))-1, _)); 294 doc_len = len(func_doc); 295 } 296 297 bool show_cpp_signature = doc_len >= int(sizeof(detail::cpp_signature_tag)/sizeof(char)-1) 298 && str(detail::cpp_signature_tag) == func_doc.slice( 1-int(sizeof(detail::cpp_signature_tag)/sizeof(char)), _); 299 300 if(show_cpp_signature) 301 { 302 func_doc = str(func_doc.slice(_, 1-int(sizeof(detail::cpp_signature_tag)/sizeof(char)))); 303 doc_len = len(func_doc); 304 } 305 306 str res="\n"; 307 str pad = "\n"; 308 309 if(show_py_signature) 310 { 311 str sig = pretty_signature(*fi, n_overloads,false); 312 res+=sig; 313 if(doc_len || show_cpp_signature )res+=" :"; 314 pad+= str(" "); 315 } 316 317 if(doc_len) 318 { 319 if(show_py_signature) 320 res+=pad; 321 res+= pad.join(func_doc.split("\n")); 322 } 323 324 if( show_cpp_signature) 325 { 326 if(len(res)>1) 327 res+="\n"+pad; 328 res+=detail::cpp_signature_tag+pad+" "+pretty_signature(*fi, n_overloads,true); 329 } 330 331 signatures.append(res); 332 } 333 ++sfi; 334 n_overloads = 0; 335 }else 336 ++n_overloads ; 337 } 338 339 return signatures; 340 } 341 342 343 }}} 344 345