Finder.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. """Contains various utility functions."""
  2. __all__ = ['findClass', 'rebindClass', 'copyFuncs', 'replaceMessengerFunc', 'replaceTaskMgrFunc', 'replaceStateFunc', 'replaceCRFunc', 'replaceAIRFunc', 'replaceIvalFunc']
  3. import types
  4. import os
  5. import sys
  6. from direct.showbase.MessengerGlobal import messenger
  7. from direct.task.TaskManagerGlobal import taskMgr
  8. def findClass(className):
  9. """
  10. Look in sys.modules dictionary for a module that defines a class
  11. with this className.
  12. """
  13. for moduleName, module in sys.modules.items():
  14. # Some modules are None for some reason
  15. if module:
  16. # print "Searching in ", moduleName
  17. classObj = module.__dict__.get(className)
  18. # If this modules defines some object called classname and the
  19. # object is a class or type definition and that class's module
  20. # is the same as the module we are looking in, then we found
  21. # the matching class and a good module namespace to redefine
  22. # our class in.
  23. if classObj and isinstance(classObj, type) and \
  24. classObj.__module__ == moduleName:
  25. return [classObj, module.__dict__]
  26. return None
  27. def rebindClass(filename):
  28. file = open(filename, 'r')
  29. lines = file.readlines()
  30. for line in lines:
  31. if line[0:6] == 'class ':
  32. # Chop off the "class " syntax and strip extra whitespace
  33. classHeader = line[6:].strip()
  34. # Look for a open paren if it does inherit
  35. parenLoc = classHeader.find('(')
  36. if parenLoc > 0:
  37. className = classHeader[:parenLoc]
  38. else:
  39. # Look for a colon if it does not inherit
  40. colonLoc = classHeader.find(':')
  41. if colonLoc > 0:
  42. className = classHeader[:colonLoc]
  43. else:
  44. print('error: className not found')
  45. # Remove that temp file
  46. file.close()
  47. os.remove(filename)
  48. return
  49. print('Rebinding class name: ' + className)
  50. break
  51. # Try to find the original class with this class name
  52. res = findClass(className)
  53. if not res:
  54. print('Warning: Finder could not find class')
  55. # Remove the temp file we made
  56. file.close()
  57. os.remove(filename)
  58. return
  59. # Store the original real class
  60. realClass, realNameSpace = res
  61. # Now execute that class def in this namespace
  62. exec(compile(open(filename).read(), filename, 'exec'), realNameSpace)
  63. # That execfile should have created a new class obj in that namespace
  64. tmpClass = realNameSpace[className]
  65. # Copy the functions that we just redefined into the real class
  66. copyFuncs(tmpClass, realClass)
  67. # Now make sure the original class is in that namespace,
  68. # not our temp one from the execfile. This will help us preserve
  69. # class variables and other state on the original class.
  70. realNameSpace[className] = realClass
  71. # Remove the temp file we made
  72. file.close()
  73. os.remove(filename)
  74. print(' Finished rebind')
  75. def copyFuncs(fromClass, toClass):
  76. replaceFuncList = []
  77. newFuncList = []
  78. # Copy the functions from fromClass into toClass dictionary
  79. for funcName, newFunc in fromClass.__dict__.items():
  80. # Filter out for functions
  81. if isinstance(newFunc, types.FunctionType):
  82. # See if we already have a function with this name
  83. oldFunc = toClass.__dict__.get(funcName)
  84. if oldFunc:
  85. # This code is nifty, but with nested functions, give an error:
  86. # SystemError: cellobject.c:22: bad argument to internal function
  87. # Give the new function code the same filename as the old function
  88. # Perhaps there is a cleaner way to do this? This was my best idea.
  89. #newCode = types.CodeType(newFunc.func_code.co_argcount,
  90. # newFunc.func_code.co_nlocals,
  91. # newFunc.func_code.co_stacksize,
  92. # newFunc.func_code.co_flags,
  93. # newFunc.func_code.co_code,
  94. # newFunc.func_code.co_consts,
  95. # newFunc.func_code.co_names,
  96. # newFunc.func_code.co_varnames,
  97. # # Use the oldFunc's filename here. Tricky!
  98. # oldFunc.func_code.co_filename,
  99. # newFunc.func_code.co_name,
  100. # newFunc.func_code.co_firstlineno,
  101. # newFunc.func_code.co_lnotab)
  102. #newFunc = types.FunctionType(newCode,
  103. # newFunc.func_globals,
  104. # newFunc.func_name,
  105. # newFunc.func_defaults,
  106. # newFunc.func_closure)
  107. replaceFuncList.append((oldFunc, funcName, newFunc))
  108. else:
  109. # TODO: give these new functions a proper code filename
  110. newFuncList.append((funcName, newFunc))
  111. # Look in the messenger, taskMgr, and other globals that store func
  112. # pointers to see if this old function pointer is stored there, and
  113. # update it to the new function pointer.
  114. replaceMessengerFunc(replaceFuncList)
  115. replaceTaskMgrFunc(replaceFuncList)
  116. replaceStateFunc(replaceFuncList)
  117. replaceCRFunc(replaceFuncList)
  118. replaceAIRFunc(replaceFuncList)
  119. replaceIvalFunc(replaceFuncList)
  120. # Now that we've the globals funcs, actually swap the pointers in
  121. # the new class to the new functions
  122. for oldFunc, funcName, newFunc in replaceFuncList:
  123. # print "replacing old func: ", oldFunc, funcName, newFunc
  124. setattr(toClass, funcName, newFunc)
  125. # Add the brand new functions too
  126. for funcName, newFunc in newFuncList:
  127. # print "adding new func: ", oldFunc, funcName, newFunc
  128. setattr(toClass, funcName, newFunc)
  129. def replaceMessengerFunc(replaceFuncList):
  130. try:
  131. messenger
  132. except Exception:
  133. return
  134. for oldFunc, funcName, newFunc in replaceFuncList:
  135. res = messenger.replaceMethod(oldFunc, newFunc)
  136. if res:
  137. print('replaced %s messenger function(s): %s' % (res, funcName))
  138. def replaceTaskMgrFunc(replaceFuncList):
  139. try:
  140. taskMgr
  141. except Exception:
  142. return
  143. for oldFunc, funcName, newFunc in replaceFuncList:
  144. if taskMgr.replaceMethod(oldFunc, newFunc):
  145. print('replaced taskMgr function: %s' % funcName)
  146. def replaceStateFunc(replaceFuncList):
  147. if not sys.modules.get('base.direct.fsm.State'):
  148. return
  149. from direct.fsm.State import State
  150. for oldFunc, funcName, newFunc in replaceFuncList:
  151. res = State.replaceMethod(oldFunc, newFunc)
  152. if res:
  153. print('replaced %s FSM transition function(s): %s' % (res, funcName))
  154. def replaceCRFunc(replaceFuncList):
  155. try:
  156. base.cr
  157. except Exception:
  158. return
  159. # masad: Gyedo's fake cr causes a crash in followingreplaceMethod on rebinding, so
  160. # I throw in the isFake check. I still think the fake cr should be eliminated.
  161. if hasattr(base.cr, 'isFake'):
  162. return
  163. for oldFunc, funcName, newFunc in replaceFuncList:
  164. if base.cr.replaceMethod(oldFunc, newFunc):
  165. print('replaced DistributedObject function: %s' % funcName)
  166. def replaceAIRFunc(replaceFuncList):
  167. try:
  168. simbase.air
  169. except Exception:
  170. return
  171. for oldFunc, funcName, newFunc in replaceFuncList:
  172. if simbase.air.replaceMethod(oldFunc, newFunc):
  173. print('replaced DistributedObject function: %s' % funcName)
  174. def replaceIvalFunc(replaceFuncList):
  175. # Make sure we have imported IntervalManager and thus created
  176. # a global ivalMgr.
  177. if not sys.modules.get('base.direct.interval.IntervalManager'):
  178. return
  179. from direct.interval.FunctionInterval import FunctionInterval
  180. for oldFunc, funcName, newFunc in replaceFuncList:
  181. res = FunctionInterval.replaceMethod(oldFunc, newFunc)
  182. if res:
  183. print('replaced %s interval function(s): %s' % (res, funcName))