Blender V2.61 - r43446

rna_cleaner.py

Go to the documentation of this file.
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