''' Description: This module allows us to easily instrument Modules, Classes, Functions and Methods. The primary use for this is to allow us to use the pyfbsdk with cProfile. Doesn't work with static/class methods only with instance methods Author: Ross George ''' import compiler _instrumentedMethodClosures = dict() _instrumentedMethodIdPairings = dict() def InstrumentMethod( targetClass, targetMethodName ): #Get the target function and it's Id targetInnerMethod = getattr( targetClass, targetMethodName ) targetInnerMethodId = id( targetInnerMethod ) #Check if we have processed this method if ( _instrumentedMethodIdPairings.has_key( targetInnerMethodId ) ): return() #Add the function to our closures list so that we can call it later #We use the objects ID to do this. _instrumentedMethodClosures[ targetInnerMethodId ] = targetInnerMethod #Get the name we will give the outer function (this is how it will #appear on the profiler so it's important that it is descriptive) targetOuterFunctionName = '{0}_{1}_{2}'.format( targetClass.__module__, targetClass.__name__, targetMethodName ) #Compile into a function. We do this because we want the correct function and filename to appear on the profile stats. outerFunctionSource = 'def {0}( *args, **kwargs ): return( _instrumentedMethodClosures[{1}]( *args, **kwargs ) )'.format( targetOuterFunctionName, targetInnerMethodId ) outerFunctionCompiled = compiler.compile( outerFunctionSource, 'pyfbsdk.pyc', 'exec' ) exec( outerFunctionCompiled ) #Get the outer function ready to override the inner method on our targetClass exec( 'targetOuterFunction = {0}'.format( targetOuterFunctionName ) ) #Add this function to our list of functions that have been instrumented outerFunctionId = id( targetOuterFunction ) _instrumentedMethodIdPairings[ outerFunctionId ] = targetInnerMethodId #Finally we set the attribute on the class setattr( targetClass, targetMethodName, targetOuterFunction ) _instrumentedFunctionClosures = dict() _instrumentedFunctionIdPairings = dict() def InstrumentFunction( targetModule, targetFunctionName ): #Get the target function targetInnerFunction = getattr( targetModule, targetFunctionName ) targetInnerFunctionId = id( targetInnerFunction ) #Check if we have processed this function if ( _instrumentedFunctionIdPairings.has_key( targetInnerFunctionId ) ): return() #Add the function to our closures list so that we can call it later #We use the objects ID to do this. _instrumentedFunctionClosures[ targetInnerFunctionId ] = targetInnerFunction #Get the name we will give the outer function (this is how it will #appear on the profiler so it's important that it is descriptive) targetOuterFunctionName = '{0}_{1}'.format( targetModule.__name__, targetFunctionName ) #Compile into a function. We do this because we want the correct function and filename to appear on the profile stats. outerFunctionSource = 'def {0}( *args, **kwargs ): return( _instrumentedFunctionClosures[{1}]( *args, **kwargs ) )'.format( targetOuterFunctionName, targetInnerFunctionId ) outerFunctionCompiled = compiler.compile( outerFunctionSource, 'pyfbsdk.pyc', 'exec' ) exec( outerFunctionCompiled ) #Get the outer function ready to override our inner function exec( 'targetOuterFunction = {0}'.format( targetOuterFunctionName ) ) #Add this function to our list of functions that have been instrumented outerFunctionId = id( targetOuterFunction ) _instrumentedFunctionIdPairings[ outerFunctionId ] = targetInnerFunctionId #Finally we set the attribute on the module setattr( targetModule, targetFunctionName, targetOuterFunction ) _instrumentedClassIdList = list() def InstrumentClass( targetClass ): #Check we haven't already processed this class targetClassId = id( targetClass ) if targetClassId in _instrumentedClassIdList: return() for attributeKey in dir( targetClass ): attributeValue = getattr( targetClass, attributeKey ) if ( 'instancemethod' in type( attributeValue ).__name__ ): InstrumentMethod( targetClass, attributeKey ) #Add the class so that we don't process it again _instrumentedClassIdList.append( targetClassId ) _instrumentedModuleIdList = list() def InstrumentModule( targetModule ): #Check we haven't already processed this module targetModuleId = id( targetModule ) if targetModuleId in _instrumentedModuleIdList: return() for attributeKey in dir( targetModule ): attributeValue = getattr( targetModule, attributeKey ) if ( 'function' in type( attributeValue ).__name__ ): InstrumentFunction( targetModule, attributeKey ) if ( 'class' in type( attributeValue ).__name__ ): InstrumentClass( attributeValue ) #Add the module so that we don't process it again _instrumentedModuleIdList.append( targetModuleId )