Browse Source

remove old task manager

David Rose 16 years ago
parent
commit
7de817fb2b
4 changed files with 1241 additions and 3369 deletions
  1. 0 17
      direct/src/showbase/ShowBase.py
  2. 1241 9
      direct/src/task/Task.py
  3. 0 1243
      direct/src/task/TaskNew.py
  4. 0 2100
      direct/src/task/TaskOrig.py

+ 0 - 17
direct/src/showbase/ShowBase.py

@@ -1681,17 +1681,6 @@ class ShowBase(DirectObject.DirectObject):
         self.taskMgr.add(self.__audioLoop, 'audioLoop', priority = 60)
         self.taskMgr.add(self.__audioLoop, 'audioLoop', priority = 60)
         self.eventMgr.restart()
         self.eventMgr.restart()
 
 
-        if not hasattr(taskMgr, 'mgr'):
-            # If we're using the old task manager, we need to have an
-            # explicit task to manage the async load requests.  (On
-            # the new task manager, this is built-in.)
-            def asyncLoad(task):
-                task.mgr.poll()
-                return task.cont
-            task = Task.Task(asyncLoad)
-            task.mgr = AsyncTaskManager.getGlobalPtr()
-            taskMgr.add(task, 'asyncLoad')
-
     def shutdown(self):
     def shutdown(self):
         self.taskMgr.remove('audioLoop')
         self.taskMgr.remove('audioLoop')
         self.taskMgr.remove('igLoop')
         self.taskMgr.remove('igLoop')
@@ -1702,12 +1691,6 @@ class ShowBase(DirectObject.DirectObject):
         self.taskMgr.remove('ivalLoop')
         self.taskMgr.remove('ivalLoop')
         self.eventMgr.shutdown()
         self.eventMgr.shutdown()
 
 
-        if not hasattr(taskMgr, 'mgr'):
-            # If we're using the old task manager, we need to have an
-            # explicit task to manage the async load requests.  (On
-            # the new task manager, this is built-in.)
-            taskMgr.remove('asyncLoad')
-
     def getBackgroundColor(self, win = None):
     def getBackgroundColor(self, win = None):
         """
         """
         Returns the current window background color.  This assumes
         Returns the current window background color.  This assumes

+ 1241 - 9
direct/src/task/Task.py

