Blender V2.61 - r43446
|
00001 #! /usr/bin/env python3 00002 00003 """ 00004 This script is used to help cleaning RNA api. 00005 00006 Typical line in the input file (elements in [] are optional). 00007 00008 [comment *] ToolSettings.snap_align_rotation -> use_snap_align_rotation: boolean [Align rotation with the snapping target] 00009 00010 Geterate output format from blender run this: 00011 ./blender.bin --background -noaudio --python ./release/scripts/modules/rna_info.py 2> source/blender/makesrna/rna_cleanup/out.txt 00012 """ 00013 00014 00015 def font_bold(mystring): 00016 """ 00017 Formats the string as bold, to be used in printouts. 00018 """ 00019 font_bold = "\033[1m" 00020 font_reset = "\033[0;0m" 00021 return font_bold + mystring + font_reset 00022 00023 00024 def usage(): 00025 """ 00026 Prints script usage. 00027 """ 00028 import sys 00029 scriptname = sys.argv[0] 00030 sort_choices_string = '|'.join(sort_choices) 00031 message = "\nUSAGE:" 00032 message += "\n%s input-file (.txt|.py) order-priority (%s).\n" % (font_bold(scriptname), sort_choices_string) 00033 message += "%s -h for help\n" % font_bold(scriptname) 00034 print(message) 00035 exit() 00036 00037 00038 def help(): 00039 """ 00040 Prints script' help. 00041 """ 00042 message = '\nHELP:' 00043 message += '\nRun this script to re-format the edits you make in the input file.\n' 00044 message += 'Do quick modification to important fields like \'to\' and don\'t care about fields like \'changed\' or \'description\' and save.\n' 00045 message += 'The script outputs 3 files:\n' 00046 message += ' 1) *_clean.txt: is formatted same as the .txt input, can be edited by user.\n' 00047 message += ' 2) *_clean.py: is formatted same as the .py input, can be edited by user.\n' 00048 message += ' 3) rna_api.py is not formatted for readability and go under complete check. Can be used for rna cleanup.\n' 00049 print(message) 00050 usage() 00051 00052 00053 def check_commandline(): 00054 """ 00055 Takes parameters from the commandline. 00056 """ 00057 import sys 00058 # Usage 00059 if len(sys.argv)==1 or len(sys.argv)>3: 00060 usage() 00061 if sys.argv[1] == '-h': 00062 help() 00063 elif not (sys.argv[1].endswith(".txt") or sys.argv[1].endswith(".py")): 00064 print ('\nBad input file extension... exiting.') 00065 usage() 00066 else: 00067 inputfile = sys.argv[1] 00068 if len(sys.argv) == 2: 00069 sort_priority = default_sort_choice 00070 print ('\nSecond parameter missing: choosing to order by %s.' % font_bold(sort_priority)) 00071 elif len(sys.argv)==3: 00072 sort_priority = sys.argv[2] 00073 if sort_priority not in sort_choices: 00074 print('\nWrong sort_priority... exiting.') 00075 usage() 00076 return (inputfile, sort_priority) 00077 00078 00079 def check_prefix(prop, btype): 00080 # reminder: props=[comment, changed, bclass, bfrom, bto, kwcheck, btype, description] 00081 if btype == "boolean": 00082 if '_' in prop: 00083 prefix = prop.split('_')[0] 00084 if prefix not in kw_prefixes: 00085 return 'BAD-PREFIX: ' + prefix 00086 else: 00087 return prefix + '_' 00088 elif prop in kw: 00089 return 'SPECIAL-KEYWORD: ' + prop 00090 else: 00091 return 'BAD-KEYWORD: ' + prop 00092 else: 00093 return "" 00094 00095 00096 def check_if_changed(a,b): 00097 if a != b: return 'changed' 00098 else: return 'same' 00099 00100 00101 def get_props_from_txt(input_filename): 00102 """ 00103 If the file is *.txt, the script assumes it is formatted as outlined in this script docstring 00104 """ 00105 00106 file=open(input_filename,'r') 00107 file_lines=file.readlines() 00108 file.close() 00109 00110 props_list=[] 00111 props_length_max=[0,0,0,0,0,0,0,0] 00112 00113 done_text = "+" 00114 done = 0 00115 tot = 0 00116 00117 for iii, line in enumerate(file_lines): 00118 00119 # debug 00120 #print(line) 00121 line_strip = line.strip() 00122 # empty line or comment 00123 if not line_strip: 00124 continue 00125 00126 if line_strip == "EOF": 00127 break 00128 00129 if line.startswith("#"): 00130 line = line[1:] 00131 00132 # class 00133 [bclass, tail] = [x.strip() for x in line.split('.', 1)] 00134 00135 # comment 00136 if '*' in bclass: 00137 [comment, bclass] = [x.strip() for x in bclass.split('*', 1)] 00138 else: 00139 comment= '' 00140 00141 # skipping the header if we have one. 00142 # the header is assumed to be "NOTE * CLASS.FROM -> TO: TYPE DESCRIPTION" 00143 if comment == 'NOTE' and bclass == 'CLASS': 00144 continue 00145 00146 # from 00147 [bfrom, tail] = [x.strip() for x in tail.split('->', 1)] 00148 00149 # to 00150 [bto, tail] = [x.strip() for x in tail.split(':', 1)] 00151 00152 # type, description 00153 try: 00154 [btype, description] = tail.split(None, 1) 00155 # make life easy and strip quotes 00156 description = description.replace("'", "").replace('"', "").replace("\\", "").strip() 00157 except ValueError: 00158 [btype, description] = [tail,'NO DESCRIPTION'] 00159 00160 # keyword-check 00161 kwcheck = check_prefix(bto, btype) 00162 00163 # changed 00164 changed = check_if_changed(bfrom, bto) 00165 00166 # lists formatting 00167 props=[comment, changed, bclass, bfrom, bto, kwcheck, btype, description] 00168 props_list.append(props) 00169 props_length_max=list(map(max,zip(props_length_max,list(map(len,props))))) 00170 00171 if done_text in comment: 00172 done += 1 00173 tot += 1 00174 00175 print("Total done %.2f" % (done / tot * 100.0) ) 00176 00177 return (props_list,props_length_max) 00178 00179 00180 def get_props_from_py(input_filename): 00181 """ 00182 If the file is *.py, the script assumes it contains a python list (as "rna_api=[...]") 00183 This means that this script executes the text in the py file with an exec(text). 00184 """ 00185 # adds the list "rna_api" to this function's scope 00186 rna_api = __import__(input_filename[:-3]).rna_api 00187 00188 props_length_max = [0 for i in rna_api[0]] # this way if the vector will take more elements we are safe 00189 for index,props in enumerate(rna_api): 00190 comment, changed, bclass, bfrom, bto, kwcheck, btype, description = props 00191 kwcheck = check_prefix(bto, btype) # keyword-check 00192 changed = check_if_changed(bfrom, bto) # changed? 00193 description = repr(description) 00194 description = description.replace("'", "").replace('"', "").replace("\\", "").strip() 00195 rna_api[index] = [comment, changed, bclass, bfrom, bto, kwcheck, btype, description] 00196 props_length = list(map(len,props)) # lengths 00197 props_length_max = list(map(max,zip(props_length_max,props_length))) # max lengths 00198 return (rna_api,props_length_max) 00199 00200 00201 def get_props(input_filename): 00202 if input_filename.endswith(".txt"): 00203 props_list,props_length_max = get_props_from_txt(input_filename) 00204 elif input_filename.endswith(".py"): 00205 props_list,props_length_max = get_props_from_py(input_filename) 00206 return (props_list,props_length_max) 00207 00208 00209 def sort(props_list, sort_priority): 00210 """ 00211 reminder 00212 props=[comment, changed, bclass, bfrom, bto, kwcheck, btype, description] 00213 """ 00214 00215 # order based on the i-th element in lists 00216 if sort_priority == "class.to": 00217 props_list = sorted(props_list, key=lambda p: (p[2], p[4])) 00218 else: 00219 i = sort_choices.index(sort_priority) 00220 if i == 0: 00221 props_list = sorted(props_list, key=lambda p: p[i], reverse=True) 00222 else: 00223 props_list = sorted(props_list, key=lambda p: p[i]) 00224 00225 print ('\nSorted by %s.' % font_bold(sort_priority)) 00226 return props_list 00227 00228 00229 def file_basename(input_filename): 00230 # if needed will use os.path 00231 if input_filename.endswith(".txt"): 00232 if input_filename.endswith("_work.txt"): 00233 base_filename = input_filename.replace("_work.txt", "") 00234 else: 00235 base_filename = input_filename.replace(".txt", "") 00236 elif input_filename.endswith(".py"): 00237 if input_filename.endswith("_work.py"): 00238 base_filename = input_filename.replace("_work.py", "") 00239 else: 00240 base_filename = input_filename.replace(".py", "") 00241 00242 return base_filename 00243 00244 00245 def write_files(basename, props_list, props_length_max): 00246 """ 00247 Writes in 3 files: 00248 * output_filename_work.txt: formatted as txt input file (can be edited) 00249 * output_filename_work.py: formatted for readability (can be edited) 00250 * rna_api.py: unformatted, just as final output 00251 """ 00252 00253 f_rna = open("rna_api.py",'w') 00254 f_txt = open(basename + '_work.txt','w') 00255 f_py = open(basename + '_work.py','w') 00256 00257 # reminder: props=[comment, changed, bclass, bfrom, bto, kwcheck, btype, description] 00258 # [comment *] ToolSettings.snap_align_rotation -> use_snap_align_rotation: boolean [Align rotation with the snapping target] 00259 rna = py = txt = '' 00260 props_list = [['NOTE', 'CHANGED', 'CLASS', 'FROM', 'TO', 'KEYWORD-CHECK', 'TYPE', 'DESCRIPTION']] + props_list 00261 for props in props_list: 00262 #txt 00263 00264 # quick way we can tell if it changed 00265 if props[3] == props[4]: txt += "#" 00266 else: txt += " " 00267 00268 if props[0] != '': txt += '%s * ' % props[0] # comment 00269 txt += '%s.%s -> %s: %s "%s"\n' % tuple(props[2:5] + props[6:]) # skipping keyword-check 00270 # rna_api 00271 if props[0] == 'NOTE': indent = '# ' 00272 else: indent = ' ' 00273 rna += indent + '("%s", "%s", "%s", "%s", "%s"),\n' % tuple(props[2:5] + props[6:]) # description is already string formatted 00274 # py 00275 blanks = [' '* (x[0]-x[1]) for x in zip(props_length_max,list(map(len,props)))] 00276 props = [('"%s"%s' if props[-1] != x[0] else "%s%s") % (x[0],x[1]) for x in zip(props,blanks)] 00277 py += indent + '(%s, %s, %s, %s, %s, %s, %s, "%s"),\n' % tuple(props) 00278 00279 f_txt.write(txt) 00280 f_py.write("rna_api = [\n%s]\n" % py) 00281 f_rna.write("rna_api = [\n%s]\n" % rna) 00282 00283 # write useful py script, wont hurt 00284 f_py.write("\n'''\n") 00285 f_py.write("for p_note, p_changed, p_class, p_from, p_to, p_check, p_type, p_desc in rna_api:\n") 00286 f_py.write(" print(p_to)\n") 00287 f_py.write("\n'''\n") 00288 00289 f_txt.close() 00290 f_py.close() 00291 f_rna.close() 00292 00293 print ('\nSaved %s, %s and %s.\n' % (font_bold(f_txt.name), font_bold(f_py.name), font_bold(f_rna.name) ) ) 00294 00295 00296 def main(): 00297 00298 global sort_choices, default_sort_choice 00299 global kw_prefixes, kw 00300 00301 sort_choices = ['note','changed','class','from','to','kw', 'class.to'] 00302 default_sort_choice = sort_choices[-1] 00303 kw_prefixes = [ 'active','apply','bl','exclude','has','invert','is','lock', \ 00304 'pressed','show','show_only','use','use_only','layers','states', 'select'] 00305 kw = ['active','hide','invert','select','layers','mute','states','use','lock'] 00306 00307 input_filename, sort_priority = check_commandline() 00308 props_list,props_length_max = get_props(input_filename) 00309 props_list = sort(props_list,sort_priority) 00310 00311 output_basename = file_basename(input_filename) 00312 write_files(output_basename, props_list,props_length_max) 00313 00314 00315 if __name__=='__main__': 00316 import sys 00317 if not sys.version.startswith("3"): 00318 print("Incorrect python version, use python 3!") 00319 else: 00320 main() 00321