Fix T52842: Incorrect description for bpy.types.MeshTextureFace.uv
[blender-staging.git] / release / scripts / modules / rna_info.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 # classes for extracting info from blenders internal classes
22
23 import bpy
24
25 # use to strip python paths
26 script_paths = bpy.utils.script_paths()
27
28 _FAKE_STRUCT_SUBCLASS = True
29
30
31 def _get_direct_attr(rna_type, attr):
32     props = getattr(rna_type, attr)
33     base = rna_type.base
34
35     if not base:
36         return [prop for prop in props]
37     else:
38         props_base = getattr(base, attr).values()
39         return [prop for prop in props if prop not in props_base]
40
41
42 def get_direct_properties(rna_type):
43     return _get_direct_attr(rna_type, "properties")
44
45
46 def get_direct_functions(rna_type):
47     return _get_direct_attr(rna_type, "functions")
48
49
50 def rna_id_ignore(rna_id):
51     if rna_id == "rna_type":
52         return True
53
54     if "_OT_" in rna_id:
55         return True
56     if "_MT_" in rna_id:
57         return True
58     if "_PT_" in rna_id:
59         return True
60     if "_HT_" in rna_id:
61         return True
62     if "_KSI_" in rna_id:
63         return True
64     return False
65
66
67 def range_str(val):
68     if val < -10000000:
69         return "-inf"
70     elif val > 10000000:
71         return "inf"
72     elif type(val) == float:
73         return '%g' % val
74     else:
75         return str(val)
76
77
78 def float_as_string(f):
79     val_str = "%g" % f
80     if '.' not in val_str and '-' not in val_str:  # value could be 1e-05
81         val_str += '.0'
82     return val_str
83
84
85 def get_py_class_from_rna(rna_type):
86     """ Get's the Python type for a class which isn't necessarily added to ``bpy.types``.
87     """
88     identifier = rna_type.identifier
89     py_class = getattr(bpy.types, identifier, None)
90     if py_class is not None:
91         return py_class
92
93     def subclasses_recurse(cls):
94         for c in cls.__subclasses__():
95             # is_registered
96             if "bl_rna" in cls.__dict__:
97                 yield c
98             yield from subclasses_recurse(c)
99
100     while py_class is None:
101         base = rna_type.base
102         if base is None:
103             raise Exception("can't find type")
104         py_class_base = getattr(bpy.types, base.identifier, None)
105         if py_class_base is not None:
106             for cls in subclasses_recurse(py_class_base):
107                 if cls.bl_rna.identifier == identifier:
108                     return cls
109
110
111 class InfoStructRNA:
112     __slots__ = (
113         "bl_rna",
114         "identifier",
115         "name",
116         "description",
117         "base",
118         "nested",
119         "full_path",
120         "functions",
121         "children",
122         "references",
123         "properties",
124     )
125
126     global_lookup = {}
127
128     def __init__(self, rna_type):
129         self.bl_rna = rna_type
130
131         self.identifier = rna_type.identifier
132         self.name = rna_type.name
133         self.description = rna_type.description.strip()
134
135         # set later
136         self.base = None
137         self.nested = None
138         self.full_path = ""
139
140         self.functions = []
141         self.children = []
142         self.references = []
143         self.properties = []
144
145     def build(self):
146         rna_type = self.bl_rna
147         parent_id = self.identifier
148         self.properties[:] = [GetInfoPropertyRNA(rna_prop, parent_id)
149                               for rna_prop in get_direct_properties(rna_type) if rna_prop.identifier != "rna_type"]
150         self.functions[:] = [GetInfoFunctionRNA(rna_prop, parent_id)
151                              for rna_prop in get_direct_functions(rna_type)]
152
153     def get_bases(self):
154         bases = []
155         item = self
156
157         while item:
158             item = item.base
159             if item:
160                 bases.append(item)
161
162         return bases
163
164     def get_nested_properties(self, ls=None):
165         if not ls:
166             ls = self.properties[:]
167
168         if self.nested:
169             self.nested.get_nested_properties(ls)
170
171         return ls
172
173     def _get_py_visible_attrs(self):
174         attrs = []
175         py_class = get_py_class_from_rna(self.bl_rna)
176
177         for attr_str in dir(py_class):
178             if attr_str.startswith("_"):
179                 continue
180             attrs.append((attr_str, getattr(py_class, attr_str)))
181         return attrs
182
183     def get_py_properties(self):
184         properties = []
185         for identifier, attr in self._get_py_visible_attrs():
186             if type(attr) is property:
187                 properties.append((identifier, attr))
188         return properties
189
190     def get_py_functions(self):
191         import types
192         functions = []
193         for identifier, attr in self._get_py_visible_attrs():
194             # methods may be python wrappers to C functions
195             attr_func = getattr(attr, "__func__", attr)
196             if type(attr_func) in {types.FunctionType, types.MethodType}:
197                 functions.append((identifier, attr))
198         return functions
199
200     def get_py_c_functions(self):
201         import types
202         functions = []
203         for identifier, attr in self._get_py_visible_attrs():
204             # methods may be python wrappers to C functions
205             attr_func = getattr(attr, "__func__", attr)
206             if type(attr_func) in {types.BuiltinMethodType, types.BuiltinFunctionType}:
207                 functions.append((identifier, attr))
208         return functions
209
210     def __str__(self):
211
212         txt = ""
213         txt += self.identifier
214         if self.base:
215             txt += "(%s)" % self.base.identifier
216         txt += ": " + self.description + "\n"
217
218         for prop in self.properties:
219             txt += prop.__repr__() + "\n"
220
221         for func in self.functions:
222             txt += func.__repr__() + "\n"
223
224         return txt
225
226
227 class InfoPropertyRNA:
228     __slots__ = (
229         "bl_prop",
230         "srna",
231         "identifier",
232         "name",
233         "description",
234         "default_str",
235         "default",
236         "enum_items",
237         "min",
238         "max",
239         "array_length",
240         "array_dimensions",
241         "collection_type",
242         "type",
243         "fixed_type",
244         "is_argument_optional",
245         "is_enum_flag",
246         "is_required",
247         "is_readonly",
248         "is_never_none",
249     )
250     global_lookup = {}
251
252     def __init__(self, rna_prop):
253         self.bl_prop = rna_prop
254         self.identifier = rna_prop.identifier
255         self.name = rna_prop.name
256         self.description = rna_prop.description.strip()
257         self.default_str = "<UNKNOWN>"
258
259     def build(self):
260         rna_prop = self.bl_prop
261
262         self.enum_items = []
263         self.min = getattr(rna_prop, "hard_min", -1)
264         self.max = getattr(rna_prop, "hard_max", -1)
265         self.array_length = getattr(rna_prop, "array_length", 0)
266         self.array_dimensions = getattr(rna_prop, "array_dimensions", ())[:]
267         self.collection_type = GetInfoStructRNA(rna_prop.srna)
268         self.is_required = rna_prop.is_required
269         self.is_readonly = rna_prop.is_readonly
270         self.is_never_none = rna_prop.is_never_none
271         self.is_argument_optional = rna_prop.is_argument_optional
272
273         self.type = rna_prop.type.lower()
274         fixed_type = getattr(rna_prop, "fixed_type", "")
275         if fixed_type:
276             self.fixed_type = GetInfoStructRNA(fixed_type)  # valid for pointer/collections
277         else:
278             self.fixed_type = None
279
280         if self.type == "enum":
281             self.enum_items[:] = [(item.identifier, item.name, item.description) for item in rna_prop.enum_items]
282             self.is_enum_flag = rna_prop.is_enum_flag
283         else:
284             self.is_enum_flag = False
285
286         self.default_str = ""  # fallback
287
288         if self.array_length:
289             self.default = tuple(getattr(rna_prop, "default_array", ()))
290             if self.array_dimensions[1] != 0:  # Multi-dimensional array, convert default flat one accordingly.
291                 self.default_str = tuple(float_as_string(v) if self.type == "float" else str(v) for v in self.default)
292                 for dim in self.array_dimensions[::-1]:
293                     if dim != 0:
294                         self.default = tuple(zip(*((iter(self.default),) * dim)))
295                         self.default_str = tuple("(%s)" % ", ".join(s for s in b) for b in zip(*((iter(self.default_str),) * dim)))
296                 self.default_str = self.default_str[0]
297         elif self.type == "enum" and self.is_enum_flag:
298             self.default = getattr(rna_prop, "default_flag", set())
299         else:
300             self.default = getattr(rna_prop, "default", None)
301
302         if self.type == "pointer":
303             # pointer has no default, just set as None
304             self.default = None
305             self.default_str = "None"
306         elif self.type == "string":
307             self.default_str = "\"%s\"" % self.default
308         elif self.type == "enum":
309             if self.is_enum_flag:
310                 # self.default_str = "%r" % self.default  # repr or set()
311                 self.default_str = "{%s}" % repr(list(sorted(self.default)))[1:-1]
312             else:
313                 self.default_str = "'%s'" % self.default
314         elif self.array_length:
315             if self.array_dimensions[1] == 0:  # single dimension array, we already took care of multi-dimensions ones.
316                 # special case for floats
317                 if self.type == "float" and len(self.default) > 0:
318                     self.default_str = "(%s)" % ", ".join(float_as_string(f) for f in self.default)
319                 else:
320                     self.default_str = str(self.default)
321         else:
322             if self.type == "float":
323                 self.default_str = float_as_string(self.default)
324             else:
325                 self.default_str = str(self.default)
326
327         self.srna = GetInfoStructRNA(rna_prop.srna)  # valid for pointer/collections
328
329     def get_arg_default(self, force=True):
330         default = self.default_str
331         if default and (force or self.is_required is False):
332             return "%s=%s" % (self.identifier, default)
333         return self.identifier
334
335     def get_type_description(self, as_ret=False, as_arg=False, class_fmt="%s", collection_id="Collection"):
336         type_str = ""
337         if self.fixed_type is None:
338             type_str += self.type
339             if self.array_length:
340                 if self.array_dimensions[1] != 0:
341                     type_str += " multi-dimensional array of %s items" % (" * ".join(str(d) for d in self.array_dimensions if d != 0))
342                 else:
343                     type_str += " array of %d items" % (self.array_length)
344
345             if self.type in {"float", "int"}:
346                 type_str += " in [%s, %s]" % (range_str(self.min), range_str(self.max))
347             elif self.type == "enum":
348                 if self.is_enum_flag:
349                     type_str += " set in {%s}" % ", ".join(("'%s'" % s[0]) for s in self.enum_items)
350                 else:
351                     type_str += " in [%s]" % ", ".join(("'%s'" % s[0]) for s in self.enum_items)
352
353             if not (as_arg or as_ret):
354                 # write default property, ignore function args for this
355                 if self.type != "pointer":
356                     if self.default_str:
357                         type_str += ", default %s" % self.default_str
358
359         else:
360             if self.type == "collection":
361                 if self.collection_type:
362                     collection_str = (class_fmt % self.collection_type.identifier) + (" %s of " % collection_id)
363                 else:
364                     collection_str = "%s of " % collection_id
365             else:
366                 collection_str = ""
367
368             type_str += collection_str + (class_fmt % self.fixed_type.identifier)
369
370         # setup qualifiers for this value.
371         type_info = []
372         if as_ret:
373             pass
374         elif as_arg:
375             if not self.is_required:
376                 type_info.append("optional")
377             if self.is_argument_optional:
378                 type_info.append("optional argument")
379         else:  # readonly is only useful for self's, not args
380             if self.is_readonly:
381                 type_info.append("readonly")
382
383         if self.is_never_none:
384             type_info.append("never None")
385
386         if type_info:
387             type_str += (", (%s)" % ", ".join(type_info))
388
389         return type_str
390
391     def __str__(self):
392         txt = ""
393         txt += " * " + self.identifier + ": " + self.description
394
395         return txt
396
397
398 class InfoFunctionRNA:
399     __slots__ = (
400         "bl_func",
401         "identifier",
402         "description",
403         "args",
404         "return_values",
405         "is_classmethod",
406     )
407     global_lookup = {}
408
409     def __init__(self, rna_func):
410         self.bl_func = rna_func
411         self.identifier = rna_func.identifier
412         # self.name = rna_func.name # functions have no name!
413         self.description = rna_func.description.strip()
414         self.is_classmethod = not rna_func.use_self
415
416         self.args = []
417         self.return_values = ()
418
419     def build(self):
420         rna_func = self.bl_func
421         parent_id = rna_func
422         self.return_values = []
423
424         for rna_prop in rna_func.parameters.values():
425             prop = GetInfoPropertyRNA(rna_prop, parent_id)
426             if rna_prop.is_output:
427                 self.return_values.append(prop)
428             else:
429                 self.args.append(prop)
430
431         self.return_values = tuple(self.return_values)
432
433     def __str__(self):
434         txt = ''
435         txt += ' * ' + self.identifier + '('
436
437         for arg in self.args:
438             txt += arg.identifier + ', '
439         txt += '): ' + self.description
440         return txt
441
442
443 class InfoOperatorRNA:
444     __slots__ = (
445         "bl_op",
446         "identifier",
447         "name",
448         "module_name",
449         "func_name",
450         "description",
451         "args",
452     )
453     global_lookup = {}
454
455     def __init__(self, rna_op):
456         self.bl_op = rna_op
457         self.identifier = rna_op.identifier
458
459         mod, name = self.identifier.split("_OT_", 1)
460         self.module_name = mod.lower()
461         self.func_name = name
462
463         # self.name = rna_func.name # functions have no name!
464         self.description = rna_op.description.strip()
465
466         self.args = []
467
468     def build(self):
469         rna_op = self.bl_op
470         parent_id = self.identifier
471         for rna_id, rna_prop in rna_op.properties.items():
472             if rna_id == "rna_type":
473                 continue
474
475             prop = GetInfoPropertyRNA(rna_prop, parent_id)
476             self.args.append(prop)
477
478     def get_location(self):
479         try:
480             op_class = getattr(bpy.types, self.identifier)
481         except AttributeError:
482             # defined in C.
483             return None, None
484         op_func = getattr(op_class, "execute", None)
485         if op_func is None:
486             op_func = getattr(op_class, "invoke", None)
487         if op_func is None:
488             op_func = getattr(op_class, "poll", None)
489
490         if op_func:
491             op_code = op_func.__code__
492             source_path = op_code.co_filename
493
494             # clear the prefix
495             for p in script_paths:
496                 source_path = source_path.split(p)[-1]
497
498             if source_path[0] in "/\\":
499                 source_path = source_path[1:]
500
501             return source_path, op_code.co_firstlineno
502         else:
503             return None, None
504
505
506 def _GetInfoRNA(bl_rna, cls, parent_id=""):
507
508     if bl_rna is None:
509         return None
510
511     key = parent_id, bl_rna.identifier
512     try:
513         return cls.global_lookup[key]
514     except KeyError:
515         instance = cls.global_lookup[key] = cls(bl_rna)
516         return instance
517
518
519 def GetInfoStructRNA(bl_rna):
520     return _GetInfoRNA(bl_rna, InfoStructRNA)
521
522
523 def GetInfoPropertyRNA(bl_rna, parent_id):
524     return _GetInfoRNA(bl_rna, InfoPropertyRNA, parent_id)
525
526
527 def GetInfoFunctionRNA(bl_rna, parent_id):
528     return _GetInfoRNA(bl_rna, InfoFunctionRNA, parent_id)
529
530
531 def GetInfoOperatorRNA(bl_rna):
532     return _GetInfoRNA(bl_rna, InfoOperatorRNA)
533
534
535 def BuildRNAInfo():
536
537     # needed on successive calls to prevent stale data access
538     for cls in (InfoStructRNA, InfoFunctionRNA, InfoOperatorRNA, InfoPropertyRNA):
539         cls.global_lookup.clear()
540     del cls
541
542     # Use for faster lookups
543     # use rna_struct.identifier as the key for each dict
544     rna_struct_dict = {}  # store identifier:rna lookups
545     rna_full_path_dict = {}      # store the result of full_rna_struct_path(rna_struct)
546     rna_children_dict = {}  # store all rna_structs nested from here
547     rna_references_dict = {}  # store a list of rna path strings that reference this type
548     # rna_functions_dict = {}  # store all functions directly in this type (not inherited)
549
550     def full_rna_struct_path(rna_struct):
551         """
552         Needed when referencing one struct from another
553         """
554         nested = rna_struct.nested
555         if nested:
556             return "%s.%s" % (full_rna_struct_path(nested), rna_struct.identifier)
557         else:
558             return rna_struct.identifier
559
560     # def write_func(rna_func, ident):
561     def base_id(rna_struct):
562         try:
563             return rna_struct.base.identifier
564         except:
565             return ""  # invalid id
566
567     #structs = [(base_id(rna_struct), rna_struct.identifier, rna_struct) for rna_struct in bpy.doc.structs.values()]
568     '''
569     structs = []
570     for rna_struct in bpy.doc.structs.values():
571         structs.append( (base_id(rna_struct), rna_struct.identifier, rna_struct) )
572     '''
573     structs = []
574     for rna_type_name in dir(bpy.types):
575         rna_type = getattr(bpy.types, rna_type_name)
576
577         rna_struct = getattr(rna_type, "bl_rna", None)
578
579         if rna_struct:
580             # if not rna_type_name.startswith('__'):
581
582             identifier = rna_struct.identifier
583
584             if not rna_id_ignore(identifier):
585                 structs.append((base_id(rna_struct), identifier, rna_struct))
586
587                 # Simple lookup
588                 rna_struct_dict[identifier] = rna_struct
589
590                 # Store full rna path 'GameObjectSettings' -> 'Object.GameObjectSettings'
591                 rna_full_path_dict[identifier] = full_rna_struct_path(rna_struct)
592
593                 # Store a list of functions, remove inherited later
594                 # NOT USED YET
595                 ## rna_functions_dict[identifier] = get_direct_functions(rna_struct)
596
597                 # fill in these later
598                 rna_children_dict[identifier] = []
599                 rna_references_dict[identifier] = []
600         else:
601             print("Ignoring", rna_type_name)
602
603     structs.sort()  # not needed but speeds up sort below, setting items without an inheritance first
604
605     # Arrange so classes are always defined in the correct order
606     deps_ok = False
607     while deps_ok is False:
608         deps_ok = True
609         rna_done = set()
610
611         for i, (rna_base, identifier, rna_struct) in enumerate(structs):
612
613             rna_done.add(identifier)
614
615             if rna_base and rna_base not in rna_done:
616                 deps_ok = False
617                 data = structs.pop(i)
618                 ok = False
619                 while i < len(structs):
620                     if structs[i][1] == rna_base:
621                         structs.insert(i + 1, data)  # insert after the item we depend on.
622                         ok = True
623                         break
624                     i += 1
625
626                 if not ok:
627                     print('Dependancy "%s" could not be found for "%s"' % (identifier, rna_base))
628
629                 break
630
631     # Done ordering structs
632
633     # precalculate vars to avoid a lot of looping
634     for (rna_base, identifier, rna_struct) in structs:
635
636         # rna_struct_path = full_rna_struct_path(rna_struct)
637         rna_struct_path = rna_full_path_dict[identifier]
638
639         for rna_prop in get_direct_properties(rna_struct):
640             rna_prop_identifier = rna_prop.identifier
641
642             if rna_prop_identifier == 'RNA' or rna_id_ignore(rna_prop_identifier):
643                 continue
644
645             for rna_prop_ptr in (getattr(rna_prop, "fixed_type", None), getattr(rna_prop, "srna", None)):
646                 # Does this property point to me?
647                 if rna_prop_ptr and rna_prop_ptr.identifier in rna_references_dict:
648                     rna_references_dict[rna_prop_ptr.identifier].append(
649                         "%s.%s" % (rna_struct_path, rna_prop_identifier))
650
651         for rna_func in get_direct_functions(rna_struct):
652             for rna_prop_identifier, rna_prop in rna_func.parameters.items():
653
654                 if rna_prop_identifier == 'RNA' or rna_id_ignore(rna_prop_identifier):
655                     continue
656
657                 rna_prop_ptr = getattr(rna_prop, "fixed_type", None)
658
659                 # Does this property point to me?
660                 if rna_prop_ptr and rna_prop_ptr.identifier in rna_references_dict:
661                     rna_references_dict[rna_prop_ptr.identifier].append(
662                         "%s.%s" % (rna_struct_path, rna_func.identifier))
663
664         # Store nested children
665         nested = rna_struct.nested
666         if nested:
667             rna_children_dict[nested.identifier].append(rna_struct)
668
669     # Sort the refs, just reads nicer
670     for rna_refs in rna_references_dict.values():
671         rna_refs.sort()
672
673     info_structs = []
674     for (rna_base, identifier, rna_struct) in structs:
675         # if rna_struct.nested:
676         #     continue
677
678         #write_struct(rna_struct, '')
679         info_struct = GetInfoStructRNA(rna_struct)
680         if rna_base:
681             info_struct.base = GetInfoStructRNA(rna_struct_dict[rna_base])
682         info_struct.nested = GetInfoStructRNA(rna_struct.nested)
683         info_struct.children[:] = rna_children_dict[identifier]
684         info_struct.references[:] = rna_references_dict[identifier]
685         info_struct.full_path = rna_full_path_dict[identifier]
686
687         info_structs.append(info_struct)
688
689     for rna_info_prop in InfoPropertyRNA.global_lookup.values():
690         rna_info_prop.build()
691
692     for rna_info_prop in InfoFunctionRNA.global_lookup.values():
693         rna_info_prop.build()
694
695     done_keys = set()
696     new_keys = set(InfoStructRNA.global_lookup.keys())
697     while new_keys:
698         for rna_key in new_keys:
699             rna_info = InfoStructRNA.global_lookup[rna_key]
700             rna_info.build()
701             for prop in rna_info.properties:
702                 prop.build()
703             for func in rna_info.functions:
704                 func.build()
705                 for prop in func.args:
706                     prop.build()
707                 for prop in func.return_values:
708                     prop.build()
709         done_keys |= new_keys
710         new_keys = set(InfoStructRNA.global_lookup.keys()) - done_keys
711
712     # there are too many invalid defaults, unless we intend to fix, leave this off
713     if 0:
714         for rna_info in InfoStructRNA.global_lookup.values():
715             for prop in rna_info.properties:
716                 # ERROR CHECK
717                 default = prop.default
718                 if type(default) in {float, int}:
719                     if default < prop.min or default > prop.max:
720                         print("\t %s.%s, %s not in [%s - %s]" %
721                               (rna_info.identifier, prop.identifier, default, prop.min, prop.max))
722
723     # now for operators
724     op_mods = dir(bpy.ops)
725
726     for op_mod_name in sorted(op_mods):
727         if op_mod_name.startswith('__'):
728             continue
729
730         op_mod = getattr(bpy.ops, op_mod_name)
731         operators = dir(op_mod)
732         for op in sorted(operators):
733             try:
734                 rna_prop = getattr(op_mod, op).get_rna()
735             except AttributeError:
736                 rna_prop = None
737             except TypeError:
738                 rna_prop = None
739
740             if rna_prop:
741                 GetInfoOperatorRNA(rna_prop.bl_rna)
742
743     for rna_info in InfoOperatorRNA.global_lookup.values():
744         rna_info.build()
745         for rna_prop in rna_info.args:
746             rna_prop.build()
747
748     # for rna_info in InfoStructRNA.global_lookup.values():
749     #     print(rna_info)
750     return InfoStructRNA.global_lookup, InfoFunctionRNA.global_lookup, InfoOperatorRNA.global_lookup, InfoPropertyRNA.global_lookup
751
752
753 def main():
754     import rna_info
755     struct = rna_info.BuildRNAInfo()[0]
756     data = []
757     for struct_id, v in sorted(struct.items()):
758         struct_id_str = v.identifier  # "".join(sid for sid in struct_id if struct_id)
759
760         for base in v.get_bases():
761             struct_id_str = base.identifier + "|" + struct_id_str
762
763         props = [(prop.identifier, prop) for prop in v.properties]
764         for prop_id, prop in sorted(props):
765             # if prop.type == "boolean":
766             #     continue
767             prop_type = prop.type
768             if prop.array_length > 0:
769                 prop_type += "[%d]" % prop.array_length
770
771             data.append(
772                 "%s.%s -> %s:    %s%s    %s" %
773                 (struct_id_str, prop.identifier, prop.identifier, prop_type,
774                  ", (read-only)" if prop.is_readonly else "", prop.description))
775         data.sort()
776
777     if bpy.app.background:
778         import sys
779         sys.stderr.write("\n".join(data))
780         sys.stderr.write("\n\nEOF\n")
781     else:
782         text = bpy.data.texts.new(name="api.py")
783         text.from_string(data)
784
785
786 if __name__ == "__main__":
787     main()