mrv.maya.automation.qa
Covered: 125 lines
Missed: 7 lines
Skipped 66 lines
Percent: 94 %
  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
  8
import sys
  9
import logging
 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"""
 17
	pass
 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
 29
	in python"""
 30
	@classmethod
 31
	def _getMelChecks( metacls, index_proc, check_cls ):
 32
		"""
 33
		:return: list( checkInstance, ... ) list of checkinstances represeting
 34
			mel based checkes
 35
		:param index_proc: method returning the index declaring the tests
 36
		:param check_cls: class used to instance new checks"""
 37
		output = list()
 38
		try:
 39
			index = Mel.call( index_proc )
 40
		except RuntimeError, e:
 41
			log.warn( str( e ) )
 42
		else:
 44
			if len( index ) % 3 == 0:
 45
				iindex = iter( index )
 46
				for checkname, description, can_fix in zip( iindex, iindex, iindex ):
 48
					if " " in checkname:
 49
						log.warn( "Invalid name: %s - it may not contain spaces, use CamelCase or underscores" % checkname )
 50
						continue
 53
					plug = check_cls( annotation = description, has_fix = int( can_fix ) )
 54
					plug.setName( checkname )
 55
					output.append( plug )
 58
			else:
 59
				log.warn( "Invalid proc index returned by %s" % index_proc )
 63
		return output
 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
 79
		newcls = super( QAMetaMel, metacls ).__new__( metacls, name, bases, clsdict )
 80
		return newcls
 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
 88
	**Configuration**:
 89
		The following variables MUST be used to setup this class once you have derived
 90
		from it:
 92
		 * mel_index_proc:
 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
 99
		 * mel_check_proc:
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
105
					[1] = header
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
113
			valid items
115
		static_mel_plugs:
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
118
			checks
119
	"""
120
	__metaclass__ = QAMetaMel
125
	mel_index_proc = None
128
	mel_check_proc = None
134
	static_mel_plugs = True
137
	check_result_cls = QACheckResult
141
	check_plug_cls = QAMELCheck
144
	def listMELChecks( self ):
145
		"""
146
		:return: list all checks ( Plugs ) available on this class that are implemented
147
			in MEL"""
148
		return self.listChecks( predicate = lambda p: isinstance( p.attr , QAMELCheckAttribute ) )
150
	def isMELCheck( self, check ):
151
		"""
152
		:return: True if the given check plug is implemented in MEL and can be handled
153
			there accordingly"""
154
		plug = check
155
		try:
156
			plug = check.plug
157
		except AttributeError:
158
			pass
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"""
165
		if not string_array:
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 )
180
	@classmethod
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 )