Source code for direct.showbase.Finder

"""Contains various utility functions."""

__all__ = ['findClass', 'rebindClass', 'copyFuncs', 'replaceMessengerFunc', 'replaceTaskMgrFunc', 'replaceStateFunc', 'replaceCRFunc', 'replaceAIRFunc', 'replaceIvalFunc']

import types
import os
import sys

from direct.showbase.MessengerGlobal import messenger
from direct.task.TaskManagerGlobal import taskMgr


[docs]def findClass(className): """ Look in sys.modules dictionary for a module that defines a class with this className. """ for moduleName, module in sys.modules.items(): # Some modules are None for some reason if module: # print "Searching in ", moduleName classObj = module.__dict__.get(className) # If this modules defines some object called classname and the # object is a class or type definition and that class's module # is the same as the module we are looking in, then we found # the matching class and a good module namespace to redefine # our class in. if classObj and isinstance(classObj, type) and \ classObj.__module__ == moduleName: return [classObj, module.__dict__] return None
[docs]def rebindClass(filename): file = open(filename, 'r') lines = file.readlines() for line in lines: if line[0:6] == 'class ': # Chop off the "class " syntax and strip extra whitespace classHeader = line[6:].strip() # Look for a open paren if it does inherit parenLoc = classHeader.find('(') if parenLoc > 0: className = classHeader[:parenLoc] else: # Look for a colon if it does not inherit colonLoc = classHeader.find(':') if colonLoc > 0: className = classHeader[:colonLoc] else: print('error: className not found') # Remove that temp file file.close() os.remove(filename) return print('Rebinding class name: ' + className) break # Try to find the original class with this class name res = findClass(className) if not res: print ('Warning: Finder could not find class') # Remove the temp file we made file.close() os.remove(filename) return # Store the original real class realClass, realNameSpace = res # Now execute that class def in this namespace exec(compile(open(filename).read(), filename, 'exec'), realNameSpace) # That execfile should have created a new class obj in that namespace tmpClass = realNameSpace[className] # Copy the functions that we just redefined into the real class copyFuncs(tmpClass, realClass) # Now make sure the original class is in that namespace, # not our temp one from the execfile. This will help us preserve # class variables and other state on the original class. realNameSpace[className] = realClass # Remove the temp file we made file.close() os.remove(filename) print (' Finished rebind')
[docs]def copyFuncs(fromClass, toClass): replaceFuncList = [] newFuncList = [] # Copy the functions from fromClass into toClass dictionary for funcName, newFunc in fromClass.__dict__.items(): # Filter out for functions if isinstance(newFunc, types.FunctionType): # See if we already have a function with this name oldFunc = toClass.__dict__.get(funcName) if oldFunc: # This code is nifty, but with nested functions, give an error: # SystemError: cellobject.c:22: bad argument to internal function # Give the new function code the same filename as the old function # Perhaps there is a cleaner way to do this? This was my best idea. #newCode = types.CodeType(newFunc.func_code.co_argcount, # newFunc.func_code.co_nlocals, # newFunc.func_code.co_stacksize, # newFunc.func_code.co_flags, # newFunc.func_code.co_code, # newFunc.func_code.co_consts, # newFunc.func_code.co_names, # newFunc.func_code.co_varnames, # # Use the oldFunc's filename here. Tricky! # oldFunc.func_code.co_filename, # newFunc.func_code.co_name, # newFunc.func_code.co_firstlineno, # newFunc.func_code.co_lnotab) #newFunc = types.FunctionType(newCode, # newFunc.func_globals, # newFunc.func_name, # newFunc.func_defaults, # newFunc.func_closure) replaceFuncList.append((oldFunc, funcName, newFunc)) else: # TODO: give these new functions a proper code filename newFuncList.append((funcName, newFunc)) # Look in the messenger, taskMgr, and other globals that store func # pointers to see if this old function pointer is stored there, and # update it to the new function pointer. replaceMessengerFunc(replaceFuncList) replaceTaskMgrFunc(replaceFuncList) replaceStateFunc(replaceFuncList) replaceCRFunc(replaceFuncList) replaceAIRFunc(replaceFuncList) replaceIvalFunc(replaceFuncList) # Now that we've the globals funcs, actually swap the pointers in # the new class to the new functions for oldFunc, funcName, newFunc in replaceFuncList: # print "replacing old func: ", oldFunc, funcName, newFunc setattr(toClass, funcName, newFunc) # Add the brand new functions too for funcName, newFunc in newFuncList: # print "adding new func: ", oldFunc, funcName, newFunc setattr(toClass, funcName, newFunc)
[docs]def replaceMessengerFunc(replaceFuncList): try: messenger except: return for oldFunc, funcName, newFunc in replaceFuncList: res = messenger.replaceMethod(oldFunc, newFunc) if res: print('replaced %s messenger function(s): %s' % (res, funcName))
[docs]def replaceTaskMgrFunc(replaceFuncList): try: taskMgr except: return for oldFunc, funcName, newFunc in replaceFuncList: if taskMgr.replaceMethod(oldFunc, newFunc): print('replaced taskMgr function: %s' % funcName)
[docs]def replaceStateFunc(replaceFuncList): if not sys.modules.get('base.direct.fsm.State'): return from direct.fsm.State import State for oldFunc, funcName, newFunc in replaceFuncList: res = State.replaceMethod(oldFunc, newFunc) if res: print('replaced %s FSM transition function(s): %s' % (res, funcName))
[docs]def replaceCRFunc(replaceFuncList): try: base.cr except: return # masad: Gyedo's fake cr causes a crash in followingreplaceMethod on rebinding, so # I throw in the isFake check. I still think the fake cr should be eliminated. if hasattr(base.cr,'isFake'): return for oldFunc, funcName, newFunc in replaceFuncList: if base.cr.replaceMethod(oldFunc, newFunc): print('replaced DistributedObject function: %s' % funcName)
[docs]def replaceAIRFunc(replaceFuncList): try: simbase.air except: return for oldFunc, funcName, newFunc in replaceFuncList: if simbase.air.replaceMethod(oldFunc, newFunc): print('replaced DistributedObject function: %s' % funcName)
[docs]def replaceIvalFunc(replaceFuncList): # Make sure we have imported IntervalManager and thus created # a global ivalMgr. if not sys.modules.get('base.direct.interval.IntervalManager'): return from direct.interval.FunctionInterval import FunctionInterval for oldFunc, funcName, newFunc in replaceFuncList: res = FunctionInterval.replaceMethod(oldFunc, newFunc) if res: print('replaced %s interval function(s): %s' % (res, funcName))