Package mrv :: Module mdp
[hide private]
[frames] | no frames]

Source Code for Module mrv.mdp

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3  """Module containing the commandline interface for the Maya Depdendency Parser""" 
  4  # may not be imported directly 
  5  __all__ = None 
  6  # assure we have the main module initialized 
  7  import mrv 
  8  from mdepparse import * 
  9   
 10  from networkx.readwrite import gpickle 
 11   
 12  from itertools import chain 
 13  import getopt 
 14  import sys 
 15   
 16   
 17   
18 -def main( fileList, **kwargs ):
19 """Called if this module is called directly, creating a file containing 20 dependency information 21 22 :param kwargs: will be passed directly to `createFromFiles`""" 23 return MayaFileGraph.createFromFiles( fileList, **kwargs )
24 25
26 -def _usageAndExit( msg = None ):
27 sys.stdout.write("""bpython mdepparse.py [-shortflags ] [--longflags] file_to_parse.ma [file_to_parse, ...] 28 29 OUTPUT 30 ------ 31 All actual information goes to stdout, everything else to stderr 32 33 EDIT 34 ----- 35 -t Target file used to store the parsed dependency information 36 If not given, the command will automatically be in query mode. 37 The file format is simply a pickle of the underlying Networkx graph 38 39 -s Source dependency file previously written with -t. If specified, this file 40 will be read to quickly be read for queries. If not given, the information 41 will be parsed first. Thus it is recommended to have a first run storing 42 the dependencies and do all queries just reading in the dependencies using 43 -s 44 45 -i if given, a list of input files will be read from stdin. The tool will start 46 parsing the files as the come through the pipe 47 48 -a if given, all paths will be parsed from the input files. This will take longer 49 than just parsing references as the whole file needs to be read 50 TODO: actual implementation 51 52 --to-fs-map tokenmap 53 map one part of the path to another in order to make it a valid path 54 in the filesystem, i.e: 55 --to-fs-map source=target[=...] 56 --to-fs-map c:\\=/mnt/data/ 57 sort it with the longest remapping first to assure no accidential matches. 58 Should be used if environment variables are used which are not set in the system 59 or if there are other path inconsistencies 60 61 --to-db-map tokenmap 62 map one part of the fs path previously remapped by --to-fs-map to a 63 more general one suitable to be a key in the dependency database. 64 The format is equal to the one used in --to-fs-map 65 66 -o output the dependency database as dot file at the given path, so it can 67 be read by any dot reader and interpreted that way. 68 If input arguments are given, only the affected portions of the database 69 will be available in the dot file. Also, the depths of the dependency information 70 is lost, thus there are only direct connections, although it might in 71 fact be a sub-reference. 72 73 QUERY 74 ----- 75 All values returned in query mode will be new-line separated file paths 76 --affects retrieve all files that are affected by the input files 77 --affected-by retrieve all files that are affect the input files 78 79 -l if set, only leaf paths, thus paths being at the end of the chain 80 will be returned. 81 If not given, all paths, i.e. all intermediate references, will 82 be returned as well 83 84 -d int if not set, all references and subreferences will be retrieved 85 if 1, only direct references will be returned 86 if > 1, also sub[sub...] references will returned 87 88 -b if set and no input arg exists, return all bad or invalid files stored in the database 89 if an input argument is given, it acts as a filter and only returns 90 filepaths that are marked invalid 91 92 -e return full edges instead of only the successors/predecessors. 93 This allows tools to parse the output and make more sense of it 94 Will be ignored in nice mode 95 96 -n nice output, designed to be human-readable 97 98 -v enable more verbose output 99 100 """) 101 if msg: 102 sys.stdout.write(msg+"\n") 103 # END print message 104 105 sys.exit( 1 )
106 107
108 -def tokensToRemapFunc( tokenstring ):
109 """Return a function applying remapping as defined by tokenstring 110 111 :note: it also applies a mapping from mb to ma, no matter what. 112 Thus we currently only store .ma files as keys even though it might be mb files""" 113 tokens = tokenstring.split( "=" ) 114 if len( tokens ) % 2 != 0: 115 raise ValueError( "Invalid map format: %s" % tokenstring ) 116 117 remap_tuples = zip( tokens[0::2], tokens[1::2] ) 118 119 def path_replace( f ): 120 for source, dest in remap_tuples: 121 f = f.replace( source, dest ) 122 return f
123 124 return path_replace 125 126 127 128 # COMMAND LINE INTERFACE 129 ############################ 130 if __name__ == "__main__": 131 # parse the arguments as retrieved from the command line ! 132 try: 133 opts, rest = getopt.getopt( sys.argv[1:], "iat:s:ld:benvo:", [ "affects", "affected-by", 134 "to-fs-map=","to-db-map=" ] ) 135 except getopt.GetoptError,e: 136 _usageAndExit( str( e ) ) 137 138 139 if not opts and not rest: 140 _usageAndExit() 141 142 opts = dict( opts ) 143 fromstdin = "-i" in opts 144 145 # PREPARE KWARGS_CREATEGRAPH 146 ##################### 147 allpaths = "-a" in opts 148 kwargs_creategraph = dict( ( ( "parse_all_paths", allpaths ), ) ) 149 kwargs_query = dict() 150 151 # PATH REMAPPING 152 ################## 153 # prepare ma to mb conversion 154 # by default, we convert from mb to ma hoping there is a corresponding 155 # ma file in the same directory 156 for kw,flag in ( "to_os_path","--to-fs-map" ),( "os_path_to_db_key", "--to-db-map" ): 157 if flag not in opts: 158 continue 159 160 remap_func = tokensToRemapFunc( opts.get( flag ) ) 161 kwargs_creategraph[ kw ] = remap_func 162 kwargs_query[ kw ] = remap_func # required in query mode as well 163 # END for each kw,flag pair 164 165 166 # PREPARE FILELIST 167 ################### 168 filelist = rest 169 if fromstdin: 170 filelist = chain( sys.stdin, rest ) 171 172 173 targetFile = opts.get( "-t", None ) 174 sourceFile = opts.get( "-s", None ) 175 176 177 # GET DEPENDS 178 ################## 179 graph = None 180 verbose = "-v" in opts 181 182 if not sourceFile: 183 graph = main( filelist, **kwargs_creategraph ) 184 else: 185 if verbose: 186 sys.stdout.write("Reading dependencies from: %s\n" % sourceFile) 187 graph = gpickle.read_gpickle( sourceFile ) 188 189 190 191 # SAVE ALL DEPENDENCIES ? 192 ######################### 193 # save to target file 194 if targetFile: 195 if verbose: 196 sys.stdout.write("Saving dependencies to %s\n" % targetFile) 197 gpickle.write_gpickle( graph, targetFile ) 198 199 200 # QUERY MODE 201 ############### 202 return_invalid = "-b" in opts 203 depth = int( opts.get( "-d", -1 ) ) 204 as_edge = "-e" in opts 205 nice_mode = "-n" in opts 206 dotgraph = None 207 dotOutputFile = opts.get( "-o", None ) 208 kwargs_query[ 'invalid_only' ] = return_invalid # if given, filtering for invalid only is enabled 209 210 if dotOutputFile: 211 dotgraph = MayaFileGraph() 212 213 queried_files = False 214 for flag, direction in ( ( "--affects", MayaFileGraph.kAffects ), 215 ("--affected-by",MayaFileGraph.kAffectedBy ) ): 216 if not flag in opts: 217 continue 218 219 # PREPARE LEAF FUNCTION 220 prune = lambda i,g: False 221 if "-l" in opts: 222 degreefunc = ( ( direction == MayaFileGraph.kAffects ) and MayaFileGraph.out_degree ) or MayaFileGraph.in_degree 223 prune = lambda i,g: degreefunc( g, i ) != 0 224 225 listcopy = list() # as we read from iterators ( stdin ), its required to copy it to iterate it again 226 227 228 # write information to stdout 229 for filepath in filelist: 230 listcopy.append( filepath ) 231 queried_files = True # used as flag to determine whether filers have been applied or not 232 filepath = filepath.strip() # could be from stdin 233 depends = graph.depends( filepath, direction = direction, prune = prune, 234 visit_once=1, branch_first=1, depth=depth, 235 return_unresolved=0, **kwargs_query ) 236 237 # skip empty depends 238 if not depends: 239 continue 240 241 # FILTERED DOT OUTPUT ? 242 ######################### 243 if dotgraph is not None: 244 for dep in depends: 245 dotgraph.add_edge( ( filepath, dep ) ) 246 247 # match with invalid files if required 248 if nice_mode: 249 depthstr = "unlimited" 250 if depth != -1: 251 depthstr = str( depth ) 252 253 affectsstr = "is affected by: " 254 if direction == MayaFileGraph.kAffects: 255 affectsstr = "affects: " 256 257 headline = "\n%s ( depth = %s, invalid only = %i )\n" % ( filepath, depthstr, return_invalid ) 258 sys.stdout.write( headline ) 259 sys.stdout.write( "-" * len( headline ) + "\n" ) 260 261 sys.stdout.write( affectsstr + "\n" ) 262 sys.stdout.writelines( "\t - " + dep + "\n" for dep in depends ) 263 else: 264 prefix = "" 265 if as_edge: 266 prefix = "%s->" % filepath 267 sys.stdout.writelines( ( prefix + dep + "\n" for dep in depends ) ) 268 # END if not nice modd 269 # END for each file in file list 270 271 # use copy after first iteration 272 filelist = listcopy 273 274 # END for each direction to search 275 276 # ALL INVALID FILES OUTPUT 277 ########################### 278 if not queried_files and return_invalid: 279 invalidFiles = graph.invalidFiles() 280 sys.stdout.writelines( ( iv + "\n" for iv in invalidFiles ) ) 281 282 283 # DOT OUTPUT 284 ################### 285 if dotOutputFile: 286 if verbose: 287 sys.stdout.write("Saving dot file to %s\n" % dotOutputFile) 288 try: 289 import networkx.drawing.nx_pydot as pydot 290 except ImportError: 291 sys.stderr.write( "Required pydot module not installed" ) 292 else: 293 if queried_files and dotgraph is not None: 294 pydot.write_dot( dotgraph, dotOutputFile ) 295 else: 296 pydot.write_dot( graph, dotOutputFile ) 297 # END dot writing 298