@@ -1,11 +1,1243 @@
-""" This module exists temporarily as a gatekeeper between
-TaskOrig.py, the original Python implementation of the task system,
-and TaskNew.py, the new C++ implementation. """
+""" This module defines a Python-level wrapper around the C++
+AsyncTaskManager interface.  It replaces the old full-Python
+implementation of the Task system. """
 
 
-from pandac.libpandaexpressModules import ConfigVariableBool
-wantNewTasks = ConfigVariableBool('want-new-tasks', True).getValue()
+__all__ = ['Task', 'TaskManager',
+           'cont', 'done', 'again', 'pickup', 'exit',
+           'sequence', 'loop', 'pause']
 
 
-if wantNewTasks:
-    from TaskNew import *
-else:
-    from TaskOrig import *
+from direct.directnotify.DirectNotifyGlobal import *
+from direct.showbase import ExceptionVarDump
+from direct.showbase.PythonUtil import *
+from direct.showbase.MessengerGlobal import messenger
+import signal
+import types
+import time
+import random
+import string
+
+from pandac.PandaModules import *
+
+def print_exc_plus():
+    """
+    Print the usual traceback information, followed by a listing of all the
+    local variables in each frame.
+    """
+    import sys
+    import traceback
+
+    tb = sys.exc_info()[2]
+    while 1:
+        if not tb.tb_next:
+            break
+        tb = tb.tb_next
+    stack = []
+    f = tb.tb_frame
+    while f:
+        stack.append(f)
+        f = f.f_back
+    stack.reverse()
+    traceback.print_exc()
+    print "Locals by frame, innermost last"
+    for frame in stack:
+        print
+        print "Frame %s in %s at line %s" % (frame.f_code.co_name,
+                                             frame.f_code.co_filename,
+                                             frame.f_lineno)
+        for key, value in frame.f_locals.items():
+            print "\t%20s = " % key,
+            #We have to be careful not to cause a new error in our error
+            #printer! Calling str() on an unknown object could cause an
+            #error we don't want.
+            try:
+                print value
+            except:
+                print "<ERROR WHILE PRINTING VALUE>"
+
+# For historical purposes, we remap the C++-defined enumeration to
+# these Python names, and define them both at the module level, here,
+# and at the class level (below).  The preferred access is via the
+# class level.
+done = AsyncTask.DSDone
+cont = AsyncTask.DSCont
+again = AsyncTask.DSAgain
+pickup = AsyncTask.DSPickup
+exit = AsyncTask.DSExit
+
+# Alias PythonTask to Task for historical purposes.
+Task = PythonTask
+
+# Copy the module-level enums above into the class level.  This funny
+# syntax is necessary because it's a C++-wrapped extension type, not a
+# true Python class.
+Task.DtoolClassDict['done'] = done
+Task.DtoolClassDict['cont'] = cont
+Task.DtoolClassDict['again'] = again
+Task.DtoolClassDict['pickup'] = pickup
+Task.DtoolClassDict['exit'] = exit
+
+# Alias the AsyncTaskPause constructor as Task.pause().
+pause = AsyncTaskPause
+Task.DtoolClassDict['pause'] = staticmethod(pause)
+
+def sequence(*taskList):
+    seq = AsyncTaskSequence('sequence')
+    for task in taskList:
+        seq.addTask(task)
+    return seq
+Task.DtoolClassDict['sequence'] = staticmethod(sequence)
+
+def loop(*taskList):
+    seq = AsyncTaskSequence('loop')
+    for task in taskList:
+        seq.addTask(task)
+    seq.setRepeatCount(-1)
+    return seq
+Task.DtoolClassDict['loop'] = staticmethod(loop)
+
+class TaskManager:
+    notify = directNotify.newCategory("TaskManager")
+
+    extendedExceptions = False
+    MaxEpochSpeed = 1.0/30.0
+
+    def __init__(self):
+        self.mgr = AsyncTaskManager.getGlobalPtr()
+
+        self.resumeFunc = None
+        self.globalClock = self.mgr.getClock()
+        self.stepping = False
+        self.running = False
+        self.fKeyboardInterrupt = False
+        self.interruptCount = 0
+
+        self._frameProfileQueue = Queue()
+
+        # this will be set when it's safe to import StateVar 
+        self._profileFrames = None
+        self._frameProfiler = None
+        self._profileTasks = None
+        self._taskProfiler = None
+        self._taskProfileInfo = ScratchPad(
+            taskId = None,
+            profiled = False,
+            session = None,
+            )
+
+    def finalInit(self):
+        # This function should be called once during startup, after
+        # most things are imported.
+        from direct.fsm.StatePush import StateVar
+        self._profileTasks = StateVar(False)
+        self.setProfileTasks(ConfigVariableBool('profile-task-spikes', 0).getValue())
+        self._profileFrames = StateVar(False)
+        self.setProfileFrames(ConfigVariableBool('profile-frames', 0).getValue())
+
+    def destroy(self):
+        # This should be safe to call multiple times.
+        
+        self._frameProfileQueue.clear()
+        self.mgr.cleanup()
+
+    def setClock(self, clockObject):
+        self.mgr.setClock(clockObject)
+        self.globalClock = clockObject
+
+    def invokeDefaultHandler(self, signalNumber, stackFrame):
+        print '*** allowing mid-frame keyboard interrupt.'
+        # Restore default interrupt handler
+        signal.signal(signal.SIGINT, signal.default_int_handler)
+        # and invoke it
+        raise KeyboardInterrupt
+
+    def keyboardInterruptHandler(self, signalNumber, stackFrame):
+        self.fKeyboardInterrupt = 1
+        self.interruptCount += 1
+        if self.interruptCount == 1:
+            print '* interrupt by keyboard'
+        elif self.interruptCount == 2:
+            print '** waiting for end of frame before interrupting...'
+            # The user must really want to interrupt this process
+            # Next time around invoke the default handler
+            signal.signal(signal.SIGINT, self.invokeDefaultHandler)
+
+    def hasTaskChain(self, chainName):
+        """ Returns true if a task chain with the indicated name has
+        already been defined, or false otherwise.  Note that
+        setupTaskChain() will implicitly define a task chain if it has
+        not already been defined, or modify an existing one if it has,
+        so in most cases there is no need to check this method
+        first. """
+
+        return (self.mgr.findTaskChain(chainName) != None)
+
+    def setupTaskChain(self, chainName, numThreads = None, tickClock = None,
+                       threadPriority = None, frameBudget = None,
+                       timeslicePriority = None):
+        """Defines a new task chain.  Each task chain executes tasks
+        potentially in parallel with all of the other task chains (if
+        numThreads is more than zero).  When a new task is created, it
+        may be associated with any of the task chains, by name (or you
+        can move a task to another task chain with
+        task.setTaskChain()).  You can have any number of task chains,
+        but each must have a unique name.
+
+        numThreads is the number of threads to allocate for this task
+        chain.  If it is 1 or more, then the tasks on this task chain
+        will execute in parallel with the tasks on other task chains.
+        If it is greater than 1, then the tasks on this task chain may
+        execute in parallel with themselves (within tasks of the same
+        sort value).
+
+        If tickClock is True, then this task chain will be responsible
+        for ticking the global clock each frame (and thereby
+        incrementing the frame counter).  There should be just one
+        task chain responsible for ticking the clock, and usually it
+        is the default, unnamed task chain.
+
+        threadPriority specifies the priority level to assign to
+        threads on this task chain.  It may be one of TPLow, TPNormal,
+        TPHigh, or TPUrgent.  This is passed to the underlying
+        threading system to control the way the threads are scheduled.
+
+        frameBudget is the maximum amount of time (in seconds) to
+        allow this task chain to run per frame.  Set it to -1 to mean
+        no limit (the default).  It's not directly related to
+        threadPriority.
+
+        timeslicePriority is False in the default mode, in which each
+        task runs exactly once each frame, round-robin style,
+        regardless of the task's priority value; or True to change the
+        meaning of priority so that certain tasks are run less often,
+        in proportion to their time used and to their priority value.
+        See AsyncTaskManager.setTimeslicePriority() for more.
+        """
+        
+        chain = self.mgr.makeTaskChain(chainName)
+        if numThreads is not None:
+            chain.setNumThreads(numThreads)
+        if tickClock is not None:
+            chain.setTickClock(tickClock)
+        if threadPriority is not None:
+            chain.setThreadPriority(threadPriority)
+        if frameBudget is not None:
+            chain.setFrameBudget(frameBudget)
+        if timeslicePriority is not None:
+            chain.setTimeslicePriority(timeslicePriority)
+
+    def hasTaskNamed(self, taskName):
+        """Returns true if there is at least one task, active or
+        sleeping, with the indicated name. """
+        
+        return bool(self.mgr.findTask(taskName))
+
+    def getTasksNamed(self, taskName):
+        """Returns a list of all tasks, active or sleeping, with the
+        indicated name. """
+        return self.__makeTaskList(self.mgr.findTasks(taskName))
+
+    def getTasksMatching(self, taskPattern):
+        """Returns a list of all tasks, active or sleeping, with a
+        name that matches the pattern, which can include standard
+        shell globbing characters like *, ?, and []. """
+        
+        return self.__makeTaskList(self.mgr.findTasksMatching(GlobPattern(taskPattern)))
+
+    def getAllTasks(self):
+        """Returns list of all tasks, active and sleeping, in
+        arbitrary order. """
+        return self.__makeTaskList(self.mgr.getTasks())
+
+    def getTasks(self):
+        """Returns list of all active tasks in arbitrary order. """
+        return self.__makeTaskList(self.mgr.getActiveTasks())
+
+    def getDoLaters(self):
+        """Returns list of all sleeping tasks in arbitrary order. """
+        return self.__makeTaskList(self.mgr.getSleepingTasks())
+
+    def __makeTaskList(self, taskCollection):
+        l = []
+        for i in range(taskCollection.getNumTasks()):
+            l.append(taskCollection.getTask(i))
+        return l
+
+    def doMethodLater(self, delayTime, funcOrTask, name, extraArgs = None,
+                      sort = None, priority = None, taskChain = None,
+                      uponDeath = None, appendTask = False, owner = None):
+
+        """Adds a task to be performed at some time in the future.
+        This is identical to add(), except that the specified
+        delayTime is applied to the Task object first, which means
+        that the task will not begin executing until at least the
+        indicated delayTime (in seconds) has elapsed.
+
+        After delayTime has elapsed, the task will become active, and
+        will run in the soonest possible frame thereafter.  If you
+        wish to specify a task that will run in the next frame, use a
+        delayTime of 0.
+        """
+        
+        if delayTime < 0:
+            assert self.notify.warning('doMethodLater: added task: %s with negative delay: %s' % (name, delayTime))
+
+        task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
+        task.setDelay(delayTime)
+        self.mgr.add(task)
+        return task
+
+    def add(self, funcOrTask, name = None, sort = None, extraArgs = None,
+            priority = None, uponDeath = None, appendTask = False,
+            taskChain = None, owner = None):
+        
+        """
+        Add a new task to the taskMgr.  The task will begin executing
+        immediately, or next frame if its sort value has already
+        passed this frame.
+
+        The parameters are:
+
+        funcOrTask - either an existing Task object (not already added
+        to the task manager), or a callable function object.  If this
+        is a function, a new Task object will be created and returned.
+
+        name - the name to assign to the Task.  Required, unless you
+        are passing in a Task object that already has a name.
+
+        extraArgs - the list of arguments to pass to the task
+        function.  If this is omitted, the list is just the task
+        object itself.
+
+        appendTask - a boolean flag.  If this is true, then the task
+        object itself will be appended to the end of the extraArgs
+        list before calling the function.
+
+        sort - the sort value to assign the task.  The default sort is
+        0.  Within a particular task chain, it is guaranteed that the
+        tasks with a lower sort value will all run before tasks with a
+        higher sort value run.
+
+        priority - the priority at which to run the task.  The default
+        priority is 0.  Higher priority tasks are run sooner, and/or
+        more often.  For historical purposes, if you specify a
+        priority without also specifying a sort, the priority value is
+        understood to actually be a sort value.  (Previously, there
+        was no priority value, only a sort value, and it was called
+        "priority".)
+
+        uponDeath - a function to call when the task terminates,
+        either because it has run to completion, or because it has
+        been explicitly removed.
+
+        taskChain - the name of the task chain to assign the task to.
+
+        owner - an optional Python object that is declared as the
+        "owner" of this task for maintenance purposes.  The owner must
+        have two methods: owner._addTask(self, task), which is called
+        when the task begins, and owner._clearTask(self, task), which
+        is called when the task terminates.  This is all the owner
+        means.
+
+        The return value of add() is the new Task object that has been
+        added, or the original Task object that was passed in.
+
+        """
+        
+        task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
+        self.mgr.add(task)
+        return task
+
+    def __setupTask(self, funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath):
+        if isinstance(funcOrTask, AsyncTask):
+            task = funcOrTask
+        elif callable(funcOrTask):
+            task = PythonTask(funcOrTask)
+        else:
+            self.notify.error(
+                'add: Tried to add a task that was not a Task or a func')
+
+        if hasattr(task, 'setArgs'):
+            # It will only accept arguments if it's a PythonTask.
+            if extraArgs is None:
+                extraArgs = []
+                appendTask = True
+            task.setArgs(extraArgs, appendTask)
+        elif extraArgs is not None:
+            self.notify.error(
+                'Task %s does not accept arguments.' % (repr(task)))
+
+        if name is not None:
+            assert isinstance(name, types.StringTypes), 'Name must be a string type'
+            task.setName(name)
+        assert task.hasName()
+
+        # For historical reasons, if priority is specified but not
+        # sort, it really means sort.
+        if priority is not None and sort is None:
+            task.setSort(priority)
+        else:
+            if priority is not None:
+                task.setPriority(priority)
+            if sort is not None:
+                task.setSort(sort)
+
+        if taskChain is not None:
+            task.setTaskChain(taskChain)
+
+        if owner is not None:
+            task.setOwner(owner)
+
+        if uponDeath is not None:
+            task.setUponDeath(uponDeath)
+
+        return task
+        
+    def remove(self, taskOrName):
+        """Removes a task from the task manager.  The task is stopped,
+        almost as if it had returned task.done.  (But if the task is
+        currently executing, it will finish out its current frame
+        before being removed.)  You may specify either an explicit
+        Task object, or the name of a task.  If you specify a name,
+        all tasks with the indicated name are removed.  Returns the
+        number of tasks removed. """
+        
+        if isinstance(taskOrName, types.StringTypes):
+            tasks = self.mgr.findTasks(taskOrName)
+            return self.mgr.remove(tasks)
+        elif isinstance(taskOrName, AsyncTask):
+            return self.mgr.remove(taskOrName)
+        elif isinstance(taskOrName, types.ListType):
+            for task in taskOrName:
+                self.remove(task)
+        else:
+            self.notify.error('remove takes a string or a Task')
+
+    def removeTasksMatching(self, taskPattern):
+        """Removes all tasks whose names match the pattern, which can
+        include standard shell globbing characters like *, ?, and [].
+        See also remove().
+
+        Returns the number of tasks removed.
+        """
+        tasks = self.mgr.findTasksMatching(GlobPattern(taskPattern))
+        return self.mgr.remove(tasks)
+
+    def step(self):
+        """Invokes the task manager for one frame, and then returns.
+        Normally, this executes each task exactly once, though task
+        chains that are in sub-threads or that have frame budgets
+        might execute their tasks differently. """
+
+        # Replace keyboard interrupt handler during task list processing
+        # so we catch the keyboard interrupt but don't handle it until
+        # after task list processing is complete.
+        self.fKeyboardInterrupt = 0
+        self.interruptCount = 0
+        signal.signal(signal.SIGINT, self.keyboardInterruptHandler)
+
+        startFrameTime = self.globalClock.getRealTime()
+
+        self.mgr.poll()
+
+        # This is the spot for an internal yield function
+        nextTaskTime = self.mgr.getNextWakeTime()
+        self.doYield(startFrameTime, nextTaskTime)
+        
+        # Restore default interrupt handler
+        signal.signal(signal.SIGINT, signal.default_int_handler)
+        if self.fKeyboardInterrupt:
+            raise KeyboardInterrupt
+
+    def run(self):
+        """Starts the task manager running.  Does not return until an
+        exception is encountered (including KeyboardInterrupt). """
+        
+        # Set the clock to have last frame's time in case we were
+        # Paused at the prompt for a long time
+        t = self.globalClock.getFrameTime()
+        timeDelta = t - self.globalClock.getRealTime()
+        self.globalClock.setRealTime(t)
+        messenger.send("resetClock", [timeDelta])
+
+        if self.resumeFunc != None:
+            self.resumeFunc()
+
+        if self.stepping:
+            self.step()
+        else:
+            self.running = True
+            while self.running:
+                try:
+                    if len(self._frameProfileQueue):
+                        numFrames, session, callback = self._frameProfileQueue.pop()
+                        def _profileFunc(numFrames=numFrames):
+                            self._doProfiledFrames(numFrames)
+                        session.setFunc(_profileFunc)
+                        session.run()
+                        _profileFunc = None
+                        if callback:
+                            callback()
+                        session.release()
+                    else:
+                        self.step()
+                except KeyboardInterrupt:
+                    self.stop()
+                except IOError, ioError:
+                    code, message = self._unpackIOError(ioError)
+                    # Since upgrading to Python 2.4.1, pausing the execution
+                    # often gives this IOError during the sleep function:
+                    #     IOError: [Errno 4] Interrupted function call
+                    # So, let's just handle that specific exception and stop.
+                    # All other IOErrors should still get raised.
+                    # Only problem: legit IOError 4s will be obfuscated.
+                    if code == 4:
+                        self.stop()
+                    else:
+                        raise
+                except Exception, e:
+                    if self.extendedExceptions:
+                        self.stop()
+                        print_exc_plus()
+                    else:
+                        if (ExceptionVarDump.wantVariableDump and
+                            ExceptionVarDump.dumpOnExceptionInit):
+                            ExceptionVarDump._varDump__print(e)
+                        raise
+                except:
+                    if self.extendedExceptions:
+                        self.stop()
+                        print_exc_plus()
+                    else:
+                        raise
+
+        self.mgr.stopThreads()
+
+    def _unpackIOError(self, ioError):
+        # IOError unpack from http://www.python.org/doc/essays/stdexceptions/
+        # this needs to be in its own method, exceptions that occur inside
+        # a nested try block are not caught by the inner try block's except
+        try:
+            (code, message) = ioError
+        except:
+            code = 0
+            message = ioError
+        return code, message
+
+    def stop(self):
+        # Set a flag so we will stop before beginning next frame
+        self.running = False
+
+    def __tryReplaceTaskMethod(self, task, oldMethod, newFunction):
+        if not isinstance(task, PythonTask):
+            return 0
+        
+        method = task.getFunction()
+        if (type(method) == types.MethodType):
+            function = method.im_func
+        else:
+            function = method
+        if (function == oldMethod):
+            import new
+            newMethod = new.instancemethod(newFunction,
+                                           method.im_self,
+                                           method.im_class)
+            task.setFunction(newMethod)
+            # Found a match
+            return 1
+        return 0
+
+    def replaceMethod(self, oldMethod, newFunction):
+        numFound = 0
+        for task in self.getAllTasks():
+            numFound += self.__tryReplaceTaskMethod(task, oldMethod, newFunction)
+        return numFound
+
+    def popupControls(self):
+        from direct.tkpanels import TaskManagerPanel
+        return TaskManagerPanel.TaskManagerPanel(self)
+
+    def getProfileSession(self, name=None):
+        # call to get a profile session that you can modify before passing to profileFrames()
+        if name is None:
+            name = 'taskMgrFrameProfile'
+
+        # 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)
+
+    def profileFrames(self, num=None, session=None, callback=None):
+        if num is None:
+            num = 1
+        if session is None:
+            session = self.getProfileSession()
+        # make sure the profile session doesn't get destroyed before we're done with it
+        session.acquire()
+        self._frameProfileQueue.push((num, session, callback))
+
+    def _doProfiledFrames(self, numFrames):
+        for i in xrange(numFrames):
+            result = self.step()
+        return result
+
+    def getProfileFrames(self):
+        return self._profileFrames.get()
+
+    def getProfileFramesSV(self):
+        return self._profileFrames
+
+    def setProfileFrames(self, profileFrames):
+        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()
+
+    def getProfileTasks(self):
+        return self._profileTasks.get()
+
+    def getProfileTasksSV(self):
+        return self._profileTasks
+
+    def setProfileTasks(self, profileTasks):
+        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()
+
+    def logTaskProfiles(self, name=None):
+        if self._taskProfiler:
+            self._taskProfiler.logProfiles(name)
+
+    def flushTaskProfiles(self, name=None):
+        if self._taskProfiler:
+            self._taskProfiler.flush(name)
+
+    def _setProfileTask(self, task):
+        if self._taskProfileInfo.session:
+            self._taskProfileInfo.session.release()
+            self._taskProfileInfo.session = None
+        self._taskProfileInfo = ScratchPad(
+            taskFunc = task.getFunction(),
+            taskArgs = task.getArgs(),
+            task = task,
+            profiled = False,
+            session = None,
+            )
+
+        # Temporarily replace the task's own function with our
+        # _profileTask method.
+        task.setFunction(self._profileTask)
+        task.setArgs([self._taskProfileInfo], True)
+
+    def _profileTask(self, profileInfo, task):
+        # This is called instead of the task function when we have
+        # decided to profile a task.
+
+        assert profileInfo.task == task
+        # don't profile the same task twice in a row
+        assert not profileInfo.profiled
+
+        # Restore the task's proper function for next time.
+        appendTask = False
+        taskArgs = profileInfo.taskArgs
+        if taskArgs and taskArgs[-1] == task:
+            appendTask = True
+            taskArgs = taskArgs[:-1]
+        task.setArgs(taskArgs, appendTask)
+        task.setFunction(profileInfo.taskFunc)
+
+        # 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))
+        ret = profileSession.run()
+
+        # set these values *after* profiling in case we're profiling the TaskProfiler
+        profileInfo.session = profileSession
+        profileInfo.profiled = True
+
+        return ret
+
+    def _hasProfiledDesignatedTask(self):
+        # have we run a profile of the designated task yet?
+        return self._taskProfileInfo.profiled
+
+    def _getLastTaskProfileSession(self):
+        return self._taskProfileInfo.session
+
+    def _getRandomTask(self):
+        # Figure out when the next frame is likely to expire, so we
+        # won't grab any tasks that are sleeping for a long time.
+        now = globalClock.getFrameTime()
+        avgFrameRate = globalClock.getAverageFrameRate()
+        if avgFrameRate < .00001:
+            avgFrameDur = 0.
+        else:
+            avgFrameDur = (1. / globalClock.getAverageFrameRate())
+        next = now + avgFrameDur
+
+        # Now grab a task at random, until we find one that we like.
+        tasks = self.mgr.getTasks()
+        i = random.randrange(tasks.getNumTasks())
+        task = tasks.getTask(i)
+        while not isinstance(task, PythonTask) or \
+              task.getWakeTime() > next:
+            tasks.removeTask(i)
+            i = random.randrange(tasks.getNumTasks())
+            task = tasks.getTask(i)
+        return task
+
+    def __repr__(self):
+        return str(self.mgr)
+
+    # In the event we want to do frame time managment, this is the
+    # function to replace or overload.
+    def doYield(self, frameStartTime, nextScheduledTaskTime):
+        pass
+
+    """
+    def doYieldExample(self, frameStartTime, nextScheduledTaskTime):
+        minFinTime = frameStartTime + self.MaxEpochSpeed
+        if nextScheduledTaskTime > 0 and nextScheduledTaskTime < minFinTime:
+            print ' Adjusting Time'
+            minFinTime = nextScheduledTaskTime
+        delta = minFinTime - self.globalClock.getRealTime()
+        while(delta > 0.002):
+            print ' sleep %s'% (delta)
+            time.sleep(delta)           
+            delta = minFinTime - self.globalClock.getRealTime()
+    """
+    
+    if __debug__:
+        # to catch memory leaks during the tests at the bottom of the file
+        def _startTrackingMemLeaks(self):
+            pass
+
+        def _stopTrackingMemLeaks(self):
+            pass
+
+        def _checkMemLeaks(self):
+            pass
+
+    def _runTests(self):
+        if __debug__:
+            tm = TaskManager()
+            tm.setClock(ClockObject())
+            tm.setupTaskChain("default", tickClock = True)
+
+            # check for memory leaks after every test
+            tm._startTrackingMemLeaks()
+            tm._checkMemLeaks()
+
+            # run-once task
+            l = []
+            def _testDone(task, l=l):
+                l.append(None)
+                return task.done
+            tm.add(_testDone, 'testDone')
+            tm.step()
+            assert len(l) == 1
+            tm.step()
+            assert len(l) == 1
+            _testDone = None
+            tm._checkMemLeaks()
+
+            # remove by name
+            def _testRemoveByName(task):
+                return task.done
+            tm.add(_testRemoveByName, 'testRemoveByName')
+            assert tm.remove('testRemoveByName') == 1
+            assert tm.remove('testRemoveByName') == 0
+            _testRemoveByName = None
+            tm._checkMemLeaks()
+
+            # duplicate named tasks
+            def _testDupNamedTasks(task):
+                return task.done
+            tm.add(_testDupNamedTasks, 'testDupNamedTasks')
+            tm.add(_testDupNamedTasks, 'testDupNamedTasks')
+            assert tm.remove('testRemoveByName') == 0
+            _testDupNamedTasks = None
+            tm._checkMemLeaks()
+
+            # continued task
+            l = []
+            def _testCont(task, l = l):
+                l.append(None)
+                return task.cont
+            tm.add(_testCont, 'testCont')
+            tm.step()
+            assert len(l) == 1
+            tm.step()
+            assert len(l) == 2
+            tm.remove('testCont')
+            _testCont = None
+            tm._checkMemLeaks()
+
+            # continue until done task
+            l = []
+            def _testContDone(task, l = l):
+                l.append(None)
+                if len(l) >= 2:
+                    return task.done
+                else:
+                    return task.cont
+            tm.add(_testContDone, 'testContDone')
+            tm.step()
+            assert len(l) == 1
+            tm.step()
+            assert len(l) == 2
+            tm.step()
+            assert len(l) == 2
+            assert not tm.hasTaskNamed('testContDone')
+            _testContDone = None
+            tm._checkMemLeaks()
+
+            # hasTaskNamed
+            def _testHasTaskNamed(task):
+                return task.done
+            tm.add(_testHasTaskNamed, 'testHasTaskNamed')
+            assert tm.hasTaskNamed('testHasTaskNamed')
+            tm.step()
+            assert not tm.hasTaskNamed('testHasTaskNamed')
+            _testHasTaskNamed = None
+            tm._checkMemLeaks()
+
+            # task sort
+            l = []
+            def _testPri1(task, l = l):
+                l.append(1)
+                return task.cont
+            def _testPri2(task, l = l):
+                l.append(2)
+                return task.cont
+            tm.add(_testPri1, 'testPri1', sort = 1)
+            tm.add(_testPri2, 'testPri2', sort = 2)
+            tm.step()
+            assert len(l) == 2
+            assert l == [1, 2,]
+            tm.step()
+            assert len(l) == 4
+            assert l == [1, 2, 1, 2,]
+            tm.remove('testPri1')
+            tm.remove('testPri2')
+            _testPri1 = None
+            _testPri2 = None
+            tm._checkMemLeaks()
+
+            # task extraArgs
+            l = []
+            def _testExtraArgs(arg1, arg2, l=l):
+                l.extend([arg1, arg2,])
+                return done
+            tm.add(_testExtraArgs, 'testExtraArgs', extraArgs=[4,5])
+            tm.step()
+            assert len(l) == 2
+            assert l == [4, 5,]
+            _testExtraArgs = None
+            tm._checkMemLeaks()
+
+            # task appendTask
+            l = []
+            def _testAppendTask(arg1, arg2, task, l=l):
+                l.extend([arg1, arg2,])
+                return task.done
+            tm.add(_testAppendTask, '_testAppendTask', extraArgs=[4,5], appendTask=True)
+            tm.step()
+            assert len(l) == 2
+            assert l == [4, 5,]
+            _testAppendTask = None
+            tm._checkMemLeaks()
+
+            # task uponDeath
+            l = []
+            def _uponDeathFunc(task, l=l):
+                l.append(task.name)
+            def _testUponDeath(task):
+                return done
+            tm.add(_testUponDeath, 'testUponDeath', uponDeath=_uponDeathFunc)
+            tm.step()
+            assert len(l) == 1
+            assert l == ['testUponDeath']
+            _testUponDeath = None
+            _uponDeathFunc = None
+            tm._checkMemLeaks()
+
+            # task owner
+            class _TaskOwner:
+                def _addTask(self, task):
+                    self.addedTaskName = task.name
+                def _clearTask(self, task):
+                    self.clearedTaskName = task.name
+            to = _TaskOwner()
+            l = []
+            def _testOwner(task):
+                return done
+            tm.add(_testOwner, 'testOwner', owner=to)
+            tm.step()
+            assert getattr(to, 'addedTaskName', None) == 'testOwner'
+            assert getattr(to, 'clearedTaskName', None) == 'testOwner'
+            _testOwner = None
+            del to
+            _TaskOwner = None
+            tm._checkMemLeaks()
+
+
+            doLaterTests = [0,]
+
+            # doLater
+            l = []
+            def _testDoLater1(task, l=l):
+                l.append(1)
+            def _testDoLater2(task, l=l):
+                l.append(2)
+            def _monitorDoLater(task, tm=tm, l=l, doLaterTests=doLaterTests):
+                if task.time > .03:
+                    assert l == [1, 2,]
+                    doLaterTests[0] -= 1
+                    return task.done
+                return task.cont
+            tm.doMethodLater(.01, _testDoLater1, 'testDoLater1')
+            tm.doMethodLater(.02, _testDoLater2, 'testDoLater2')
+            doLaterTests[0] += 1
+            # make sure we run this task after the doLaters if they all occur on the same frame
+            tm.add(_monitorDoLater, 'monitorDoLater', sort=10)
+            _testDoLater1 = None
+            _testDoLater2 = None
+            _monitorDoLater = None
+            # don't check until all the doLaters are finished
+            #tm._checkMemLeaks()
+
+            # doLater sort
+            l = []
+            def _testDoLaterPri1(task, l=l):
+                l.append(1)
+            def _testDoLaterPri2(task, l=l):
+                l.append(2)
+            def _monitorDoLaterPri(task, tm=tm, l=l, doLaterTests=doLaterTests):
+                if task.time > .02:
+                    assert l == [1, 2,]
+                    doLaterTests[0] -= 1
+                    return task.done
+                return task.cont
+            tm.doMethodLater(.01, _testDoLaterPri1, 'testDoLaterPri1', sort=1)
+            tm.doMethodLater(.01, _testDoLaterPri2, 'testDoLaterPri2', sort=2)
+            doLaterTests[0] += 1
+            # make sure we run this task after the doLaters if they all occur on the same frame
+            tm.add(_monitorDoLaterPri, 'monitorDoLaterPri', sort=10)
+            _testDoLaterPri1 = None
+            _testDoLaterPri2 = None
+            _monitorDoLaterPri = None
+            # don't check until all the doLaters are finished
+            #tm._checkMemLeaks()
+
+            # doLater extraArgs
+            l = []
+            def _testDoLaterExtraArgs(arg1, l=l):
+                l.append(arg1)
+            def _monitorDoLaterExtraArgs(task, tm=tm, l=l, doLaterTests=doLaterTests):
+                if task.time > .02:
+                    assert l == [3,]
+                    doLaterTests[0] -= 1
+                    return task.done
+                return task.cont
+            tm.doMethodLater(.01, _testDoLaterExtraArgs, 'testDoLaterExtraArgs', extraArgs=[3,])
+            doLaterTests[0] += 1
+            # make sure we run this task after the doLaters if they all occur on the same frame
+            tm.add(_monitorDoLaterExtraArgs, 'monitorDoLaterExtraArgs', sort=10)
+            _testDoLaterExtraArgs = None
+            _monitorDoLaterExtraArgs = None
+            # don't check until all the doLaters are finished
+            #tm._checkMemLeaks()
+
+            # doLater appendTask
+            l = []
+            def _testDoLaterAppendTask(arg1, task, l=l):
+                assert task.name == 'testDoLaterAppendTask'
+                l.append(arg1)
+            def _monitorDoLaterAppendTask(task, tm=tm, l=l, doLaterTests=doLaterTests):
+                if task.time > .02:
+                    assert l == [4,]
+                    doLaterTests[0] -= 1
+                    return task.done
+                return task.cont
+            tm.doMethodLater(.01, _testDoLaterAppendTask, 'testDoLaterAppendTask',
+                             extraArgs=[4,], appendTask=True)
+            doLaterTests[0] += 1
+            # make sure we run this task after the doLaters if they all occur on the same frame
+            tm.add(_monitorDoLaterAppendTask, 'monitorDoLaterAppendTask', sort=10)
+            _testDoLaterAppendTask = None
+            _monitorDoLaterAppendTask = None
+            # don't check until all the doLaters are finished
+            #tm._checkMemLeaks()
+
+            # doLater uponDeath
+            l = []
+            def _testUponDeathFunc(task, l=l):
+                assert task.name == 'testDoLaterUponDeath'
+                l.append(10)
+            def _testDoLaterUponDeath(arg1, l=l):
+                return done
+            def _monitorDoLaterUponDeath(task, tm=tm, l=l, doLaterTests=doLaterTests):
+                if task.time > .02:
+                    assert l == [10,]
+                    doLaterTests[0] -= 1
+                    return task.done
+                return task.cont
+            tm.doMethodLater(.01, _testDoLaterUponDeath, 'testDoLaterUponDeath',
+                             uponDeath=_testUponDeathFunc)
+            doLaterTests[0] += 1
+            # make sure we run this task after the doLaters if they all occur on the same frame
+            tm.add(_monitorDoLaterUponDeath, 'monitorDoLaterUponDeath', sort=10)
+            _testUponDeathFunc = None
+            _testDoLaterUponDeath = None
+            _monitorDoLaterUponDeath = None
+            # don't check until all the doLaters are finished
+            #tm._checkMemLeaks()
+
+            # doLater owner
+            class _DoLaterOwner:
+                def _addTask(self, task):
+                    self.addedTaskName = task.name
+                def _clearTask(self, task):
+                    self.clearedTaskName = task.name
+            doLaterOwner = _DoLaterOwner()
+            l = []
+            def _testDoLaterOwner(l=l):
+                pass
+            def _monitorDoLaterOwner(task, tm=tm, l=l, doLaterOwner=doLaterOwner,
+                                     doLaterTests=doLaterTests):
+                if task.time > .02:
+                    assert getattr(doLaterOwner, 'addedTaskName', None) == 'testDoLaterOwner'
+                    assert getattr(doLaterOwner, 'clearedTaskName', None) == 'testDoLaterOwner'
+                    doLaterTests[0] -= 1
+                    return task.done
+                return task.cont
+            tm.doMethodLater(.01, _testDoLaterOwner, 'testDoLaterOwner',
+                             owner=doLaterOwner)
+            doLaterTests[0] += 1
+            # make sure we run this task after the doLaters if they all occur on the same frame
+            tm.add(_monitorDoLaterOwner, 'monitorDoLaterOwner', sort=10)
+            _testDoLaterOwner = None
+            _monitorDoLaterOwner = None
+            del doLaterOwner
+            _DoLaterOwner = None
+            # don't check until all the doLaters are finished
+            #tm._checkMemLeaks()
+
+            # run the doLater tests
+            while doLaterTests[0] > 0:
+                tm.step()
+            del doLaterTests
+            tm._checkMemLeaks()
+
+            # getTasks
+            def _testGetTasks(task):
+                return task.cont
+            # No doLaterProcessor in the new world.
+            assert len(tm.getTasks()) == 0
+            tm.add(_testGetTasks, 'testGetTasks1')
+            assert len(tm.getTasks()) == 1
+            assert (tm.getTasks()[0].name == 'testGetTasks1' or
+                    tm.getTasks()[1].name == 'testGetTasks1')
+            tm.add(_testGetTasks, 'testGetTasks2')
+            tm.add(_testGetTasks, 'testGetTasks3')
+            assert len(tm.getTasks()) == 3
+            tm.remove('testGetTasks2')
+            assert len(tm.getTasks()) == 2
+            tm.remove('testGetTasks1')
+            tm.remove('testGetTasks3')
+            assert len(tm.getTasks()) == 0
+            _testGetTasks = None
+            tm._checkMemLeaks()
+
+            # getDoLaters
+            def _testGetDoLaters():
+                pass
+            assert len(tm.getDoLaters()) == 0
+            tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater1')
+            assert len(tm.getDoLaters()) == 1
+            assert tm.getDoLaters()[0].name == 'testDoLater1'
+            tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater2')
+            tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater3')
+            assert len(tm.getDoLaters()) == 3
+            tm.remove('testDoLater2')
+            assert len(tm.getDoLaters()) == 2
+            tm.remove('testDoLater1')
+            tm.remove('testDoLater3')
+            assert len(tm.getDoLaters()) == 0
+            _testGetDoLaters = None
+            tm._checkMemLeaks()
+
+            # duplicate named doLaters removed via taskMgr.remove
+            def _testDupNameDoLaters():
+                pass
+            # the doLaterProcessor is always running
+            tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
+            tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
+            assert len(tm.getDoLaters()) == 2
+            tm.remove('testDupNameDoLater')
+            assert len(tm.getDoLaters()) == 0
+            _testDupNameDoLaters = None
+            tm._checkMemLeaks()
+
+            # duplicate named doLaters removed via remove()
+            def _testDupNameDoLatersRemove():
+                pass
+            # the doLaterProcessor is always running
+            dl1 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
+            dl2 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
+            assert len(tm.getDoLaters()) == 2
+            dl2.remove()
+            assert len(tm.getDoLaters()) == 1
+            dl1.remove()
+            assert len(tm.getDoLaters()) == 0
+            _testDupNameDoLatersRemove = None
+            # nameDict etc. isn't cleared out right away with task.remove()
+            tm._checkMemLeaks()
+
+            # getTasksNamed
+            def _testGetTasksNamed(task):
+                return task.cont
+            assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
+            tm.add(_testGetTasksNamed, 'testGetTasksNamed')
+            assert len(tm.getTasksNamed('testGetTasksNamed')) == 1
+            assert tm.getTasksNamed('testGetTasksNamed')[0].name == 'testGetTasksNamed'
+            tm.add(_testGetTasksNamed, 'testGetTasksNamed')
+            tm.add(_testGetTasksNamed, 'testGetTasksNamed')
+            assert len(tm.getTasksNamed('testGetTasksNamed')) == 3
+            tm.remove('testGetTasksNamed')
+            assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
+            _testGetTasksNamed = None
+            tm._checkMemLeaks()
+
+            # removeTasksMatching
+            def _testRemoveTasksMatching(task):
+                return task.cont
+            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching')
+            assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 1
+            tm.removeTasksMatching('testRemoveTasksMatching')
+            assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 0
+            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1')
+            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2')
+            assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 1
+            assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 1
+            tm.removeTasksMatching('testRemoveTasksMatching*')
+            assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 0
+            assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 0
+            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1a')
+            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2a')
+            assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 1
+            assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 1
+            tm.removeTasksMatching('testRemoveTasksMatching?a')
+            assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 0
+            assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 0
+            _testRemoveTasksMatching = None
+            tm._checkMemLeaks()
+
+            # create Task object and add to mgr
+            l = []
+            def _testTaskObj(task, l=l):
+                l.append(None)
+                return task.cont
+            t = Task(_testTaskObj)
+            tm.add(t, 'testTaskObj')
+            tm.step()
+            assert len(l) == 1
+            tm.step()
+            assert len(l) == 2
+            tm.remove('testTaskObj')
+            tm.step()
+            assert len(l) == 2
+            _testTaskObj = None
+            tm._checkMemLeaks()
+
+            # remove Task via task.remove()
+            l = []
+            def _testTaskObjRemove(task, l=l):
+                l.append(None)
+                return task.cont
+            t = Task(_testTaskObjRemove)
+            tm.add(t, 'testTaskObjRemove')
+            tm.step()
+            assert len(l) == 1
+            tm.step()
+            assert len(l) == 2
+            t.remove()
+            tm.step()
+            assert len(l) == 2
+            del t
+            _testTaskObjRemove = None
+            tm._checkMemLeaks()
+
+            """
+            # this test fails, and it's not clear what the correct behavior should be.
+            # sort passed to Task.__init__ is always overridden by taskMgr.add()
+            # even if no sort is specified, and calling Task.setSort() has no
+            # effect on the taskMgr's behavior.
+            # set/get Task sort
+            l = []
+            def _testTaskObjSort(arg, task, l=l):
+                l.append(arg)
+                return task.cont
+            t1 = Task(_testTaskObjSort, sort=1)
+            t2 = Task(_testTaskObjSort, sort=2)
+            tm.add(t1, 'testTaskObjSort1', extraArgs=['a',], appendTask=True)
+            tm.add(t2, 'testTaskObjSort2', extraArgs=['b',], appendTask=True)
+            tm.step()
+            assert len(l) == 2
+            assert l == ['a', 'b']
+            assert t1.getSort() == 1
+            assert t2.getSort() == 2
+            t1.setSort(3)
+            assert t1.getSort() == 3
+            tm.step()
+            assert len(l) == 4
+            assert l == ['a', 'b', 'b', 'a',]
+            t1.remove()
+            t2.remove()
+            tm.step()
+            assert len(l) == 4
+            del t1
+            del t2
+            _testTaskObjSort = None
+            tm._checkMemLeaks()
+            """
+
+            del l
+            tm.destroy()
+            del tm
+
+if __debug__:
+    def checkLeak():
+        import sys
+        import gc
+        gc.enable()
+        from direct.showbase.DirectObject import DirectObject
+        class TestClass(DirectObject):
+            def doTask(self, task):
+                return task.done
+        obj = TestClass()
+        startRefCount = sys.getrefcount(obj)
+        print 'sys.getrefcount(obj): %s' % sys.getrefcount(obj)
+        print '** addTask'
+        t = obj.addTask(obj.doTask, 'test')
+        print 'sys.getrefcount(obj): %s' % sys.getrefcount(obj)
+        print 'task.getRefCount(): %s' % t.getRefCount()
+        print '** removeTask'
+        obj.removeTask('test')
+        print 'sys.getrefcount(obj): %s' % sys.getrefcount(obj)
+        print 'task.getRefCount(): %s' % t.getRefCount()
+        print '** step'
+        taskMgr.step()
+        taskMgr.step()
+        taskMgr.step()
+        print 'sys.getrefcount(obj): %s' % sys.getrefcount(obj)
+        print 'task.getRefCount(): %s' % t.getRefCount()
+        print '** task release'
+        t = None
+        print 'sys.getrefcount(obj): %s' % sys.getrefcount(obj)
+        assert sys.getrefcount(obj) == startRefCount

