mrv.mdp
Covered: 18 lines
Missed: 170 lines
Skipped 110 lines
Percent: 9 %
  3
"""Module containing the commandline interface for the Maya Depdendency Parser"""
  5
__all__ = None
  7
import mrv
  8
from mdepparse import *
 10
from networkx.readwrite import gpickle
 12
from itertools import chain
 13
import getopt
 14
import sys
 18
def main( fileList, **kwargs ):
 19
	"""Called if this module is called directly, creating a file containing
 20
		dependency information
 22
	:param kwargs: will be passed directly to `createFromFiles`"""
 23
	return MayaFileGraph.createFromFiles( fileList, **kwargs )
 26
def _usageAndExit( msg = None ):
 27
	sys.stdout.write("""bpython mdepparse.py [-shortflags ] [--longflags] file_to_parse.ma [file_to_parse, ...]
 29
OUTPUT
 30
------
 31
All actual information goes to stdout, everything else to stderr
 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
 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
 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
 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
 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
 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
 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.
 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
 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
 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
 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
 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
 96
-n 				nice output, designed to be human-readable
 98
-v				enable more verbose output
100
""")
101
	if msg:
102
		sys.stdout.write(msg+"\n")
105
	sys.exit( 1 )
108
def tokensToRemapFunc( tokenstring ):
109
	"""Return a function applying remapping as defined by tokenstring
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 )
117
	remap_tuples = zip( tokens[0::2], tokens[1::2] )
119
	def path_replace( f ):
120
		for source, dest in remap_tuples:
121
			f = f.replace( source, dest )
122
		return f
124
	return path_replace
130
if __name__ == "__main__":
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 ) )
139
	if not opts and not rest:
140
		_usageAndExit()
142
	opts = dict( opts )
143
	fromstdin = "-i" in opts
147
	allpaths = "-a" in opts
148
	kwargs_creategraph = dict( ( ( "parse_all_paths", allpaths ), ) )
149
	kwargs_query = dict()
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
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
168
	filelist = rest
169
	if fromstdin:
170
		filelist = chain( sys.stdin, rest )
173
	targetFile = opts.get( "-t", None )
174
	sourceFile = opts.get( "-s", None )
179
	graph = None
180
	verbose = "-v" in opts
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 )
194
	if targetFile:
195
		if verbose:
196
			sys.stdout.write("Saving dependencies to %s\n" % targetFile)
197
		gpickle.write_gpickle( graph, targetFile )
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
210
	if dotOutputFile:
211
		dotgraph = MayaFileGraph()
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
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
225
		listcopy = list()			# as we read from iterators ( stdin ), its required to copy it to iterate it again
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 )
238
			if not depends:
239
				continue
243
			if dotgraph is not None:
244
				for dep in depends:
245
					dotgraph.add_edge( ( filepath, dep ) )
248
			if nice_mode:
249
				depthstr = "unlimited"
250
				if depth != -1:
251
					depthstr = str( depth )
253
				affectsstr = "is affected by: "
254
				if direction == MayaFileGraph.kAffects:
255
					affectsstr = "affects: "
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" )
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 )  )
272
		filelist = listcopy
278
	if not queried_files and return_invalid:
279
		invalidFiles = graph.invalidFiles()
280
		sys.stdout.writelines( ( iv + "\n" for iv in invalidFiles ) )
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 )