1 #! /usr/bin/env python3.1
4 This script is used to help cleaning RNA api.
6 Typical line in the input file (elements in [] are optional).
8 [comment *] ToolSettings.snap_align_rotation -> use_snap_align_rotation: boolean [Align rotation with the snapping target]
10 Geterate output format from blender run this:
11 ./blender.bin --background --python ./release/scripts/modules/rna_info.py > source/blender/makesrna/rna_cleanup/out.txt
15 def font_bold(mystring):
17 Formats the string as bold, to be used in printouts.
20 font_reset = "\033[0;0m"
21 return font_bold + mystring + font_reset
29 scriptname = sys.argv[0]
30 sort_choices_string = '|'.join(sort_choices)
32 message += "\n%s input-file (.txt|.py) order-priority (%s).\n" % (font_bold(scriptname), sort_choices_string)
33 message += "%s -h for help\n" % font_bold(scriptname)
43 message += '\nRun this script to re-format the edits you make in the input file.\n'
44 message += 'Do quick modification to important fields like \'to\' and don\'t care about fields like \'changed\' or \'description\' and save.\n'
45 message += 'The script outputs 3 files:\n'
46 message += ' 1) *_clean.txt: is formatted same as the .txt input, can be edited by user.\n'
47 message += ' 2) *_clean.py: is formatted same as the .py input, can be edited by user.\n'
48 message += ' 3) rna_api.py is not formatted for readability and go under complete check. Can be used for rna cleanup.\n'
53 def check_commandline():
55 Takes parameters from the commandline.
59 if len(sys.argv)==1 or len(sys.argv)>3:
61 if sys.argv[1] == '-h':
63 elif not (sys.argv[1].endswith(".txt") or sys.argv[1].endswith(".py")):
64 print ('\nBad input file extension... exiting.')
67 inputfile = sys.argv[1]
68 if len(sys.argv) == 2:
69 sort_priority = default_sort_choice
70 print ('\nSecond parameter missing: choosing to order by %s.' % font_bold(sort_priority))
71 elif len(sys.argv)==3:
72 sort_priority = sys.argv[2]
73 if sort_priority not in sort_choices:
74 print('\nWrong sort_priority... exiting.')
76 return (inputfile, sort_priority)
79 def check_prefix(prop, btype):
80 # reminder: props=[comment, changed, bclass, bfrom, bto, kwcheck, btype, description]
81 if btype == "boolean":
83 prefix = prop.split('_')[0]
84 if prefix not in kw_prefixes:
85 return 'BAD-PREFIX: ' + prefix
89 return 'SPECIAL-KEYWORD: ' + prop
91 return 'BAD-KEYWORD: ' + prop
96 def check_if_changed(a,b):
97 if a != b: return 'changed'
101 def get_props_from_txt(input_filename):
103 If the file is *.txt, the script assumes it is formatted as outlined in this script docstring
106 file=open(input_filename,'r')
107 file_lines=file.readlines()
111 props_length_max=[0,0,0,0,0,0,0,0]
112 for line in file_lines:
117 # empty line or comment
118 if not line.strip() or line.startswith('#'):
122 [bclass, tail] = [x.strip() for x in line.split('.', 1)]
126 [comment, bclass] = [x.strip() for x in bclass.split('*', 1)]
130 # skipping the header if we have one.
131 # the header is assumed to be "NOTE * CLASS.FROM -> TO: TYPE DESCRIPTION"
132 if comment == 'NOTE' and bclass == 'CLASS':
136 [bfrom, tail] = [x.strip() for x in tail.split('->', 1)]
139 [bto, tail] = [x.strip() for x in tail.split(':', 1)]
143 [btype, description] = tail.split(None, 1)
144 if '"' in description:
145 description.replace('"', "'")
147 [btype, description] = [tail,'NO DESCRIPTION']
150 kwcheck = check_prefix(bto, btype)
153 changed = check_if_changed(bfrom, bto)
156 props=[comment, changed, bclass, bfrom, bto, kwcheck, btype, repr(description)]
157 props_list.append(props)
158 props_length_max=list(map(max,zip(props_length_max,list(map(len,props)))))
160 return (props_list,props_length_max)
163 def get_props_from_py(input_filename):
165 If the file is *.py, the script assumes it contains a python list (as "rna_api=[...]")
166 This means that this script executes the text in the py file with an exec(text).
168 # adds the list "rna_api" to this function's scope
169 rna_api = __import__(input_filename[:-3]).rna_api
171 props_length_max = [0 for i in rna_api[0]] # this way if the vector will take more elements we are safe
172 for index,props in enumerate(rna_api):
173 comment, changed, bclass, bfrom, bto, kwcheck, btype, description = props
174 kwcheck = check_prefix(bto, btype) # keyword-check
175 changed = check_if_changed(bfrom, bto) # changed?
176 description = repr(description)
177 rna_api[index] = [comment, changed, bclass, bfrom, bto, kwcheck, btype, description]
178 props_length = list(map(len,props)) # lengths
179 props_length_max = list(map(max,zip(props_length_max,props_length))) # max lengths
180 return (rna_api,props_length_max)
183 def get_props(input_filename):
184 if input_filename.endswith(".txt"):
185 props_list,props_length_max = get_props_from_txt(input_filename)
186 elif input_filename.endswith(".py"):
187 props_list,props_length_max = get_props_from_py(input_filename)
188 return (props_list,props_length_max)
191 def sort(props_list, sort_priority):
194 props=[comment, changed, bclass, bfrom, bto, kwcheck, btype, description]
197 # order based on the i-th element in lists
198 if sort_priority == "class.from":
199 props_list = sorted(props_list, key=lambda p: (p[2], p[3]))
201 i = sort_choices.index(sort_priority)
203 props_list = sorted(props_list, key=lambda p: p[i], reverse=True)
205 props_list = sorted(props_list, key=lambda p: p[i])
207 print ('\nSorted by %s.' % font_bold(sort_priority))
211 def file_basename(input_filename):
212 # if needed will use os.path
213 if input_filename.endswith(".txt"):
214 if input_filename.endswith("_work.txt"):
215 base_filename = input_filename.replace("_work.txt", "")
217 base_filename = input_filename.replace(".txt", "")
218 elif input_filename.endswith(".py"):
219 if input_filename.endswith("_work.py"):
220 base_filename = input_filename.replace("_work.py", "")
222 base_filename = input_filename.replace(".py", "")
227 def write_files(basename, props_list, props_length_max):
230 * output_filename_work.txt: formatted as txt input file (can be edited)
231 * output_filename_work.py: formatted for readability (can be edited)
232 * rna_api.py: unformatted, just as final output
235 f_rna = open("rna_api.py",'w')
236 f_txt = open(basename + '_work.txt','w')
237 f_py = open(basename + '_work.py','w')
239 # reminder: props=[comment, changed, bclass, bfrom, bto, kwcheck, btype, description]
240 # [comment *] ToolSettings.snap_align_rotation -> use_snap_align_rotation: boolean [Align rotation with the snapping target]
242 props_list = [['NOTE', 'CHANGED', 'CLASS', 'FROM', 'TO', 'KEYWORD-CHECK', 'TYPE', 'DESCRIPTION']] + props_list
243 for props in props_list:
245 if props[0] != '': txt += '%s * ' % props[0] # comment
246 txt += '%s.%s -> %s: %s %s\n' % tuple(props[2:5] + props[6:]) # skipping keyword-check
248 if props[0] == 'NOTE': indent = '# '
250 rna += indent + '("%s", "%s", "%s", "%s", %s),\n' % tuple(props[2:5] + props[6:]) # description is already string formatted
252 blanks = [' '* (x[0]-x[1]) for x in zip(props_length_max,list(map(len,props)))]
253 props = [('"%s"%s' if props[-1] != x[0] else "%s%s") % (x[0],x[1]) for x in zip(props,blanks)]
254 py += indent + '(%s, %s, %s, %s, %s, %s, %s, %s),\n' % tuple(props)
257 f_py.write("rna_api = [\n%s]\n" % py)
258 f_rna.write("rna_api = [\n%s]\n" % rna)
260 # write useful py script, wont hurt
261 f_py.write("\n'''\n")
262 f_py.write("for p_note, p_changed, p_class, p_from, p_to, p_check, p_type, p_desc in rna_api:\n")
263 f_py.write(" print(p_to)\n")
264 f_py.write("\n'''\n")
270 print ('\nSaved %s, %s and %s.\n' % (font_bold(f_txt.name), font_bold(f_py.name), font_bold(f_rna.name) ) )
275 global sort_choices, default_sort_choice
276 global kw_prefixes, kw
278 sort_choices = ['note','changed','class','from','to','kw', 'class.from']
279 default_sort_choice = sort_choices[-1]
280 kw_prefixes = [ 'active','apply','bl','exclude','has','invert','is','lock', \
281 'pressed','show','show_only','use','use_only','layers','states', 'select']
282 kw = ['active','hide','invert','select','layers','mute','states','use','lock']
284 input_filename, sort_priority = check_commandline()
285 props_list,props_length_max = get_props(input_filename)
286 props_list = sort(props_list,sort_priority)
288 output_basename = file_basename(input_filename)
289 write_files(output_basename, props_list,props_length_max)
292 if __name__=='__main__':
294 if not sys.version.startswith("3"):
295 print("Incorrect python version, use python 3!")