+ 0 - 1243
direct/src/task/TaskNew.py

@@ -1,1243 +0,0 @@
-""" This module defines a Python-level wrapper around the C++
-AsyncTaskManager interface.  It replaces the old full-Python
-implementation of the Task system. """
-
-__all__ = ['Task', 'TaskManager',
-           'cont', 'done', 'again', 'pickup', 'exit',
-           'sequence', 'loop', 'pause']
-
-from direct.directnotify.DirectNotifyGlobal import *
-from direct.showbase import ExceptionVarDump
-from direct.showbase.PythonUtil import *
-from direct.showbase.MessengerGlobal import messenger
-import signal
-import types
-import time
-import random
-import string
-
-from pandac.PandaModules import *
-
-def print_exc_plus():
-    """
-    Print the usual traceback information, followed by a listing of all the
-    local variables in each frame.
-    """
-    import sys
-    import traceback
-
-    tb = sys.exc_info()[2]
-    while 1:
-        if not tb.tb_next:
-            break
-        tb = tb.tb_next
-    stack = []
-    f = tb.tb_frame
-    while f:
-        stack.append(f)
-        f = f.f_back
-    stack.reverse()
-    traceback.print_exc()
-    print "Locals by frame, innermost last"
-    for frame in stack:
-        print
-        print "Frame %s in %s at line %s" % (frame.f_code.co_name,
-                                             frame.f_code.co_filename,
-                                             frame.f_lineno)
-        for key, value in frame.f_locals.items():
-            print "\t%20s = " % key,
-            #We have to be careful not to cause a new error in our error
-            #printer! Calling str() on an unknown object could cause an
-            #error we don't want.
-            try:
-                print value
-            except:
-                print "<ERROR WHILE PRINTING VALUE>"
-
-# For historical purposes, we remap the C++-defined enumeration to
-# these Python names, and define them both at the module level, here,
-# and at the class level (below).  The preferred access is via the
-# class level.
-done = AsyncTask.DSDone
-cont = AsyncTask.DSCont
-again = AsyncTask.DSAgain
-pickup = AsyncTask.DSPickup
-exit = AsyncTask.DSExit
-
-# Alias PythonTask to Task for historical purposes.
-Task = PythonTask
-
-# Copy the module-level enums above into the class level.  This funny
-# syntax is necessary because it's a C++-wrapped extension type, not a
-# true Python class.
-Task.DtoolClassDict['done'] = done
-Task.DtoolClassDict['cont'] = cont
-Task.DtoolClassDict['again'] = again
-Task.DtoolClassDict['pickup'] = pickup
-Task.DtoolClassDict['exit'] = exit
-
-# Alias the AsyncTaskPause constructor as Task.pause().
-pause = AsyncTaskPause
-Task.DtoolClassDict['pause'] = staticmethod(pause)
-
-def sequence(*taskList):
-    seq = AsyncTaskSequence('sequence')
-    for task in taskList:
-        seq.addTask(task)
-    return seq
-Task.DtoolClassDict['sequence'] = staticmethod(sequence)
-
-def loop(*taskList):
-    seq = AsyncTaskSequence('loop')
-    for task in taskList:
-        seq.addTask(task)
-    seq.setRepeatCount(-1)
-    return seq
-Task.DtoolClassDict['loop'] = staticmethod(loop)
-
-class TaskManager:
-    notify = directNotify.newCategory("TaskManager")
-
-    extendedExceptions = False
-    MaxEpochSpeed = 1.0/30.0
-
-    def __init__(self):
-        self.mgr = AsyncTaskManager.getGlobalPtr()
-
-        self.resumeFunc = None
-        self.globalClock = self.mgr.getClock()
-        self.stepping = False
-        self.running = False
-        self.fKeyboardInterrupt = False
-        self.interruptCount = 0
-
-        self._frameProfileQueue = Queue()
-
-        # this will be set when it's safe to import StateVar 
-        self._profileFrames = None
-        self._frameProfiler = None
-        self._profileTasks = None
-        self._taskProfiler = None
-        self._taskProfileInfo = ScratchPad(
-            taskId = None,
-            profiled = False,
-            session = None,
-            )
-
-    def finalInit(self):
-        # This function should be called once during startup, after
-        # most things are imported.
-        from direct.fsm.StatePush import StateVar
-        self._profileTasks = StateVar(False)
-        self.setProfileTasks(ConfigVariableBool('profile-task-spikes', 0).getValue())
-        self._profileFrames = StateVar(False)
-        self.setProfileFrames(ConfigVariableBool('profile-frames', 0).getValue())
-
-    def destroy(self):
-        # This should be safe to call multiple times.
-        
-        self._frameProfileQueue.clear()
-        self.mgr.cleanup()
-
-    def setClock(self, clockObject):
-        self.mgr.setClock(clockObject)
-        self.globalClock = clockObject
-
-    def invokeDefaultHandler(self, signalNumber, stackFrame):
-        print '*** allowing mid-frame keyboard interrupt.'
-        # Restore default interrupt handler
-        signal.signal(signal.SIGINT, signal.default_int_handler)
-        # and invoke it
-        raise KeyboardInterrupt
-
-    def keyboardInterruptHandler(self, signalNumber, stackFrame):
-        self.fKeyboardInterrupt = 1
-        self.interruptCount += 1
-        if self.interruptCount == 1:
-            print '* interrupt by keyboard'
-        elif self.interruptCount == 2:
-            print '** waiting for end of frame before interrupting...'
-            # The user must really want to interrupt this process
-            # Next time around invoke the default handler
-            signal.signal(signal.SIGINT, self.invokeDefaultHandler)
-
-    def hasTaskChain(self, chainName):
-        """ Returns true if a task chain with the indicated name has
-        already been defined, or false otherwise.  Note that
-        setupTaskChain() will implicitly define a task chain if it has
-        not already been defined, or modify an existing one if it has,
-        so in most cases there is no need to check this method
-        first. """
-
-        return (self.mgr.findTaskChain(chainName) != None)
-
-    def setupTaskChain(self, chainName, numThreads = None, tickClock = None,
-                       threadPriority = None, frameBudget = None,
-                       timeslicePriority = None):
-        """Defines a new task chain.  Each task chain executes tasks
-        potentially in parallel with all of the other task chains (if
-        numThreads is more than zero).  When a new task is created, it
-        may be associated with any of the task chains, by name (or you
-        can move a task to another task chain with
-        task.setTaskChain()).  You can have any number of task chains,
-        but each must have a unique name.
-
-        numThreads is the number of threads to allocate for this task
-        chain.  If it is 1 or more, then the tasks on this task chain
-        will execute in parallel with the tasks on other task chains.
-        If it is greater than 1, then the tasks on this task chain may
-        execute in parallel with themselves (within tasks of the same
-        sort value).
-
-        If tickClock is True, then this task chain will be responsible
-        for ticking the global clock each frame (and thereby
-        incrementing the frame counter).  There should be just one
-        task chain responsible for ticking the clock, and usually it
-        is the default, unnamed task chain.
-
-        threadPriority specifies the priority level to assign to
-        threads on this task chain.  It may be one of TPLow, TPNormal,
-        TPHigh, or TPUrgent.  This is passed to the underlying
-        threading system to control the way the threads are scheduled.
-
-        frameBudget is the maximum amount of time (in seconds) to
-        allow this task chain to run per frame.  Set it to -1 to mean
-        no limit (the default).  It's not directly related to
-        threadPriority.
-
-        timeslicePriority is False in the default mode, in which each
-        task runs exactly once each frame, round-robin style,
-        regardless of the task's priority value; or True to change the
-        meaning of priority so that certain tasks are run less often,
-        in proportion to their time used and to their priority value.
-        See AsyncTaskManager.setTimeslicePriority() for more.
-        """
-        
-        chain = self.mgr.makeTaskChain(chainName)
-        if numThreads is not None:
-            chain.setNumThreads(numThreads)
-        if tickClock is not None:
-            chain.setTickClock(tickClock)
-        if threadPriority is not None:
-            chain.setThreadPriority(threadPriority)
-        if frameBudget is not None:
-            chain.setFrameBudget(frameBudget)
-        if timeslicePriority is not None:
-            chain.setTimeslicePriority(timeslicePriority)
-
-    def hasTaskNamed(self, taskName):
-        """Returns true if there is at least one task, active or
-        sleeping, with the indicated name. """
-        
-        return bool(self.mgr.findTask(taskName))
-
-    def getTasksNamed(self, taskName):
-        """Returns a list of all tasks, active or sleeping, with the
-        indicated name. """
-        return self.__makeTaskList(self.mgr.findTasks(taskName))
-
-    def getTasksMatching(self, taskPattern):
-        """Returns a list of all tasks, active or sleeping, with a
-        name that matches the pattern, which can include standard
-        shell globbing characters like *, ?, and []. """
-        
-        return self.__makeTaskList(self.mgr.findTasksMatching(GlobPattern(taskPattern)))
-
-    def getAllTasks(self):
-        """Returns list of all tasks, active and sleeping, in
-        arbitrary order. """
-        return self.__makeTaskList(self.mgr.getTasks())
-
-    def getTasks(self):
-        """Returns list of all active tasks in arbitrary order. """
-        return self.__makeTaskList(self.mgr.getActiveTasks())
-
-    def getDoLaters(self):
-        """Returns list of all sleeping tasks in arbitrary order. """
-        return self.__makeTaskList(self.mgr.getSleepingTasks())
-
-    def __makeTaskList(self, taskCollection):
-        l = []
-        for i in range(taskCollection.getNumTasks()):
-            l.append(taskCollection.getTask(i))
-        return l
-
-    def doMethodLater(self, delayTime, funcOrTask, name, extraArgs = None,
-                      sort = None, priority = None, taskChain = None,
-                      uponDeath = None, appendTask = False, owner = None):
-
-        """Adds a task to be performed at some time in the future.
-        This is identical to add(), except that the specified
-        delayTime is applied to the Task object first, which means
-        that the task will not begin executing until at least the
-        indicated delayTime (in seconds) has elapsed.
-
-        After delayTime has elapsed, the task will become active, and
-        will run in the soonest possible frame thereafter.  If you
-        wish to specify a task that will run in the next frame, use a
-        delayTime of 0.
-        """
-        
-        if delayTime < 0:
-            assert self.notify.warning('doMethodLater: added task: %s with negative delay: %s' % (name, delayTime))
-
-        task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
-        task.setDelay(delayTime)
-        self.mgr.add(task)
-        return task
-
-    def add(self, funcOrTask, name = None, sort = None, extraArgs = None,
-            priority = None, uponDeath = None, appendTask = False,
-            taskChain = None, owner = None):
-        
-        """
-        Add a new task to the taskMgr.  The task will begin executing
-        immediately, or next frame if its sort value has already
-        passed this frame.
-
-        The parameters are:
-
-        funcOrTask - either an existing Task object (not already added
-        to the task manager), or a callable function object.  If this
-        is a function, a new Task object will be created and returned.
-
-        name - the name to assign to the Task.  Required, unless you
-        are passing in a Task object that already has a name.
-
-        extraArgs - the list of arguments to pass to the task
-        function.  If this is omitted, the list is just the task
-        object itself.
-
-        appendTask - a boolean flag.  If this is true, then the task
-        object itself will be appended to the end of the extraArgs
-        list before calling the function.
-
-        sort - the sort value to assign the task.  The default sort is
-        0.  Within a particular task chain, it is guaranteed that the
-        tasks with a lower sort value will all run before tasks with a
-        higher sort value run.
-
-        priority - the priority at which to run the task.  The default
-        priority is 0.  Higher priority tasks are run sooner, and/or
-        more often.  For historical purposes, if you specify a
-        priority without also specifying a sort, the priority value is
-        understood to actually be a sort value.  (Previously, there
-        was no priority value, only a sort value, and it was called
-        "priority".)
-
-        uponDeath - a function to call when the task terminates,
-        either because it has run to completion, or because it has
-        been explicitly removed.
-
-        taskChain - the name of the task chain to assign the task to.
-
-        owner - an optional Python object that is declared as the
-        "owner" of this task for maintenance purposes.  The owner must
-        have two methods: owner._addTask(self, task), which is called
-        when the task begins, and owner._clearTask(self, task), which
-        is called when the task terminates.  This is all the owner
-        means.
-
-        The return value of add() is the new Task object that has been
-        added, or the original Task object that was passed in.
-
-        """
-        
-        task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
-        self.mgr.add(task)
-        return task
-
-    def __setupTask(self, funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath):
-        if isinstance(funcOrTask, AsyncTask):
-            task = funcOrTask
-        elif callable(funcOrTask):
-            task = PythonTask(funcOrTask)
-        else:
-            self.notify.error(
-                'add: Tried to add a task that was not a Task or a func')
-
-        if hasattr(task, 'setArgs'):
-            # It will only accept arguments if it's a PythonTask.
-            if extraArgs is None:
-                extraArgs = []
-                appendTask = True
-            task.setArgs(extraArgs, appendTask)
-        elif extraArgs is not None:
-            self.notify.error(
-                'Task %s does not accept arguments.' % (repr(task)))
-
-        if name is not None:
-            assert isinstance(name, types.StringTypes), 'Name must be a string type'
-            task.setName(name)
-        assert task.hasName()
-
-        # For historical reasons, if priority is specified but not
-        # sort, it really means sort.
-        if priority is not None and sort is None:
-            task.setSort(priority)
-        else:
-            if priority is not None:
-                task.setPriority(priority)
-            if sort is not None:
-                task.setSort(sort)
-
-        if taskChain is not None:
-            task.setTaskChain(taskChain)
-
-        if owner is not None:
-            task.setOwner(owner)
-
-        if uponDeath is not None:
-            task.setUponDeath(uponDeath)
-
-        return task
-        
-    def remove(self, taskOrName):
-        """Removes a task from the task manager.  The task is stopped,
-        almost as if it had returned task.done.  (But if the task is
-        currently executing, it will finish out its current frame
-        before being removed.)  You may specify either an explicit
-        Task object, or the name of a task.  If you specify a name,
-        all tasks with the indicated name are removed.  Returns the
-        number of tasks removed. """
-        
-        if isinstance(taskOrName, types.StringTypes):
-            tasks = self.mgr.findTasks(taskOrName)
-            return self.mgr.remove(tasks)
-        elif isinstance(taskOrName, AsyncTask):
-            return self.mgr.remove(taskOrName)
-        elif isinstance(taskOrName, types.ListType):
-            for task in taskOrName:
-                self.remove(task)
-        else:
-            self.notify.error('remove takes a string or a Task')
-
-    def removeTasksMatching(self, taskPattern):
-        """Removes all tasks whose names match the pattern, which can
-        include standard shell globbing characters like *, ?, and [].
-        See also remove().
-
-        Returns the number of tasks removed.
-        """
-        tasks = self.mgr.findTasksMatching(GlobPattern(taskPattern))
-        return self.mgr.remove(tasks)
-
-    def step(self):
-        """Invokes the task manager for one frame, and then returns.
-        Normally, this executes each task exactly once, though task
-        chains that are in sub-threads or that have frame budgets
-        might execute their tasks differently. """
-
-        # Replace keyboard interrupt handler during task list processing
-        # so we catch the keyboard interrupt but don't handle it until
-        # after task list processing is complete.
-        self.fKeyboardInterrupt = 0
-        self.interruptCount = 0
-        signal.signal(signal.SIGINT, self.keyboardInterruptHandler)
-
-        startFrameTime = self.globalClock.getRealTime()
-
-        self.mgr.poll()
-
-        # This is the spot for an internal yield function
-        nextTaskTime = self.mgr.getNextWakeTime()
-        self.doYield(startFrameTime, nextTaskTime)
-        
-        # Restore default interrupt handler
-        signal.signal(signal.SIGINT, signal.default_int_handler)
-        if self.fKeyboardInterrupt:
-            raise KeyboardInterrupt
-
-    def run(self):
-        """Starts the task manager running.  Does not return until an
-        exception is encountered (including KeyboardInterrupt). """
-        
-        # Set the clock to have last frame's time in case we were
-        # Paused at the prompt for a long time
-        t = self.globalClock.getFrameTime()
-        timeDelta = t - self.globalClock.getRealTime()
-        self.globalClock.setRealTime(t)
-        messenger.send("resetClock", [timeDelta])
-
-        if self.resumeFunc != None:
-            self.resumeFunc()
-
-        if self.stepping:
-            self.step()
-        else:
-            self.running = True
-            while self.running:
-                try:
-                    if len(self._frameProfileQueue):
-                        numFrames, session, callback = self._frameProfileQueue.pop()
-                        def _profileFunc(numFrames=numFrames):
-                            self._doProfiledFrames(numFrames)
-                        session.setFunc(_profileFunc)
-                        session.run()
-                        _profileFunc = None
-                        if callback:
-                            callback()
-                        session.release()
-                    else:
-                        self.step()
-                except KeyboardInterrupt:
-                    self.stop()
-                except IOError, ioError:
-                    code, message = self._unpackIOError(ioError)
-                    # Since upgrading to Python 2.4.1, pausing the execution
-                    # often gives this IOError during the sleep function:
-                    #     IOError: [Errno 4] Interrupted function call
-                    # So, let's just handle that specific exception and stop.
-                    # All other IOErrors should still get raised.
-                    # Only problem: legit IOError 4s will be obfuscated.
-                    if code == 4:
-                        self.stop()
-                    else:
-                        raise
-                except Exception, e:
-                    if self.extendedExceptions:
-                        self.stop()
-                        print_exc_plus()
-                    else:
-                        if (ExceptionVarDump.wantVariableDump and
-                            ExceptionVarDump.dumpOnExceptionInit):
-                            ExceptionVarDump._varDump__print(e)
-                        raise
-                except:
-                    if self.extendedExceptions:
-                        self.stop()
-                        print_exc_plus()
-                    else:
-                        raise
-
-        self.mgr.stopThreads()
-
-    def _unpackIOError(self, ioError):
-        # IOError unpack from http://www.python.org/doc/essays/stdexceptions/
-        # this needs to be in its own method, exceptions that occur inside
-        # a nested try block are not caught by the inner try block's except
-        try:
-            (code, message) = ioError
-        except:
-            code = 0
-            message = ioError
-        return code, message
-
-    def stop(self):
-        # Set a flag so we will stop before beginning next frame
-        self.running = False
-
-    def __tryReplaceTaskMethod(self, task, oldMethod, newFunction):
-        if not isinstance(task, PythonTask):
-            return 0
-        
-        method = task.getFunction()
-        if (type(method) == types.MethodType):
-            function = method.im_func
-        else:
-            function = method
-        if (function == oldMethod):
-            import new
-            newMethod = new.instancemethod(newFunction,
-                                           method.im_self,
-                                           method.im_class)
-            task.setFunction(newMethod)
-            # Found a match
-            return 1
-        return 0
-
-    def replaceMethod(self, oldMethod, newFunction):
-        numFound = 0
-        for task in self.getAllTasks():
-            numFound += self.__tryReplaceTaskMethod(task, oldMethod, newFunction)
-        return numFound
-
-    def popupControls(self):
-        from direct.tkpanels import TaskManagerPanel
-        return TaskManagerPanel.TaskManagerPanel(self)
-
-    def getProfileSession(self, name=None):
-        # call to get a profile session that you can modify before passing to profileFrames()
-        if name is None:
-            name = 'taskMgrFrameProfile'
-
-        # 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)
-
-    def profileFrames(self, num=None, session=None, callback=None):
-        if num is None:
-            num = 1
-        if session is None:
-            session = self.getProfileSession()
-        # make sure the profile session doesn't get destroyed before we're done with it
-        session.acquire()
-        self._frameProfileQueue.push((num, session, callback))
-
-    def _doProfiledFrames(self, numFrames):
-        for i in xrange(numFrames):
-            result = self.step()
-        return result
-
-    def getProfileFrames(self):
-        return self._profileFrames.get()
-
-    def getProfileFramesSV(self):
-        return self._profileFrames
-
-    def setProfileFrames(self, profileFrames):
-        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()
-
-    def getProfileTasks(self):
-        return self._profileTasks.get()
-
-    def getProfileTasksSV(self):
-        return self._profileTasks
-
-    def setProfileTasks(self, profileTasks):
-        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()
-
-    def logTaskProfiles(self, name=None):
-        if self._taskProfiler:
-            self._taskProfiler.logProfiles(name)
-
-    def flushTaskProfiles(self, name=None):
-        if self._taskProfiler:
-            self._taskProfiler.flush(name)
-
-    def _setProfileTask(self, task):
-        if self._taskProfileInfo.session:
-            self._taskProfileInfo.session.release()
-            self._taskProfileInfo.session = None
-        self._taskProfileInfo = ScratchPad(
-            taskFunc = task.getFunction(),
-            taskArgs = task.getArgs(),
-            task = task,
-            profiled = False,
-            session = None,
-            )
-
-        # Temporarily replace the task's own function with our
-        # _profileTask method.
-        task.setFunction(self._profileTask)
-        task.setArgs([self._taskProfileInfo], True)
-
-    def _profileTask(self, profileInfo, task):
-        # This is called instead of the task function when we have
-        # decided to profile a task.
-
-        assert profileInfo.task == task
-        # don't profile the same task twice in a row
-        assert not profileInfo.profiled
-
-        # Restore the task's proper function for next time.
-        appendTask = False
-        taskArgs = profileInfo.taskArgs
-        if taskArgs and taskArgs[-1] == task:
-            appendTask = True
-            taskArgs = taskArgs[:-1]
-        task.setArgs(taskArgs, appendTask)
-        task.setFunction(profileInfo.taskFunc)
-
-        # 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))
-        ret = profileSession.run()
-
-        # set these values *after* profiling in case we're profiling the TaskProfiler
-        profileInfo.session = profileSession
-        profileInfo.profiled = True
-
-        return ret
-
-    def _hasProfiledDesignatedTask(self):
-        # have we run a profile of the designated task yet?
-        return self._taskProfileInfo.profiled
-
-    def _getLastTaskProfileSession(self):
-        return self._taskProfileInfo.session
-
-    def _getRandomTask(self):
-        # Figure out when the next frame is likely to expire, so we
-        # won't grab any tasks that are sleeping for a long time.
-        now = globalClock.getFrameTime()
-        avgFrameRate = globalClock.getAverageFrameRate()
-        if avgFrameRate < .00001:
-            avgFrameDur = 0.
-        else:
-            avgFrameDur = (1. / globalClock.getAverageFrameRate())
-        next = now + avgFrameDur
-
-        # Now grab a task at random, until we find one that we like.
-        tasks = self.mgr.getTasks()
-        i = random.randrange(tasks.getNumTasks())
-        task = tasks.getTask(i)
-        while not isinstance(task, PythonTask) or \
-              task.getWakeTime() > next:
-            tasks.removeTask(i)
-            i = random.randrange(tasks.getNumTasks())
-            task = tasks.getTask(i)
-        return task
-
-    def __repr__(self):
-        return str(self.mgr)
-
-    # In the event we want to do frame time managment, this is the
-    # function to replace or overload.
-    def doYield(self, frameStartTime, nextScheduledTaskTime):
-        pass
-
-    """
-    def doYieldExample(self, frameStartTime, nextScheduledTaskTime):
-        minFinTime = frameStartTime + self.MaxEpochSpeed
-        if nextScheduledTaskTime > 0 and nextScheduledTaskTime < minFinTime:
-            print ' Adjusting Time'
-            minFinTime = nextScheduledTaskTime
-        delta = minFinTime - self.globalClock.getRealTime()
-        while(delta > 0.002):
-            print ' sleep %s'% (delta)
-            time.sleep(delta)           
-            delta = minFinTime - self.globalClock.getRealTime()
-    """
-    
-    if __debug__:
-        # to catch memory leaks during the tests at the bottom of the file
-        def _startTrackingMemLeaks(self):
-            pass
-
-        def _stopTrackingMemLeaks(self):
-            pass
-
-        def _checkMemLeaks(self):
-            pass
-
-    def _runTests(self):
-        if __debug__:
-            tm = TaskManager()
-            tm.setClock(ClockObject())
-            tm.setupTaskChain("default", tickClock = True)
-
-            # check for memory leaks after every test
-            tm._startTrackingMemLeaks()
-            tm._checkMemLeaks()
-
-            # run-once task
-            l = []
-            def _testDone(task, l=l):
-                l.append(None)
-                return task.done
-            tm.add(_testDone, 'testDone')
-            tm.step()
-            assert len(l) == 1
-            tm.step()
-            assert len(l) == 1
-            _testDone = None
-            tm._checkMemLeaks()
-
-            # remove by name
-            def _testRemoveByName(task):
-                return task.done
-            tm.add(_testRemoveByName, 'testRemoveByName')
-            assert tm.remove('testRemoveByName') == 1
-            assert tm.remove('testRemoveByName') == 0
-            _testRemoveByName = None
-            tm._checkMemLeaks()
-
-            # duplicate named tasks
-            def _testDupNamedTasks(task):
-                return task.done
-            tm.add(_testDupNamedTasks, 'testDupNamedTasks')
-            tm.add(_testDupNamedTasks, 'testDupNamedTasks')
-            assert tm.remove('testRemoveByName') == 0
-            _testDupNamedTasks = None
-            tm._checkMemLeaks()
-
-            # continued task
-            l = []
-            def _testCont(task, l = l):
-                l.append(None)
-                return task.cont
-            tm.add(_testCont, 'testCont')
-            tm.step()
-            assert len(l) == 1
-            tm.step()
-            assert len(l) == 2
-            tm.remove('testCont')
-            _testCont = None
-            tm._checkMemLeaks()
-
-            # continue until done task
-            l = []
-            def _testContDone(task, l = l):
-                l.append(None)
-                if len(l) >= 2:
-                    return task.done
-                else:
-                    return task.cont
-            tm.add(_testContDone, 'testContDone')
-            tm.step()
-            assert len(l) == 1
-            tm.step()
-            assert len(l) == 2
-            tm.step()
-            assert len(l) == 2
-            assert not tm.hasTaskNamed('testContDone')
-            _testContDone = None
-            tm._checkMemLeaks()
-
-            # hasTaskNamed
-            def _testHasTaskNamed(task):
-                return task.done
-            tm.add(_testHasTaskNamed, 'testHasTaskNamed')
-            assert tm.hasTaskNamed('testHasTaskNamed')
-            tm.step()
-            assert not tm.hasTaskNamed('testHasTaskNamed')
-            _testHasTaskNamed = None
-            tm._checkMemLeaks()
-
-            # task sort
-            l = []
-            def _testPri1(task, l = l):
-                l.append(1)
-                return task.cont
-            def _testPri2(task, l = l):
-                l.append(2)
-                return task.cont
-            tm.add(_testPri1, 'testPri1', sort = 1)
-            tm.add(_testPri2, 'testPri2', sort = 2)
-            tm.step()
-            assert len(l) == 2
-            assert l == [1, 2,]
-            tm.step()
-            assert len(l) == 4
-            assert l == [1, 2, 1, 2,]
-            tm.remove('testPri1')
-            tm.remove('testPri2')
-            _testPri1 = None
-            _testPri2 = None
-            tm._checkMemLeaks()
-
-            # task extraArgs
-            l = []
-            def _testExtraArgs(arg1, arg2, l=l):
-                l.extend([arg1, arg2,])
-                return done
-            tm.add(_testExtraArgs, 'testExtraArgs', extraArgs=[4,5])
-            tm.step()
-            assert len(l) == 2
-            assert l == [4, 5,]
-            _testExtraArgs = None
-            tm._checkMemLeaks()
-
-            # task appendTask
-            l = []
-            def _testAppendTask(arg1, arg2, task, l=l):
-                l.extend([arg1, arg2,])
-                return task.done
-            tm.add(_testAppendTask, '_testAppendTask', extraArgs=[4,5], appendTask=True)
-            tm.step()
-            assert len(l) == 2
-            assert l == [4, 5,]
-            _testAppendTask = None
-            tm._checkMemLeaks()
-
-            # task uponDeath
-            l = []
-            def _uponDeathFunc(task, l=l):
-                l.append(task.name)
-            def _testUponDeath(task):
-                return done
-            tm.add(_testUponDeath, 'testUponDeath', uponDeath=_uponDeathFunc)
-            tm.step()
-            assert len(l) == 1
-            assert l == ['testUponDeath']
-            _testUponDeath = None
-            _uponDeathFunc = None
-            tm._checkMemLeaks()
-
-            # task owner
-            class _TaskOwner:
-                def _addTask(self, task):
-                    self.addedTaskName = task.name
-                def _clearTask(self, task):
-                    self.clearedTaskName = task.name
-            to = _TaskOwner()
-            l = []
-            def _testOwner(task):
-                return done
-            tm.add(_testOwner, 'testOwner', owner=to)
-            tm.step()
-            assert getattr(to, 'addedTaskName', None) == 'testOwner'
-            assert getattr(to, 'clearedTaskName', None) == 'testOwner'
-            _testOwner = None
-            del to
-            _TaskOwner = None
-            tm._checkMemLeaks()
-
-
-            doLaterTests = [0,]
-
-            # doLater
-            l = []
-            def _testDoLater1(task, l=l):
-                l.append(1)
-            def _testDoLater2(task, l=l):
-                l.append(2)
-            def _monitorDoLater(task, tm=tm, l=l, doLaterTests=doLaterTests):
-                if task.time > .03:
-                    assert l == [1, 2,]
-                    doLaterTests[0] -= 1
-                    return task.done
-                return task.cont
-            tm.doMethodLater(.01, _testDoLater1, 'testDoLater1')
-            tm.doMethodLater(.02, _testDoLater2, 'testDoLater2')
-            doLaterTests[0] += 1
-            # make sure we run this task after the doLaters if they all occur on the same frame
-            tm.add(_monitorDoLater, 'monitorDoLater', sort=10)
-            _testDoLater1 = None
-            _testDoLater2 = None
-            _monitorDoLater = None
-            # don't check until all the doLaters are finished
-            #tm._checkMemLeaks()
-
-            # doLater sort
-            l = []
-            def _testDoLaterPri1(task, l=l):
-                l.append(1)
-            def _testDoLaterPri2(task, l=l):
-                l.append(2)
-            def _monitorDoLaterPri(task, tm=tm, l=l, doLaterTests=doLaterTests):
-                if task.time > .02:
-                    assert l == [1, 2,]
-                    doLaterTests[0] -= 1
-                    return task.done
-                return task.cont
-            tm.doMethodLater(.01, _testDoLaterPri1, 'testDoLaterPri1', sort=1)
-            tm.doMethodLater(.01, _testDoLaterPri2, 'testDoLaterPri2', sort=2)
-            doLaterTests[0] += 1
-            # make sure we run this task after the doLaters if they all occur on the same frame
-            tm.add(_monitorDoLaterPri, 'monitorDoLaterPri', sort=10)
-            _testDoLaterPri1 = None
-            _testDoLaterPri2 = None
-            _monitorDoLaterPri = None
-            # don't check until all the doLaters are finished
-            #tm._checkMemLeaks()
-
-            # doLater extraArgs
-            l = []
-            def _testDoLaterExtraArgs(arg1, l=l):
-                l.append(arg1)
-            def _monitorDoLaterExtraArgs(task, tm=tm, l=l, doLaterTests=doLaterTests):
-                if task.time > .02:
-                    assert l == [3,]
-                    doLaterTests[0] -= 1
-                    return task.done
-                return task.cont
-            tm.doMethodLater(.01, _testDoLaterExtraArgs, 'testDoLaterExtraArgs', extraArgs=[3,])
-            doLaterTests[0] += 1
-            # make sure we run this task after the doLaters if they all occur on the same frame
-            tm.add(_monitorDoLaterExtraArgs, 'monitorDoLaterExtraArgs', sort=10)
-            _testDoLaterExtraArgs = None
-            _monitorDoLaterExtraArgs = None
-            # don't check until all the doLaters are finished
-            #tm._checkMemLeaks()
-
-            # doLater appendTask
-            l = []
-            def _testDoLaterAppendTask(arg1, task, l=l):
-                assert task.name == 'testDoLaterAppendTask'
-                l.append(arg1)
-            def _monitorDoLaterAppendTask(task, tm=tm, l=l, doLaterTests=doLaterTests):
-                if task.time > .02:
-                    assert l == [4,]
-                    doLaterTests[0] -= 1
-                    return task.done
-                return task.cont
-            tm.doMethodLater(.01, _testDoLaterAppendTask, 'testDoLaterAppendTask',
-                             extraArgs=[4,], appendTask=True)
-            doLaterTests[0] += 1
-            # make sure we run this task after the doLaters if they all occur on the same frame
-            tm.add(_monitorDoLaterAppendTask, 'monitorDoLaterAppendTask', sort=10)
-            _testDoLaterAppendTask = None
-            _monitorDoLaterAppendTask = None
-            # don't check until all the doLaters are finished
-            #tm._checkMemLeaks()
-
-            # doLater uponDeath
-            l = []
-            def _testUponDeathFunc(task, l=l):
-                assert task.name == 'testDoLaterUponDeath'
-                l.append(10)
-            def _testDoLaterUponDeath(arg1, l=l):
-                return done
-            def _monitorDoLaterUponDeath(task, tm=tm, l=l, doLaterTests=doLaterTests):
-                if task.time > .02:
-                    assert l == [10,]
-                    doLaterTests[0] -= 1
-                    return task.done
-                return task.cont
-            tm.doMethodLater(.01, _testDoLaterUponDeath, 'testDoLaterUponDeath',
-                             uponDeath=_testUponDeathFunc)
-            doLaterTests[0] += 1
-            # make sure we run this task after the doLaters if they all occur on the same frame
-            tm.add(_monitorDoLaterUponDeath, 'monitorDoLaterUponDeath', sort=10)
-            _testUponDeathFunc = None
-            _testDoLaterUponDeath = None
-            _monitorDoLaterUponDeath = None
-            # don't check until all the doLaters are finished
-            #tm._checkMemLeaks()
-
-            # doLater owner
-            class _DoLaterOwner:
-                def _addTask(self, task):
-                    self.addedTaskName = task.name
-                def _clearTask(self, task):
-                    self.clearedTaskName = task.name
-            doLaterOwner = _DoLaterOwner()
-            l = []
-            def _testDoLaterOwner(l=l):
-                pass
-            def _monitorDoLaterOwner(task, tm=tm, l=l, doLaterOwner=doLaterOwner,
-                                     doLaterTests=doLaterTests):
-                if task.time > .02:
-                    assert getattr(doLaterOwner, 'addedTaskName', None) == 'testDoLaterOwner'
-                    assert getattr(doLaterOwner, 'clearedTaskName', None) == 'testDoLaterOwner'
-                    doLaterTests[0] -= 1
-                    return task.done
-                return task.cont
-            tm.doMethodLater(.01, _testDoLaterOwner, 'testDoLaterOwner',
-                             owner=doLaterOwner)
-            doLaterTests[0] += 1
-            # make sure we run this task after the doLaters if they all occur on the same frame
-            tm.add(_monitorDoLaterOwner, 'monitorDoLaterOwner', sort=10)
-            _testDoLaterOwner = None
-            _monitorDoLaterOwner = None
-            del doLaterOwner
-            _DoLaterOwner = None
-            # don't check until all the doLaters are finished
-            #tm._checkMemLeaks()
-
-            # run the doLater tests
-            while doLaterTests[0] > 0:
-                tm.step()
-            del doLaterTests
-            tm._checkMemLeaks()
-
-            # getTasks
-            def _testGetTasks(task):
-                return task.cont
-            # No doLaterProcessor in the new world.
-            assert len(tm.getTasks()) == 0
-            tm.add(_testGetTasks, 'testGetTasks1')
-            assert len(tm.getTasks()) == 1
-            assert (tm.getTasks()[0].name == 'testGetTasks1' or
-                    tm.getTasks()[1].name == 'testGetTasks1')
-            tm.add(_testGetTasks, 'testGetTasks2')
-            tm.add(_testGetTasks, 'testGetTasks3')
-            assert len(tm.getTasks()) == 3
-            tm.remove('testGetTasks2')
-            assert len(tm.getTasks()) == 2
-            tm.remove('testGetTasks1')
-            tm.remove('testGetTasks3')
-            assert len(tm.getTasks()) == 0
-            _testGetTasks = None
-            tm._checkMemLeaks()
-
-            # getDoLaters
-            def _testGetDoLaters():
-                pass
-            assert len(tm.getDoLaters()) == 0
-            tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater1')
-            assert len(tm.getDoLaters()) == 1
-            assert tm.getDoLaters()[0].name == 'testDoLater1'
-            tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater2')
-            tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater3')
-            assert len(tm.getDoLaters()) == 3
-            tm.remove('testDoLater2')
-            assert len(tm.getDoLaters()) == 2
-            tm.remove('testDoLater1')
-            tm.remove('testDoLater3')
-            assert len(tm.getDoLaters()) == 0
-            _testGetDoLaters = None
-            tm._checkMemLeaks()
-
-            # duplicate named doLaters removed via taskMgr.remove
-            def _testDupNameDoLaters():
-                pass
-            # the doLaterProcessor is always running
-            tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
-            tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
-            assert len(tm.getDoLaters()) == 2
-            tm.remove('testDupNameDoLater')
-            assert len(tm.getDoLaters()) == 0
-            _testDupNameDoLaters = None
-            tm._checkMemLeaks()
-
-            # duplicate named doLaters removed via remove()
-            def _testDupNameDoLatersRemove():
-                pass
-            # the doLaterProcessor is always running
-            dl1 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
-            dl2 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
-            assert len(tm.getDoLaters()) == 2
-            dl2.remove()
-            assert len(tm.getDoLaters()) == 1
-            dl1.remove()
-            assert len(tm.getDoLaters()) == 0
-            _testDupNameDoLatersRemove = None
-            # nameDict etc. isn't cleared out right away with task.remove()
-            tm._checkMemLeaks()
-
-            # getTasksNamed
-            def _testGetTasksNamed(task):
-                return task.cont
-            assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
-            tm.add(_testGetTasksNamed, 'testGetTasksNamed')
-            assert len(tm.getTasksNamed('testGetTasksNamed')) == 1
-            assert tm.getTasksNamed('testGetTasksNamed')[0].name == 'testGetTasksNamed'
-            tm.add(_testGetTasksNamed, 'testGetTasksNamed')
-            tm.add(_testGetTasksNamed, 'testGetTasksNamed')
-            assert len(tm.getTasksNamed('testGetTasksNamed')) == 3
-            tm.remove('testGetTasksNamed')
-            assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
-            _testGetTasksNamed = None
-            tm._checkMemLeaks()
-
-            # removeTasksMatching
-            def _testRemoveTasksMatching(task):
-                return task.cont
-            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching')
-            assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 1
-            tm.removeTasksMatching('testRemoveTasksMatching')
-            assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 0
-            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1')
-            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2')
-            assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 1
-            assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 1
-            tm.removeTasksMatching('testRemoveTasksMatching*')
-            assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 0
-            assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 0
-            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1a')
-            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2a')
-            assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 1
-            assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 1
-            tm.removeTasksMatching('testRemoveTasksMatching?a')
-            assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 0
-            assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 0
-            _testRemoveTasksMatching = None
-            tm._checkMemLeaks()
-
-            # create Task object and add to mgr
-            l = []
-            def _testTaskObj(task, l=l):
-                l.append(None)
-                return task.cont
-            t = Task(_testTaskObj)
-            tm.add(t, 'testTaskObj')
-            tm.step()
-            assert len(l) == 1
-            tm.step()
-            assert len(l) == 2
-            tm.remove('testTaskObj')
-            tm.step()
-            assert len(l) == 2
-            _testTaskObj = None
-            tm._checkMemLeaks()
-
-            # remove Task via task.remove()
-            l = []
-            def _testTaskObjRemove(task, l=l):
-                l.append(None)
-                return task.cont
-            t = Task(_testTaskObjRemove)
-            tm.add(t, 'testTaskObjRemove')
-            tm.step()
-            assert len(l) == 1
-            tm.step()
-            assert len(l) == 2
-            t.remove()
-            tm.step()
-            assert len(l) == 2
-            del t
-            _testTaskObjRemove = None
-            tm._checkMemLeaks()
-
-            """
-            # this test fails, and it's not clear what the correct behavior should be.
-            # sort passed to Task.__init__ is always overridden by taskMgr.add()
-            # even if no sort is specified, and calling Task.setSort() has no
-            # effect on the taskMgr's behavior.
-            # set/get Task sort
-            l = []
-            def _testTaskObjSort(arg, task, l=l):
-                l.append(arg)
-                return task.cont
-            t1 = Task(_testTaskObjSort, sort=1)
-            t2 = Task(_testTaskObjSort, sort=2)
-            tm.add(t1, 'testTaskObjSort1', extraArgs=['a',], appendTask=True)
-            tm.add(t2, 'testTaskObjSort2', extraArgs=['b',], appendTask=True)
-            tm.step()
-            assert len(l) == 2
-            assert l == ['a', 'b']
-            assert t1.getSort() == 1
-            assert t2.getSort() == 2
-            t1.setSort(3)
-            assert t1.getSort() == 3
-            tm.step()
-            assert len(l) == 4
-            assert l == ['a', 'b', 'b', 'a',]
-            t1.remove()
-            t2.remove()
-            tm.step()
-            assert len(l) == 4
-            del t1
-            del t2
-            _testTaskObjSort = None
-            tm._checkMemLeaks()
-            """
-
-            del l
-            tm.destroy()
-            del tm
-
-if __debug__:
-    def checkLeak():
-        import sys
-        import gc
-        gc.enable()
-        from direct.showbase.DirectObject import DirectObject
-        class TestClass(DirectObject):
-            def doTask(self, task):
-                return task.done
-        obj = TestClass()
-        startRefCount = sys.getrefcount(obj)
-        print 'sys.getrefcount(obj): %s' % sys.getrefcount(obj)
-        print '** addTask'
-        t = obj.addTask(obj.doTask, 'test')
-        print 'sys.getrefcount(obj): %s' % sys.getrefcount(obj)
-        print 'task.getRefCount(): %s' % t.getRefCount()
-        print '** removeTask'
-        obj.removeTask('test')
-        print 'sys.getrefcount(obj): %s' % sys.getrefcount(obj)
-        print 'task.getRefCount(): %s' % t.getRefCount()
-        print '** step'
-        taskMgr.step()
-        taskMgr.step()
-        taskMgr.step()
-        print 'sys.getrefcount(obj): %s' % sys.getrefcount(obj)
-        print 'task.getRefCount(): %s' % t.getRefCount()
-        print '** task release'
-        t = None
-        print 'sys.getrefcount(obj): %s' % sys.getrefcount(obj)
-        assert sys.getrefcount(obj) == startRefCount

