2
"""Specialization of workflow to allow checks to be natively implemented in MEL """
3
__docformat__ = "restructuredtext"
5
from mrv.automation.qa import QACheck, QACheckAttribute, QACheckResult
6
from mrv.maya.util import Mel
7
from mrv.dge import _NodeBaseCheckMeta
10
log = logging.getLogger("mrv.maya.automation.qa")
12
__all__ = ("QAMELCheckAttribute", "QAMELCheck", "QAMetaMel", "QAMELMixin")
15
class QAMELCheckAttribute( QACheckAttribute ):
16
"""Attribute identifying a MEL check carrying additional mel specific attributes"""
20
class QAMELCheck( QACheck ):
21
"""Specialized version of the QACheck allowing to use our own MEL attribute
22
contianiing more information"""
23
check_attribute_cls = QAMELCheckAttribute
26
class QAMetaMel( _NodeBaseCheckMeta ):
27
"""Metaclass allowing to create plugs based on a MEL implementation, allowing
28
to decide whether checks are Python or MEL implemented, but still running natively
31
def _getMelChecks( metacls, index_proc, check_cls ):
33
:return: list( checkInstance, ... ) list of checkinstances represeting
35
:param index_proc: method returning the index declaring the tests
36
:param check_cls: class used to instance new checks"""
39
index = Mel.call( index_proc )
40
except RuntimeError, e:
43
# assure its working , never fail here
44
if len( index ) % 3 == 0:
45
iindex = iter( index )
46
for checkname, description, can_fix in zip( iindex, iindex, iindex ):
47
# check name - it may not contain spaces for now
49
log.warn( "Invalid name: %s - it may not contain spaces, use CamelCase or underscores" % checkname )
53
plug = check_cls( annotation = description, has_fix = int( can_fix ) )
54
plug.setName( checkname )
56
# END for each information tuple
57
# END if index is valid
59
log.warn( "Invalid proc index returned by %s" % index_proc )
60
# END index has valid format
61
# END index could be retrieved
65
def __new__( metacls, name, bases, clsdict ):
66
"""Search for configuration attributes allowing to auto-generate plugs
67
referring to the respective mel implementation"""
68
index_proc = clsdict.get( "mel_index_proc", None )
69
check_cls = clsdict.get( "check_plug_cls", QAMELCheck )
70
static_plugs = clsdict.get( "static_mel_plugs", True )
72
if static_plugs and index_proc and check_cls is not None:
73
check_list = metacls._getMelChecks( index_proc, check_cls )
74
for check in check_list:
75
clsdict[ check.name() ] = check
78
# finally create the class
79
newcls = super( QAMetaMel, metacls ).__new__( metacls, name, bases, clsdict )
83
class QAMELMixin( object ):
84
"""Base class allowing to process MEL baesd plugs as created by our metaclass
86
:note: this class assumes it is used on a process
89
The following variables MUST be used to setup this class once you have derived
93
produdure name with signature func( ) returning string array in following format:
94
[n*3+0] = checkname : the name of the check, use CamelCase names or names_with_underscore
95
The checkname is also used as id to identify the check lateron
96
[n*3+1] = description: Single sentence desciption of the check targeted at the end user
97
[n*3+2] = can_fix: Boolean value indicating whether the check can also fix the issue
100
procedure called to actually process the given check, signature is:
101
func( check_name, should_fix )
102
returning list of strings as follows:
104
[0] = x number of fixed items
106
[1:1+x] = x fixed items
107
[2+x:n] = n invalid items
109
items are either objects or in general anything you check for. The check is
110
considered to be failed if there is at least one invalid item.
112
If you fixed items, all previously failed items should now be returned as
116
Please note that your class must implemnent plugs and extend the super class
117
result by the result of `listMELChecks` to dynamically retrieve the available
120
__metaclass__ = QAMetaMel
125
mel_index_proc = None
128
mel_check_proc = None
130
# if True, the mel based checks will be created as class members upon class
131
# creation. If False, they will be retrieved on demand whenever plugs are
132
# queried. The latter one can be slow, but might be required if the indices
133
# are dynamically generated
134
static_mel_plugs = True
136
# qa check result compatible class to be used as container for MEL return values
137
check_result_cls = QACheckResult
139
# qa check plug class to use for the plugs to be created - it will always default
141
check_plug_cls = QAMELCheck
144
def listMELChecks( self ):
146
:return: list all checks ( Plugs ) available on this class that are implemented
148
return self.listChecks( predicate = lambda p: isinstance( p.attr , QAMELCheckAttribute ) )
150
def isMELCheck( self, check ):
152
:return: True if the given check plug is implemented in MEL and can be handled
157
except AttributeError:
160
return isinstance( plug.attr, QAMELCheckAttribute )
162
def _rval_to_checkResult( self, string_array, **kwargs ):
163
""":return: check result as parsed fom string array
164
:param kwargs: will be given to initializer of check result instance"""
166
return self.check_result_cls( **kwargs )
168
assert len( string_array ) > 1 # need a header at least
170
num_fixed = int( string_array[0] )
171
end_num_fixed = 2 + num_fixed
173
kwargs[ 'header' ] = string_array[1]
174
kwargs[ 'fixed_items' ] = string_array[ 2 : end_num_fixed ]
175
kwargs[ 'failed_items' ] = string_array[ end_num_fixed : ]
177
return self.check_result_cls( **kwargs )
181
def melChecks( cls, predicate = lambda p: True ):
182
""":return: list of MEL checks ( plugs ) representing checks defined by MEL
183
:param predicate: only return plug if predicate( item ) yield True"""
184
return [ c for c in QAMetaMel._getMelChecks( cls.mel_index_proc, cls.check_plug_cls ) if predicate( c ) ]
186
def handleMELCheck( self, check, mode ):
187
"""Called to handle the given check in the given mode
189
:raise RuntimeError: If MEL throws an error
190
:return: QACheckResult of the result generated by MEL"""
191
assert self.mel_check_proc
192
assert isinstance( check.attr, QAMELCheckAttribute )
194
rval = Mel.call( self.mel_check_proc, check.name(), int( mode == self.eMode.fix ) )
196
return self._rval_to_checkResult( rval )