Undo revision 23130 which was a merge with 2.5, a messy one because I did something...
[blender.git] / source / blender / python / epy_doc_gen.py
1  # ***** BEGIN GPL LICENSE BLOCK *****
2  #
3  # This program is free software; you can redistribute it and/or
4  # modify it under the terms of the GNU General Public License
5  # as published by the Free Software Foundation; either version 2
6  # of the License, or (at your option) any later version.
7  #
8  # This program is distributed in the hope that it will be useful,
9  # but WITHOUT ANY WARRANTY; without even the implied warranty of
10  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  # GNU General Public License for more details.
12  #
13  # You should have received a copy of the GNU General Public License
14  # along with this program; if not, write to the Free Software Foundation,
15  # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16  #
17  # Contributor(s): Campbell Barton
18  #
19  # #**** END GPL LICENSE BLOCK #****
20
21 script_help_msg = '''
22 Usage,
23 run this script from blenders root path once you have compiled blender
24         ./blender.bin -P source/blender/python/epy_doc_gen.py
25
26 This will generate rna.py and bpyoperator.py in "./source/blender/python/doc/"
27 Generate html docs  by running...
28
29         epydoc source/blender/python/doc/*.py -v \\
30                         -o source/blender/python/doc/html \\
31                         --inheritance=included \\
32                         --no-sourcecode \\
33                         --graph=classtree \\
34                         --graph-font-size=8
35
36 '''
37
38 # if you dont have graphvis installed ommit the --graph arg.
39
40 def range_str(val):
41         if val < -10000000:     return '-inf'
42         if val >  10000000:     return 'inf'
43         if type(val)==float:
44                 return '%g'  % val
45         else:
46                 return str(val) 
47
48 def get_array_str(length):
49         if length > 0:  return ' array of %d items' % length
50         else:           return ''
51
52 def full_rna_struct_path(rna_struct):
53         '''
54         Needed when referencing one struct from another
55         '''
56         nested = rna_struct.nested
57         if nested:
58                 return "%s.%s" % (full_rna_struct_path(nested), rna_struct.identifier)
59         else:
60                 return rna_struct.identifier
61
62 def write_func(rna, ident, out, func_type):
63         # Keyword attributes
64         kw_args = [] # "foo = 1", "bar=0.5", "spam='ENUM'"
65         kw_arg_attrs = [] # "@type mode: int"
66         
67         rna_struct= rna.rna_type
68         
69         # Operators and functions work differently
70         if func_type=='OPERATOR':
71                 rna_func_name = rna_struct.identifier
72                 rna_func_desc = rna_struct.description.strip()
73                 items = rna_struct.properties.items()
74         else:
75                 rna_func_name = rna.identifier
76                 rna_func_desc = rna.description.strip()
77                 items = rna.parameters.items()
78         
79         
80         for rna_prop_identifier, rna_prop in items:
81                 if rna_prop_identifier=='rna_type':
82                         continue
83                 
84                 # clear vars                    
85                 val = val_error = val_str = rna_prop_type = None
86                 
87                 # ['rna_type', 'name', 'array_length', 'description', 'hard_max', 'hard_min', 'identifier', 'precision', 'readonly', 'soft_max', 'soft_min', 'step', 'subtype', 'type']
88                 #rna_prop=  op_rna.rna_type.properties[attr]
89                 rna_prop_type = rna_prop.type.lower() # enum, float, int, boolean
90                 
91                 
92                 # only for rna functions, operators should not get pointers as args
93                 if rna_prop_type=='pointer':
94                         rna_prop_type_refine = "L{%s}" % rna_prop.fixed_type.identifier
95                 else:
96                         rna_prop_type_refine = rna_prop_type
97                 
98                 
99                 try:            length = rna_prop.array_length
100                 except: length = 0
101                 
102                 array_str = get_array_str(length)
103                 
104                 if rna_prop.use_return:
105                         kw_type_str= "@rtype: %s%s" % (rna_prop_type_refine, array_str)
106                         kw_param_str= "@return: %s" % (rna_prop.description.strip())
107                 else:
108                         kw_type_str= "@type %s: %s%s" % (rna_prop_identifier, rna_prop_type_refine, array_str)
109                         kw_param_str= "@param %s: %s" % (rna_prop_identifier, rna_prop.description.strip())
110                 
111                 kw_param_set = False
112                 
113                 if func_type=='OPERATOR':
114                         try:
115                                 val = getattr(rna, rna_prop_identifier)
116                                 val_error = False
117                         except:
118                                 val = "'<UNDEFINED>'"
119                                 val_error = True
120                         
121                                 
122                         if val_error:
123                                 val_str = val
124                         elif rna_prop_type=='float':
125                                 if length==0:
126                                         val_str= '%g' % val
127                                         if '.' not in val_str:
128                                                 val_str += '.0'
129                                 else:
130                                         # array
131                                         val_str = str(tuple(val))
132                                 
133                                 kw_param_str += (' in (%s, %s)' % (range_str(rna_prop.hard_min), range_str(rna_prop.hard_max)))
134                                 kw_param_set= True
135                                 
136                         elif rna_prop_type=='int':
137                                 if length==0:
138                                         val_str='%d' % val
139                                 else:
140                                         val_str = str(tuple(val))
141                                 
142                                 # print(dir(rna_prop))
143                                 kw_param_str += (' in (%s, %s)' % (range_str(rna_prop.hard_min), range_str(rna_prop.hard_max)))
144                                 # These strings dont have a max length???
145                                 #kw_param_str += ' (maximum length of %s)' %  (rna_prop.max_length)
146                                 kw_param_set= True
147                                 
148                         elif rna_prop_type=='boolean':
149                                 if length==0:
150                                         if val: val_str='True'
151                                         else:   val_str='False'
152                                 else:
153                                         val_str = str(tuple(val))
154                         
155                         elif rna_prop_type=='enum':
156                                 # no array here?
157                                 val_str="'%s'" % val
158                                 # Too cramped
159                                 kw_param_str += (' in (%s)' % ', '.join(rna_prop.items.keys()))
160                                 
161                                 kw_param_set= True
162                                 
163                         elif rna_prop_type=='string':
164                                 # no array here?
165                                 val_str='"%s"' % val
166                         
167                         # todo - collection - array
168                         # print (rna_prop.type)
169                         
170                         kw_args.append('%s = %s' % (rna_prop_identifier, val_str))
171                         
172                         # stora 
173                 else:
174                         # currently functions dont have a default value
175                         if not rna_prop.use_return:
176                                 kw_args.append('%s' % (rna_prop_identifier))
177                         else:
178                                 kw_param_set = True
179
180                 
181                 # Same for operators and functions
182                 kw_arg_attrs.append(kw_type_str)
183                 if kw_param_set:
184                         kw_arg_attrs.append(kw_param_str)
185                 
186         
187         
188         out.write(ident+'def %s(%s):\n' % (rna_func_name, ', '.join(kw_args)))
189         out.write(ident+'\t"""\n')
190         out.write(ident+'\t%s\n' % rna_func_desc)
191         for desc in kw_arg_attrs:
192                 out.write(ident+'\t%s\n' % desc)
193                 
194         # out.write(ident+'\t@rtype: None\n') # implicit
195         out.write(ident+'\t"""\n')
196         
197
198
199 def rna2epy(target_path):
200         
201         # Use for faster lookups
202         # use rna_struct.identifier as the key for each dict
203         rna_struct_dict =               {}  # store identifier:rna lookups
204         rna_full_path_dict =    {}      # store the result of full_rna_struct_path(rna_struct)
205         rna_children_dict =             {}      # store all rna_structs nested from here
206         rna_references_dict =   {}      # store a list of rna path strings that reference this type
207         rna_functions_dict =    {}      # store all functions directly in this type (not inherited)
208         rna_words = set()
209         
210         # def write_func(rna_func, ident):
211         
212         
213         def write_struct(rna_struct, ident):
214                 identifier = rna_struct.identifier
215                 
216                 rna_base = rna_struct.base
217                 
218                 if rna_base:
219                         out.write(ident+ 'class %s(%s):\n' % (identifier, rna_base.identifier))
220                         rna_base_prop_keys = rna_base.properties.keys() # could be cached
221                         rna_base_func_keys = [f.identifier for f in rna_base.functions]
222                 else:
223                         out.write(ident+ 'class %s:\n' % identifier)
224                         rna_base_prop_keys = []
225                         rna_base_func_keys = []
226                 
227                 out.write(ident+ '\t"""\n')
228                 
229                 title = 'The %s Object' % rna_struct.name
230                 description = rna_struct.description.strip()
231                 out.write(ident+ '\t%s\n' %  title)
232                 out.write(ident+ '\t%s\n' %  ('=' * len(title)))
233                 out.write(ident+ '\t\t%s\n' %  description)
234                 rna_words.update(description.split())
235                 
236                 
237                 # For convenience, give a list of all places were used.
238                 rna_refs= rna_references_dict[identifier]
239                 
240                 if rna_refs:
241                         out.write(ident+ '\t\t\n')
242                         out.write(ident+ '\t\tReferences\n')
243                         out.write(ident+ '\t\t==========\n')
244                         
245                         for rna_ref_string in rna_refs:
246                                 out.write(ident+ '\t\t\t- L{%s}\n' % rna_ref_string)
247                         
248                         out.write(ident+ '\t\t\n')
249                 
250                 else:
251                         out.write(ident+ '\t\t\n')
252                         out.write(ident+ '\t\t(no references to this struct found)\n')
253                         out.write(ident+ '\t\t\n')
254                 
255                 for rna_prop_identifier, rna_prop in rna_struct.properties.items():
256                         
257                         if rna_prop_identifier=='RNA':                                  continue
258                         if rna_prop_identifier=='rna_type':                             continue
259                         if rna_prop_identifier in rna_base_prop_keys:   continue # does this prop exist in our parent class, if so skip
260                         
261                         rna_desc = rna_prop.description.strip()
262                         
263                         if rna_desc: rna_words.update(rna_desc.split())
264                         if not rna_desc: rna_desc = rna_prop.name
265                         if not rna_desc: rna_desc = 'Note - No documentation for this property!'
266                         
267                         rna_prop_type = rna_prop.type.lower()
268                         
269                         if rna_prop_type=='collection': collection_str = 'Collection of '
270                         else:                                                   collection_str = ''
271                         
272                         try:            rna_prop_ptr = rna_prop.fixed_type
273                         except: rna_prop_ptr = None
274                         
275                         try:            length = rna_prop.array_length
276                         except: length = 0
277                         
278                         array_str = get_array_str(length)
279                         
280                         if rna_prop.editable:   readonly_str = ''
281                         else:                           readonly_str = ' (readonly)'
282                         
283                         if rna_prop_ptr: # Use the pointer type
284                                 out.write(ident+ '\t@ivar %s: %s\n' %  (rna_prop_identifier, rna_desc))
285                                 out.write(ident+ '\t@type %s: %sL{%s}%s%s\n' %  (rna_prop_identifier, collection_str, rna_prop_ptr.identifier, array_str, readonly_str))
286                         else:
287                                 if rna_prop_type == 'enum':
288                                         if 0:
289                                                 out.write(ident+ '\t@ivar %s: %s in (%s)\n' %  (rna_prop_identifier, rna_desc, ', '.join(rna_prop.items.keys())))
290                                         else:
291                                                 out.write(ident+ '\t@ivar %s: %s in...\n' %  (rna_prop_identifier, rna_desc))
292                                                 for e, e_rna_prop in rna_prop.items.items():
293                                                         #out.write(ident+ '\t\t- %s: %s\n' % (e, e_rna_prop.description)) # XXX - segfaults, FIXME
294                                                         out.write(ident+ '\t\t- %s\n' % e)
295                                                 
296                                         out.write(ident+ '\t@type %s: %s%s%s\n' %  (rna_prop_identifier, rna_prop_type,  array_str, readonly_str))
297                                 elif rna_prop_type == 'int' or rna_prop_type == 'float':
298                                         out.write(ident+ '\t@ivar %s: %s\n' %  (rna_prop_identifier, rna_desc))
299                                         out.write(ident+ '\t@type %s: %s%s%s in [%s, %s]\n' %  (rna_prop_identifier, rna_prop_type, array_str, readonly_str, range_str(rna_prop.hard_min), range_str(rna_prop.hard_max) ))
300                                 elif rna_prop_type == 'string':
301                                         out.write(ident+ '\t@ivar %s: %s (maximum length of %s)\n' %  (rna_prop_identifier, rna_desc, rna_prop.max_length))
302                                         out.write(ident+ '\t@type %s: %s%s%s\n' %  (rna_prop_identifier, rna_prop_type, array_str, readonly_str))
303                                 else:
304                                         out.write(ident+ '\t@ivar %s: %s\n' %  (rna_prop_identifier, rna_desc))
305                                         out.write(ident+ '\t@type %s: %s%s%s\n' %  (rna_prop_identifier, rna_prop_type, array_str, readonly_str))
306                                 
307                         
308                 out.write(ident+ '\t"""\n\n')
309                 
310                 
311                 # Write functions 
312                 # for rna_func in rna_struct.functions: # Better ignore inherited (line below)
313                 for rna_func in rna_functions_dict[identifier]:
314                         if rna_func not in rna_base_func_keys:
315                                 write_func(rna_func, ident+'\t', out, 'FUNCTION')
316                 
317                 out.write('\n')
318                 
319                 # Now write children recursively
320                 for child in rna_children_dict[identifier]:
321                         write_struct(child, ident + '\t')
322         
323         out = open(target_path, 'w')
324
325         def base_id(rna_struct):
326                 try:            return rna_struct.base.identifier
327                 except: return '' # invalid id
328
329         #structs = [(base_id(rna_struct), rna_struct.identifier, rna_struct) for rna_struct in bpy.doc.structs.values()]
330         '''
331         structs = []
332         for rna_struct in bpy.doc.structs.values():
333                 structs.append( (base_id(rna_struct), rna_struct.identifier, rna_struct) )
334         '''
335         structs = []
336         for rna_type_name in dir(bpy.types):
337                 rna_type = getattr(bpy.types, rna_type_name)
338                 
339                 try:            rna_struct = rna_type.__rna__
340                 except: rna_struct = None
341                 
342                 if rna_struct:
343                         #if not rna_type_name.startswith('__'):
344                         
345                         identifier = rna_struct.identifier
346                         structs.append( (base_id(rna_struct), identifier, rna_struct) ) 
347                         
348                         # Simple lookup
349                         rna_struct_dict[identifier] = rna_struct
350                         
351                         # Store full rna path 'GameObjectSettings' -> 'Object.GameObjectSettings'
352                         rna_full_path_dict[identifier] = full_rna_struct_path(rna_struct)
353                         
354                         # Store a list of functions, remove inherited later
355                         rna_functions_dict[identifier]= list(rna_struct.functions)
356                         
357                         
358                         # fill in these later
359                         rna_children_dict[identifier]= []
360                         rna_references_dict[identifier]= []
361                         
362                         
363                 else:
364                         print("Ignoring", rna_type_name)
365         
366         
367         # Sucks but we need to copy this so we can check original parent functions
368         rna_functions_dict__copy = {}
369         for key, val in rna_functions_dict.items():
370                 rna_functions_dict__copy[key] = val[:]
371         
372         
373         structs.sort() # not needed but speeds up sort below, setting items without an inheritance first
374         
375         # Arrange so classes are always defined in the correct order
376         deps_ok = False
377         while deps_ok == False:
378                 deps_ok = True
379                 rna_done = set()
380                 
381                 for i, (rna_base, identifier, rna_struct) in enumerate(structs):
382                         
383                         rna_done.add(identifier)
384                         
385                         if rna_base and rna_base not in rna_done:
386                                 deps_ok = False
387                                 data = structs.pop(i)
388                                 ok = False
389                                 while i < len(structs):
390                                         if structs[i][1]==rna_base:
391                                                 structs.insert(i+1, data) # insert after the item we depend on.
392                                                 ok = True
393                                                 break
394                                         i+=1
395                                         
396                                 if not ok:
397                                         print('Dependancy "%s" could not be found for "%s"' % (identifier, rna_base))
398                                 
399                                 break
400         
401         # Done ordering structs
402         
403         
404         # precalc vars to avoid a lot of looping
405         for (rna_base, identifier, rna_struct) in structs:
406                 
407                 if rna_base:
408                         rna_base_prop_keys = rna_struct_dict[rna_base].properties.keys() # could cache
409                         rna_base_func_keys = [f.identifier for f in rna_struct_dict[rna_base].functions]
410                 else:
411                         rna_base_prop_keys = []
412                         rna_base_func_keys= []
413                 
414                 # rna_struct_path = full_rna_struct_path(rna_struct)
415                 rna_struct_path = rna_full_path_dict[identifier]
416                 
417                 for rna_prop_identifier, rna_prop in rna_struct.properties.items():
418                         
419                         if rna_prop_identifier=='RNA':                                  continue
420                         if rna_prop_identifier=='rna_type':                             continue
421                         if rna_prop_identifier in rna_base_prop_keys:   continue
422                         
423                         try:            rna_prop_ptr = rna_prop.fixed_type
424                         except: rna_prop_ptr = None
425                         
426                         # Does this property point to me?
427                         if rna_prop_ptr:
428                                 rna_references_dict[rna_prop_ptr.identifier].append( "%s.%s" % (rna_struct_path, rna_prop_identifier) )
429                 
430                 for rna_func in rna_struct.functions:
431                         for rna_prop_identifier, rna_prop in rna_func.parameters.items():
432                                 
433                                 if rna_prop_identifier=='RNA':                                  continue
434                                 if rna_prop_identifier=='rna_type':                             continue
435                                 if rna_prop_identifier in rna_base_func_keys:   continue
436                                         
437                                 
438                                 try:            rna_prop_ptr = rna_prop.fixed_type
439                                 except: rna_prop_ptr = None
440                                 
441                                 # Does this property point to me?
442                                 if rna_prop_ptr:
443                                         rna_references_dict[rna_prop_ptr.identifier].append( "%s.%s" % (rna_struct_path, rna_func.identifier) )
444                         
445                 
446                 # Store nested children
447                 nested = rna_struct.nested
448                 if nested:
449                         rna_children_dict[nested.identifier].append(rna_struct)
450                 
451                 
452                 if rna_base:
453                         rna_funcs =                     rna_functions_dict[identifier]
454                         if rna_funcs:
455                                 # Remove inherited functions if we have any
456                                 rna_base_funcs =        rna_functions_dict__copy[rna_base]
457                                 rna_funcs[:] =          [f for f in rna_funcs if f not in rna_base_funcs]
458         
459         rna_functions_dict__copy.clear()
460         del rna_functions_dict__copy
461         
462         # Sort the refs, just reads nicer
463         for rna_refs in rna_references_dict.values():
464                 rna_refs.sort()
465         
466         for (rna_base, identifier, rna_struct) in structs:
467                 if rna_struct.nested:
468                         continue
469                 
470                 write_struct(rna_struct, '')
471                 
472                 
473         out.write('\n')
474         out.close()
475         
476         # # We could also just run....
477         # os.system('epydoc source/blender/python/doc/rna.py -o ./source/blender/python/doc/html -v')
478         
479         
480         # Write graphviz
481         out= open(target_path.replace('.py', '.dot'), 'w')
482         out.write('digraph "rna data api" {\n')
483         out.write('\tnode [style=filled, shape = "box"];\n')
484         out.write('\toverlap=false;\n')
485         out.write('\trankdir = LR;\n')
486         out.write('\tsplines=true;\n')
487         out.write('\tratio=auto;\n')
488         
489         
490         
491         out.write('\tsize="10,10"\n')
492         #out.write('\tpage="8.5,11"\n')
493         #out.write('\tcenter=""\n')
494         
495         def isop(rna_struct):
496                 return '_OT_' in rna_struct.identifier
497         
498         
499         for (rna_base, identifier, rna_struct) in structs:
500                 if isop(rna_struct):
501                         continue
502                 
503                 base = rna_struct.base
504                 
505                 
506                 out.write('\t"%s";\n' % identifier)
507         
508         for (rna_base, identifier, rna_struct) in structs:
509                 
510                 if isop(rna_struct):
511                         continue
512                         
513                 base = rna_struct.base
514                 
515                 if base and not isop(base):
516                         out.write('\t"%s" -> "%s" [label="(base)" weight=1.0];\n' % (base.identifier, identifier))
517                 
518                 nested = rna_struct.nested
519                 if nested and not isop(nested):
520                         out.write('\t"%s" -> "%s" [label="(nested)"  weight=1.0];\n' % (nested.identifier, identifier))
521                 
522                 
523                 
524                 rna_refs= rna_references_dict[identifier]
525                 
526                 for rna_ref_string in rna_refs:
527                         
528                         if '_OT_' in rna_ref_string:
529                                 continue
530                         
531                         ref = rna_ref_string.split('.')[-2]
532                         out.write('\t"%s" -> "%s" [label="%s" weight=0.01];\n' % (ref, identifier, rna_ref_string))
533         
534         
535         out.write('}\n')
536         out.close()
537         
538         # # We could also just run....
539         # os.system('dot source/blender/python/doc/rna.dot -Tsvg -o ./source/blender/python/doc/rna.svg')
540         
541         
542         out= open(target_path.replace('.py', '.words'), 'w')
543         rna_words = list(rna_words)
544         rna_words.sort()
545         for w in rna_words:
546                 out.write('%s\n' % w)
547         
548
549 def op2epy(target_path):
550         out = open(target_path, 'w')
551         
552         op_mods = dir(bpy.ops)
553         op_mods.remove('add')
554         op_mods.remove('remove')
555         
556         for op_mod_name in sorted(op_mods):
557                 if op_mod_name.startswith('__'):
558                         continue
559
560                 op_mod = getattr(bpy.ops, op_mod_name)
561                 
562                 operators = dir(op_mod)
563                 for op in sorted(operators):
564                         # rna = getattr(bpy.types, op).__rna__
565                         rna = getattr(op_mod, op).get_rna()
566                         write_func(rna, '', out, 'OPERATOR')
567         
568         out.write('\n')
569         out.close()
570
571 if __name__ == '__main__':
572         if 'bpy' not in dir():
573                 print("\nError, this script must run from inside blender2.5")
574                 print(script_help_msg)
575                 
576         else:
577                 rna2epy('source/blender/python/doc/rna.py')
578                 op2epy('source/blender/python/doc/bpyoperator.py')
579         
580         import sys
581         sys.exit()