diff --git a/direct/src/directutil/Verify.py b/direct/src/directutil/Verify.py index db28ac7909..8a1f41acb5 100755 --- a/direct/src/directutil/Verify.py +++ b/direct/src/directutil/Verify.py @@ -41,7 +41,9 @@ only when debugging (i.e. when it won't be checked-in) or where it helps you resist using assert for error handling. """ -wantVerifyPdb = 0 # Set to true to load pdb on failure. +from panda3d.core import ConfigVariableBool + +wantVerifyPdb = ConfigVariableBool('want-verify-pdb', False) # Set to true to load pdb on failure. def verify(assertion): diff --git a/direct/src/gui/DirectGuiBase.py b/direct/src/gui/DirectGuiBase.py index bb959a1601..41322bcdff 100644 --- a/direct/src/gui/DirectGuiBase.py +++ b/direct/src/gui/DirectGuiBase.py @@ -12,7 +12,6 @@ from OnscreenImage import * from direct.directtools.DirectUtil import ROUND_TO from direct.showbase import DirectObject from direct.task import Task -from direct.showbase.PythonUtil import recordCreationStackStr import types guiObjectCollector = PStatCollector("Client::GuiObjects") @@ -651,13 +650,6 @@ def toggleGuiGridSnap(): def setGuiGridSpacing(spacing): DirectGuiWidget.gridSpacing = spacing -# this should trigger off of __dev__, but it's not available at this point. -# __debug__ works because the production client is not __debug__ and the -# production AI doesn't create any GUI. -if get_config_showbase().GetBool('record-gui-creation-stack', __debug__): - # this will help track down the code that created DirectGui objects - # call obj.printCreationStackTrace() to figure out what code created it - DirectGuiBase = recordCreationStackStr(DirectGuiBase) class DirectGuiWidget(DirectGuiBase, NodePath): # Toggle if you wish widget's to snap to grid when draggin diff --git a/direct/src/interval/ProjectileInterval.py b/direct/src/interval/ProjectileInterval.py index 39077bf1fe..d87253a5ad 100755 --- a/direct/src/interval/ProjectileInterval.py +++ b/direct/src/interval/ProjectileInterval.py @@ -72,13 +72,6 @@ class ProjectileInterval(Interval): self.projectileIntervalNum) ProjectileInterval.projectileIntervalNum += 1 - """ - # attempt to add info about the caller - file, line, func = PythonUtil.callerInfo() - if file is not None: - name += '-%s:%s:%s' % (file, line, func) - """ - args = (startPos, endPos, duration, startVel, endZ, wayPoint, timeToWayPoint, gravityMult) self.implicitStartPos = 0 diff --git a/direct/src/showbase/ExceptionVarDump.py b/direct/src/showbase/ExceptionVarDump.py index a9daafe272..f07161d1f7 100755 --- a/direct/src/showbase/ExceptionVarDump.py +++ b/direct/src/showbase/ExceptionVarDump.py @@ -1,4 +1,5 @@ -from panda3d.direct import get_config_showbase +__all__ = ["install"] + from direct.directnotify.DirectNotifyGlobal import directNotify from direct.showbase.PythonUtil import fastRepr import sys @@ -6,7 +7,6 @@ import types import traceback notify = directNotify.newCategory("ExceptionVarDump") -config = get_config_showbase() reentry = 0 @@ -187,7 +187,7 @@ def install(log, upload): wantStackDumpLog = log wantStackDumpUpload = upload - dumpOnExceptionInit = config.GetBool('variable-dump-on-exception-init', 0) + dumpOnExceptionInit = ConfigVariableBool('variable-dump-on-exception-init', False) if dumpOnExceptionInit: # this mode doesn't completely work because exception objects # thrown by the interpreter don't get created until the diff --git a/direct/src/showbase/GarbageReport.py b/direct/src/showbase/GarbageReport.py index 72dcaf13c7..72c46b4a0e 100755 --- a/direct/src/showbase/GarbageReport.py +++ b/direct/src/showbase/GarbageReport.py @@ -281,7 +281,7 @@ class GarbageReport(Job): if self._args.findCycles: s = ['===== GarbageReport: \'%s\' (%s %s) =====' % ( self._args.name, self.numCycles, - choice(self.numCycles == 1, 'cycle', 'cycles'))] + ('cycle' if self.numCycles == 1 else 'cycles'))] else: s = ['===== GarbageReport: \'%s\' =====' % ( self._args.name)] @@ -499,7 +499,7 @@ class GarbageReport(Job): rootId = index # check if the root object is one of the garbage instances (has __del__) objId = id(self.garbage[rootId]) - numDelInstances = choice(objId in self.garbageInstanceIds, 1, 0) + numDelInstances = int(objId in self.garbageInstanceIds) stateStack.push(([rootId], rootId, numDelInstances, 0)) while True: yield None @@ -535,7 +535,7 @@ class GarbageReport(Job): elif refId is not None: # check if this object is one of the garbage instances (has __del__) objId = id(self.garbage[refId]) - numDelInstances += choice(objId in self.garbageInstanceIds, 1, 0) + numDelInstances += int(objId in self.garbageInstanceIds) # this refId does not complete a cycle. Mark down # where we are in this list of referents, then # start looking through the referents of the new refId diff --git a/direct/src/showbase/ObjectPool.py b/direct/src/showbase/ObjectPool.py index 80109226ce..506a7e2ba5 100755 --- a/direct/src/showbase/ObjectPool.py +++ b/direct/src/showbase/ObjectPool.py @@ -5,7 +5,6 @@ __all__ = ['Diff', 'ObjectPool'] from direct.directnotify.DirectNotifyGlobal import directNotify from direct.showbase.PythonUtil import invertDictLossless, makeList, safeRepr from direct.showbase.PythonUtil import getNumberedTypedString, getNumberedTypedSortedString -from direct.showbase.PythonUtil import getNumberedTypedSortedStringWithReferrersGen import gc class Diff: @@ -97,7 +96,7 @@ class ObjectPool: s += '\n%s\t%s' % (count, typ) return s - def printObjsByType(self, printReferrers=False): + def printObjsByType(self): print 'Object Pool: Objects By Type' print '\n============================' counts = list(set(self._count2types.keys())) @@ -109,11 +108,7 @@ class ObjectPool: types = makeList(self._count2types[count]) for typ in types: print 'TYPE: %s, %s objects' % (repr(typ), len(self._type2objs[typ])) - if printReferrers: - for line in getNumberedTypedSortedStringWithReferrersGen(self._type2objs[typ]): - print line - else: - print getNumberedTypedSortedString(self._type2objs[typ]) + print getNumberedTypedSortedString(self._type2objs[typ]) def containerLenStr(self): s = 'Object Pool: Container Lengths' diff --git a/direct/src/showbase/PythonUtil.py b/direct/src/showbase/PythonUtil.py index 16ac46d487..3e8e8aa581 100644 --- a/direct/src/showbase/PythonUtil.py +++ b/direct/src/showbase/PythonUtil.py @@ -1,18 +1,14 @@ - -"""Undocumented Module""" +"""Contains miscellaneous utility functions and classes.""" __all__ = ['indent', -'StackTrace', 'traceFunctionCall', 'traceParentCall', 'printThisCall', 'doc', 'adjust', 'difference', 'intersection', 'union', 'sameElements', 'makeList', 'makeTuple', 'list2dict', 'invertDict', 'invertDictLossless', 'uniqueElements', 'disjoint', 'contains', 'replace', 'reduceAngle', 'fitSrcAngle2Dest', 'fitDestAngle2Src', -'closestDestAngle2', 'closestDestAngle', 'binaryRepr', 'profileFunc', -'profiled', 'startProfile', 'printProfile', 'getSetterName', +'closestDestAngle2', 'closestDestAngle', 'getSetterName', 'getSetter', 'Functor', 'Stack', 'Queue', 'bound', 'clamp', 'lerp', 'average', 'addListsByValue', 'boolEqual', 'lineupPos', 'formatElapsedSeconds', 'solveQuadratic', -'stackEntryInfo', 'lineInfo', 'callerInfo', 'lineTag', 'findPythonModule', 'mostDerivedLast', 'weightedChoice', 'randFloat', 'normalDistrib', 'weightedRand', 'randUint31', 'randInt32', 'randUint32', @@ -20,41 +16,36 @@ __all__ = ['indent', 'SingletonError', 'printListEnum', 'safeRepr', 'fastRepr', 'isDefaultValue', 'ScratchPad', 'Sync', 'itype', 'getNumberedTypedString', -'getNumberedTypedSortedString', 'getNumberedTypedSortedStringWithReferrers', -'getNumberedTypedSortedStringWithReferrersGen', +'getNumberedTypedSortedString', 'printNumberedTyped', 'DelayedCall', 'DelayedFunctor', -'FrameDelayedCall', 'SubframeCall', 'getBase', -'HotkeyBreaker','logMethodCalls','GoldenRatio', +'FrameDelayedCall', 'SubframeCall', 'getBase', 'GoldenRatio', 'GoldenRectangle', 'rad90', 'rad180', 'rad270', 'rad360', -'nullGen', 'loopGen', 'makeFlywheelGen', 'flywheel', 'choice', -'printStack', 'printReverseStack', 'listToIndex2item', 'listToItem2index', -'pandaBreak','pandaTrace','formatTimeCompact', -'deeptype','getProfileResultString','StdoutCapture','StdoutPassthrough', +'nullGen', 'loopGen', 'makeFlywheelGen', 'flywheel', +'listToIndex2item', 'listToItem2index', +'formatTimeCompact','deeptype','StdoutCapture','StdoutPassthrough', 'Averager', 'getRepository', 'formatTimeExact', 'startSuperLog', 'endSuperLog', 'typeName', 'safeTypeName', 'histogramDict', 'unescapeHtmlString'] +if __debug__: + __all__ += ['StackTrace', 'traceFunctionCall', 'traceParentCall', 'printThisCall', + 'stackEntryInfo', 'lineInfo', 'callerInfo', 'lineTag', + 'profileFunc', 'profiled', 'startProfile', 'printProfile', + 'getProfileResultString', 'printStack', 'printReverseStack'] + import types import string import math -import operator -import inspect import os import sys import random import time -import gc -#if __debug__: -import traceback import __builtin__ -from StringIO import StringIO -import marshal +import importlib __report_indent = 3 from panda3d.core import ConfigVariableBool -ScalarTypes = (types.FloatType, types.IntType, types.LongType) - """ # with one integer positional arg, this uses about 4/5 of the memory of the Functor class below def Functor(function, *args, **kArgs): @@ -92,15 +83,6 @@ class Functor: _kargs.update(kargs) return self._function(*(self._args + args), **_kargs) - # this method is used in place of __call__ if we are recording creation stacks - def _exceptionLoggedCreationStack__call__(self, *args, **kargs): - try: - return self._do__call__(*args, **kargs) - except Exception, e: - print '-->Functor creation stack (%s): %s' % ( - self.__name__, self.getCreationStackTraceCompactStr()) - raise - __call__ = _do__call__ def __repr__(self): @@ -186,113 +168,113 @@ def indent(stream, numIndents, str): stream.write(' ' * numIndents + str) -#if __debug__: #RAU accdg to Darren its's ok that StackTrace is not protected by __debug__ -# DCR: if somebody ends up using StackTrace in production, either -# A) it will be OK because it hardly ever gets called, or -# B) it will be easy to track it down (grep for StackTrace) -class StackTrace: - def __init__(self, label="", start=0, limit=None): - """ - label is a string (or anything that be be a string) - that is printed as part of the trace back. - This is just to make it easier to tell what the - stack trace is referring to. - start is an integer number of stack frames back - from the most recent. (This is automatically - bumped up by one to skip the __init__ call - to the StackTrace). - limit is an integer number of stack frames - to record (or None for unlimited). - """ - self.label = label - if limit is not None: - self.trace = traceback.extract_stack(sys._getframe(1+start), - limit=limit) - else: - self.trace = traceback.extract_stack(sys._getframe(1+start)) +if __debug__: + import traceback + import marshal - def compact(self): - r = '' - comma = ',' - for filename, lineNum, funcName, text in self.trace: - r += '%s.%s:%s%s' % (filename[:filename.rfind('.py')][filename.rfind('\\')+1:], funcName, lineNum, comma) - if len(r): - r = r[:-len(comma)] - return r - - def reverseCompact(self): - r = '' - comma = ',' - for filename, lineNum, funcName, text in self.trace: - r = '%s.%s:%s%s%s' % (filename[:filename.rfind('.py')][filename.rfind('\\')+1:], funcName, lineNum, comma, r) - if len(r): - r = r[:-len(comma)] - return r - - def __str__(self): - r = "Debug stack trace of %s (back %s frames):\n"%( - self.label, len(self.trace),) - for i in traceback.format_list(self.trace): - r+=i - r+="***** NOTE: This is not a crash. This is a debug stack trace. *****" - return r - -def printStack(): - print StackTrace(start=1).compact() - return True -def printReverseStack(): - print StackTrace(start=1).reverseCompact() - return True -def printVerboseStack(): - print StackTrace(start=1) - return True - -#----------------------------------------------------------------------------- - -def traceFunctionCall(frame): - """ - return a string that shows the call frame with calling arguments. - e.g. - foo(x=234, y=135) - """ - f = frame - co = f.f_code - dict = f.f_locals - n = co.co_argcount - if co.co_flags & 4: n = n+1 - if co.co_flags & 8: n = n+1 - r='' - if 'self' in dict: - r = '%s.'%(dict['self'].__class__.__name__,) - r+="%s("%(f.f_code.co_name,) - comma=0 # formatting, whether we should type a comma. - for i in range(n): - name = co.co_varnames[i] - if name=='self': - continue - if comma: - r+=', ' - else: - # ok, we skipped the first one, the rest get commas: - comma=1 - r+=name - r+='=' - if name in dict: - v=safeRepr(dict[name]) - if len(v)>2000: - # r+="" - r += (v[:2000] + "...") + class StackTrace: + def __init__(self, label="", start=0, limit=None): + """ + label is a string (or anything that be be a string) + that is printed as part of the trace back. + This is just to make it easier to tell what the + stack trace is referring to. + start is an integer number of stack frames back + from the most recent. (This is automatically + bumped up by one to skip the __init__ call + to the StackTrace). + limit is an integer number of stack frames + to record (or None for unlimited). + """ + self.label = label + if limit is not None: + self.trace = traceback.extract_stack(sys._getframe(1+start), + limit=limit) else: - r+=v - else: r+="*** undefined ***" - return r+')' + self.trace = traceback.extract_stack(sys._getframe(1+start)) -def traceParentCall(): - return traceFunctionCall(sys._getframe(2)) + def compact(self): + r = '' + comma = ',' + for filename, lineNum, funcName, text in self.trace: + r += '%s.%s:%s%s' % (filename[:filename.rfind('.py')][filename.rfind('\\')+1:], funcName, lineNum, comma) + if len(r): + r = r[:-len(comma)] + return r -def printThisCall(): - print traceFunctionCall(sys._getframe(1)) - return 1 # to allow "assert printThisCall()" + def reverseCompact(self): + r = '' + comma = ',' + for filename, lineNum, funcName, text in self.trace: + r = '%s.%s:%s%s%s' % (filename[:filename.rfind('.py')][filename.rfind('\\')+1:], funcName, lineNum, comma, r) + if len(r): + r = r[:-len(comma)] + return r + + def __str__(self): + r = "Debug stack trace of %s (back %s frames):\n"%( + self.label, len(self.trace),) + for i in traceback.format_list(self.trace): + r+=i + r+="***** NOTE: This is not a crash. This is a debug stack trace. *****" + return r + + def printStack(): + print StackTrace(start=1).compact() + return True + def printReverseStack(): + print StackTrace(start=1).reverseCompact() + return True + def printVerboseStack(): + print StackTrace(start=1) + return True + + #----------------------------------------------------------------------------- + + def traceFunctionCall(frame): + """ + return a string that shows the call frame with calling arguments. + e.g. + foo(x=234, y=135) + """ + f = frame + co = f.f_code + dict = f.f_locals + n = co.co_argcount + if co.co_flags & 4: n = n+1 + if co.co_flags & 8: n = n+1 + r='' + if 'self' in dict: + r = '%s.'%(dict['self'].__class__.__name__,) + r+="%s("%(f.f_code.co_name,) + comma=0 # formatting, whether we should type a comma. + for i in range(n): + name = co.co_varnames[i] + if name=='self': + continue + if comma: + r+=', ' + else: + # ok, we skipped the first one, the rest get commas: + comma=1 + r+=name + r+='=' + if name in dict: + v=safeRepr(dict[name]) + if len(v)>2000: + # r+="" + r += (v[:2000] + "...") + else: + r+=v + else: r+="*** undefined ***" + return r+')' + + def traceParentCall(): + return traceFunctionCall(sys._getframe(2)) + + def printThisCall(): + print traceFunctionCall(sys._getframe(1)) + return 1 # to allow "assert printThisCall()" # Magic numbers: These are the bit masks in func_code.co_flags that # reveal whether or not the function has a *arg or **kw argument. @@ -327,7 +309,6 @@ def adjust(command = None, dim = 1, parent = None, **kw): # Make sure we enable Tk # Don't use a regular import, to prevent ModuleFinder from picking # it up as a dependency when building a .p3d package. - import importlib Valuator = importlib.import_module('direct.tkwidgets.Valuator') # Set command if specified if command: @@ -579,19 +560,6 @@ def closestDestAngle(src, dest): # otherwise just go to the original destination return dest - -def binaryRepr(number, max_length = 32): - # This will only work reliably for relatively small numbers. - # Increase the value of max_length if you think you're going - # to use long integers - assert number < 2L << max_length - shifts = map (operator.rshift, max_length * [number], \ - range (max_length - 1, -1, -1)) - digits = map (operator.mod, shifts, max_length * [2]) - if not digits.count (1): return 0 - digits = digits [digits.index (1):] - return ''.join([repr(digit) for digit in digits]) - class StdoutCapture: # redirects stdout to a string def __init__(self): @@ -618,260 +586,263 @@ class StdoutPassthrough(StdoutCapture): self._oldStdout.write(string) # constant profile defaults -PyUtilProfileDefaultFilename = 'profiledata' -PyUtilProfileDefaultLines = 80 -PyUtilProfileDefaultSorts = ['cumulative', 'time', 'calls'] +if __debug__: + from StringIO import StringIO -_ProfileResultStr = '' + PyUtilProfileDefaultFilename = 'profiledata' + PyUtilProfileDefaultLines = 80 + PyUtilProfileDefaultSorts = ['cumulative', 'time', 'calls'] -def getProfileResultString(): - # if you called profile with 'log' not set to True, - # you can call this function to get the results as - # a string - global _ProfileResultStr - return _ProfileResultStr + _ProfileResultStr = '' -def profileFunc(callback, name, terse, log=True): - global _ProfileResultStr - if 'globalProfileFunc' in __builtin__.__dict__: - # rats. Python profiler is not re-entrant... - base.notify.warning( - 'PythonUtil.profileStart(%s): aborted, already profiling %s' - #'\nStack Trace:\n%s' - % (name, __builtin__.globalProfileFunc, - #StackTrace() - )) - return - __builtin__.globalProfileFunc = callback - __builtin__.globalProfileResult = [None] - prefix = '***** START PROFILE: %s *****' % name - if log: - print prefix - startProfile(cmd='globalProfileResult[0]=globalProfileFunc()', callInfo=(not terse), silent=not log) - suffix = '***** END PROFILE: %s *****' % name - if log: - print suffix - else: - _ProfileResultStr = '%s\n%s\n%s' % (prefix, _ProfileResultStr, suffix) - result = globalProfileResult[0] - del __builtin__.__dict__['globalProfileFunc'] - del __builtin__.__dict__['globalProfileResult'] - return result + def getProfileResultString(): + # if you called profile with 'log' not set to True, + # you can call this function to get the results as + # a string + global _ProfileResultStr + return _ProfileResultStr -def profiled(category=None, terse=False): - """ decorator for profiling functions - turn categories on and off via "want-profile-categoryName 1" - - e.g. - - @profiled('particles') - def loadParticles(): - ... - - want-profile-particles 1 - """ - assert type(category) in (types.StringType, types.NoneType), "must provide a category name for @profiled" - - # allow profiling in published versions - """ - try: - null = not __dev__ - except: - null = not __debug__ - if null: - # if we're not in __dev__, just return the function itself. This - # results in zero runtime overhead, since decorators are evaluated - # at module-load. - def nullDecorator(f): - return f - return nullDecorator - """ - - def profileDecorator(f): - def _profiled(*args, **kArgs): - name = '(%s) %s from %s' % (category, f.func_name, f.__module__) - - # showbase might not be loaded yet, so don't use - # base.config. Instead, query the ConfigVariableBool. - if (category is None) or ConfigVariableBool('want-profile-%s' % category, 0).getValue(): - return profileFunc(Functor(f, *args, **kArgs), name, terse) - else: - return f(*args, **kArgs) - _profiled.__doc__ = f.__doc__ - return _profiled - return profileDecorator - -# intercept profile-related file operations to avoid disk access -movedOpenFuncs = [] -movedDumpFuncs = [] -movedLoadFuncs = [] -profileFilenames = set() -profileFilenameList = Stack() -profileFilename2file = {} -profileFilename2marshalData = {} - -def _profileOpen(filename, *args, **kArgs): - # this is a replacement for the file open() builtin function - # for use during profiling, to intercept the file open - # operation used by the Python profiler and profile stats - # systems - if filename in profileFilenames: - # if this is a file related to profiling, create an - # in-RAM file object - if filename not in profileFilename2file: - file = StringIO() - file._profFilename = filename - profileFilename2file[filename] = file + def profileFunc(callback, name, terse, log=True): + global _ProfileResultStr + if 'globalProfileFunc' in __builtin__.__dict__: + # rats. Python profiler is not re-entrant... + base.notify.warning( + 'PythonUtil.profileStart(%s): aborted, already profiling %s' + #'\nStack Trace:\n%s' + % (name, __builtin__.globalProfileFunc, + #StackTrace() + )) + return + __builtin__.globalProfileFunc = callback + __builtin__.globalProfileResult = [None] + prefix = '***** START PROFILE: %s *****' % name + if log: + print prefix + startProfile(cmd='globalProfileResult[0]=globalProfileFunc()', callInfo=(not terse), silent=not log) + suffix = '***** END PROFILE: %s *****' % name + if log: + print suffix else: - file = profileFilename2file[filename] - else: - file = movedOpenFuncs[-1](filename, *args, **kArgs) - return file + _ProfileResultStr = '%s\n%s\n%s' % (prefix, _ProfileResultStr, suffix) + result = globalProfileResult[0] + del __builtin__.__dict__['globalProfileFunc'] + del __builtin__.__dict__['globalProfileResult'] + return result -def _profileMarshalDump(data, file): - # marshal.dump doesn't work with StringIO objects - # simulate it - if isinstance(file, StringIO) and hasattr(file, '_profFilename'): - if file._profFilename in profileFilenames: - profileFilename2marshalData[file._profFilename] = data - return None - return movedDumpFuncs[-1](data, file) + def profiled(category=None, terse=False): + """ decorator for profiling functions + turn categories on and off via "want-profile-categoryName 1" -def _profileMarshalLoad(file): - # marshal.load doesn't work with StringIO objects - # simulate it - if isinstance(file, StringIO) and hasattr(file, '_profFilename'): - if file._profFilename in profileFilenames: - return profileFilename2marshalData[file._profFilename] - return movedLoadFuncs[-1](file) + e.g. -def _installProfileCustomFuncs(filename): - assert filename not in profileFilenames - profileFilenames.add(filename) - profileFilenameList.push(filename) - movedOpenFuncs.append(__builtin__.open) - __builtin__.open = _profileOpen - movedDumpFuncs.append(marshal.dump) - marshal.dump = _profileMarshalDump - movedLoadFuncs.append(marshal.load) - marshal.load = _profileMarshalLoad + @profiled('particles') + def loadParticles(): + ... -def _getProfileResultFileInfo(filename): - return (profileFilename2file.get(filename, None), - profileFilename2marshalData.get(filename, None)) + want-profile-particles 1 + """ + assert type(category) in (types.StringType, types.NoneType), "must provide a category name for @profiled" -def _setProfileResultsFileInfo(filename, info): - f, m = info - if f: - profileFilename2file[filename] = f - if m: - profileFilename2marshalData[filename] = m + # allow profiling in published versions + """ + try: + null = not __dev__ + except: + null = not __debug__ + if null: + # if we're not in __dev__, just return the function itself. This + # results in zero runtime overhead, since decorators are evaluated + # at module-load. + def nullDecorator(f): + return f + return nullDecorator + """ -def _clearProfileResultFileInfo(filename): - profileFilename2file.pop(filename, None) - profileFilename2marshalData.pop(filename, None) + def profileDecorator(f): + def _profiled(*args, **kArgs): + name = '(%s) %s from %s' % (category, f.func_name, f.__module__) -def _removeProfileCustomFuncs(filename): - assert profileFilenameList.top() == filename - marshal.load = movedLoadFuncs.pop() - marshal.dump = movedDumpFuncs.pop() - __builtin__.open = movedOpenFuncs.pop() - profileFilenames.remove(filename) - profileFilenameList.pop() - profileFilename2file.pop(filename, None) - # don't let marshalled data pile up - profileFilename2marshalData.pop(filename, None) + # showbase might not be loaded yet, so don't use + # base.config. Instead, query the ConfigVariableBool. + if (category is None) or ConfigVariableBool('want-profile-%s' % category, 0).getValue(): + return profileFunc(Functor(f, *args, **kArgs), name, terse) + else: + return f(*args, **kArgs) + _profiled.__doc__ = f.__doc__ + return _profiled + return profileDecorator + + # intercept profile-related file operations to avoid disk access + movedOpenFuncs = [] + movedDumpFuncs = [] + movedLoadFuncs = [] + profileFilenames = set() + profileFilenameList = Stack() + profileFilename2file = {} + profileFilename2marshalData = {} + + def _profileOpen(filename, *args, **kArgs): + # this is a replacement for the file open() builtin function + # for use during profiling, to intercept the file open + # operation used by the Python profiler and profile stats + # systems + if filename in profileFilenames: + # if this is a file related to profiling, create an + # in-RAM file object + if filename not in profileFilename2file: + file = StringIO() + file._profFilename = filename + profileFilename2file[filename] = file + else: + file = profileFilename2file[filename] + else: + file = movedOpenFuncs[-1](filename, *args, **kArgs) + return file + + def _profileMarshalDump(data, file): + # marshal.dump doesn't work with StringIO objects + # simulate it + if isinstance(file, StringIO) and hasattr(file, '_profFilename'): + if file._profFilename in profileFilenames: + profileFilename2marshalData[file._profFilename] = data + return None + return movedDumpFuncs[-1](data, file) + + def _profileMarshalLoad(file): + # marshal.load doesn't work with StringIO objects + # simulate it + if isinstance(file, StringIO) and hasattr(file, '_profFilename'): + if file._profFilename in profileFilenames: + return profileFilename2marshalData[file._profFilename] + return movedLoadFuncs[-1](file) + + def _installProfileCustomFuncs(filename): + assert filename not in profileFilenames + profileFilenames.add(filename) + profileFilenameList.push(filename) + movedOpenFuncs.append(__builtin__.open) + __builtin__.open = _profileOpen + movedDumpFuncs.append(marshal.dump) + marshal.dump = _profileMarshalDump + movedLoadFuncs.append(marshal.load) + marshal.load = _profileMarshalLoad + + def _getProfileResultFileInfo(filename): + return (profileFilename2file.get(filename, None), + profileFilename2marshalData.get(filename, None)) + + def _setProfileResultsFileInfo(filename, info): + f, m = info + if f: + profileFilename2file[filename] = f + if m: + profileFilename2marshalData[filename] = m + + def _clearProfileResultFileInfo(filename): + profileFilename2file.pop(filename, None) + profileFilename2marshalData.pop(filename, None) + + def _removeProfileCustomFuncs(filename): + assert profileFilenameList.top() == filename + marshal.load = movedLoadFuncs.pop() + marshal.dump = movedDumpFuncs.pop() + __builtin__.open = movedOpenFuncs.pop() + profileFilenames.remove(filename) + profileFilenameList.pop() + profileFilename2file.pop(filename, None) + # don't let marshalled data pile up + profileFilename2marshalData.pop(filename, None) -# call this from the prompt, and break back out to the prompt -# to stop profiling -# -# OR to do inline profiling, you must make a globally-visible -# function to be profiled, i.e. to profile 'self.load()', do -# something like this: -# -# def func(self=self): -# self.load() -# import __builtin__ -# __builtin__.func = func -# PythonUtil.startProfile(cmd='func()', filename='profileData') -# del __builtin__.func -# -def _profileWithoutGarbageLeak(cmd, filename): - # The profile module isn't necessarily installed on every Python - # installation, so we import it here, instead of in the module - # scope. - import profile - # this is necessary because the profile module creates a memory leak - Profile = profile.Profile - statement = cmd - sort = -1 - retVal = None - #### COPIED FROM profile.run #### - prof = Profile() - try: - prof = prof.run(statement) - except SystemExit: - pass - if filename is not None: - prof.dump_stats(filename) - else: - #return prof.print_stats(sort) #DCR - retVal = prof.print_stats(sort) #DCR - ################################# - # eliminate the garbage leak - del prof.dispatcher - return retVal + # call this from the prompt, and break back out to the prompt + # to stop profiling + # + # OR to do inline profiling, you must make a globally-visible + # function to be profiled, i.e. to profile 'self.load()', do + # something like this: + # + # def func(self=self): + # self.load() + # import __builtin__ + # __builtin__.func = func + # PythonUtil.startProfile(cmd='func()', filename='profileData') + # del __builtin__.func + # + def _profileWithoutGarbageLeak(cmd, filename): + # The profile module isn't necessarily installed on every Python + # installation, so we import it here, instead of in the module + # scope. + import profile + # this is necessary because the profile module creates a memory leak + Profile = profile.Profile + statement = cmd + sort = -1 + retVal = None + #### COPIED FROM profile.run #### + prof = Profile() + try: + prof = prof.run(statement) + except SystemExit: + pass + if filename is not None: + prof.dump_stats(filename) + else: + #return prof.print_stats(sort) #DCR + retVal = prof.print_stats(sort) #DCR + ################################# + # eliminate the garbage leak + del prof.dispatcher + return retVal -def startProfile(filename=PyUtilProfileDefaultFilename, - lines=PyUtilProfileDefaultLines, - sorts=PyUtilProfileDefaultSorts, - silent=0, - callInfo=1, - useDisk=False, - cmd='run()'): - # uniquify the filename to allow multiple processes to profile simultaneously - filename = '%s.%s%s' % (filename, randUint31(), randUint31()) - if not useDisk: - # use a RAM file - _installProfileCustomFuncs(filename) - _profileWithoutGarbageLeak(cmd, filename) - if silent: - extractProfile(filename, lines, sorts, callInfo) - else: - printProfile(filename, lines, sorts, callInfo) - if not useDisk: - # discard the RAM file - _removeProfileCustomFuncs(filename) - else: - os.remove(filename) + def startProfile(filename=PyUtilProfileDefaultFilename, + lines=PyUtilProfileDefaultLines, + sorts=PyUtilProfileDefaultSorts, + silent=0, + callInfo=1, + useDisk=False, + cmd='run()'): + # uniquify the filename to allow multiple processes to profile simultaneously + filename = '%s.%s%s' % (filename, randUint31(), randUint31()) + if not useDisk: + # use a RAM file + _installProfileCustomFuncs(filename) + _profileWithoutGarbageLeak(cmd, filename) + if silent: + extractProfile(filename, lines, sorts, callInfo) + else: + printProfile(filename, lines, sorts, callInfo) + if not useDisk: + # discard the RAM file + _removeProfileCustomFuncs(filename) + else: + os.remove(filename) -# call these to see the results again, as a string or in the log -def printProfile(filename=PyUtilProfileDefaultFilename, - lines=PyUtilProfileDefaultLines, - sorts=PyUtilProfileDefaultSorts, - callInfo=1): - import pstats - s = pstats.Stats(filename) - s.strip_dirs() - for sort in sorts: - s.sort_stats(sort) - s.print_stats(lines) - if callInfo: - s.print_callees(lines) - s.print_callers(lines) + # call these to see the results again, as a string or in the log + def printProfile(filename=PyUtilProfileDefaultFilename, + lines=PyUtilProfileDefaultLines, + sorts=PyUtilProfileDefaultSorts, + callInfo=1): + import pstats + s = pstats.Stats(filename) + s.strip_dirs() + for sort in sorts: + s.sort_stats(sort) + s.print_stats(lines) + if callInfo: + s.print_callees(lines) + s.print_callers(lines) -# same args as printProfile -def extractProfile(*args, **kArgs): - global _ProfileResultStr - # capture print output - sc = StdoutCapture() - # print the profile output, redirected to the result string - printProfile(*args, **kArgs) - # make a copy of the print output - _ProfileResultStr = sc.getString() - # restore stdout to what it was before - sc.destroy() + # same args as printProfile + def extractProfile(*args, **kArgs): + global _ProfileResultStr + # capture print output + sc = StdoutCapture() + # print the profile output, redirected to the result string + printProfile(*args, **kArgs) + # make a copy of the print output + _ProfileResultStr = sc.getString() + # restore stdout to what it was before + sc.destroy() def getSetterName(valueName, prefix='set'): # getSetterName('color') -> 'setColor' @@ -1032,77 +1003,80 @@ def solveQuadratic(a, b, c): root2 = ((-b) + sqrtD) / twoA return [root1, root2] -def stackEntryInfo(depth=0, baseFileName=1): - """ - returns the sourcefilename, line number, and function name of - an entry in the stack. - 'depth' is how far back to go in the stack; 0 is the caller of this - function, 1 is the function that called the caller of this function, etc. - by default, strips off the path of the filename; override with baseFileName - returns (fileName, lineNum, funcName) --> (string, int, string) - returns (None, None, None) on error - """ - try: - stack = None - frame = None +if __debug__: + def stackEntryInfo(depth=0, baseFileName=1): + """ + returns the sourcefilename, line number, and function name of + an entry in the stack. + 'depth' is how far back to go in the stack; 0 is the caller of this + function, 1 is the function that called the caller of this function, etc. + by default, strips off the path of the filename; override with baseFileName + returns (fileName, lineNum, funcName) --> (string, int, string) + returns (None, None, None) on error + """ + import inspect + try: - stack = inspect.stack() - # add one to skip the frame associated with this function - frame = stack[depth+1] - filename = frame[1] - if baseFileName: - filename = os.path.basename(filename) - lineNum = frame[2] - funcName = frame[3] - result = (filename, lineNum, funcName) - finally: - del stack - del frame - except: - result = (None, None, None) + stack = None + frame = None + try: + stack = inspect.stack() + # add one to skip the frame associated with this function + frame = stack[depth+1] + filename = frame[1] + if baseFileName: + filename = os.path.basename(filename) + lineNum = frame[2] + funcName = frame[3] + result = (filename, lineNum, funcName) + finally: + del stack + del frame + except: + result = (None, None, None) - return result + return result -def lineInfo(baseFileName=1): - """ - returns the sourcefilename, line number, and function name of the - code that called this function - (answers the question: 'hey lineInfo, where am I in the codebase?') - see stackEntryInfo, above, for info on 'baseFileName' and return types - """ - return stackEntryInfo(1, baseFileName) + def lineInfo(baseFileName=1): + """ + returns the sourcefilename, line number, and function name of the + code that called this function + (answers the question: 'hey lineInfo, where am I in the codebase?') + see stackEntryInfo, above, for info on 'baseFileName' and return types + """ + return stackEntryInfo(1, baseFileName) -def callerInfo(baseFileName=1, howFarBack=0): - """ - returns the sourcefilename, line number, and function name of the - caller of the function that called this function - (answers the question: 'hey callerInfo, who called me?') - see stackEntryInfo, above, for info on 'baseFileName' and return types - """ - return stackEntryInfo(2+howFarBack, baseFileName) + def callerInfo(baseFileName=1, howFarBack=0): + """ + returns the sourcefilename, line number, and function name of the + caller of the function that called this function + (answers the question: 'hey callerInfo, who called me?') + see stackEntryInfo, above, for info on 'baseFileName' and return types + """ + return stackEntryInfo(2+howFarBack, baseFileName) -def lineTag(baseFileName=1, verbose=0, separator=':'): - """ - returns a string containing the sourcefilename and line number - of the code that called this function - (equivalent to lineInfo, above, with different return type) - see stackEntryInfo, above, for info on 'baseFileName' + def lineTag(baseFileName=1, verbose=0, separator=':'): + """ + returns a string containing the sourcefilename and line number + of the code that called this function + (equivalent to lineInfo, above, with different return type) + see stackEntryInfo, above, for info on 'baseFileName' - if 'verbose' is false, returns a compact string of the form - 'fileName:lineNum:funcName' - if 'verbose' is true, returns a longer string that matches the - format of Python stack trace dumps + if 'verbose' is false, returns a compact string of the form + 'fileName:lineNum:funcName' + if 'verbose' is true, returns a longer string that matches the + format of Python stack trace dumps - returns empty string on error - """ - fileName, lineNum, funcName = callerInfo(baseFileName) - if fileName is None: - return '' - if verbose: - return 'File "%s", line %s, in %s' % (fileName, lineNum, funcName) - else: - return '%s%s%s%s%s' % (fileName, separator, lineNum, separator, - funcName) + returns empty string on error + """ + fileName, lineNum, funcName = callerInfo(baseFileName) + if fileName is None: + return '' + if verbose: + return 'File "%s", line %s, in %s' % (fileName, lineNum, funcName) + else: + return '%s%s%s%s%s' % (fileName, separator, lineNum, separator, + funcName) def findPythonModule(module): # Look along the python load path for the indicated filename. @@ -1756,43 +1730,6 @@ def getNumberedTypedSortedString(items, maxLen=5000, numPrefix=''): s += format % (i, itype(items[i]), strs[i]) return s -def getNumberedTypedSortedStringWithReferrersGen(items, maxLen=10000, numPrefix=''): - """get a string that has each item of the list on its own line, - the items are stringwise-sorted, the object's referrers are shown, - and each item is numbered on the left from zero""" - digits = 0 - n = len(items) - while n > 0: - digits += 1 - n //= 10 - digits = digits - format = numPrefix + '%0' + '%s' % digits + 'i:%s @ %s \t%s' - snip = '' - strs = [] - for item in items: - strs.append(fastRepr(item)) - strs.sort() - for i in xrange(len(strs)): - item = items[i] - objStr = strs[i] - objStr += ', \tREFERRERS=[' - referrers = gc.get_referrers(item) - for ref in referrers: - objStr += '%s@%s, ' % (itype(ref), id(ref)) - objStr += ']' - if len(objStr) > maxLen: - objStr = '%s%s' % (objStr[:(maxLen-len(snip))], snip) - yield format % (i, itype(items[i]), id(items[i]), objStr) - -def getNumberedTypedSortedStringWithReferrers(items, maxLen=10000, numPrefix=''): - """get a string that has each item of the list on its own line, - the items are stringwise-sorted, the object's referrers are shown, - and each item is numbered on the left from zero""" - s = '' - for line in getNumberedTypedSortedStringWithReferrersGen(items, maxLen, numPrefix): - s += '%s\n' % line - return s - def printNumberedTyped(items, maxLen=5000): """print out each item of the list on its own line, with each item numbered on the left from zero""" @@ -2114,7 +2051,7 @@ def report(types = [], prefix = '', xform = None, notifyFunc = None, dConfigPara except NameError,e: return decorator - from direct.distributed.ClockDelta import globalClockDelta + globalClockDelta = importlib.import_module("direct.distributed.ClockDelta").globalClockDelta def decorator(f): def wrap(*args,**kwargs): @@ -2210,127 +2147,57 @@ def getRepository(): return simbase.air exceptionLoggedNotify = None +if __debug__: + def exceptionLogged(append=True): + """decorator that outputs the function name and all arguments + if an exception passes back through the stack frame + if append is true, string is appended to the __str__ output of + the exception. if append is false, string is printed to the log + directly. If the output will take up many lines, it's recommended + to set append to False so that the exception stack is not hidden + by the output of this decorator. + """ + try: + null = not __dev__ + except: + null = not __debug__ + if null: + # if we're not in __dev__, just return the function itself. This + # results in zero runtime overhead, since decorators are evaluated + # at module-load. + def nullDecorator(f): + return f + return nullDecorator -def exceptionLogged(append=True): - """decorator that outputs the function name and all arguments - if an exception passes back through the stack frame - if append is true, string is appended to the __str__ output of - the exception. if append is false, string is printed to the log - directly. If the output will take up many lines, it's recommended - to set append to False so that the exception stack is not hidden - by the output of this decorator. - """ - try: - null = not __dev__ - except: - null = not __debug__ - if null: - # if we're not in __dev__, just return the function itself. This - # results in zero runtime overhead, since decorators are evaluated - # at module-load. - def nullDecorator(f): - return f - return nullDecorator - - def _decoratorFunc(f, append=append): - global exceptionLoggedNotify - if exceptionLoggedNotify is None: - from direct.directnotify.DirectNotifyGlobal import directNotify - exceptionLoggedNotify = directNotify.newCategory("ExceptionLogged") - def _exceptionLogged(*args, **kArgs): - try: - return f(*args, **kArgs) - except Exception, e: + def _decoratorFunc(f, append=append): + global exceptionLoggedNotify + if exceptionLoggedNotify is None: + from direct.directnotify.DirectNotifyGlobal import directNotify + exceptionLoggedNotify = directNotify.newCategory("ExceptionLogged") + def _exceptionLogged(*args, **kArgs): try: - s = '%s(' % f.func_name - for arg in args: - s += '%s, ' % arg - for key, value in kArgs.items(): - s += '%s=%s, ' % (key, value) - if len(args) or len(kArgs): - s = s[:-2] - s += ')' - if append: - appendStr(e, '\n%s' % s) - else: - exceptionLoggedNotify.info(s) - except: - exceptionLoggedNotify.info( - '%s: ERROR IN PRINTING' % f.func_name) - raise - _exceptionLogged.__doc__ = f.__doc__ - return _exceptionLogged - return _decoratorFunc - -# class 'decorator' that records the stack at the time of creation -# be careful with this, it creates a StackTrace, and that can take a -# lot of CPU -def recordCreationStack(cls): - if not hasattr(cls, '__init__'): - raise 'recordCreationStack: class \'%s\' must define __init__' % cls.__name__ - cls.__moved_init__ = cls.__init__ - def __recordCreationStack_init__(self, *args, **kArgs): - self._creationStackTrace = StackTrace(start=1) - return self.__moved_init__(*args, **kArgs) - def getCreationStackTrace(self): - return self._creationStackTrace - def getCreationStackTraceCompactStr(self): - return self._creationStackTrace.compact() - def printCreationStackTrace(self): - print self._creationStackTrace - cls.__init__ = __recordCreationStack_init__ - cls.getCreationStackTrace = getCreationStackTrace - cls.getCreationStackTraceCompactStr = getCreationStackTraceCompactStr - cls.printCreationStackTrace = printCreationStackTrace - return cls - -# like recordCreationStack but stores the stack as a compact stack list-of-strings -# scales well for memory usage -def recordCreationStackStr(cls): - if not hasattr(cls, '__init__'): - raise 'recordCreationStackStr: class \'%s\' must define __init__' % cls.__name__ - cls.__moved_init__ = cls.__init__ - def __recordCreationStackStr_init__(self, *args, **kArgs): - # store as list of strings to conserve memory - self._creationStackTraceStrLst = StackTrace(start=1).compact().split(',') - return self.__moved_init__(*args, **kArgs) - def getCreationStackTraceCompactStr(self): - return ','.join(self._creationStackTraceStrLst) - def printCreationStackTrace(self): - print ','.join(self._creationStackTraceStrLst) - cls.__init__ = __recordCreationStackStr_init__ - cls.getCreationStackTraceCompactStr = getCreationStackTraceCompactStr - cls.printCreationStackTrace = printCreationStackTrace - return cls - - -# class 'decorator' that logs all method calls for a particular class -def logMethodCalls(cls): - if not hasattr(cls, 'notify'): - raise 'logMethodCalls: class \'%s\' must have a notify' % cls.__name__ - for name in dir(cls): - method = getattr(cls, name) - if hasattr(method, '__call__'): - def getLoggedMethodCall(method): - def __logMethodCall__(obj, *args, **kArgs): - s = '%s(' % method.__name__ - for arg in args: - try: - argStr = repr(arg) - except: - argStr = 'bad repr: %s' % arg.__class__ - s += '%s, ' % argStr - for karg, value in kArgs.items(): - s += '%s=%s, ' % (karg, repr(value)) - if len(args) or len(kArgs): - s = s[:-2] - s += ')' - obj.notify.info(s) - return method(obj, *args, **kArgs) - return __logMethodCall__ - setattr(cls, name, getLoggedMethodCall(method)) - __logMethodCall__ = None - return cls + return f(*args, **kArgs) + except Exception, e: + try: + s = '%s(' % f.func_name + for arg in args: + s += '%s, ' % arg + for key, value in kArgs.items(): + s += '%s=%s, ' % (key, value) + if len(args) or len(kArgs): + s = s[:-2] + s += ')' + if append: + appendStr(e, '\n%s' % s) + else: + exceptionLoggedNotify.info(s) + except: + exceptionLoggedNotify.info( + '%s: ERROR IN PRINTING' % f.func_name) + raise + _exceptionLogged.__doc__ = f.__doc__ + return _exceptionLogged + return _decoratorFunc # http://en.wikipedia.org/wiki/Golden_ratio GoldenRatio = (1. + math.sqrt(5.)) / 2. @@ -2342,45 +2209,6 @@ class GoldenRectangle: def getShorterEdge(longer): return longer / GoldenRatio -class HotkeyBreaker: - def __init__(self,breakKeys = []): - from direct.showbase.DirectObject import DirectObject - self.do = DirectObject() - self.breakKeys = {} - if not isinstance(breakKeys, (list,tuple)): - breakKeys = (breakKeys,) - for key in breakKeys: - self.addBreakKey(key) - - def addBreakKey(self,breakKey): - if __dev__: - self.do.accept(breakKey,self.breakFunc,extraArgs = [breakKey]) - - def removeBreakKey(self,breakKey): - if __dev__: - self.do.ignore(breakKey) - - def breakFunc(self,breakKey): - if __dev__: - self.breakKeys[breakKey] = True - - def setBreakPt(self, breakKey = None, persistent = False): - if __dev__: - if not breakKey: - import pdb;pdb.set_trace() - return True - else: - if self.breakKeys.get(breakKey,False): - if not persistent: - self.breakKeys.pop(breakKey) - import pdb;pdb.set_trace() - return True - return True - - def clearBreakPt(self, breakKey): - if __dev__: - return bool(self.breakKeys.pop(breakKey,None)) - def nullGen(): # generator that ends immediately if False: @@ -2480,40 +2308,42 @@ if __debug__ and __name__ == '__main__': assert obj2count[3] == 3 * 3 assert obj2count[4] == 4 * 3 -def quickProfile(name="unnamed"): - import pstats - def profileDecorator(f): - if(not config.GetBool("use-profiler",0)): - return f - def _profiled(*args, **kArgs): - # must do this in here because we don't have base/simbase - # at the time that PythonUtil is loaded - if(not config.GetBool("profile-debug",0)): - #dumb timings - st=globalClock.getRealTime() - f(*args,**kArgs) - s=globalClock.getRealTime()-st - print "Function %s.%s took %s seconds"%(f.__module__, f.__name__,s) - else: - import profile as prof, pstats - #detailed profile, stored in base.stats under ( - if(not hasattr(base,"stats")): - base.stats={} - if(not base.stats.get(name)): - base.stats[name]=[] +if __debug__: + def quickProfile(name="unnamed"): + import pstats + def profileDecorator(f): + if(not config.GetBool("use-profiler",0)): + return f + def _profiled(*args, **kArgs): + # must do this in here because we don't have base/simbase + # at the time that PythonUtil is loaded + if(not config.GetBool("profile-debug",0)): + #dumb timings + st=globalClock.getRealTime() + f(*args,**kArgs) + s=globalClock.getRealTime()-st + print "Function %s.%s took %s seconds"%(f.__module__, f.__name__,s) + else: + import profile as prof, pstats - prof.runctx('f(*args, **kArgs)', {'f':f,'args':args,'kArgs':kArgs},None,"t.prof") - s=pstats.Stats("t.prof") - #p=hotshot.Profile("t.prof") - #p.runctx('f(*args, **kArgs)', {'f':f,'args':args,'kArgs':kArgs},None) - #s = hotshot.stats.load("t.prof") - s.strip_dirs() - s.sort_stats("cumulative") - base.stats[name].append(s) + #detailed profile, stored in base.stats under ( + if(not hasattr(base,"stats")): + base.stats={} + if(not base.stats.get(name)): + base.stats[name]=[] - _profiled.__doc__ = f.__doc__ - return _profiled - return profileDecorator + prof.runctx('f(*args, **kArgs)', {'f':f,'args':args,'kArgs':kArgs},None,"t.prof") + s=pstats.Stats("t.prof") + #p=hotshot.Profile("t.prof") + #p.runctx('f(*args, **kArgs)', {'f':f,'args':args,'kArgs':kArgs},None) + #s = hotshot.stats.load("t.prof") + s.strip_dirs() + s.sort_stats("cumulative") + base.stats[name].append(s) + + _profiled.__doc__ = f.__doc__ + return _profiled + return profileDecorator def getTotalAnnounceTime(): td=0 @@ -2533,13 +2363,6 @@ def getAnnounceGenerateTime(stat): return val -def choice(condition, ifTrue, ifFalse): - # equivalent of C++ (condition ? ifTrue : ifFalse) - if condition: - return ifTrue - else: - return ifFalse - class MiniLog: def __init__(self, name): self.indent = 1 @@ -2611,14 +2434,6 @@ class HierarchyException(Exception): def __repr__(self): return 'HierarchyException(%s)' % (self.owner, ) -# __dev__ is not defined at import time, call this after it's defined -def recordFunctorCreationStacks(): - global Functor - if not hasattr(Functor, '_functorCreationStacksRecorded'): - Functor = recordCreationStackStr(Functor) - Functor._functorCreationStacksRecorded = True - Functor.__call__ = Functor._exceptionLoggedCreationStack__call__ - def formatTimeCompact(seconds): # returns string in format '1d3h22m43s' result = '' @@ -2735,57 +2550,6 @@ if __debug__ and __name__ == '__main__': testAlphabetCounter() del testAlphabetCounter -globalPdb = None - -traceCalled = False - -def setupPdb(): - import pdb; - class pandaPdb(pdb.Pdb): - def stop_here(self, frame): - global traceCalled - if(traceCalled): - result = pdb.Pdb.stop_here(self, frame) - if(result == True): - traceCalled = False - return result - if frame is self.stopframe: - return True - return False - global globalPdb - globalPdb = pandaPdb() - globalPdb.reset() - sys.settrace(globalPdb.trace_dispatch) - -def pandaTrace(): - if __dev__: - if not globalPdb: - setupPdb() - global traceCalled - globalPdb.set_trace(sys._getframe().f_back) - traceCalled = True - -packageMap = { - "toontown":"$TOONTOWN", - "direct":"$DIRECT", - "otp":"$OTP", - "pirates":"$PIRATES", -} - - -#assuming . dereferncing for nice linking to imports -def pandaBreak(dotpath, linenum, temporary = 0, cond = None): - if __dev__: - from panda3d.core import Filename - if not globalPdb: - setupPdb() - dirs = dotpath.split(".") - root = Filename.expandFrom(packageMap[dirs[0]]).toOsSpecific() - filename = root + "\\src" - for d in dirs[1:]: - filename="%s\\%s"%(filename,d) - print filename - globalPdb.set_break(filename+".py", linenum, temporary, cond) class Default: # represents 'use the default value' @@ -2917,18 +2681,20 @@ __builtin__.SerialMaskedGen = SerialMaskedGen __builtin__.ScratchPad = ScratchPad __builtin__.uniqueName = uniqueName __builtin__.serialNum = serialNum -__builtin__.profiled = profiled +if __debug__: + __builtin__.profiled = profiled + __builtin__.exceptionLogged = exceptionLogged __builtin__.itype = itype -__builtin__.exceptionLogged = exceptionLogged __builtin__.appendStr = appendStr __builtin__.bound = bound __builtin__.clamp = clamp __builtin__.lerp = lerp __builtin__.makeList = makeList __builtin__.makeTuple = makeTuple -__builtin__.printStack = printStack -__builtin__.printReverseStack = printReverseStack -__builtin__.printVerboseStack = printVerboseStack +if __debug__: + __builtin__.printStack = printStack + __builtin__.printReverseStack = printReverseStack + __builtin__.printVerboseStack = printVerboseStack __builtin__.DelayedCall = DelayedCall __builtin__.DelayedFunctor = DelayedFunctor __builtin__.FrameDelayedCall = FrameDelayedCall @@ -2942,8 +2708,8 @@ __builtin__.fastRepr = fastRepr __builtin__.nullGen = nullGen __builtin__.flywheel = flywheel __builtin__.loopGen = loopGen -__builtin__.StackTrace = StackTrace -__builtin__.choice = choice +if __debug__: + __builtin__.StackTrace = StackTrace __builtin__.report = report __builtin__.pstatcollect = pstatcollect __builtin__.MiniLog = MiniLog diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 523c225574..13ffbc7f45 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -26,24 +26,22 @@ from BulletinBoardGlobal import bulletinBoard from direct.task.TaskManagerGlobal import taskMgr from JobManagerGlobal import jobMgr from EventManagerGlobal import eventMgr -from PythonUtil import * -from direct.showbase import PythonUtil -#from direct.interval.IntervalManager import ivalMgr +#from PythonUtil import * from direct.interval import IntervalManager from direct.showbase.BufferViewer import BufferViewer from direct.task import Task -from direct.directutil import Verify -from direct.showbase import GarbageReport import sys import Loader import time import atexit +import importlib from direct.showbase import ExceptionVarDump import DirectObject import SfxPlayer if __debug__: + from direct.showbase import GarbageReport from direct.directutil import DeltaProfiler -import OnScreenDebug + import OnScreenDebug import AppRunnerGlobal def legacyRun(): @@ -73,7 +71,8 @@ class ShowBase(DirectObject.DirectObject): if logStackDump or uploadStackDump: ExceptionVarDump.install(logStackDump, uploadStackDump) - self.__autoGarbageLogging = self.__dev__ and self.config.GetBool('auto-garbage-logging', False) + if __debug__: + self.__autoGarbageLogging = self.__dev__ and self.config.GetBool('auto-garbage-logging', False) ## The directory containing the main Python file of this application. self.mainDir = ExecutionEnvironment.getEnvironmentVariable("MAIN_DIR") @@ -88,9 +87,6 @@ class ShowBase(DirectObject.DirectObject): #debug running multiplier self.debugRunningMultiplier = 4 - # Setup wantVerifyPdb as soon as reasonable: - Verify.wantVerifyPdb = self.config.GetBool('want-verify-pdb', 0) - # [gjeon] to disable sticky keys if self.config.GetBool('disable-sticky-keys', 0): storeAccessibilityShortcutKeys() @@ -373,8 +369,8 @@ class ShowBase(DirectObject.DirectObject): builtins.wantUberdog = self.config.GetBool('want-uberdog', 1) if __debug__: builtins.deltaProfiler = DeltaProfiler.DeltaProfiler("ShowBase") - self.onScreenDebug = OnScreenDebug.OnScreenDebug() - builtins.onScreenDebug = self.onScreenDebug + self.onScreenDebug = OnScreenDebug.OnScreenDebug() + builtins.onScreenDebug = self.onScreenDebug if self.wantRender2dp: builtins.render2dp = self.render2dp @@ -388,10 +384,6 @@ class ShowBase(DirectObject.DirectObject): self.createBaseAudioManagers() - # set up recording of Functor creation stacks in __dev__ - if self.__dev__ and self.config.GetBool('record-functor-creation-stacks', False): - PythonUtil.recordFunctorCreationStacks() - if self.__dev__ or self.config.GetBool('want-e3-hacks', False): if self.config.GetBool('track-gui-items', True): # dict of guiId to gui item, for tracking down leaks @@ -465,7 +457,8 @@ class ShowBase(DirectObject.DirectObject): some Panda config settings. """ try: - import profile, pstats + profile = importlib.import_module('profile') + pstats = importlib.import_module('pstats') except ImportError: return @@ -1647,23 +1640,26 @@ class ShowBase(DirectObject.DirectObject): def addAngularIntegrator(self): if not self.physicsMgrAngular: - from panda3d.physics import AngularEulerIntegrator + physics = importlib.import_module('panda3d.physics') self.physicsMgrAngular = 1 - integrator = AngularEulerIntegrator() + integrator = physics.AngularEulerIntegrator() self.physicsMgr.attachAngularIntegrator(integrator) def enableParticles(self): if not self.particleMgrEnabled: + # Use importlib to prevent this import from being picked up + # by modulefinder when packaging an application. + if not self.particleMgr: - from direct.particles.ParticleManagerGlobal import particleMgr - self.particleMgr = particleMgr + PMG = importlib.import_module('direct.particles.ParticleManagerGlobal') + self.particleMgr = PMG.particleMgr self.particleMgr.setFrameStepping(1) if not self.physicsMgr: - from PhysicsManagerGlobal import physicsMgr - from panda3d.physics import LinearEulerIntegrator - self.physicsMgr = physicsMgr - integrator = LinearEulerIntegrator() + PMG = importlib.import_module('direct.showbase.PhysicsManagerGlobal') + physics = importlib.import_module('panda3d.physics') + self.physicsMgr = PMG.physicsMgr + integrator = physics.LinearEulerIntegrator() self.physicsMgr.attachLinearIntegrator(integrator) self.particleMgrEnabled = 1 @@ -1886,9 +1882,10 @@ class ShowBase(DirectObject.DirectObject): return Task.cont def __igLoop(self, state): - # We render the watch variables for the onScreenDebug as soon - # as we reasonably can before the renderFrame(). - self.onScreenDebug.render() + if __debug__: + # We render the watch variables for the onScreenDebug as soon + # as we reasonably can before the renderFrame(). + self.onScreenDebug.render() if self.recorder: self.recorder.recordFrame() @@ -1900,9 +1897,10 @@ class ShowBase(DirectObject.DirectObject): if self.multiClientSleep: time.sleep(0) - # We clear the text buffer for the onScreenDebug as soon - # as we reasonably can after the renderFrame(). - self.onScreenDebug.clear() + if __debug__: + # We clear the text buffer for the onScreenDebug as soon + # as we reasonably can after the renderFrame(). + self.onScreenDebug.clear() if self.recorder: self.recorder.playFrame() @@ -1925,9 +1923,10 @@ class ShowBase(DirectObject.DirectObject): def __igLoopSync(self, state): - # We render the watch variables for the onScreenDebug as soon - # as we reasonably can before the renderFrame(). - self.onScreenDebug.render() + if __debug__: + # We render the watch variables for the onScreenDebug as soon + # as we reasonably can before the renderFrame(). + self.onScreenDebug.render() if self.recorder: self.recorder.recordFrame() @@ -1941,9 +1940,10 @@ class ShowBase(DirectObject.DirectObject): if self.multiClientSleep: time.sleep(0) - # We clear the text buffer for the onScreenDebug as soon - # as we reasonably can after the renderFrame(). - self.onScreenDebug.clear() + if __debug__: + # We clear the text buffer for the onScreenDebug as soon + # as we reasonably can after the renderFrame(). + self.onScreenDebug.clear() if self.recorder: self.recorder.playFrame() @@ -2178,8 +2178,10 @@ class ShowBase(DirectObject.DirectObject): self.texmem = None return - from direct.showutil.TexMemWatcher import TexMemWatcher - self.texmem = TexMemWatcher() + # Use importlib to prevent this import from being picked up + # by modulefinder when packaging an application. + TMW = importlib.import_module('direct.showutil.TexMemWatcher') + self.texmem = TMW.TexMemWatcher() def toggleShowVertices(self): """ Toggles a mode that visualizes vertex density per screen @@ -2675,16 +2677,18 @@ class ShowBase(DirectObject.DirectObject): if not properties.getOpen(): # If the user closes the main window, we should exit. self.notify.info("User closed main window.") - if self.__autoGarbageLogging: - GarbageReport.b_checkForGarbageLeaks() + if __debug__: + if self.__autoGarbageLogging: + GarbageReport.b_checkForGarbageLeaks() self.userExit() if properties.getForeground() and not self.mainWinForeground: self.mainWinForeground = 1 elif not properties.getForeground() and self.mainWinForeground: self.mainWinForeground = 0 - if self.__autoGarbageLogging: - GarbageReport.b_checkForGarbageLeaks() + if __debug__: + if self.__autoGarbageLogging: + GarbageReport.b_checkForGarbageLeaks() if properties.getMinimized() and not self.mainWinMinimized: # If the main window is minimized, throw an event to @@ -2814,7 +2818,10 @@ class ShowBase(DirectObject.DirectObject): init_app_for_gui() - import wx + # Use importlib to prevent this import from being picked up + # by modulefinder when packaging an application. + wx = importlib.import_module('wx') + # Create a new base.wxApp. self.wxApp = wx.PySimpleApp(redirect = False) @@ -2889,8 +2896,10 @@ class ShowBase(DirectObject.DirectObject): # Don't do this twice. return - from Tkinter import tkinter - import Pmw + # Use importlib to prevent this import from being picked up + # by modulefinder when packaging an application. + tkinter = importlib.import_module('Tkinter').tkinter + Pmw = importlib.import_module('Pmw') # Create a new Tk root. self.tkRoot = Pmw.initialise() @@ -2953,8 +2962,10 @@ class ShowBase(DirectObject.DirectObject): self.startWx(fWantWx) self.wantDirect = fWantDirect if self.wantDirect: - from direct.directtools.DirectSession import DirectSession - self.direct = DirectSession() + # Use importlib to prevent this import from being picked up + # by modulefinder when packaging an application. + DirectSession = importlib.import_module('direct.directtools.DirectSession') + self.direct = DirectSession.DirectSession() self.direct.enable() builtins.direct = self.direct else: diff --git a/direct/src/showbase/Transitions.py b/direct/src/showbase/Transitions.py index 48c79aec1b..c60ac8c09e 100644 --- a/direct/src/showbase/Transitions.py +++ b/direct/src/showbase/Transitions.py @@ -3,7 +3,7 @@ __all__ = ['Transitions'] from panda3d.core import * -from direct.gui.DirectGui import * +from direct.gui.DirectGui import DirectFrame from direct.gui import DirectGuiGlobals as DGG from direct.interval.LerpInterval import LerpColorScaleInterval, LerpColorInterval, LerpScaleInterval, LerpPosInterval from direct.interval.MetaInterval import Sequence, Parallel diff --git a/direct/src/showutil/FreezeTool.py b/direct/src/showutil/FreezeTool.py index 7b1ee6ef6b..04f8dc8baf 100644 --- a/direct/src/showutil/FreezeTool.py +++ b/direct/src/showutil/FreezeTool.py @@ -30,8 +30,8 @@ isDebugBuild = (python.lower().endswith('_d')) # These are modules that Python always tries to import up-front. They # must be frozen in any main.exe. startupModules = [ - 'site', 'sitecustomize', 'os', 'encodings.cp1252', - 'encodings.latin_1', 'encodings.utf_8', 'io', 'org', + 'os', 'encodings.cp1252', + 'encodings.latin_1', 'encodings.utf_8', 'io', ] # These are missing modules that we've reported already this session. diff --git a/direct/src/stdpy/thread.py b/direct/src/stdpy/thread.py index ba85a6dcd1..f8291488d1 100644 --- a/direct/src/stdpy/thread.py +++ b/direct/src/stdpy/thread.py @@ -70,6 +70,13 @@ class LockType: def __exit__(self, t, v, tb): self.release() +# Helper to generate new thread names +_counter = 0 +def _newname(template="Thread-%d"): + global _counter + _counter = _counter + 1 + return template % _counter + _threads = {} _nextThreadId = 0 _threadsLock = core.Mutex('thread._threadsLock') diff --git a/direct/src/stdpy/threading.py b/direct/src/stdpy/threading.py index b3f2bc57bd..3fa576e712 100644 --- a/direct/src/stdpy/threading.py +++ b/direct/src/stdpy/threading.py @@ -42,6 +42,7 @@ __all__ = [ ] local = _thread._local +_newname = _thread._newname class ThreadBase: """ A base class for both Thread and ExternalThread in this @@ -98,8 +99,7 @@ class Thread(ThreadBase): self.__kwargs = kwargs if not name: - import threading2 - name = threading2._newname() + name = _newname() current = current_thread() self.__dict__['daemon'] = current.daemon @@ -404,106 +404,107 @@ def setprofile(func): def stack_size(size = None): raise ThreadError -def _test(): +if __debug__: + def _test(): + from collections import deque - from collections import deque - _sleep = core.Thread.sleep + _sleep = core.Thread.sleep - _VERBOSE = False + _VERBOSE = False - class _Verbose(object): + class _Verbose(object): - def __init__(self, verbose=None): - if verbose is None: - verbose = _VERBOSE - self.__verbose = verbose + def __init__(self, verbose=None): + if verbose is None: + verbose = _VERBOSE + self.__verbose = verbose - def _note(self, format, *args): - if self.__verbose: - format = format % args - format = "%s: %s\n" % ( - currentThread().getName(), format) - _sys.stderr.write(format) + def _note(self, format, *args): + if self.__verbose: + format = format % args + format = "%s: %s\n" % ( + currentThread().getName(), format) + _sys.stderr.write(format) - class BoundedQueue(_Verbose): + class BoundedQueue(_Verbose): - def __init__(self, limit): - _Verbose.__init__(self) - self.mon = Lock(name = "BoundedQueue.mon") - self.rc = Condition(self.mon) - self.wc = Condition(self.mon) - self.limit = limit - self.queue = deque() + def __init__(self, limit): + _Verbose.__init__(self) + self.mon = Lock(name = "BoundedQueue.mon") + self.rc = Condition(self.mon) + self.wc = Condition(self.mon) + self.limit = limit + self.queue = deque() - def put(self, item): - self.mon.acquire() - while len(self.queue) >= self.limit: - self._note("put(%s): queue full", item) - self.wc.wait() - self.queue.append(item) - self._note("put(%s): appended, length now %d", - item, len(self.queue)) - self.rc.notify() - self.mon.release() + def put(self, item): + self.mon.acquire() + while len(self.queue) >= self.limit: + self._note("put(%s): queue full", item) + self.wc.wait() + self.queue.append(item) + self._note("put(%s): appended, length now %d", + item, len(self.queue)) + self.rc.notify() + self.mon.release() - def get(self): - self.mon.acquire() - while not self.queue: - self._note("get(): queue empty") - self.rc.wait() - item = self.queue.popleft() - self._note("get(): got %s, %d left", item, len(self.queue)) - self.wc.notify() - self.mon.release() - return item + def get(self): + self.mon.acquire() + while not self.queue: + self._note("get(): queue empty") + self.rc.wait() + item = self.queue.popleft() + self._note("get(): got %s, %d left", item, len(self.queue)) + self.wc.notify() + self.mon.release() + return item - class ProducerThread(Thread): + class ProducerThread(Thread): - def __init__(self, queue, quota): - Thread.__init__(self, name="Producer") - self.queue = queue - self.quota = quota + def __init__(self, queue, quota): + Thread.__init__(self, name="Producer") + self.queue = queue + self.quota = quota - def run(self): - from random import random - counter = 0 - while counter < self.quota: - counter = counter + 1 - self.queue.put("%s.%d" % (self.getName(), counter)) - _sleep(random() * 0.00001) + def run(self): + from random import random + counter = 0 + while counter < self.quota: + counter = counter + 1 + self.queue.put("%s.%d" % (self.getName(), counter)) + _sleep(random() * 0.00001) - class ConsumerThread(Thread): + class ConsumerThread(Thread): - def __init__(self, queue, count): - Thread.__init__(self, name="Consumer") - self.queue = queue - self.count = count + def __init__(self, queue, count): + Thread.__init__(self, name="Consumer") + self.queue = queue + self.count = count - def run(self): - while self.count > 0: - item = self.queue.get() - print item - self.count = self.count - 1 + def run(self): + while self.count > 0: + item = self.queue.get() + print item + self.count = self.count - 1 - NP = 3 - QL = 4 - NI = 5 + NP = 3 + QL = 4 + NI = 5 - Q = BoundedQueue(QL) - P = [] - for i in range(NP): - t = ProducerThread(Q, NI) - t.setName("Producer-%d" % (i+1)) - P.append(t) - C = ConsumerThread(Q, NI*NP) - for t in P: - t.start() - _sleep(0.000001) - C.start() - for t in P: - t.join() - C.join() + Q = BoundedQueue(QL) + P = [] + for i in range(NP): + t = ProducerThread(Q, NI) + t.setName("Producer-%d" % (i+1)) + P.append(t) + C = ConsumerThread(Q, NI*NP) + for t in P: + t.start() + _sleep(0.000001) + C.start() + for t in P: + t.join() + C.join() -if __name__ == '__main__': - _test() + if __name__ == '__main__': + _test() diff --git a/direct/src/stdpy/threading2.py b/direct/src/stdpy/threading2.py index b45d893f6c..d03ac01c3c 100644 --- a/direct/src/stdpy/threading2.py +++ b/direct/src/stdpy/threading2.py @@ -16,13 +16,12 @@ implementation. """ import sys as _sys from direct.stdpy import thread -from direct.stdpy.thread import stack_size, _local as local +from direct.stdpy.thread import stack_size, _newname, _local as local from panda3d import core _sleep = core.Thread.sleep from time import time as _time from traceback import format_exc as _format_exc -from collections import deque # Rename some stuff so "from threading import *" is safe __all__ = ['activeCount', 'Condition', 'currentThread', 'enumerate', 'Event', @@ -377,13 +376,6 @@ class _Event(_Verbose): finally: self.__cond.release() -# Helper to generate new thread names -_counter = 0 -def _newname(template="Thread-%d"): - global _counter - _counter = _counter + 1 - return template % _counter - # Active thread administration _active_limbo_lock = _allocate_lock() _active = {} # maps thread id to Thread object @@ -741,88 +733,89 @@ _shutdown = _MainThread()._exitfunc # Self-test code +if __debug__: + def _test(): + from collections import deque -def _test(): + class BoundedQueue(_Verbose): - class BoundedQueue(_Verbose): + def __init__(self, limit): + _Verbose.__init__(self) + self.mon = RLock() + self.rc = Condition(self.mon) + self.wc = Condition(self.mon) + self.limit = limit + self.queue = deque() - def __init__(self, limit): - _Verbose.__init__(self) - self.mon = RLock() - self.rc = Condition(self.mon) - self.wc = Condition(self.mon) - self.limit = limit - self.queue = deque() + def put(self, item): + self.mon.acquire() + while len(self.queue) >= self.limit: + self._note("put(%s): queue full", item) + self.wc.wait() + self.queue.append(item) + self._note("put(%s): appended, length now %d", + item, len(self.queue)) + self.rc.notify() + self.mon.release() - def put(self, item): - self.mon.acquire() - while len(self.queue) >= self.limit: - self._note("put(%s): queue full", item) - self.wc.wait() - self.queue.append(item) - self._note("put(%s): appended, length now %d", - item, len(self.queue)) - self.rc.notify() - self.mon.release() + def get(self): + self.mon.acquire() + while not self.queue: + self._note("get(): queue empty") + self.rc.wait() + item = self.queue.popleft() + self._note("get(): got %s, %d left", item, len(self.queue)) + self.wc.notify() + self.mon.release() + return item - def get(self): - self.mon.acquire() - while not self.queue: - self._note("get(): queue empty") - self.rc.wait() - item = self.queue.popleft() - self._note("get(): got %s, %d left", item, len(self.queue)) - self.wc.notify() - self.mon.release() - return item + class ProducerThread(Thread): - class ProducerThread(Thread): + def __init__(self, queue, quota): + Thread.__init__(self, name="Producer") + self.queue = queue + self.quota = quota - def __init__(self, queue, quota): - Thread.__init__(self, name="Producer") - self.queue = queue - self.quota = quota - - def run(self): - from random import random - counter = 0 - while counter < self.quota: - counter = counter + 1 - self.queue.put("%s.%d" % (self.getName(), counter)) - _sleep(random() * 0.00001) + def run(self): + from random import random + counter = 0 + while counter < self.quota: + counter = counter + 1 + self.queue.put("%s.%d" % (self.getName(), counter)) + _sleep(random() * 0.00001) - class ConsumerThread(Thread): + class ConsumerThread(Thread): - def __init__(self, queue, count): - Thread.__init__(self, name="Consumer") - self.queue = queue - self.count = count + def __init__(self, queue, count): + Thread.__init__(self, name="Consumer") + self.queue = queue + self.count = count - def run(self): - while self.count > 0: - item = self.queue.get() - print item - self.count = self.count - 1 + def run(self): + while self.count > 0: + item = self.queue.get() + print item + self.count = self.count - 1 - NP = 3 - QL = 4 - NI = 5 + NP = 3 + QL = 4 + NI = 5 - Q = BoundedQueue(QL) - P = [] - for i in range(NP): - t = ProducerThread(Q, NI) - t.setName("Producer-%d" % (i+1)) - P.append(t) - C = ConsumerThread(Q, NI*NP) - for t in P: - t.start() - _sleep(0.000001) - C.start() - for t in P: - t.join() - C.join() + Q = BoundedQueue(QL) + P = [] + for i in range(NP): + t = ProducerThread(Q, NI) + t.setName("Producer-%d" % (i+1)) + P.append(t) + C = ConsumerThread(Q, NI*NP) + for t in P: + t.start() + _sleep(0.000001) + C.start() + for t in P: + t.join() + C.join() -if __name__ == '__main__': - _test() + if __name__ == '__main__': + _test() diff --git a/direct/src/task/Task.py b/direct/src/task/Task.py index 3308b6f192..5d4a831f32 100644 --- a/direct/src/task/Task.py +++ b/direct/src/task/Task.py @@ -12,6 +12,7 @@ from direct.showbase.PythonUtil import * from direct.showbase.MessengerGlobal import messenger import types import random +import importlib try: import signal @@ -591,7 +592,6 @@ class TaskManager: def popupControls(self): # Don't use a regular import, to prevent ModuleFinder from picking # it up as a dependency when building a .p3d package. - import importlib TaskManagerPanel = importlib.import_module('direct.tkpanels.TaskManagerPanel') return TaskManagerPanel.TaskManagerPanel(self) @@ -602,8 +602,8 @@ class TaskManager: # Defer this import until we need it: some Python # distributions don't provide the profile and pstats modules. - from direct.showbase.ProfileSession import ProfileSession - return ProfileSession(name) + PS = importlib.import_module('direct.showbase.ProfileSession') + return PS.ProfileSession(name) def profileFrames(self, num=None, session=None, callback=None): if num is None: @@ -629,8 +629,8 @@ class TaskManager: self._profileFrames.set(profileFrames) if (not self._frameProfiler) and profileFrames: # import here due to import dependencies - from direct.task.FrameProfiler import FrameProfiler - self._frameProfiler = FrameProfiler() + FP = importlib.import_module('direct.task.FrameProfiler') + self._frameProfiler = FP.FrameProfiler() def getProfileTasks(self): return self._profileTasks.get() @@ -642,8 +642,8 @@ class TaskManager: self._profileTasks.set(profileTasks) if (not self._taskProfiler) and profileTasks: # import here due to import dependencies - from direct.task.TaskProfiler import TaskProfiler - self._taskProfiler = TaskProfiler() + TP = importlib.import_module('direct.task.TaskProfiler') + self._taskProfiler = TP.TaskProfiler() def logTaskProfiles(self, name=None): if self._taskProfiler: @@ -689,9 +689,9 @@ class TaskManager: # Defer this import until we need it: some Python # distributions don't provide the profile and pstats modules. - from direct.showbase.ProfileSession import ProfileSession - profileSession = ProfileSession('profiled-task-%s' % task.getName(), - Functor(profileInfo.taskFunc, *profileInfo.taskArgs)) + PS = importlib.import_module('direct.showbase.ProfileSession') + profileSession = PS.ProfileSession('profiled-task-%s' % task.getName(), + Functor(profileInfo.taskFunc, *profileInfo.taskArgs)) ret = profileSession.run() # set these values *after* profiling in case we're profiling the TaskProfiler