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