2.5
[blender-staging.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
73                 items = rna_struct.properties.items()
74         else:
75                 rna_func_name = rna.identifier
76                 rna_func_desc = rna.description
77                 items = rna.parameters.items()
78         
79         for rna_prop_identifier, rna_prop in items:
80                 if rna_prop_identifier=='rna_type':
81                         continue
82                 
83                 # clear vars                    
84                 val = val_error = val_str = rna_prop_type = None
85                 
86                 # ['rna_type', 'name', 'array_length', 'description', 'hard_max', 'hard_min', 'identifier', 'precision', 'readonly', 'soft_max', 'soft_min', 'step', 'subtype', 'type']
87                 #rna_prop=  op_rna.rna_type.properties[attr]
88                 rna_prop_type = rna_prop.type.lower() # enum, float, int, boolean
89                 
90                 
91                 try:            length = rna_prop.array_length
92                 except: length = 0
93                 
94                 array_str = get_array_str(length)
95                 
96                 kw_type_str= "@type %s: %s%s" % (rna_prop_identifier, rna_prop_type, array_str)
97                 kw_param_str= "@param %s: %s" % (rna_prop_identifier, rna_prop.description)
98                 kw_param_set = False
99                 
100                 if func_type=='OPERATOR':
101                         try:
102                                 val = getattr(rna, rna_prop_identifier)
103                                 val_error = False
104                         except:
105                                 val = "'<UNDEFINED>'"
106                                 val_error = True
107                         
108                                 
109                         if val_error:
110                                 val_str = val
111                         elif rna_prop_type=='float':
112                                 if length==0:
113                                         val_str= '%g' % val
114                                         if '.' not in val_str:
115                                                 val_str += '.0'
116                                 else:
117                                         # array
118                                         val_str = str(tuple(val))
119                                 
120                                 kw_param_str += (' in (%s, %s)' % (range_str(rna_prop.hard_min), range_str(rna_prop.hard_max)))
121                                 kw_param_set= True
122                                 
123                         elif rna_prop_type=='int':
124                                 if length==0:
125                                         val_str='%d' % val
126                                 else:
127                                         val_str = str(tuple(val))
128                                 
129                                 # print(dir(rna_prop))
130                                 kw_param_str += (' in (%s, %s)' % (range_str(rna_prop.hard_min), range_str(rna_prop.hard_max)))
131                                 # These strings dont have a max length???
132                                 #kw_param_str += ' (maximum length of %s)' %  (rna_prop.max_length)
133                                 kw_param_set= True
134                                 
135                         elif rna_prop_type=='boolean':
136                                 if length==0:
137                                         if val: val_str='True'
138                                         else:   val_str='False'
139                                 else:
140                                         val_str = str(tuple(val))
141                         
142                         elif rna_prop_type=='enum':
143                                 # no array here?
144                                 val_str="'%s'" % val
145                                 # Too cramped
146                                 kw_param_str += (' in (%s)' % ', '.join(rna_prop.items.keys()))
147                                 
148                                 kw_param_set= True
149                                 
150                         elif rna_prop_type=='string':
151                                 # no array here?
152                                 val_str='"%s"' % val
153                         
154                         # todo - collection - array
155                         # print (rna_prop.type)
156                         
157                         kw_args.append('%s = %s' % (rna_prop_identifier, val_str))
158                         
159                         # stora 
160                 else:
161                         # currently functions dont have a default value
162                         kw_args.append('%s' % (rna_prop_identifier))
163                 
164                 
165                 # Same for operators and functions
166                 kw_arg_attrs.append(kw_type_str)
167                 if kw_param_set:
168                         kw_arg_attrs.append(kw_param_str)
169                 
170         
171         
172         out.write(ident+'def %s(%s):\n' % (rna_func_name, ', '.join(kw_args)))
173         out.write(ident+'\t"""\n')
174         out.write(ident+'\t%s\n' % rna_func_desc)
175         for desc in kw_arg_attrs:
176                 out.write(ident+'\t%s\n' % desc)
177         out.write(ident+'\t@rtype: None\n')
178         out.write(ident+'\t"""\n')
179         
180
181
182 def rna2epy(target_path):
183         
184         # Use for faster lookups
185         # use rna_struct.identifier as the key for each dict
186         rna_full_path_dict =    {}      # store the result of full_rna_struct_path(rna_struct)
187         rna_children_dict =             {}      # store all rna_structs nested from here
188         rna_references_dict =   {}      # store a list of rna path strings that reference this type
189         rna_functions_dict =    {}      # store all functions directly in this type (not inherited)
190         rna_words = set()
191         
192         # def write_func(rna_func, ident):
193         
194         
195         def write_struct(rna_struct, ident):
196                 identifier = rna_struct.identifier
197                 
198                 rna_base = rna_struct.base
199                 
200                 if rna_base:
201                         out.write(ident+ 'class %s(%s):\n' % (identifier, rna_base.identifier))
202                 else:
203                         out.write(ident+ 'class %s:\n' % identifier)
204                 
205                 out.write(ident+ '\t"""\n')
206                 
207                 title = 'The %s Object' % rna_struct.name
208                 description = rna_struct.description
209                 out.write(ident+ '\t%s\n' %  title)
210                 out.write(ident+ '\t%s\n' %  ('=' * len(title)))
211                 out.write(ident+ '\t\t%s\n' %  description)
212                 rna_words.update(description.split())
213                 
214                 
215                 # For convenience, give a list of all places were used.
216                 rna_refs= rna_references_dict[identifier]
217                 
218                 if rna_refs:
219                         out.write(ident+ '\t\t\n')
220                         out.write(ident+ '\t\tReferences\n')
221                         out.write(ident+ '\t\t==========\n')
222                         
223                         for rna_ref_string in rna_refs:
224                                 out.write(ident+ '\t\t\t- L{%s}\n' % rna_ref_string)
225                         
226                         out.write(ident+ '\t\t\n')
227                 
228                 else:
229                         out.write(ident+ '\t\t\n')
230                         out.write(ident+ '\t\t(no references to this struct found)\n')
231                         out.write(ident+ '\t\t\n')
232                 
233                 for rna_prop_identifier, rna_prop in rna_struct.properties.items():
234                         
235                         if rna_prop_identifier=='RNA':
236                                 continue
237                         
238                         if rna_prop_identifier=='rna_type':
239                                 continue
240                         
241                         rna_desc = rna_prop.description
242                         
243                         if rna_desc: rna_words.update(rna_desc.split())
244                         if not rna_desc: rna_desc = rna_prop.name
245                         if not rna_desc: rna_desc = 'Note - No documentation for this property!'
246                         
247                         rna_prop_type = rna_prop.type.lower()
248                         
249                         if rna_prop_type=='collection': collection_str = 'Collection of '
250                         else:                                                   collection_str = ''
251                         
252                         try:            rna_prop_ptr = rna_prop.fixed_type
253                         except: rna_prop_ptr = None
254                         
255                         try:            length = rna_prop.array_length
256                         except: length = 0
257                         
258                         array_str = get_array_str(length)
259                         
260                         if rna_prop.editable:   readonly_str = ''
261                         else:                           readonly_str = ' (readonly)'
262                         
263                         if rna_prop_ptr: # Use the pointer type
264                                 out.write(ident+ '\t@ivar %s: %s\n' %  (rna_prop_identifier, rna_desc))
265                                 out.write(ident+ '\t@type %s: %sL{%s}%s%s\n' %  (rna_prop_identifier, collection_str, rna_prop_ptr.identifier, array_str, readonly_str))
266                         else:
267                                 if rna_prop_type == 'enum':
268                                         if 0:
269                                                 out.write(ident+ '\t@ivar %s: %s in (%s)\n' %  (rna_prop_identifier, rna_desc, ', '.join(rna_prop.items.keys())))
270                                         else:
271                                                 out.write(ident+ '\t@ivar %s: %s in...\n' %  (rna_prop_identifier, rna_desc))
272                                                 for e in rna_prop.items.keys():
273                                                         out.write(ident+ '\t\t- %s\n' % e)
274                                                 
275                                         out.write(ident+ '\t@type %s: %s%s%s\n' %  (rna_prop_identifier, rna_prop_type,  array_str, readonly_str))
276                                 elif rna_prop_type == 'int' or rna_prop_type == 'float':
277                                         out.write(ident+ '\t@ivar %s: %s\n' %  (rna_prop_identifier, rna_desc))
278                                         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) ))
279                                 elif rna_prop_type == 'string':
280                                         out.write(ident+ '\t@ivar %s: %s (maximum length of %s)\n' %  (rna_prop_identifier, rna_desc, rna_prop.max_length))
281                                         out.write(ident+ '\t@type %s: %s%s%s\n' %  (rna_prop_identifier, rna_prop_type, array_str, readonly_str))
282                                 else:
283                                         out.write(ident+ '\t@ivar %s: %s\n' %  (rna_prop_identifier, rna_desc))
284                                         out.write(ident+ '\t@type %s: %s%s%s\n' %  (rna_prop_identifier, rna_prop_type, array_str, readonly_str))
285                                 
286                         
287                 out.write(ident+ '\t"""\n\n')
288                 
289                 
290                 # Write functions 
291                 # for rna_func in rna_struct.functions: # Better ignore inherited (line below)
292                 for rna_func in rna_functions_dict[identifier]:
293                         write_func(rna_func, ident+'\t', out, 'FUNCTION')
294                 
295                 out.write('\n')
296                 
297                 # Now write children recursively
298                 for child in rna_children_dict[identifier]:
299                         write_struct(child, ident + '\t')
300         
301         out = open(target_path, 'w')
302
303         def base_id(rna_struct):
304                 try:            return rna_struct.base.identifier
305                 except: return '' # invalid id
306
307         #structs = [(base_id(rna_struct), rna_struct.identifier, rna_struct) for rna_struct in bpy.doc.structs.values()]
308         '''
309         structs = []
310         for rna_struct in bpy.doc.structs.values():
311                 structs.append( (base_id(rna_struct), rna_struct.identifier, rna_struct) )
312         '''
313         structs = []
314         for rna_type_name in dir(bpy.types):
315                 rna_type = getattr(bpy.types, rna_type_name)
316                 if hasattr(rna_type, '__rna__'):
317                         #if not rna_type_name.startswith('__'):
318                         rna_struct = rna_type.__rna__
319                         identifier = rna_struct.identifier
320                         structs.append( (base_id(rna_struct), identifier, rna_struct) ) 
321                         
322                         
323                         
324                         # Store full rna path 'GameObjectSettings' -> 'Object.GameObjectSettings'
325                         rna_full_path_dict[identifier] = full_rna_struct_path(rna_struct)
326                         
327                         # Store a list of functions, remove inherited later
328                         rna_functions_dict[identifier]= list(rna_struct.functions)
329                         
330                         
331                         # fill in these later
332                         rna_children_dict[identifier]= []
333                         rna_references_dict[identifier]= []
334                         
335                         
336                 else:
337                         print("Ignoring", rna_type_name)
338         
339         
340         # Sucks but we need to copy this so we can check original parent functions
341         rna_functions_dict__copy = {}
342         for key, val in rna_functions_dict.items():
343                 rna_functions_dict__copy[key] = val[:]
344         
345         
346         structs.sort() # not needed but speeds up sort below, setting items without an inheritance first
347         
348         # Arrange so classes are always defined in the correct order
349         deps_ok = False
350         while deps_ok == False:
351                 deps_ok = True
352                 rna_done = set()
353                 
354                 for i, (rna_base, identifier, rna_struct) in enumerate(structs):
355                         
356                         rna_done.add(identifier)
357                         
358                         if rna_base and rna_base not in rna_done:
359                                 deps_ok = False
360                                 data = structs.pop(i)
361                                 ok = False
362                                 while i < len(structs):
363                                         if structs[i][1]==rna_base:
364                                                 structs.insert(i+1, data) # insert after the item we depend on.
365                                                 ok = True
366                                                 break
367                                         i+=1
368                                         
369                                 if not ok:
370                                         print('Dependancy "%s" could not be found for "%s"' % (identifier, rna_base))
371                                 
372                                 break
373         
374         # Done ordering structs
375         
376         
377         # precalc vars to avoid a lot of looping
378         for (rna_base, identifier, rna_struct) in structs:
379                 
380                 
381                 # rna_struct_path = full_rna_struct_path(rna_struct)
382                 rna_struct_path = rna_full_path_dict[identifier]
383                 
384                 for rna_prop_identifier, rna_prop in rna_struct.properties.items():
385                         if rna_prop_identifier=='RNA':
386                                 continue
387                         
388                         if rna_prop_identifier=='rna_type':
389                                 continue
390                         
391                         try:            rna_prop_ptr = rna_prop.fixed_type
392                         except: rna_prop_ptr = None
393                         
394                         # Does this property point to me?
395                         if rna_prop_ptr:
396                                 rna_references_dict[rna_prop_ptr.identifier].append( "%s.%s" % (rna_struct_path, rna_prop_identifier) )
397                 
398                 
399                 
400                 # Store nested children
401                 nested = rna_struct.nested
402                 if nested:
403                         rna_children_dict[nested.identifier].append(rna_struct)
404                 
405                 
406                 if rna_base:
407                         rna_funcs =                     rna_functions_dict[identifier]
408                         if rna_funcs:
409                                 # Remove inherited functions if we have any
410                                 rna_base_funcs =        rna_functions_dict__copy[rna_base]
411                                 rna_funcs[:] =          [f for f in rna_funcs if f not in rna_base_funcs]
412         
413         rna_functions_dict__copy.clear()
414         del rna_functions_dict__copy
415         
416         # Sort the refs, just reads nicer
417         for rna_refs in rna_references_dict.values():
418                 rna_refs.sort()
419         
420         for (rna_base, identifier, rna_struct) in structs:
421                 if rna_struct.nested:
422                         continue
423                 
424                 write_struct(rna_struct, '')
425                 
426                 
427         out.write('\n')
428         out.close()
429         
430         # # We could also just run....
431         # os.system('epydoc source/blender/python/doc/rna.py -o ./source/blender/python/doc/html -v')
432         
433         
434         # Write graphviz
435         out= open(target_path.replace('.py', '.dot'), 'w')
436         out.write('digraph "rna data api" {\n')
437         out.write('\tnode [style=filled, shape = "box"];\n')
438         out.write('\toverlap=false;\n')
439         out.write('\trankdir = LR;\n')
440         out.write('\tsplines=true;\n')
441         out.write('\tratio=auto;\n')
442         
443         
444         
445         out.write('\tsize="10,10"\n')
446         #out.write('\tpage="8.5,11"\n')
447         #out.write('\tcenter=""\n')
448         
449         def isop(rna_struct):
450                 return '_OT_' in rna_struct.identifier
451         
452         
453         for (rna_base, identifier, rna_struct) in structs:
454                 if isop(rna_struct):
455                         continue
456                 
457                 base = rna_struct.base
458                 
459                 
460                 out.write('\t"%s";\n' % identifier)
461         
462         for (rna_base, identifier, rna_struct) in structs:
463                 
464                 if isop(rna_struct):
465                         continue
466                         
467                 base = rna_struct.base
468                 
469                 if base and not isop(base):
470                         out.write('\t"%s" -> "%s" [label="(base)" weight=1.0];\n' % (base.identifier, identifier))
471                 
472                 nested = rna_struct.nested
473                 if nested and not isop(nested):
474                         out.write('\t"%s" -> "%s" [label="(nested)"  weight=1.0];\n' % (nested.identifier, identifier))
475                 
476                 
477                 
478                 rna_refs= rna_references_dict[identifier]
479                 
480                 for rna_ref_string in rna_refs:
481                         
482                         if '_OT_' in rna_ref_string:
483                                 continue
484                         
485                         ref = rna_ref_string.split('.')[-2]
486                         out.write('\t"%s" -> "%s" [label="%s" weight=0.01];\n' % (ref, identifier, rna_ref_string))
487         
488         
489         out.write('}\n')
490         out.close()
491         
492         # # We could also just run....
493         # os.system('dot source/blender/python/doc/rna.dot -Tsvg -o ./source/blender/python/doc/rna.svg')
494         
495         
496         out= open(target_path.replace('.py', '.words'), 'w')
497         rna_words = list(rna_words)
498         rna_words.sort()
499         for w in rna_words:
500                 out.write('%s\n' % w)
501         
502
503 def op2epy(target_path):
504         out = open(target_path, 'w')
505         
506         operators = dir(bpy.ops)
507         operators.remove('add')
508         operators.remove('remove')
509         operators.sort()
510         
511         for op in operators:
512                 
513                 if op.startswith('__'):
514                         continue
515                 
516                 # rna = getattr(bpy.types, op).__rna__
517                 rna = bpy.ops.__rna__(op)
518                 
519                 write_func(rna, '', out, 'OPERATOR')
520         
521         out.write('\n')
522         out.close()
523
524 if __name__ == '__main__':
525         if 'bpy' not in dir():
526                 print("\nError, this script must run from inside blender2.5")
527                 print(script_help_msg)
528                 
529         else:
530                 rna2epy('source/blender/python/doc/rna.py')
531                 op2epy('source/blender/python/doc/bpyoperator.py')
532         
533         import sys
534         sys.exit()