+ 0 - 2100
direct/src/task/TaskOrig.py

@@ -1,2100 +0,0 @@
-"""Undocumented Module"""
-
-__all__ = ['Task', 'TaskSortList', 'TaskManager',
-           'exit', 'cont', 'done', 'again',
-           'sequence', 'loop', 'pause']
-
-# This module may not import pandac.PandaModules, since it is imported
-# by the Toontown Launcher before the complete PandaModules have been
-# downloaded.  Instead, it imports only libpandaexpressModules, the
-# subset of PandaModules that we know is available immediately.
-# Methods that require more advanced C++ methods may import the
-# appropriate files within their own scope.
-
-# Actually, the above is no longer true.  The Task module is no longer
-# imported before PandaModules is available.  Instead, we make use of
-# the much more limited MiniTask.py for initial startup.
-
-from pandac.libpandaexpressModules import *
-
-from direct.directnotify.DirectNotifyGlobal import *
-from direct.showbase.PythonUtil import *
-from direct.showbase.MessengerGlobal import *
-from direct.showbase import ExceptionVarDump
-import time
-import fnmatch
-import string
-import signal
-import random
-try:
-    Dtool_PreloadDLL("libp3heapq")
-    from libp3heapq import heappush, heappop, heapify
-except:
-    Dtool_PreloadDLL("libheapq")
-    from libheapq import heappush, heappop, heapify
-import types
-
-if __debug__:
-    # For pstats
-    from pandac.PandaModules import PStatCollector
-
-def print_exc_plus():
-    """
-    Print the usual traceback information, followed by a listing of all the
-    local variables in each frame.
-    """
-    import sys
-    import traceback
-
-    tb = sys.exc_info()[2]
-    while 1:
-        if not tb.tb_next:
-            break
-        tb = tb.tb_next
-    stack = []
-    f = tb.tb_frame
-    while f:
-        stack.append(f)
-        f = f.f_back
-    stack.reverse()
-    traceback.print_exc()
-    print "Locals by frame, innermost last"
-    for frame in stack:
-        print
-        print "Frame %s in %s at line %s" % (frame.f_code.co_name,
-                                             frame.f_code.co_filename,
-                                             frame.f_lineno)
-        for key, value in frame.f_locals.items():
-            print "\t%20s = " % key,
-            #We have to be careful not to cause a new error in our error
-            #printer! Calling str() on an unknown object could cause an
-            #error we don't want.
-            try:
-                print value
-            except:
-                print "<ERROR WHILE PRINTING VALUE>"
-
-class Task:
-
-    # This enum is a copy of the one at the top-level.
-    exit = -1
-    done = 0
-    cont = 1
-    again = 2
-
-    count = 0
-    def __init__(self, callback, sort = 0):
-        try:
-            config
-        except:
-            pass
-        else:
-            if config.GetBool('record-task-creation-stack', 0):
-                self.debugInitTraceback = StackTrace("Task "+str(callback), 1, 10)
-        # Unique ID for each task
-        self.id = Task.count
-        Task.count += 1
-
-        #set to have the task managed
-        self.owner = None
-
-        self.__call__ = callback
-        self._sort = sort
-        self._removed = 0
-        self.dt = 0.0
-        if TaskManager.taskTimerVerbose:
-            self.avgDt = 0.0
-            self.maxDt = 0.0
-            self.runningTotal = 0.0
-            self.pstats = None
-            self.pstatCollector = None
-        self.extraArgs = []
-        # Used for doLaters
-        self.wakeTime = 0.0
-        # for repeating doLaters
-        self.delayTime = 0.0
-        self.time = 0.0
-
-#     # Used for putting into the doLaterList
-#     # the heapq calls __cmp__ via the rich compare function
-#     def __cmp__(self, other):
-#         if isinstance(other, Task):
-#             if self.wakeTime < other.wakeTime:
-#                 return -1
-#             elif self.wakeTime > other.wakeTime:
-#                 return 1
-#             # If the wakeTimes happen to be the same, just
-#             # sort them based on id
-#             else:
-#                 return cmp(id(self), id(other))
-#         # This is important for people doing a (task != None) and such.
-#         else:
-#             return cmp(id(self), id(other))
-
-#     # According to the Python manual (3.3.1), if you define a cmp operator
-#     # you should also define a hash operator or your objects will not be
-#     # usable in dictionaries. Since no two task objects are unique, we can
-#     # just return the unique id.
-#     def __hash__(self):
-#         return self.id
-
-    def remove(self):
-        if not self._removed:
-            if(self.owner):
-                self.owner._clearTask(self)
-            self._removed = 1
-            # Remove any refs to real objects
-            # In case we hang around the doLaterList for a while
-            del self.__call__
-            del self.extraArgs
-            if TaskManager.taskTimerVerbose and self.pstatCollector:
-                self.pstatCollector.subLevelNow(1)
-
-    def isRemoved(self):
-        return self._removed
-
-    def isAlive(self):
-        """ Returns true if the task is alive; that is, it has never
-        been removed and it has not finished.  This method may
-        inaccurately return true before the task has been initially
-        added to the task manager. """
-        
-        return not self._removed
-
-    def getSort(self):
-        return self._sort
-
-    def setSort(self, pri):
-        self._sort = pri
-
-    def getPriority(self):
-        return 0
-
-    def setPriority(self, pri):
-        TaskManager.notify.error("deprecated task.setPriority() called; use setSort() instead")
-        pass
-
-    def getName(self):
-        return self.name
-
-    def setName(self, name):
-        self.name = name
-
-    def getDelay(self):
-        return self.delayTime
-
-    def setDelay(self, delay):
-        self.delayTime = delay
-
-    def getUponDeath(self):
-        return self.uponDeath
-
-    def setUponDeath(self, uponDeath):
-        self.uponDeath = uponDeath
-
-    def recalcWakeTime(self):
-        """If the task is currently sleeping, this resets its wake
-        time to the current time + self.delayTime.  It is as if the
-        task had suddenly returned task.again.  The task will sleep
-        for its current delay seconds before running again.  This
-        method may therefore be used to make the task wake up sooner
-        or later than it would have otherwise.
-
-        If the task is not already sleeping, this method has no
-        effect."""
-
-        # This is a little bit hacky--we're poking around in global
-        # space more than we should--but this code will be going away
-        # soon in favor of the new task manager anyway.
-        
-        now = globalClock.getFrameTime()
-        self.wakeTime = now + self.delayTime
-
-        # Very important that we re-sort the priority queue after
-        # monkeying with the wake times.
-        heapify(taskMgr._TaskManager__doLaterList)
-        
-
-    def setStartTimeFrame(self, startTime, startFrame):
-        self.starttime = startTime
-        self.startframe = startFrame
-
-    def setCurrentTimeFrame(self, currentTime, currentFrame):
-        # Calculate and store this task's time (relative to when it started)
-        self.time = currentTime - self.starttime
-        self.frame = currentFrame - self.startframe
-
-    def getNamePrefix(self):
-        # get a version of the task name, omitting a hyphen or
-        # underscore followed by a string of digits at the end of the
-        # name.
-        name = self.name
-        trimmed = len(name)
-        p = trimmed
-        while True:
-            while p > 0 and name[p - 1] in string.digits:
-                p -= 1
-            if p > 0 and name[p - 1] in '-_':
-                p -= 1
-                trimmed = p
-            else:
-                p = trimmed
-                break
-
-        return name[:trimmed]
-
-    def setupPStats(self):
-        if __debug__ and TaskManager.taskTimerVerbose and not self.pstats:
-            # Get the PStats name for the task.  By convention,
-            # this is everything until the first hyphen; the part
-            # of the task name following the hyphen is generally
-            # used to differentiate particular tasks that do the
-            # same thing to different objects.
-            name = self.name
-            hyphen = name.find('-')
-            if hyphen >= 0:
-                name = name[0:hyphen]
-            self.pstats = PStatCollector("App:Show code:" + name)
-            if self.wakeTime or self.delayTime:
-                self.pstatCollector = PStatCollector("Tasks:doLaters:" + name)
-            else:
-                self.pstatCollector = PStatCollector("Tasks:" + name)
-            self.pstatCollector.addLevelNow(1)
-
-    def finishTask(self):
-        if hasattr(self, "uponDeath"):
-            self.uponDeath(self)
-            del self.uponDeath
-        # We regret to announce...
-        messenger.send('TaskManager-removeTask', sentArgs = [self])
-
-    def __repr__(self):
-        if hasattr(self, 'name'):
-            return ('Task id: %s, name %s' % (self.id, self.name))
-        else:
-            return ('Task id: %s, no name' % (self.id))
-
-def pause(delayTime):
-    def func(self):
-        if (self.time < self.delayTime):
-            return cont
-        else:
-            return done
-    task = Task(func)
-    task.name = 'pause'
-    task.delayTime = delayTime
-    return task
-Task.pause = staticmethod(pause)
-
-def sequence(*taskList):
-    return make_sequence(taskList)
-Task.sequence = staticmethod(sequence)
-
-
-def make_sequence(taskList):
-    def func(self):
-        frameFinished = 0
-        taskDoneStatus = -1
-        while not frameFinished:
-            task = self.taskList[self.index]
-            # If this is a new task, set its start time and frame
-            if self.index > self.prevIndex:
-                task.setStartTimeFrame(self.time, self.frame)
-            self.prevIndex = self.index
-            # Calculate this task's time since it started
-            task.setCurrentTimeFrame(self.time, self.frame)
-            # Execute the current task
-            ret = task(task)
-            # Check the return value from the task
-            if ret == cont:
-                # If this current task wants to continue,
-                # come back to it next frame
-                taskDoneStatus = cont
-                frameFinished = 1
-            elif ret == done:
-                # If this task is done, increment the index so that next frame
-                # we will start executing the next task on the list
-                self.index = self.index + 1
-                taskDoneStatus = cont
-                frameFinished = 0
-            elif ret == exit:
-                # If this task wants to exit, the sequence exits
-                taskDoneStatus = exit
-                frameFinished = 1
-
-            # If we got to the end of the list, this sequence is done
-            if self.index >= len(self.taskList):
-                # TaskManager.notify.debug('sequence done: ' + self.name)
-                frameFinished = 1
-                taskDoneStatus = done
-
-        return taskDoneStatus
-
-    task = Task(func)
-    task.name = 'sequence'
-    task.taskList = taskList
-    task.prevIndex = -1
-    task.index = 0
-    return task
-
-def resetSequence(task):
-    # Should this automatically be done as part of spawnTaskNamed?
-    # Or should one have to create a new task instance every time
-    # one wishes to spawn a task (currently sequences and can
-    # only be fired off once
-    task.index = 0
-    task.prevIndex = -1
-
-def loop(*taskList):
-    return make_loop(taskList)
-Task.loop = staticmethod(loop)
-
-def make_loop(taskList):
-    def func(self):
-        frameFinished = 0
-        taskDoneStatus = -1
-        while (not frameFinished):
-            task = self.taskList[self.index]
-            # If this is a new task, set its start time and frame
-            if (self.index > self.prevIndex):
-                task.setStartTimeFrame(self.time, self.frame)
-            self.prevIndex = self.index
-            # Calculate this task's time since it started
-            task.setCurrentTimeFrame(self.time, self.frame)
-            # Execute the current task
-            ret = task(task)
-            # Check the return value from the task
-            if (ret == cont):
-                # If this current task wants to continue,
-                # come back to it next frame
-                taskDoneStatus = cont
-                frameFinished = 1
-            elif (ret == done):
-                # If this task is done, increment the index so that next frame
-                # we will start executing the next task on the list
-                # TODO: we should go to the next frame now
-                self.index = self.index + 1
-                taskDoneStatus = cont
-                frameFinished = 0
-            elif (ret == exit):
-                # If this task wants to exit, the sequence exits
-                taskDoneStatus = exit
-                frameFinished = 1
-            if (self.index >= len(self.taskList)):
-                # If we got to the end of the list, wrap back around
-                self.prevIndex = -1
-                self.index = 0
-                frameFinished = 1
-        return taskDoneStatus
-    task = Task(func)
-    task.name = 'loop'
-    task.taskList = taskList
-    task.prevIndex = -1
-    task.index = 0
-    return task
-
-
-class TaskSortList(list):
-    def __init__(self, sort):
-        self._sort = sort
-        self.__emptyIndex = 0
-    def getSort(self):
-        return self._sort
-    def add(self, task):
-        if (self.__emptyIndex >= len(self)):
-            self.append(task)
-            self.__emptyIndex += 1
-        else:
-            self[self.__emptyIndex] = task
-            self.__emptyIndex += 1
-    def remove(self, i):
-        assert i <= len(self)
-        if (len(self) == 1) and (i == 1):
-            self[i] = None
-            self.__emptyIndex = 0
-        else:
-            # Swap the last element for this one
-            lastElement = self[self.__emptyIndex-1]
-            self[i] = lastElement
-            self[self.__emptyIndex-1] = None
-            self.__emptyIndex -= 1
-
-class TaskManager:
-
-    # These class vars are generally overwritten by Config variables which
-    # are read in at the start of a show (ShowBase.py or AIStart.py)
-
-    notify = None
-    # TODO: there is a bit of a bug when you default this to 0. The first
-    # task we make, the doLaterProcessor, needs to have this set to 1 or
-    # else we get an error.
-    taskTimerVerbose = 1
-    extendedExceptions = 0
-    pStatsTasks = 0
-
-    doLaterCleanupCounter = 2000
-
-    OsdPrefix = 'task.'
-
-    # multiple of average frame duration
-    DefTaskDurationWarningThreshold = 40.
-
-    _DidTests = False
-
-    def __init__(self):
-        self._destroyed = False
-        self.running = 0
-        self.stepping = 0
-        self.taskList = []
-        # Dictionary of sort to newTaskLists
-        self.pendingTaskDict = {}
-        # List of tasks scheduled to execute in the future
-        self.__doLaterList = []
-
-        self._frameProfileQueue = Queue()
-        self.MaxEpockSpeed = 1.0/30.0;   
-
-        # this will be set when it's safe to import StateVar
-        self._profileFrames = None
-        self._frameProfiler = None
-        self._profileTasks = None
-        self._taskProfiler = None
-        self._taskProfileInfo = ScratchPad(
-            taskId = None,
-            profiled = False,
-            session = None,
-            )
-
-        # We copy this value in from __builtins__ when it gets set.
-        # But since the TaskManager might have to run before it gets
-        # set--before it can even be available--we also have to have
-        # special-case code that handles the possibility that we don't
-        # have a globalClock yet.
-        self.globalClock = None
-
-        # To help cope with the possibly-missing globalClock, we get a
-        # handle to Panda's low-level TrueClock object for measuring
-        # small intervals.
-        self.trueClock = TrueClock.getGlobalPtr()
-
-        # We don't have a base yet, but we can query the config
-        # variables directly.
-        self.warnTaskDuration = ConfigVariableBool('want-task-duration-warnings', 1).getValue()
-        self.taskDurationWarningThreshold = ConfigVariableDouble(
-            'task-duration-warning-threshold',
-            TaskManager.DefTaskDurationWarningThreshold).getValue()
-
-        self.currentTime, self.currentFrame = self.__getTimeFrame()
-        if (TaskManager.notify == None):
-            TaskManager.notify = directNotify.newCategory("TaskManager")
-        self.fKeyboardInterrupt = 0
-        self.interruptCount = 0
-        self.resumeFunc = None
-        # Dictionary of task name to list of tasks with that name
-        self.nameDict = {}
-
-        # A default task.
-        self._doLaterTask = self.add(self.__doLaterProcessor, "doLaterProcessor", -10)
-
-    def finalInit(self):
-        # This function should be called once during startup, after
-        # most things are imported.
-        pass
-
-    def destroy(self):
-        if self._destroyed:
-            return
-
-        self._frameProfileQueue.clear()
-        if self._doLaterTask:
-            self._doLaterTask.remove()
-        if self._taskProfiler:
-            self._taskProfiler.destroy()
-        del self.nameDict
-        del self.trueClock
-        del self.globalClock
-        del self.__doLaterList
-        del self.pendingTaskDict
-        del self.taskList
-        self._destroyed = True
-
-    def setStepping(self, value):
-        self.stepping = value
-
-    def getTaskDurationWarningThreshold(self):
-        return self.taskDurationWarningThreshold
-
-    def setTaskDurationWarningThreshold(self, threshold):
-        self.taskDurationWarningThreshold = threshold
-
-    def invokeDefaultHandler(self, signalNumber, stackFrame):
-        print '*** allowing mid-frame keyboard interrupt.'
-        # Restore default interrupt handler
-        signal.signal(signal.SIGINT, signal.default_int_handler)
-        # and invoke it
-        raise KeyboardInterrupt
-
-    def keyboardInterruptHandler(self, signalNumber, stackFrame):
-        self.fKeyboardInterrupt = 1
-        self.interruptCount += 1
-        if self.interruptCount == 1:
-            print '* interrupt by keyboard'
-        elif self.interruptCount == 2:
-            print '** waiting for end of frame before interrupting...'
-            # The user must really want to interrupt this process
-            # Next time around invoke the default handler
-            signal.signal(signal.SIGINT, self.invokeDefaultHandler)
-
-    def setupTaskChain(self, *args, **kw):
-        # This is a no-op in the original task implementation; task
-        # chains are not supported here.
-        pass
-
-    def hasTaskNamed(self, taskName):
-        # TODO: check pending task list
-        # Get the tasks with this name
-        # If we found some, see if any of them are still active (not removed)
-        for task in self.nameDict.get(taskName, []):
-            if not task._removed:
-                return 1
-        # Didnt find any, return 0
-        return 0
-
-    def getTasksNamed(self, taskName):
-        # TODO: check pending tasks
-        # Get the tasks with this name
-        return [task for task in self.nameDict.get(taskName, []) #grab all tasks with name
-                   if not task._removed] #filter removed tasks
-
-    def getTasksMatching(self, taskPattern):
-        """getTasksMatching(self, string taskPattern)
-
-        Returns tasks whose names match the pattern, which can include
-        standard shell globbing characters like *, ?, and [].
-        """
-        keyList = filter(
-            lambda key: fnmatch.fnmatchcase(key, taskPattern),
-            self.nameDict.keys())
-
-        result = []
-        for key in keyList:
-            for task in self.nameDict.get(key, []):
-                if not task._removed:
-                    result.append(task)
-        return result
-
-    def __doLaterFilter(self):
-        # Filter out all the tasks that have been removed like a mark and
-        # sweep garbage collector. Returns the number of tasks that have
-        # been removed Warning: this creates an entirely new doLaterList.
-        oldLen = len(self.__doLaterList)
-        # grab all the tasks being removed so we can remove them from the nameDict
-        # TODO: would be more efficient to remove from nameDict in task.remove()
-        removedTasks = [task for task in self.__doLaterList
-                        if task._removed]
-        self.__doLaterList = [task for task in self.__doLaterList #grab all tasks with name
-                              if not task._removed] #filter removed tasks
-        for task in removedTasks:
-            self.__removeTaskFromNameDict(task)
-        # Re heapify to maintain ordering after filter
-        heapify(self.__doLaterList)
-        newLen = len(self.__doLaterList)
-        return oldLen - newLen
-        
-    def __getNextDoLaterTime(self):
-        if self.__doLaterList:                        
-            dl = self.__doLaterList[0]
-            return dl.wakeTime
-        return -1;
-                       
-
-    def __doLaterProcessor(self, task):
-        # Removing the tasks during the for loop is a bad idea
-        # Instead we just flag them as removed
-        # Later, somebody else cleans them out
-        currentTime = self.__getTime()
-        while self.__doLaterList:
-            # Check the first one on the list to see if it is ready
-            dl = self.__doLaterList[0]
-            if dl._removed:
-                # Get rid of this task forever
-                heappop(self.__doLaterList)
-                continue
-            # If the time now is less than the start of the doLater + delay
-            # then we are not ready yet, continue to next one
-            elif currentTime < dl.wakeTime:
-                # Since the list is sorted, the first one we get to, that
-                # is not ready to go, we can return
-                break
-            else:
-                # Take it off the doLaterList, set its time, and make
-                # it pending
-                heappop(self.__doLaterList)
-                dl.setStartTimeFrame(self.currentTime, self.currentFrame)
-                self.__addPendingTask(dl)
-                continue
-            
-        # Every nth pass, let's clean out the list of removed tasks
-        # This is basically a mark and sweep garbage collection of doLaters
-        if ((task.frame % self.doLaterCleanupCounter) == 0):
-            numRemoved = self.__doLaterFilter()
-            # TaskManager.notify.debug("filtered %s removed doLaters" % numRemoved)
-        return cont
-
-    def doMethodLater(self, delayTime, funcOrTask, name, extraArgs = None,
-                      sort = None, priority = None, taskChain = None,
-                      uponDeath = None, appendTask = False, owner = None):
-        if delayTime < 0:
-            assert self.notify.warning('doMethodLater: added task: %s with negative delay: %s' % (name, delayTime))
-        if isinstance(funcOrTask, Task):
-            task = funcOrTask
-        elif callable(funcOrTask):
-            task = Task(funcOrTask, sort)
-        else:
-            self.notify.error('doMethodLater: Tried to add a task that was not a Task or a func')
-        assert isinstance(name, str), 'Name must be a string type'
-
-        # For historical reasons, if priority is specified but not
-        # sort, it really means sort.
-        if priority is not None and sort is None:
-            sort = priority
-
-        task.setSort(sort or 0)
-        task.name = name
-        task.owner = owner
-        if extraArgs == None:
-            extraArgs = []
-            appendTask = True
-
-        # if told to, append the task object to the extra args list so the
-        # method called will be able to access any properties on the task
-        if appendTask:
-            extraArgs.append(task)
-          
-        task.extraArgs = extraArgs
-        if uponDeath:
-            task.uponDeath = uponDeath
-
-        # TaskManager.notify.debug('spawning doLater: %s' % (task))
-        # Add this task to the nameDict
-        nameList = self.nameDict.get(name)
-        if nameList:
-            nameList.append(task)
-        else:
-            self.nameDict[name] = [task]
-        currentTime = self.__getTime()
-        # Cache the time we should wake up for easier sorting
-        task.delayTime = delayTime
-        task.wakeTime = currentTime + delayTime
-        # Push this onto the doLaterList. The heap maintains the sorting.
-        heappush(self.__doLaterList, task)
-
-        # Alert the world, a new task is born!
-        #messenger.send('TaskManager-spawnDoLater', sentArgs = [task])
-
-        if task.owner:
-            task.owner._addTask(task)
-        return task
-
-    def add(self, funcOrTask, name, sort = None, extraArgs = None,
-            priority = None, uponDeath = None, appendTask = False,
-            taskChain = None, owner = None):
-        
-        """
-        Add a new task to the taskMgr.
-        You can add a Task object or a method that takes one argument.
-        """
-
-        # For historical reasons, if priority is specified but not
-        # sort, it really means sort.
-        if priority is not None and sort is None:
-            sort = priority
-
-        # TaskManager.notify.debug('add: %s' % (name))
-        if isinstance(funcOrTask, Task):
-            task = funcOrTask
-        elif callable(funcOrTask):
-            task = Task(funcOrTask, sort)
-        else:
-            self.notify.error(
-                'add: Tried to add a task that was not a Task or a func')
-        assert isinstance(name, str), 'Name must be a string type'
-        task.setSort(sort or 0)
-        task.name = name
-        task.owner = owner
-        if extraArgs == None:
-            extraArgs = []
-            appendTask = True
-
-        # if told to, append the task object to the extra args list so the
-        # method called will be able to access any properties on the task
-        if appendTask:
-            extraArgs.append(task)
-
-        task.extraArgs = extraArgs
-        if uponDeath:
-            task.uponDeath = uponDeath
-        currentTime = self.__getTime()
-        task.setStartTimeFrame(currentTime, self.currentFrame)
-        nameList = self.nameDict.get(name)
-        if nameList:
-            nameList.append(task)
-        else:
-            self.nameDict[name] = [task]
-        # Put it on the list for the end of this frame
-        self.__addPendingTask(task)
-        if task.owner:
-            task.owner._addTask(task)
-        return task
-
-    def __addPendingTask(self, task):
-        # TaskManager.notify.debug('__addPendingTask: %s' % (task.name))
-        pri = task._sort
-        taskPriList = self.pendingTaskDict.get(pri)
-        if not taskPriList:
-            taskPriList = TaskSortList(pri)
-            self.pendingTaskDict[pri] = taskPriList
-        taskPriList.add(task)
-
-    def __addNewTask(self, task):
-        # The taskList is really an ordered list of TaskSortLists
-        # search back from the end of the list until we find a
-        # taskList with a lower sort, or we hit the start of the list
-        taskSort = task._sort
-        index = len(self.taskList) - 1
-        while (1):
-            if (index < 0):
-                newList = TaskSortList(taskSort)
-                newList.add(task)
-                # Add the new list to the beginning of the taskList
-                self.taskList.insert(0, newList)
-                break
-            taskListSort = self.taskList[index]._sort
-            if (taskListSort == taskSort):
-                self.taskList[index].add(task)
-                break
-            elif (taskListSort > taskSort):
-                index = index - 1
-            elif (taskListSort < taskSort):
-                # Time to insert
-                newList = TaskSortList(taskSort)
-                newList.add(task)
-                # Insert this new sort level
-                # If we are already at the end, just append it
-                if (index == len(self.taskList)-1):
-                    self.taskList.append(newList)
-                else:
-                    # Otherwise insert it
-                    self.taskList.insert(index+1, newList)
-                break
-
-        if __debug__:
-            if self.pStatsTasks and task.name != "igLoop":                
-                task.setupPStats()
-
-        # Alert the world, a new task is born!
-        messenger.send('TaskManager-spawnTask', sentArgs = [task])
-        return task
-
-    def remove(self, taskOrName):
-        if type(taskOrName) == type(''):
-            return self.__removeTasksNamed(taskOrName)
-        elif isinstance(taskOrName, Task):
-            return self.__removeTasksEqual(taskOrName)
-        else:
-            self.notify.error('remove takes a string or a Task')
-
-    def removeTasksMatching(self, taskPattern):
-        """removeTasksMatching(self, string taskPattern)
-
-        Removes tasks whose names match the pattern, which can include
-        standard shell globbing characters like *, ?, and [].
-        """
-        # TaskManager.notify.debug('removing tasks matching: ' + taskPattern)
-        num = 0
-        keyList = filter(
-            lambda key: fnmatch.fnmatchcase(key, taskPattern),
-            self.nameDict.keys())
-        for key in keyList:
-            num += self.__removeTasksNamed(key)
-        return num
-
-    def __removeTasksEqual(self, task):
-        # Remove this task from the nameDict (should be a short list)
-        if self.__removeTaskFromNameDict(task):
-            # TaskManager.notify.debug(
-            #    '__removeTasksEqual: removing task: %s' % (task))
-            # Flag the task for removal from the real list
-            task.remove()
-            task.finishTask()
-            return 1
-        else:
-            return 0
-
-    def __removeTasksNamed(self, taskName):
-        tasks = self.nameDict.get(taskName)
-        if not tasks:
-            return 0
-        # TaskManager.notify.debug(
-        #    '__removeTasksNamed: removing tasks named: %s' % (taskName))
-        for task in tasks:
-            # Flag for removal
-            task.remove()
-            task.finishTask()
-        # Record the number of tasks removed
-        num = len(tasks)
-        # Blow away the nameDict entry completely
-        del self.nameDict[taskName]
-        return num
-
-    def __removeTaskFromNameDict(self, task):
-        taskName = task.name
-        # If this is the only task with that name, remove the dict entry
-        tasksWithName = self.nameDict.get(taskName)
-        if tasksWithName:
-            if task in tasksWithName:
-                # If this is the last element, just remove the entry
-                # from the dictionary
-                if len(tasksWithName) == 1:
-                    del self.nameDict[taskName]
-                else:
-                    tasksWithName.remove(task)
-                return 1
-        return 0
-
-    def __executeTask(self, task):
-        task.setCurrentTimeFrame(self.currentTime, self.currentFrame)
-        
-        # cache reference to profile info here, self._taskProfileInfo might get swapped out
-        # by the task when it runs
-        profileInfo = self._taskProfileInfo
-        doProfile = (task.id == profileInfo.taskId)
-        # don't profile the same task twice in a row
-        doProfile = doProfile and (not profileInfo.profiled)
-
-        # Defer this import until we need it: some Python
-        # distributions don't provide the profile and pstats modules.
-        from direct.showbase.ProfileSession import ProfileSession
-        
-        if not self.taskTimerVerbose:
-            startTime = self.trueClock.getShortTime()
-            
-            # don't record timing info
-            if doProfile:
-                profileSession = ProfileSession('TASK_PROFILE:%s' % task.name,
-                                                Functor(task, *task.extraArgs))
-                ret = profileSession.run()
-                # set these values *after* profiling in case we're profiling the TaskProfiler
-                profileInfo.session = profileSession
-                profileInfo.profiled = True
-            else:
-                ret = task(*task.extraArgs)
-            endTime = self.trueClock.getShortTime()
-            
-            # Record the dt
-            dt = endTime - startTime
-            if doProfile:
-                # if we profiled, don't pollute the task's recorded duration
-                dt = task.avgDt
-            task.dt = dt
-
-        else:
-            # Run the task and check the return value
-            if task.pstats:
-                task.pstats.start()
-            startTime = self.trueClock.getShortTime()
-            if doProfile:
-                profileSession = ProfileSession('profiled-task-%s' % task.name,
-                                                Functor(task, *task.extraArgs))
-                ret = profileSession.run()
-                # set these values *after* profiling in case we're profiling the TaskProfiler
-                profileInfo.session = profileSession
-                profileInfo.profiled = True
-            else:
-                ret = task(*task.extraArgs)
-            endTime = self.trueClock.getShortTime()
-            if task.pstats:
-                task.pstats.stop()
-
-            # Record the dt
-            dt = endTime - startTime
-            if doProfile:
-                # if we profiled, don't pollute the task's recorded duration
-                dt = task.avgDt
-            task.dt = dt
-
-            # See if this is the new max
-            if dt > task.maxDt:
-                task.maxDt = dt
-
-            # Record the running total of all dts so we can compute an average
-            task.runningTotal = task.runningTotal + dt
-            if (task.frame > 0):
-                task.avgDt = (task.runningTotal / task.frame)
-            else:
-                task.avgDt = 0
-
-        # warn if the task took too long
-        if self.warnTaskDuration and self.globalClock:
-            avgFrameRate = self.globalClock.getAverageFrameRate()
-            if avgFrameRate > .00001:
-                avgFrameDur = (1. / avgFrameRate)
-                if dt >= (self.taskDurationWarningThreshold * avgFrameDur):
-                    assert TaskManager.notify.warning('frame %s: task %s ran for %.2f seconds, avg frame duration=%.2f seconds' % (
-                        globalClock.getFrameCount(), task.name, dt, avgFrameDur))
-            
-        return ret
-
-    def __repeatDoMethod(self, task):
-        """
-        Called when a task execute function returns Task.again because
-        it wants the task to execute again after the same or a modified
-        delay (set 'delayTime' on the task object to change the delay)
-        """
-        if (not task._removed):
-            # be sure to ask the globalClock for the current frame time
-            # rather than use a cached value; globalClock's frame time may
-            # have been synced since the start of this frame
-            currentTime = self.__getTime()
-            # Cache the time we should wake up for easier sorting
-            task.wakeTime = currentTime + task.delayTime
-            # Push this onto the doLaterList. The heap maintains the sorting.
-            heappush(self.__doLaterList, task)
-
-            # Alert the world, a new task is born!
-            #messenger.send('TaskManager-againDoLater', sentArgs = [task])
-
-    def __stepThroughList(self, taskPriList):
-        # Traverse the taskPriList with an iterator
-        i = 0
-        while (i < len(taskPriList)):
-            task = taskPriList[i]
-            # See if we are at the end of the real tasks
-            if task is None:
-                break
-            # See if this task has been removed in show code
-            if task._removed:
-                # assert TaskManager.notify.debug(
-                #    '__stepThroughList: task is flagged for removal %s' % (task))
-                # If it was removed in show code, it will need finishTask run
-                # If it was removed by the taskMgr, it will not, but that is ok
-                # because finishTask is safe to call twice
-                task.finishTask()
-                taskPriList.remove(i)
-                self.__removeTaskFromNameDict(task)
-                # Do not increment the iterator
-                continue
-            # Now actually execute the task
-            ret = self.__executeTask(task)
-            # See if the task is done
-            if (ret == cont):
-                # Leave it for next frame, its not done yet
-                pass
-            elif (ret == again):
-                # repeat doLater again after a delay
-                self.__repeatDoMethod(task)
-                taskPriList.remove(i)
-                continue
-            elif ((ret == done) or (ret == exit) or (ret == None)):
-                # assert TaskManager.notify.debug(
-                #    '__stepThroughList: task is finished %s' % (task))
-                # Remove the task
-                if not task._removed:
-                    # assert TaskManager.notify.debug(
-                    #    '__stepThroughList: task not removed %s' % (task))
-                    task.remove()
-                    # Note: Should not need to remove from doLaterList here
-                    # because this task is not in the doLaterList
-                    task.finishTask()
-                    self.__removeTaskFromNameDict(task)
-                else:
-                    # assert TaskManager.notify.debug(
-                    #    '__stepThroughList: task already removed %s' % (task))
-                    self.__removeTaskFromNameDict(task)
-                taskPriList.remove(i)
-                # Do not increment the iterator
-                continue
-            else:
-                raise StandardError, \
-                    "Task named %s did not return cont, exit, done, or None" % \
-                    (task.name,)
-            # Move to the next element
-            i += 1
-
-    def __addPendingTasksToTaskList(self):
-        # Now that we are all done, add any left over pendingTasks
-        # generated in sort levels lower or higher than where
-        # we were when we iterated
-        for taskList in self.pendingTaskDict.values():
-            for task in taskList:
-                if (task and not task._removed):
-                    # assert TaskManager.notify.debug(
-                    #    'step: moving %s from pending to taskList' % (task.name))
-                    self.__addNewTask(task)
-        self.pendingTaskDict.clear()
-
-    def getProfileSession(self, name=None):
-        # call to get a profile session that you can modify before passing to profileFrames()
-        if name is None:
-            name = 'taskMgrFrameProfile'
-
-        # 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)
-
-    def profileFrames(self, num=None, session=None, callback=None):
-        if num is None:
-            num = 1
-        if session is None:
-            session = self.getProfileSession()
-        # make sure the profile session doesn't get destroyed before we're done with it
-        session.acquire()
-        self._frameProfileQueue.push((num, session, callback))
-    
-
-    # in the event we want to do frame time managment.. this is the function to 
-    #  replace or overload..        
-    def  doYield(self , frameStartTime, nextScheuledTaksTime):
-          None
-          
-    def  doYieldExample(self , frameStartTime, nextScheuledTaksTime):
-        minFinTime = frameStartTime + self.MaxEpockSpeed
-        if nextScheuledTaksTime > 0 and nextScheuledTaksTime < minFinTime:
-            print ' Adjusting Time'
-            minFinTime = nextScheuledTaksTime;
-        delta = minFinTime - self.globalClock.getRealTime();
-        while(delta > 0.002):
-            print ' sleep %s'% (delta)
-            time.sleep(delta)           
-            delta = minFinTime - self.globalClock.getRealTime();
-    
-
-    def _doProfiledFrames(self, numFrames):
-        for i in xrange(numFrames):
-            result = self.step()
-        return result
-
-    def getProfileFrames(self):
-        return self._profileFrames.get()
-
-    def getProfileFramesSV(self):
-        return self._profileFrames
-
-    def setProfileFrames(self, profileFrames):
-        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()
-
-    def getProfileTasks(self):
-        return self._profileTasks.get()
-
-    def getProfileTasksSV(self):
-        return self._profileTasks
-
-    def setProfileTasks(self, profileTasks):
-        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()
-
-    def logTaskProfiles(self, name=None):
-        if self._taskProfiler:
-            self._taskProfiler.logProfiles(name)
-
-    def flushTaskProfiles(self, name=None):
-        if self._taskProfiler:
-            self._taskProfiler.flush(name)
-
-    def _setProfileTask(self, task):
-        if self._taskProfileInfo.session:
-            self._taskProfileInfo.session.release()
-            self._taskProfileInfo.session = None
-        self._taskProfileInfo = ScratchPad(
-            taskId = task.id,
-            profiled = False,
-            session = None,
-            )
-
-    def _hasProfiledDesignatedTask(self):
-        # have we run a profile of the designated task yet?
-        return self._taskProfileInfo.profiled
-
-    def _getLastTaskProfileSession(self):
-        return self._taskProfileInfo.session
-
-    def _getRandomTask(self):
-        numTasks = 0
-        for name in self.nameDict.iterkeys():
-            numTasks += len(self.nameDict[name])
-        numDoLaters = len(self.__doLaterList)
-        if random.random() < (numDoLaters / float(numTasks + numDoLaters)):
-            # grab a doLater that will most likely trigger in the next frame
-            tNow = globalClock.getFrameTime()
-            avgFrameRate = globalClock.getAverageFrameRate()
-            if avgFrameRate < .00001:
-                avgFrameDur = 0.
-            else:
-                avgFrameDur = (1. / globalClock.getAverageFrameRate())
-            tNext = tNow + avgFrameDur
-            # binary search to find doLaters that are likely to trigger on the next frame
-            curIndex = int(numDoLaters / 2)
-            rangeStart = 0
-            rangeEnd = numDoLaters
-            while True:
-                if tNext < self.__doLaterList[curIndex].wakeTime:
-                    rangeEnd = curIndex
-                else:
-                    rangeStart = curIndex
-                prevIndex = curIndex
-                curIndex = int((rangeStart + rangeEnd) / 2)
-                if curIndex == prevIndex:
-                    break
-            index = curIndex
-            task = self.__doLaterList[random.randrange(index+1)]
-        else:
-            # grab a task
-            name = random.choice(self.nameDict.keys())
-            task = random.choice(self.nameDict[name])
-        return task
-
-    def step(self):
-        # assert TaskManager.notify.debug('step: begin')
-        self.currentTime, self.currentFrame = self.__getTimeFrame()
-        startFrameTime = None
-        if self.globalClock:
-            startFrameTime = self.globalClock.getRealTime()
-            
-        # Replace keyboard interrupt handler during task list processing
-        # so we catch the keyboard interrupt but don't handle it until
-        # after task list processing is complete.
-        self.fKeyboardInterrupt = 0
-        self.interruptCount = 0
-        signal.signal(signal.SIGINT, self.keyboardInterruptHandler)
-
-        # Traverse the task list in order because it is in sort order
-        priIndex = 0
-        while priIndex < len(self.taskList):
-            taskPriList = self.taskList[priIndex]
-            pri = taskPriList._sort
-            # assert TaskManager.notify.debug(
-            #    'step: running through taskList at pri: %s, priIndex: %s' %
-            #    (pri, priIndex))
-            self.__stepThroughList(taskPriList)
-
-            # Now see if that generated any pending tasks for this taskPriList
-            pendingTasks = self.pendingTaskDict.get(pri)
-            while pendingTasks:
-                # assert TaskManager.notify.debug('step: running through pending tasks at pri: %s' % (pri))
-                # Remove them from the pendingTaskDict
-                del self.pendingTaskDict[pri]
-                # Execute them
-                self.__stepThroughList(pendingTasks)
-                # Add these to the real taskList
-                for task in pendingTasks:
-                    if (task and not task._removed):
-                        # assert TaskManager.notify.debug('step: moving %s from pending to taskList' % (task.name))
-                        self.__addNewTask(task)
-                # See if we generated any more for this pri level
-                pendingTasks = self.pendingTaskDict.get(pri)
-
-            # Any new tasks that were made pending should be converted
-            # to real tasks now in case they need to run this frame at a
-            # later sort level
-            self.__addPendingTasksToTaskList()
-
-            # Go to the next sort level
-            priIndex += 1
-
-        # Add new pending tasks
-        self.__addPendingTasksToTaskList()
-        
-        if startFrameTime:
-            #this is the spot for a Internal Yield Function
-            nextTaskTime = self.__getNextDoLaterTime()
-            self.doYield(startFrameTime,nextTaskTime)            
-        
-        # Restore default interrupt handler
-        signal.signal(signal.SIGINT, signal.default_int_handler)
-        if self.fKeyboardInterrupt:
-            raise KeyboardInterrupt
-
-
-    def run(self):
-        # do things that couldn't be done earlier because of import dependencies
-        if (not TaskManager._DidTests) and __debug__:
-            TaskManager._DidTests = True
-            self._runTests()
-
-        if not self._profileTasks:
-            if 'base' in __builtins__ or \
-               'simbase' in __builtins__:
-                from direct.fsm.StatePush import StateVar
-                self._profileTasks = StateVar(False)
-                self.setProfileTasks(getBase().config.GetBool('profile-task-spikes', 0))
-                self._profileFrames = StateVar(False)
-                self.setProfileFrames(ConfigVariableBool('profile-frames', 0).getValue())
-
-        # Set the clock to have last frame's time in case we were
-        # Paused at the prompt for a long time
-        if self.globalClock:
-            t = self.globalClock.getFrameTime()
-            timeDelta = t - globalClock.getRealTime()
-            self.globalClock.setRealTime(t)
-            messenger.send("resetClock", [timeDelta])
-
-        if self.resumeFunc != None:
-            self.resumeFunc()
-
-        if self.stepping:
-            self.step()
-        else:
-            self.running = 1
-            while self.running:
-                try:
-                    if len(self._frameProfileQueue):
-                        numFrames, session, callback = self._frameProfileQueue.pop()
-                        def _profileFunc(numFrames=numFrames):
-                            self._doProfiledFrames(numFrames)
-                        session.setFunc(_profileFunc)
-                        session.run()
-                        _profileFunc = None
-                        if callback:
-                            callback()
-                        session.release()
-                    else:
-                        self.step()
-                except KeyboardInterrupt:
-                    self.stop()
-                except IOError, ioError:
-                    code, message = self._unpackIOError(ioError)
-                    # Since upgrading to Python 2.4.1, pausing the execution
-                    # often gives this IOError during the sleep function:
-                    #     IOError: [Errno 4] Interrupted function call
-                    # So, let's just handle that specific exception and stop.
-                    # All other IOErrors should still get raised.
-                    # Only problem: legit IOError 4s will be obfuscated.
-                    if code == 4:
-                        self.stop()
-                    else:
-                        raise
-                except Exception, e:
-                    if self.extendedExceptions:
-                        self.stop()
-                        print_exc_plus()
-                    else:
-                        if (ExceptionVarDump.wantVariableDump and
-                            ExceptionVarDump.dumpOnExceptionInit):
-                            ExceptionVarDump._varDump__print(e)
-                        raise
-                except:
-                    if self.extendedExceptions:
-                        self.stop()
-                        print_exc_plus()
-                    else:
-                        raise
-
-    def _unpackIOError(self, ioError):
-        # IOError unpack from http://www.python.org/doc/essays/stdexceptions/
-        # this needs to be in its own method, exceptions that occur inside
-        # a nested try block are not caught by the inner try block's except
-        try:
-            (code, message) = ioError
-        except:
-            code = 0
-            message = ioError
-        return code, message
-
-    def stop(self):
-        # Set a flag so we will stop before beginning next frame
-        self.running = 0
-
-    def __tryReplaceTaskMethod(self, task, oldMethod, newFunction):
-        if (task is None) or task._removed:
-            return 0
-        method = task.__call__
-        if (type(method) == types.MethodType):
-            function = method.im_func
-        else:
-            function = method
-        #print ('function: ' + `function` + '\n' +
-        #       'method: ' + `method` + '\n' +
-        #       'oldMethod: ' + `oldMethod` + '\n' +
-        #       'newFunction: ' + `newFunction` + '\n')
-        if (function == oldMethod):
-            import new
-            newMethod = new.instancemethod(newFunction,
-                                           method.im_self,
-                                           method.im_class)
-            task.__call__ = newMethod
-            # Found a match
-            return 1
-        return 0
-
-    def replaceMethod(self, oldMethod, newFunction):
-        numFound = 0
-        # Look through the regular tasks
-        for taskPriList in self.taskList:
-            for task in taskPriList:
-                if task:
-                    numFound += self.__tryReplaceTaskMethod(task, oldMethod, newFunction)
-        # Look through the pending tasks
-        for pri, taskList in self.pendingTaskDict.items():
-            for task in taskList:
-                if task:
-                    numFound += self.__tryReplaceTaskMethod(task, oldMethod, newFunction)
-        # Look through the doLaters
-        for task in self.__doLaterList:
-            if task:
-                numFound += self.__tryReplaceTaskMethod(task, oldMethod, newFunction)
-        return numFound
-
-    def __repr__(self):
-        taskNameWidth = 32
-        dtWidth = 10
-        sortWidth = 10
-        totalDt = 0
-        totalAvgDt = 0
-        str = "The taskMgr is handling:\n"
-        str += ('taskList'.ljust(taskNameWidth)
-               + 'dt(ms)'.rjust(dtWidth)
-               + 'avg'.rjust(dtWidth)
-               + 'max'.rjust(dtWidth)
-               + 'sort'.rjust(sortWidth)
-               + '\n')
-        str += '-------------------------------------------------------------------------\n'
-        dtfmt = '%%%d.2f' % (dtWidth)
-        for taskPriList in self.taskList:
-            sort = `taskPriList._sort`
-            for task in taskPriList:
-                if task is None:
-                    break
-                if task._removed:
-                    taskName = '(R)' + task.name
-                else:
-                    taskName = task.name
-                if self.taskTimerVerbose:
-                    totalDt = totalDt + task.dt
-                    totalAvgDt = totalAvgDt + task.avgDt
-                    str += (taskName.ljust(taskNameWidth)
-                            + dtfmt % (task.dt*1000)
-                            + dtfmt % (task.avgDt*1000)
-                            + dtfmt % (task.maxDt*1000)
-                            + sort.rjust(sortWidth)
-                            + '\n')
-                else:
-                    str += (task.name.ljust(taskNameWidth)
-                            + '----'.rjust(dtWidth)
-                            + '----'.rjust(dtWidth)
-                            + '----'.rjust(dtWidth)
-                            + sort.rjust(sortWidth)
-                            + '\n')
-        str += '-------------------------------------------------------------------------\n'
-        str += 'pendingTasks\n'
-        str += '-------------------------------------------------------------------------\n'
-        for pri, taskList in self.pendingTaskDict.items():
-            for task in taskList:
-                if task._removed:
-                    taskName = '(PR)' + task.name
-                else:
-                    taskName = '(P)' + task.name
-                if (self.taskTimerVerbose):
-                    str += ('  ' + taskName.ljust(taskNameWidth-2)
-                            +  dtfmt % (pri)
-                            + '\n')
-                else:
-                    str += ('  ' + taskName.ljust(taskNameWidth-2)
-                            +  '----'.rjust(dtWidth)
-                            + '\n')
-        str += '-------------------------------------------------------------------------\n'
-        if (self.taskTimerVerbose):
-            str += ('total'.ljust(taskNameWidth)
-                    + dtfmt % (totalDt*1000)
-                    + dtfmt % (totalAvgDt*1000)
-                    + '\n')
-        else:
-            str += ('total'.ljust(taskNameWidth)
-                    + '----'.rjust(dtWidth)
-                    + '----'.rjust(dtWidth)
-                    + '\n')
-        str += '-------------------------------------------------------------------------\n'
-        str += ('doLaterList'.ljust(taskNameWidth)
-               + 'waitTime(s)'.rjust(dtWidth)
-               + '\n')
-        str += '-------------------------------------------------------------------------\n'
-        # When we print, show the doLaterList in actual sorted order.
-        # The sort heap is not actually in order - it is a tree
-        # Make a shallow copy so we can sort it
-        sortedDoLaterList = self.__doLaterList[:]
-        sortedDoLaterList.sort(lambda a, b: cmp(a.wakeTime, b.wakeTime))
-        sortedDoLaterList.reverse()
-        for task in sortedDoLaterList:
-            remainingTime = ((task.wakeTime) - self.currentTime)
-            if task._removed:
-                taskName = '(R)' + task.name
-            else:
-                taskName = task.name
-            str += ('  ' + taskName.ljust(taskNameWidth-2)
-                    +  dtfmt % (remainingTime)
-                    + '\n')
-        str += '-------------------------------------------------------------------------\n'
-        str += "End of taskMgr info\n"
-        return str
-
-    def getTasks(self):
-        # returns list of all tasks in arbitrary order
-        tasks = []
-        for taskPriList in self.taskList:
-            for task in taskPriList:
-                if task is not None and not task._removed:
-                    tasks.append(task)
-        for pri, taskList in self.pendingTaskDict.iteritems():
-            for task in taskList:
-                if not task._removed:
-                    tasks.append(task)
-        return tasks
-
-    def getDoLaters(self):
-        # returns list of all doLaters in arbitrary order
-        return [doLater for doLater in self.__doLaterList
-                if not doLater._removed]
-
-    def resetStats(self):
-        # WARNING: this screws up your do-later timings
-        if self.taskTimerVerbose:
-            for task in self.taskList:
-                task.dt = 0
-                task.avgDt = 0
-                task.maxDt = 0
-                task.runningTotal = 0
-                task.setStartTimeFrame(self.currentTime, self.currentFrame)
-
-    def popupControls(self):
-        from direct.tkpanels import TaskManagerPanel
-        return TaskManagerPanel.TaskManagerPanel(self)
-
-    def __getTimeFrame(self):
-        # WARNING: If you are testing tasks without an igLoop,
-        # you must manually tick the clock
-        # Ask for the time last frame
-        if self.globalClock:
-            return self.globalClock.getFrameTime(), self.globalClock.getFrameCount()
-        # OK, we don't have a globalClock yet.  This is therefore
-        # running before the first frame.
-        return self.trueClock.getShortTime(), 0
-
-    def __getTime(self):
-        if self.globalClock:
-            return self.globalClock.getFrameTime()
-        return self.trueClock.getShortTime()
-
-    if __debug__:
-        # to catch memory leaks during the tests at the bottom of the file
-        def _startTrackingMemLeaks(self):
-            self._memUsage = ScratchPad()
-            mu = self._memUsage
-            mu.lenTaskList = len(self.taskList)
-            mu.lenPendingTaskDict = len(self.pendingTaskDict)
-            mu.lenDoLaterList = len(self.__doLaterList)
-            mu.lenNameDict = len(self.nameDict)
-
-        def _stopTrackingMemLeaks(self):
-            self._memUsage.destroy()
-            del self._memUsage
-
-        def _checkMemLeaks(self):
-            # flush removed doLaters
-            self.__doLaterFilter()
-            # give the mgr a chance to clear out removed tasks
-            self.step()
-            mu = self._memUsage
-            # the task list looks like it grows and never shrinks, replacing finished
-            # tasks with 'None' in the TaskSortLists.
-            # TODO: look at reducing memory usage here--clear out excess at the end of every frame?
-            #assert mu.lenTaskList == len(self.taskList)
-            assert mu.lenPendingTaskDict == len(self.pendingTaskDict)
-            assert mu.lenDoLaterList == len(self.__doLaterList)
-            assert mu.lenNameDict == len(self.nameDict)
-
-    def startOsd(self):
-        self.add(self.doOsd, 'taskMgr.doOsd')
-        self._osdEnabled = None
-    def osdEnabled(self):
-        return hasattr(self, '_osdEnabled')
-    def stopOsd(self):
-        onScreenDebug.removeAllWithPrefix(TaskManager.OsdPrefix)
-        self.remove('taskMgr.doOsd')
-        del self._osdEnabled
-    def doOsd(self, task):
-        if not onScreenDebug.enabled:
-            return
-        prefix = TaskManager.OsdPrefix
-        onScreenDebug.removeAllWithPrefix(prefix)
-        taskNameWidth = 32
-        dtWidth = 10
-        sortWidth = 10
-        totalDt = 0
-        totalAvgDt = 0
-        i = 0
-        onScreenDebug.add(
-            ('%s%02i.taskList' % (prefix, i)).ljust(taskNameWidth),
-            '%s %s %s %s' % (
-            'dt(ms)'.rjust(dtWidth),
-            'avg'.rjust(dtWidth),
-            'max'.rjust(dtWidth),
-            'sort'.rjust(sortWidth),))
-        i += 1
-        for taskPriList in self.taskList:
-            sort = `taskPriList._sort`
-            for task in taskPriList:
-                if task is None:
-                    break
-                if task._removed:
-                    taskName = '(R)' + task.name
-                else:
-                    taskName = task.name
-                totalDt = totalDt + task.dt
-                totalAvgDt = totalAvgDt + task.avgDt
-                onScreenDebug.add(
-                    ('%s%02i.%s' % (prefix, i, task.name)).ljust(taskNameWidth),
-                    '%s %s %s %s' % (
-                    dtfmt % (task.dt*1000),
-                    dtfmt % (task.avgDt*1000),
-                    dtfmt % (task.maxDt*1000),
-                    sort.rjust(sortWidth)))
-                i += 1
-        onScreenDebug.add(('%s%02i.total' % (prefix, i)).ljust(taskNameWidth),
-                          '%s %s' % (
-            dtfmt % (totalDt*1000),
-            dtfmt % (totalAvgDt*1000),))
-        return cont
-
-    def _runTests(self):
-        if __debug__:
-            tm = TaskManager()
-            # looks like nothing runs on the first frame...?
-            # step to get past the first frame
-            tm.step()
-
-            # check for memory leaks after every test
-            tm._startTrackingMemLeaks()
-            tm._checkMemLeaks()
-
-            # run-once task
-            l = []
-            def _testDone(task, l=l):
-                l.append(None)
-                return task.done
-            tm.add(_testDone, 'testDone')
-            tm.step()
-            assert len(l) == 1
-            tm.step()
-            assert len(l) == 1
-            _testDone = None
-            tm._checkMemLeaks()
-
-            # remove by name
-            def _testRemoveByName(task):
-                return task.done
-            tm.add(_testRemoveByName, 'testRemoveByName')
-            assert tm.remove('testRemoveByName') == 1
-            assert tm.remove('testRemoveByName') == 0
-            _testRemoveByName = None
-            tm._checkMemLeaks()
-
-            # duplicate named tasks
-            def _testDupNamedTasks(task):
-                return task.done
-            tm.add(_testDupNamedTasks, 'testDupNamedTasks')
-            tm.add(_testDupNamedTasks, 'testDupNamedTasks')
-            assert tm.remove('testRemoveByName') == 0
-            _testDupNamedTasks = None
-            tm._checkMemLeaks()
-
-            # continued task
-            l = []
-            def _testCont(task, l = l):
-                l.append(None)
-                return task.cont
-            tm.add(_testCont, 'testCont')
-            tm.step()
-            assert len(l) == 1
-            tm.step()
-            assert len(l) == 2
-            tm.remove('testCont')
-            _testCont = None
-            tm._checkMemLeaks()
-
-            # continue until done task
-            l = []
-            def _testContDone(task, l = l):
-                l.append(None)
-                if len(l) >= 2:
-                    return task.done
-                else:
-                    return task.cont
-            tm.add(_testContDone, 'testContDone')
-            tm.step()
-            assert len(l) == 1
-            tm.step()
-            assert len(l) == 2
-            tm.step()
-            assert len(l) == 2
-            assert not tm.hasTaskNamed('testContDone')
-            _testContDone = None
-            tm._checkMemLeaks()
-
-            # hasTaskNamed
-            def _testHasTaskNamed(task):
-                return task.done
-            tm.add(_testHasTaskNamed, 'testHasTaskNamed')
-            assert tm.hasTaskNamed('testHasTaskNamed')
-            tm.step()
-            assert not tm.hasTaskNamed('testHasTaskNamed')
-            _testHasTaskNamed = None
-            tm._checkMemLeaks()
-
-            # task sort
-            l = []
-            def _testPri1(task, l = l):
-                l.append(1)
-                return task.cont
-            def _testPri2(task, l = l):
-                l.append(2)
-                return task.cont
-            tm.add(_testPri1, 'testPri1', sort = 1)
-            tm.add(_testPri2, 'testPri2', sort = 2)
-            tm.step()
-            assert len(l) == 2
-            assert l == [1, 2,]
-            tm.step()
-            assert len(l) == 4
-            assert l == [1, 2, 1, 2,]
-            tm.remove('testPri1')
-            tm.remove('testPri2')
-            _testPri1 = None
-            _testPri2 = None
-            tm._checkMemLeaks()
-
-            # task extraArgs
-            l = []
-            def _testExtraArgs(arg1, arg2, l=l):
-                l.extend([arg1, arg2,])
-                return done
-            tm.add(_testExtraArgs, 'testExtraArgs', extraArgs=[4,5])
-            tm.step()
-            assert len(l) == 2
-            assert l == [4, 5,]
-            _testExtraArgs = None
-            tm._checkMemLeaks()
-
-            # task appendTask
-            l = []
-            def _testAppendTask(arg1, arg2, task, l=l):
-                l.extend([arg1, arg2,])
-                return task.done
-            tm.add(_testAppendTask, '_testAppendTask', extraArgs=[4,5], appendTask=True)
-            tm.step()
-            assert len(l) == 2
-            assert l == [4, 5,]
-            _testAppendTask = None
-            tm._checkMemLeaks()
-
-            # task uponDeath
-            l = []
-            def _uponDeathFunc(task, l=l):
-                l.append(task.name)
-            def _testUponDeath(task):
-                return done
-            tm.add(_testUponDeath, 'testUponDeath', uponDeath=_uponDeathFunc)
-            tm.step()
-            assert len(l) == 1
-            assert l == ['testUponDeath']
-            _testUponDeath = None
-            _uponDeathFunc = None
-            tm._checkMemLeaks()
-
-            # task owner
-            class _TaskOwner:
-                def _addTask(self, task):
-                    self.addedTaskName = task.name
-                def _clearTask(self, task):
-                    self.clearedTaskName = task.name
-            to = _TaskOwner()
-            l = []
-            def _testOwner(task):
-                return done
-            tm.add(_testOwner, 'testOwner', owner=to)
-            tm.step()
-            assert getattr(to, 'addedTaskName', None) == 'testOwner'
-            assert getattr(to, 'clearedTaskName', None) == 'testOwner'
-            _testOwner = None
-            del to
-            _TaskOwner = None
-            tm._checkMemLeaks()
-
-
-            doLaterTests = [0,]
-
-            # doLater
-            l = []
-            def _testDoLater1(task, l=l):
-                l.append(1)
-            def _testDoLater2(task, l=l):
-                l.append(2)
-            def _monitorDoLater(task, tm=tm, l=l, doLaterTests=doLaterTests):
-                if task.time > .03:
-                    assert l == [1, 2,]
-                    doLaterTests[0] -= 1
-                    return task.done
-                return task.cont
-            tm.doMethodLater(.01, _testDoLater1, 'testDoLater1')
-            tm.doMethodLater(.02, _testDoLater2, 'testDoLater2')
-            doLaterTests[0] += 1
-            # make sure we run this task after the doLaters if they all occur on the same frame
-            tm.add(_monitorDoLater, 'monitorDoLater', sort=10)
-            _testDoLater1 = None
-            _testDoLater2 = None
-            _monitorDoLater = None
-            # don't check until all the doLaters are finished
-            #tm._checkMemLeaks()
-
-            # doLater sort
-            l = []
-            def _testDoLaterPri1(task, l=l):
-                l.append(1)
-            def _testDoLaterPri2(task, l=l):
-                l.append(2)
-            def _monitorDoLaterPri(task, tm=tm, l=l, doLaterTests=doLaterTests):
-                if task.time > .02:
-                    assert l == [1, 2,]
-                    doLaterTests[0] -= 1
-                    return task.done
-                return task.cont
-            tm.doMethodLater(.01, _testDoLaterPri1, 'testDoLaterPri1', sort=1)
-            tm.doMethodLater(.01, _testDoLaterPri2, 'testDoLaterPri2', sort=2)
-            doLaterTests[0] += 1
-            # make sure we run this task after the doLaters if they all occur on the same frame
-            tm.add(_monitorDoLaterPri, 'monitorDoLaterPri', sort=10)
-            _testDoLaterPri1 = None
-            _testDoLaterPri2 = None
-            _monitorDoLaterPri = None
-            # don't check until all the doLaters are finished
-            #tm._checkMemLeaks()
-
-            # doLater extraArgs
-            l = []
-            def _testDoLaterExtraArgs(arg1, l=l):
-                l.append(arg1)
-            def _monitorDoLaterExtraArgs(task, tm=tm, l=l, doLaterTests=doLaterTests):
-                if task.time > .02:
-                    assert l == [3,]
-                    doLaterTests[0] -= 1
-                    return task.done
-                return task.cont
-            tm.doMethodLater(.01, _testDoLaterExtraArgs, 'testDoLaterExtraArgs', extraArgs=[3,])
-            doLaterTests[0] += 1
-            # make sure we run this task after the doLaters if they all occur on the same frame
-            tm.add(_monitorDoLaterExtraArgs, 'monitorDoLaterExtraArgs', sort=10)
-            _testDoLaterExtraArgs = None
-            _monitorDoLaterExtraArgs = None
-            # don't check until all the doLaters are finished
-            #tm._checkMemLeaks()
-
-            # doLater appendTask
-            l = []
-            def _testDoLaterAppendTask(arg1, task, l=l):
-                assert task.name == 'testDoLaterAppendTask'
-                l.append(arg1)
-            def _monitorDoLaterAppendTask(task, tm=tm, l=l, doLaterTests=doLaterTests):
-                if task.time > .02:
-                    assert l == [4,]
-                    doLaterTests[0] -= 1
-                    return task.done
-                return task.cont
-            tm.doMethodLater(.01, _testDoLaterAppendTask, 'testDoLaterAppendTask',
-                             extraArgs=[4,], appendTask=True)
-            doLaterTests[0] += 1
-            # make sure we run this task after the doLaters if they all occur on the same frame
-            tm.add(_monitorDoLaterAppendTask, 'monitorDoLaterAppendTask', sort=10)
-            _testDoLaterAppendTask = None
-            _monitorDoLaterAppendTask = None
-            # don't check until all the doLaters are finished
-            #tm._checkMemLeaks()
-
-            # doLater uponDeath
-            l = []
-            def _testUponDeathFunc(task, l=l):
-                assert task.name == 'testDoLaterUponDeath'
-                l.append(10)
-            def _testDoLaterUponDeath(arg1, l=l):
-                return done
-            def _monitorDoLaterUponDeath(task, tm=tm, l=l, doLaterTests=doLaterTests):
-                if task.time > .02:
-                    assert l == [10,]
-                    doLaterTests[0] -= 1
-                    return task.done
-                return task.cont
-            tm.doMethodLater(.01, _testDoLaterUponDeath, 'testDoLaterUponDeath',
-                             uponDeath=_testUponDeathFunc)
-            doLaterTests[0] += 1
-            # make sure we run this task after the doLaters if they all occur on the same frame
-            tm.add(_monitorDoLaterUponDeath, 'monitorDoLaterUponDeath', sort=10)
-            _testUponDeathFunc = None
-            _testDoLaterUponDeath = None
-            _monitorDoLaterUponDeath = None
-            # don't check until all the doLaters are finished
-            #tm._checkMemLeaks()
-
-            # doLater owner
-            class _DoLaterOwner:
-                def _addTask(self, task):
-                    self.addedTaskName = task.name
-                def _clearTask(self, task):
-                    self.clearedTaskName = task.name
-            doLaterOwner = _DoLaterOwner()
-            l = []
-            def _testDoLaterOwner(l=l):
-                pass
-            def _monitorDoLaterOwner(task, tm=tm, l=l, doLaterOwner=doLaterOwner,
-                                     doLaterTests=doLaterTests):
-                if task.time > .02:
-                    assert getattr(doLaterOwner, 'addedTaskName', None) == 'testDoLaterOwner'
-                    assert getattr(doLaterOwner, 'clearedTaskName', None) == 'testDoLaterOwner'
-                    doLaterTests[0] -= 1
-                    return task.done
-                return task.cont
-            tm.doMethodLater(.01, _testDoLaterOwner, 'testDoLaterOwner',
-                             owner=doLaterOwner)
-            doLaterTests[0] += 1
-            # make sure we run this task after the doLaters if they all occur on the same frame
-            tm.add(_monitorDoLaterOwner, 'monitorDoLaterOwner', sort=10)
-            _testDoLaterOwner = None
-            _monitorDoLaterOwner = None
-            del doLaterOwner
-            _DoLaterOwner = None
-            # don't check until all the doLaters are finished
-            #tm._checkMemLeaks()
-
-            # run the doLater tests
-            while doLaterTests[0] > 0:
-                tm.step()
-            del doLaterTests
-            tm._checkMemLeaks()
-
-            # getTasks
-            def _testGetTasks(task):
-                return task.cont
-            # the doLaterProcessor is always running
-            assert len(tm.getTasks()) == 1
-            tm.add(_testGetTasks, 'testGetTasks1')
-            assert len(tm.getTasks()) == 2
-            assert (tm.getTasks()[0].name == 'testGetTasks1' or
-                    tm.getTasks()[1].name == 'testGetTasks1')
-            tm.add(_testGetTasks, 'testGetTasks2')
-            tm.add(_testGetTasks, 'testGetTasks3')
-            assert len(tm.getTasks()) == 4
-            tm.remove('testGetTasks2')
-            assert len(tm.getTasks()) == 3
-            tm.remove('testGetTasks1')
-            tm.remove('testGetTasks3')
-            assert len(tm.getTasks()) == 1
-            _testGetTasks = None
-            tm._checkMemLeaks()
-
-            # getDoLaters
-            def _testGetDoLaters():
-                pass
-            # the doLaterProcessor is always running
-            assert len(tm.getDoLaters()) == 0
-            tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater1')
-            assert len(tm.getDoLaters()) == 1
-            assert tm.getDoLaters()[0].name == 'testDoLater1'
-            tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater2')
-            tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater3')
-            assert len(tm.getDoLaters()) == 3
-            tm.remove('testDoLater2')
-            assert len(tm.getDoLaters()) == 2
-            tm.remove('testDoLater1')
-            tm.remove('testDoLater3')
-            assert len(tm.getDoLaters()) == 0
-            _testGetDoLaters = None
-            tm._checkMemLeaks()
-
-            # duplicate named doLaters removed via taskMgr.remove
-            def _testDupNameDoLaters():
-                pass
-            # the doLaterProcessor is always running
-            tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
-            tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
-            assert len(tm.getDoLaters()) == 2
-            tm.remove('testDupNameDoLater')
-            assert len(tm.getDoLaters()) == 0
-            _testDupNameDoLaters = None
-            tm._checkMemLeaks()
-
-            # duplicate named doLaters removed via remove()
-            def _testDupNameDoLatersRemove():
-                pass
-            # the doLaterProcessor is always running
-            dl1 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
-            dl2 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
-            assert len(tm.getDoLaters()) == 2
-            dl2.remove()
-            assert len(tm.getDoLaters()) == 1
-            dl1.remove()
-            assert len(tm.getDoLaters()) == 0
-            _testDupNameDoLatersRemove = None
-            # nameDict etc. isn't cleared out right away with task.remove()
-            tm._checkMemLeaks()
-
-            # getTasksNamed
-            def _testGetTasksNamed(task):
-                return task.cont
-            assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
-            tm.add(_testGetTasksNamed, 'testGetTasksNamed')
-            assert len(tm.getTasksNamed('testGetTasksNamed')) == 1
-            assert tm.getTasksNamed('testGetTasksNamed')[0].name == 'testGetTasksNamed'
-            tm.add(_testGetTasksNamed, 'testGetTasksNamed')
-            tm.add(_testGetTasksNamed, 'testGetTasksNamed')
-            assert len(tm.getTasksNamed('testGetTasksNamed')) == 3
-            tm.remove('testGetTasksNamed')
-            assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
-            _testGetTasksNamed = None
-            tm._checkMemLeaks()
-
-            # removeTasksMatching
-            def _testRemoveTasksMatching(task):
-                return task.cont
-            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching')
-            assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 1
-            tm.removeTasksMatching('testRemoveTasksMatching')
-            assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 0
-            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1')
-            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2')
-            assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 1
-            assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 1
-            tm.removeTasksMatching('testRemoveTasksMatching*')
-            assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 0
-            assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 0
-            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1a')
-            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2a')
-            assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 1
-            assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 1
-            tm.removeTasksMatching('testRemoveTasksMatching?a')
-            assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 0
-            assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 0
-            _testRemoveTasksMatching = None
-            tm._checkMemLeaks()
-
-            # create Task object and add to mgr
-            l = []
-            def _testTaskObj(task, l=l):
-                l.append(None)
-                return task.cont
-            t = Task(_testTaskObj)
-            tm.add(t, 'testTaskObj')
-            tm.step()
-            assert len(l) == 1
-            tm.step()
-            assert len(l) == 2
-            tm.remove('testTaskObj')
-            tm.step()
-            assert len(l) == 2
-            _testTaskObj = None
-            tm._checkMemLeaks()
-
-            # remove Task via task.remove()
-            l = []
-            def _testTaskObjRemove(task, l=l):
-                l.append(None)
-                return task.cont
-            t = Task(_testTaskObjRemove)
-            tm.add(t, 'testTaskObjRemove')
-            tm.step()
-            assert len(l) == 1
-            tm.step()
-            assert len(l) == 2
-            t.remove()
-            tm.step()
-            assert len(l) == 2
-            del t
-            _testTaskObjRemove = None
-            tm._checkMemLeaks()
-
-            """
-            # this test fails, and it's not clear what the correct behavior should be.
-            # sort passed to Task.__init__ is always overridden by taskMgr.add()
-            # even if no sort is specified, and calling Task.setSort() has no
-            # effect on the taskMgr's behavior.
-            # set/get Task sort
-            l = []
-            def _testTaskObjSort(arg, task, l=l):
-                l.append(arg)
-                return task.cont
-            t1 = Task(_testTaskObjSort, sort=1)
-            t2 = Task(_testTaskObjSort, sort=2)
-            tm.add(t1, 'testTaskObjSort1', extraArgs=['a',], appendTask=True)
-            tm.add(t2, 'testTaskObjSort2', extraArgs=['b',], appendTask=True)
-            tm.step()
-            assert len(l) == 2
-            assert l == ['a', 'b']
-            assert t1.getSort() == 1
-            assert t2.getSort() == 2
-            t1.setSort(3)
-            assert t1.getSort() == 3
-            tm.step()
-            assert len(l) == 4
-            assert l == ['a', 'b', 'b', 'a',]
-            t1.remove()
-            t2.remove()
-            tm.step()
-            assert len(l) == 4
-            del t1
-            del t2
-            _testTaskObjSort = None
-            tm._checkMemLeaks()
-            """
-
-            del l
-            tm.destroy()
-            del tm
-
-
-# These constants are moved to the top level of the module,
-# to make it easier for legacy code.  In general though, putting
-# constants at the top level of a module is deprecated.
-
-exit  = Task.exit
-done  = Task.done
-cont  = Task.cont
-again = Task.again
-
-
-if __debug__:
-    pass # 'if __debug__' is hint for CVS diff output
-
-"""
-
-import Task
-
-def goo(task):
-    print 'goo'
-    return Task.done
-
-def bar(task):
-    print 'bar'
-    taskMgr.add(goo, 'goo')
-    return Task.done
-
-def foo(task):
-    print 'foo'
-    taskMgr.add(bar, 'bar')
-    return Task.done
-
-taskMgr.add(foo, 'foo')
-"""
-