1
2 """Contains parser allowing to retrieve dependency information from maya ascii files
3 and convert it into an easy-to-use networkx graph with convenience methods.
4 """
5 __docformat__ = "restructuredtext"
6
7 from networkx import DiGraph, NetworkXError
8 from util import iterNetworkxGraph
9 from path import make_path
10
11 import sys
12 import os
13 import re
14
15 import logging
16 log = logging.getLogger("mrv.mdepparse")
19 """Contains dependnecies between maya files including utility functions
20 allowing to more easily find what you are looking for"""
21 kAffects,kAffectedBy = range( 2 )
22
23 refpathregex = re.compile( '.*-r .*"(.*)";' )
24
25 invalidNodeID = "__invalid__"
26 invalidPrefix = ":_iv_:"
27
28
29 @classmethod
31 """:return: MayaFileGraph providing dependency information about the files
32 in fileList and their subReference.
33 :param fileList: iterable providing the filepaths to be parsed and added
34 to this graph
35 :param kwargs: alll arguemnts of `addFromFiles` are supported """
36 graph = cls( )
37 graph.addFromFiles( fileList, **kwargs )
38 return graph
39
40
42 """Add an invalid file to our special location
43 :note: we prefix it to assure it does not popup in our results"""
44 self.add_edge( self.invalidNodeID, self.invalidPrefix + str( invalidfile ) )
45
46 @classmethod
48 """:return: list of reference strings parsed from the given maya ascii file
49 :raise IOError: if the file could not be read"""
50 outrefs = list()
51 filehandle = open( os.path.expandvars( mafile ), "r" )
52
53 num_rdi_paths = 0
54
55
56
57 for line in filehandle:
58
59
60 line = line.strip()
61 if not line.endswith( ";" ):
62 try:
63 line = line + filehandle.next()
64 except StopIteration:
65 break
66
67
68 match = cls.refpathregex.match( line )
69
70 if match:
71 outrefs.append( match.group(1) )
72
73
74
75 if not allPaths and line.startswith( "requires" ):
76 break
77
78
79 filehandle.close()
80
81 return outrefs
82
84 """:return: list of filepath as parsed from the given mafile.
85 :param allPaths: if True, the whole file will be parsed, if False, only
86 the reference section will be parsed"""
87 outdepends = list()
88 log.info("Parsing %s" % ( mafile ))
89
90 try:
91 outdepends = self._parseReferences( mafile, allPaths )
92 except IOError,e:
93
94 self._addInvalid( mafile )
95 log.warn("Parsing Failed: %s" % str( e ))
96
97 return outdepends
98
99
103 """Parse the dependencies from the given maya ascii files and add them to
104 this graph
105
106 :note: the more files are given, the more efficient the method can be
107 :param parse_all_paths: if True, default False, all paths found in the file will be used.
108 This will slow down the parsing as the whole file will be searched for references
109 instead of just the header of the file
110 :param to_os_path: functor returning an MA file from given posssibly parsed file
111 that should be existing on the system parsing the files.
112 The passed in file could also be an mb file ( which cannot be parsed ), thus it
113 would be advantageous to return a corresponding ma file
114 This is required as references can have environment variables inside of them
115 :param os_path_to_db_key: converts the given path as used in the filesystem into
116 a path to be used as key in the database. It should be general.
117 Ideally, os_path_to_db_key is the inverse as to_os_path.
118 :note: if the parsed path contain environment variables you must start the
119 tool such that these can be resolved by the system. Otherwise files might
120 not be found
121 :todo: parse_all_paths still to be implemented"""
122 files_parsed = set()
123 for mafile in mafiles:
124 depfiles = [ mafile.strip() ]
125 while depfiles:
126 curfile = to_os_path( depfiles.pop() )
127
128
129 if os.path.splitext( curfile )[1] != ".ma":
130 log.info( "Skipped non-ma file: %s" % curfile )
131 continue
132
133
134 if curfile in files_parsed:
135 continue
136
137 curfiledepends = self._parseDepends( curfile, parse_all_paths )
138 files_parsed.add( curfile )
139
140
141 curfilestr = str( curfile )
142 valid_depends = list()
143 for depfile in curfiledepends:
144 print depfile
145
146 dbdepfile = to_os_path( depfile )
147 print dbdepfile
148 if os.path.exists( dbdepfile ):
149 valid_depends.append( depfile )
150 dbdepfile = os_path_to_db_key( dbdepfile )
151 else:
152 dbdepfile = depfile
153 self._addInvalid( depfile )
154
155 self.add_edge( dbdepfile, os_path_to_db_key( curfilestr ) )
156
157
158 depfiles.extend( valid_depends )
159
160
161
162
163
164
165 - def depends( self, filePath, direction = kAffects,
166 to_os_path = lambda f: os.path.expandvars( f ),
167 os_path_to_db_key = lambda f: f, return_unresolved = False,
168 invalid_only = False, **kwargs ):
169 """:return: list of paths ( converted to os paths ) that are related to
170 the given filePath
171 :param direction: specifies search direction, either :
172 kAffects = Files that filePath affects
173 kAffectedBy = Files that affect filePath
174 :param return_unresolved: if True, the output paths will not be translated to
175 an os paths and you get the paths as stored in the graph.
176 Please not that the to_os_path function is still needed to generate
177 a valid key, depending on the format of filepaths stored in this graph
178 :param invalid_only: if True, only invalid dependencies will be returned, all
179 including the invalid ones otherwise
180 :param to_os_path: see `addFromFiles`
181 :param os_path_to_db_key: see `addFromFiles`
182 :param kwargs: passed to `iterNetworkxGraph`"""
183 kwargs[ 'direction' ] = direction
184 kwargs[ 'ignore_startitem' ] = 1
185 kwargs[ 'branch_first' ] = 1
186
187 keypath = os_path_to_db_key( to_os_path( filePath ) )
188 invalid = set( self.invalidFiles() )
189
190 if return_unresolved:
191 to_os_path = lambda f: f
192
193 outlist = list()
194
195 try:
196 for d, f in iterNetworkxGraph( self, keypath, **kwargs ):
197 is_valid = f not in invalid
198 f = to_os_path( f )
199
200 if is_valid and invalid_only:
201 continue
202
203 outlist.append( f )
204
205 except NetworkXError:
206 log.debug( "Skipped Path %s ( %s ): unknown to dependency graph" % ( filePath, keypath ) )
207
208 return outlist
209
211 """
212 :return: list of filePaths that could not be parsed, most probably
213 because they could not be found by the system"""
214 lenp = len( self.invalidPrefix )
215
216 try:
217 return [ iv[ lenp : ] for iv in self.successors( self.invalidNodeID ) ]
218 except NetworkXError:
219 return list()
220
221
222