瀏覽代碼

task: Annotate core functions (#1548)

WMOkiishi 2 年之前
父節點
當前提交
098fe634a5
共有 3 個文件被更改,包括 249 次插入125 次删除
  1. 1 1
      direct/src/showbase/ShowBase.py
  2. 99 49
      direct/src/task/Task.py
  3. 149 75
      tests/task/test_Task.py

+ 1 - 1
direct/src/showbase/ShowBase.py

@@ -3421,7 +3421,7 @@ class ShowBase(DirectObject.DirectObject):
         # Set fWantTk to 0 to avoid starting Tk with this call
         # Set fWantTk to 0 to avoid starting Tk with this call
         self.startDirect(fWantDirect = fDirect, fWantTk = fTk, fWantWx = fWx)
         self.startDirect(fWantDirect = fDirect, fWantTk = fTk, fWantWx = fWx)
 
 
-    def run(self): # pylint: disable=method-hidden
+    def run(self) -> None: # pylint: disable=method-hidden
         """This method runs the :class:`~direct.task.Task.TaskManager`
         """This method runs the :class:`~direct.task.Task.TaskManager`
         when ``self.appRunner is None``, which is to say, when we are
         when ``self.appRunner is None``, which is to say, when we are
         not running from within a p3d file.  When we *are* within a p3d
         not running from within a p3d file.  When we *are* within a p3d

+ 99 - 49
direct/src/task/Task.py

@@ -6,6 +6,8 @@ For more information about the task system, consult the
 :ref:`tasks-and-event-handling` page in the programming manual.
 :ref:`tasks-and-event-handling` page in the programming manual.
 """
 """
 
 
+from __future__ import annotations
+
 __all__ = ['Task', 'TaskManager',
 __all__ = ['Task', 'TaskManager',
            'cont', 'done', 'again', 'pickup', 'exit',
            'cont', 'done', 'again', 'pickup', 'exit',
            'sequence', 'loop', 'pause']
            'sequence', 'loop', 'pause']
@@ -13,7 +15,7 @@ __all__ = ['Task', 'TaskManager',
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.showbase.PythonUtil import Functor, ScratchPad
 from direct.showbase.PythonUtil import Functor, ScratchPad
 from direct.showbase.MessengerGlobal import messenger
 from direct.showbase.MessengerGlobal import messenger
-from typing import Any, Optional
+from typing import Any, Callable, Coroutine, Final, Generator, Sequence, TypeVar, Union
 import types
 import types
 import random
 import random
 import importlib
 import importlib
@@ -21,7 +23,7 @@ import sys
 
 
 # On Android, there's no use handling SIGINT, and in fact we can't, since we
 # On Android, there's no use handling SIGINT, and in fact we can't, since we
 # run the application in a separate thread from the main thread.
 # run the application in a separate thread from the main thread.
-signal: Optional[types.ModuleType]
+signal: types.ModuleType | None
 if hasattr(sys, 'getandroidapilevel'):
 if hasattr(sys, 'getandroidapilevel'):
     signal = None
     signal = None
 else:
 else:
@@ -43,8 +45,15 @@ from panda3d.core import (
 )
 )
 from direct.extensions_native import HTTPChannel_extensions # pylint: disable=unused-import
 from direct.extensions_native import HTTPChannel_extensions # pylint: disable=unused-import
 
 
+# The following variables are typing constructs used in annotations
+# to succinctly express all the types that can be converted into tasks.
+_T = TypeVar('_T', covariant=True)
+_TaskCoroutine = Union[Coroutine[Any, None, _T], Generator[Any, None, _T]]
+_TaskFunction = Callable[..., Union[int, _TaskCoroutine[Union[int, None]], None]]
+_FuncOrTask = Union[_TaskFunction, _TaskCoroutine[Any], AsyncTask]
+
 
 
-def print_exc_plus():
+def print_exc_plus() -> None:
     """
     """
     Print the usual traceback information, followed by a listing of all the
     Print the usual traceback information, followed by a listing of all the
     local variables in each frame.
     local variables in each frame.
@@ -52,12 +61,13 @@ def print_exc_plus():
     import traceback
     import traceback
 
 
     tb = sys.exc_info()[2]
     tb = sys.exc_info()[2]
+    assert tb is not None
     while 1:
     while 1:
         if not tb.tb_next:
         if not tb.tb_next:
             break
             break
         tb = tb.tb_next
         tb = tb.tb_next
     stack = []
     stack = []
-    f = tb.tb_frame
+    f: types.FrameType | None = tb.tb_frame
     while f:
     while f:
         stack.append(f)
         stack.append(f)
         f = f.f_back
         f = f.f_back
@@ -84,11 +94,11 @@ def print_exc_plus():
 # these Python names, and define them both at the module level, here,
 # these Python names, and define them both at the module level, here,
 # and at the class level (below).  The preferred access is via the
 # and at the class level (below).  The preferred access is via the
 # class level.
 # class level.
-done = AsyncTask.DSDone
-cont = AsyncTask.DSCont
-again = AsyncTask.DSAgain
-pickup = AsyncTask.DSPickup
-exit = AsyncTask.DSExit
+done: Final = AsyncTask.DSDone
+cont: Final = AsyncTask.DSCont
+again: Final = AsyncTask.DSAgain
+pickup: Final = AsyncTask.DSPickup
+exit: Final = AsyncTask.DSExit
 
 
 #: Task aliases to :class:`panda3d.core.PythonTask` for historical purposes.
 #: Task aliases to :class:`panda3d.core.PythonTask` for historical purposes.
 Task = PythonTask
 Task = PythonTask
@@ -112,7 +122,7 @@ gather = Task.gather
 shield = Task.shield
 shield = Task.shield
 
 
 
 
-def sequence(*taskList):
+def sequence(*taskList: AsyncTask) -> AsyncTaskSequence:
     seq = AsyncTaskSequence('sequence')
     seq = AsyncTaskSequence('sequence')
     for task in taskList:
     for task in taskList:
         seq.addTask(task)
         seq.addTask(task)
@@ -122,7 +132,7 @@ def sequence(*taskList):
 Task.DtoolClassDict['sequence'] = staticmethod(sequence)
 Task.DtoolClassDict['sequence'] = staticmethod(sequence)
 
 
 
 
-def loop(*taskList):
+def loop(*taskList: AsyncTask) -> AsyncTaskSequence:
     seq = AsyncTaskSequence('loop')
     seq = AsyncTaskSequence('loop')
     for task in taskList:
     for task in taskList:
         seq.addTask(task)
         seq.addTask(task)
@@ -144,10 +154,10 @@ class TaskManager:
 
 
     __prevHandler: Any
     __prevHandler: Any
 
 
-    def __init__(self):
+    def __init__(self) -> None:
         self.mgr = AsyncTaskManager.getGlobalPtr()
         self.mgr = AsyncTaskManager.getGlobalPtr()
 
 
-        self.resumeFunc = None
+        self.resumeFunc: Callable[[], object] | None = None
         self.globalClock = self.mgr.getClock()
         self.globalClock = self.mgr.getClock()
         self.stepping = False
         self.stepping = False
         self.running = False
         self.running = False
@@ -157,12 +167,12 @@ class TaskManager:
         if signal:
         if signal:
             self.__prevHandler = signal.default_int_handler
             self.__prevHandler = signal.default_int_handler
 
 
-        self._frameProfileQueue = []
+        self._frameProfileQueue: list[tuple[int, Any, Callable[[], object] | None]] = []
 
 
         # this will be set when it's safe to import StateVar
         # this will be set when it's safe to import StateVar
-        self._profileFrames = None
+        self._profileFrames: Any = None
         self._frameProfiler = None
         self._frameProfiler = None
-        self._profileTasks = None
+        self._profileTasks: Any = None
         self._taskProfiler = None
         self._taskProfiler = None
         self._taskProfileInfo = ScratchPad(
         self._taskProfileInfo = ScratchPad(
             taskId = None,
             taskId = None,
@@ -170,7 +180,7 @@ class TaskManager:
             session = None,
             session = None,
         )
         )
 
 
-    def finalInit(self):
+    def finalInit(self) -> None:
         # This function should be called once during startup, after
         # This function should be called once during startup, after
         # most things are imported.
         # most things are imported.
         from direct.fsm.StatePush import StateVar
         from direct.fsm.StatePush import StateVar
@@ -179,7 +189,7 @@ class TaskManager:
         self._profileFrames = StateVar(False)
         self._profileFrames = StateVar(False)
         self.setProfileFrames(ConfigVariableBool('profile-frames', 0).getValue())
         self.setProfileFrames(ConfigVariableBool('profile-frames', 0).getValue())
 
 
-    def destroy(self):
+    def destroy(self) -> None:
         # This should be safe to call multiple times.
         # This should be safe to call multiple times.
         self.running = False
         self.running = False
         self.notify.info("TaskManager.destroy()")
         self.notify.info("TaskManager.destroy()")
@@ -187,10 +197,10 @@ class TaskManager:
         self._frameProfileQueue.clear()
         self._frameProfileQueue.clear()
         self.mgr.cleanup()
         self.mgr.cleanup()
 
 
-    def __getClock(self):
+    def __getClock(self) -> ClockObject:
         return self.mgr.getClock()
         return self.mgr.getClock()
 
 
-    def setClock(self, clockObject):
+    def setClock(self, clockObject: ClockObject) -> None:
         self.mgr.setClock(clockObject)
         self.mgr.setClock(clockObject)
         self.globalClock = clockObject
         self.globalClock = clockObject
 
 
@@ -215,13 +225,13 @@ class TaskManager:
             # Next time around invoke the default handler
             # Next time around invoke the default handler
             signal.signal(signal.SIGINT, self.invokeDefaultHandler)
             signal.signal(signal.SIGINT, self.invokeDefaultHandler)
 
 
-    def getCurrentTask(self):
+    def getCurrentTask(self) -> AsyncTask | None:
         """ Returns the task currently executing on this thread, or
         """ Returns the task currently executing on this thread, or
         None if this is being called outside of the task manager. """
         None if this is being called outside of the task manager. """
 
 
         return Thread.getCurrentThread().getCurrentTask()
         return Thread.getCurrentThread().getCurrentTask()
 
 
-    def hasTaskChain(self, chainName):
+    def hasTaskChain(self, chainName: str) -> bool:
         """ Returns true if a task chain with the indicated name has
         """ Returns true if a task chain with the indicated name has
         already been defined, or false otherwise.  Note that
         already been defined, or false otherwise.  Note that
         setupTaskChain() will implicitly define a task chain if it has
         setupTaskChain() will implicitly define a task chain if it has
@@ -231,9 +241,16 @@ class TaskManager:
 
 
         return self.mgr.findTaskChain(chainName) is not None
         return self.mgr.findTaskChain(chainName) is not None
 
 
-    def setupTaskChain(self, chainName, numThreads = None, tickClock = None,
-                       threadPriority = None, frameBudget = None,
-                       frameSync = None, timeslicePriority = None):
+    def setupTaskChain(
+        self,
+        chainName: str,
+        numThreads: int | None = None,
+        tickClock: bool | None = None,
+        threadPriority: int | None = None,
+        frameBudget: float | None = None,
+        frameSync: bool | None = None,
+        timeslicePriority: bool | None = None,
+    ) -> None:
         """Defines a new task chain.  Each task chain executes tasks
         """Defines a new task chain.  Each task chain executes tasks
         potentially in parallel with all of the other task chains (if
         potentially in parallel with all of the other task chains (if
         numThreads is more than zero).  When a new task is created, it
         numThreads is more than zero).  When a new task is created, it
@@ -297,40 +314,50 @@ class TaskManager:
         if timeslicePriority is not None:
         if timeslicePriority is not None:
             chain.setTimeslicePriority(timeslicePriority)
             chain.setTimeslicePriority(timeslicePriority)
 
 
-    def hasTaskNamed(self, taskName):
+    def hasTaskNamed(self, taskName: str) -> bool:
         """Returns true if there is at least one task, active or
         """Returns true if there is at least one task, active or
         sleeping, with the indicated name. """
         sleeping, with the indicated name. """
 
 
         return bool(self.mgr.findTask(taskName))
         return bool(self.mgr.findTask(taskName))
 
 
-    def getTasksNamed(self, taskName):
+    def getTasksNamed(self, taskName: str) -> list[AsyncTask]:
         """Returns a list of all tasks, active or sleeping, with the
         """Returns a list of all tasks, active or sleeping, with the
         indicated name. """
         indicated name. """
         return list(self.mgr.findTasks(taskName))
         return list(self.mgr.findTasks(taskName))
 
 
-    def getTasksMatching(self, taskPattern):
+    def getTasksMatching(self, taskPattern: GlobPattern | str) -> list[AsyncTask]:
         """Returns a list of all tasks, active or sleeping, with a
         """Returns a list of all tasks, active or sleeping, with a
         name that matches the pattern, which can include standard
         name that matches the pattern, which can include standard
         shell globbing characters like \\*, ?, and []. """
         shell globbing characters like \\*, ?, and []. """
 
 
         return list(self.mgr.findTasksMatching(GlobPattern(taskPattern)))
         return list(self.mgr.findTasksMatching(GlobPattern(taskPattern)))
 
 
-    def getAllTasks(self):
+    def getAllTasks(self) -> list[AsyncTask]:
         """Returns list of all tasks, active and sleeping, in
         """Returns list of all tasks, active and sleeping, in
         arbitrary order. """
         arbitrary order. """
         return list(self.mgr.getTasks())
         return list(self.mgr.getTasks())
 
 
-    def getTasks(self):
+    def getTasks(self) -> list[AsyncTask]:
         """Returns list of all active tasks in arbitrary order. """
         """Returns list of all active tasks in arbitrary order. """
         return list(self.mgr.getActiveTasks())
         return list(self.mgr.getActiveTasks())
 
 
-    def getDoLaters(self):
+    def getDoLaters(self) -> list[AsyncTask]:
         """Returns list of all sleeping tasks in arbitrary order. """
         """Returns list of all sleeping tasks in arbitrary order. """
         return list(self.mgr.getSleepingTasks())
         return list(self.mgr.getSleepingTasks())
 
 
-    def doMethodLater(self, delayTime, funcOrTask, name, extraArgs = None,
-                      sort = None, priority = None, taskChain = None,
-                      uponDeath = None, appendTask = False, owner = None):
+    def doMethodLater(
+        self,
+        delayTime: float,
+        funcOrTask: _FuncOrTask,
+        name: str | None,
+        extraArgs: Sequence | None = None,
+        sort: int | None = None,
+        priority: int | None = None,
+        taskChain: str | None = None,
+        uponDeath: Callable[[], object] | None = None,
+        appendTask: bool = False,
+        owner = None,
+    ) -> AsyncTask:
         """Adds a task to be performed at some time in the future.
         """Adds a task to be performed at some time in the future.
         This is identical to `add()`, except that the specified
         This is identical to `add()`, except that the specified
         delayTime is applied to the Task object first, which means
         delayTime is applied to the Task object first, which means
@@ -353,9 +380,19 @@ class TaskManager:
 
 
     do_method_later = doMethodLater
     do_method_later = doMethodLater
 
 
-    def add(self, funcOrTask, name = None, sort = None, extraArgs = None,
-            priority = None, uponDeath = None, appendTask = False,
-            taskChain = None, owner = None, delay = None):
+    def add(
+        self,
+        funcOrTask: _FuncOrTask,
+        name: str | None = None,
+        sort: int | None = None,
+        extraArgs: Sequence | None = None,
+        priority: int | None = None,
+        uponDeath: Callable[[], object] | None = None,
+        appendTask: bool = False,
+        taskChain: str | None = None,
+        owner = None,
+        delay: float | None = None,
+    ) -> AsyncTask:
         """
         """
         Add a new task to the taskMgr.  The task will begin executing
         Add a new task to the taskMgr.  The task will begin executing
         immediately, or next frame if its sort value has already
         immediately, or next frame if its sort value has already
@@ -422,7 +459,18 @@ class TaskManager:
         self.mgr.add(task)
         self.mgr.add(task)
         return task
         return task
 
 
-    def __setupTask(self, funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath):
+    def __setupTask(
+        self,
+        funcOrTask: _FuncOrTask,
+        name: str | None,
+        priority: int | None,
+        sort: int | None,
+        extraArgs: Sequence | None,
+        taskChain: str | None,
+        appendTask: bool,
+        owner,
+        uponDeath: Callable[[], object] | None,
+    ) -> AsyncTask:
         wasTask = False
         wasTask = False
         if isinstance(funcOrTask, AsyncTask):
         if isinstance(funcOrTask, AsyncTask):
             task = funcOrTask
             task = funcOrTask
@@ -480,7 +528,7 @@ class TaskManager:
 
 
         return task
         return task
 
 
-    def remove(self, taskOrName):
+    def remove(self, taskOrName: AsyncTask | str | list[AsyncTask | str]) -> int:
         """Removes a task from the task manager.  The task is stopped,
         """Removes a task from the task manager.  The task is stopped,
         almost as if it had returned task.done.  (But if the task is
         almost as if it had returned task.done.  (But if the task is
         currently executing, it will finish out its current frame
         currently executing, it will finish out its current frame
@@ -492,13 +540,15 @@ class TaskManager:
         if isinstance(taskOrName, AsyncTask):
         if isinstance(taskOrName, AsyncTask):
             return self.mgr.remove(taskOrName)
             return self.mgr.remove(taskOrName)
         elif isinstance(taskOrName, list):
         elif isinstance(taskOrName, list):
+            count = 0
             for task in taskOrName:
             for task in taskOrName:
-                self.remove(task)
+                count += self.remove(task)
+            return count
         else:
         else:
             tasks = self.mgr.findTasks(taskOrName)
             tasks = self.mgr.findTasks(taskOrName)
             return self.mgr.remove(tasks)
             return self.mgr.remove(tasks)
 
 
-    def removeTasksMatching(self, taskPattern):
+    def removeTasksMatching(self, taskPattern: GlobPattern | str) -> int:
         """Removes all tasks whose names match the pattern, which can
         """Removes all tasks whose names match the pattern, which can
         include standard shell globbing characters like \\*, ?, and [].
         include standard shell globbing characters like \\*, ?, and [].
         See also :meth:`remove()`.
         See also :meth:`remove()`.
@@ -508,7 +558,7 @@ class TaskManager:
         tasks = self.mgr.findTasksMatching(GlobPattern(taskPattern))
         tasks = self.mgr.findTasksMatching(GlobPattern(taskPattern))
         return self.mgr.remove(tasks)
         return self.mgr.remove(tasks)
 
 
-    def step(self):
+    def step(self) -> None:
         """Invokes the task manager for one frame, and then returns.
         """Invokes the task manager for one frame, and then returns.
         Normally, this executes each task exactly once, though task
         Normally, this executes each task exactly once, though task
         chains that are in sub-threads or that have frame budgets
         chains that are in sub-threads or that have frame budgets
@@ -519,7 +569,7 @@ class TaskManager:
         # Replace keyboard interrupt handler during task list processing
         # Replace keyboard interrupt handler during task list processing
         # so we catch the keyboard interrupt but don't handle it until
         # so we catch the keyboard interrupt but don't handle it until
         # after task list processing is complete.
         # after task list processing is complete.
-        self.fKeyboardInterrupt = 0
+        self.fKeyboardInterrupt = False
         self.interruptCount = 0
         self.interruptCount = 0
 
 
         if signal:
         if signal:
@@ -541,7 +591,7 @@ class TaskManager:
         if self.fKeyboardInterrupt:
         if self.fKeyboardInterrupt:
             raise KeyboardInterrupt
             raise KeyboardInterrupt
 
 
-    def run(self):
+    def run(self) -> None:
         """Starts the task manager running.  Does not return until an
         """Starts the task manager running.  Does not return until an
         exception is encountered (including KeyboardInterrupt). """
         exception is encountered (including KeyboardInterrupt). """
 
 
@@ -567,11 +617,11 @@ class TaskManager:
                     if len(self._frameProfileQueue) > 0:
                     if len(self._frameProfileQueue) > 0:
                         numFrames, session, callback = self._frameProfileQueue.pop(0)
                         numFrames, session, callback = self._frameProfileQueue.pop(0)
 
 
-                        def _profileFunc(numFrames=numFrames):
+                        def _profileFunc(numFrames: int = numFrames) -> None:
                             self._doProfiledFrames(numFrames)
                             self._doProfiledFrames(numFrames)
                         session.setFunc(_profileFunc)
                         session.setFunc(_profileFunc)
                         session.run()
                         session.run()
-                        _profileFunc = None
+                        del _profileFunc
                         if callback:
                         if callback:
                             callback()
                             callback()
                         session.release()
                         session.release()
@@ -624,7 +674,7 @@ class TaskManager:
             message = ioError
             message = ioError
         return code, message
         return code, message
 
 
-    def stop(self):
+    def stop(self) -> None:
         # Set a flag so we will stop before beginning next frame
         # Set a flag so we will stop before beginning next frame
         self.running = False
         self.running = False
 
 
@@ -789,12 +839,12 @@ class TaskManager:
             task = tasks.getTask(i)
             task = tasks.getTask(i)
         return task
         return task
 
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return str(self.mgr)
         return str(self.mgr)
 
 
     # In the event we want to do frame time managment, this is the
     # In the event we want to do frame time managment, this is the
     # function to replace or overload.
     # function to replace or overload.
-    def doYield(self, frameStartTime, nextScheduledTaskTime):
+    def doYield(self, frameStartTime: float, nextScheduledTaskTime: float) -> None:
         pass
         pass
 
 
     #def doYieldExample(self, frameStartTime, nextScheduledTaskTime):
     #def doYieldExample(self, frameStartTime, nextScheduledTaskTime):

+ 149 - 75
tests/task/test_Task.py

@@ -1,22 +1,82 @@
+import pytest
 from panda3d import core
 from panda3d import core
 from direct.task import Task
 from direct.task import Task
 
 
 
 
-def test_TaskManager():
-    tm = Task.TaskManager()
-    tm.mgr = core.AsyncTaskManager("Test manager")
-    tm.setClock(core.ClockObject())
-    tm.setupTaskChain("default", tickClock = True)
+TASK_NAME = 'Arbitrary task name'
+TASK_CHAIN_NAME = 'Arbitrary task chain name'
 
 
-    tm._startTrackingMemLeaks = lambda: None
-    tm._stopTrackingMemLeaks = lambda: None
-    tm._checkMemLeaks = lambda: None
 
 
-    # check for memory leaks after every test
-    tm._startTrackingMemLeaks()
-    tm._checkMemLeaks()
+def DUMMY_FUNCTION(*_):
+    pass
 
 
+
[email protected]
+def task_manager():
+    manager = Task.TaskManager()
+    manager.mgr = core.AsyncTaskManager('Test manager')
+    manager.clock = core.ClockObject()
+    manager.setupTaskChain('default', tickClock=True)
+    manager.finalInit()
+    yield manager
+    manager.destroy()
+
+
+def test_sequence(task_manager):
+    numbers = []
+
+    def append_1(task):
+        numbers.append(1)
+
+    def append_2(task):
+        numbers.append(2)
+
+    sequence = Task.sequence(core.PythonTask(append_1), core.PythonTask(append_2))
+    task_manager.add(sequence)
+    for _ in range(3):
+        task_manager.step()
+    assert not task_manager.getTasks()
+    assert numbers == [1, 2]
+
+
+def test_loop(task_manager):
+    numbers = []
+
+    def append_1(task):
+        numbers.append(1)
+
+    def append_2(task):
+        numbers.append(2)
+
+    loop = Task.loop(core.PythonTask(append_1), core.PythonTask(append_2))
+    task_manager.add(loop)
+    for _ in range(5):
+        task_manager.step()
+    assert numbers == [1, 2, 1, 2]
+
+
+def test_get_current_task(task_manager):
+    def check_current_task(task):
+        assert task_manager.getCurrentTask().name == TASK_NAME
+
+    task_manager.add(check_current_task, TASK_NAME)
+    assert len(task_manager.getTasks()) == 1
+    assert task_manager.getCurrentTask() is None
+
+    task_manager.step()
+    assert len(task_manager.getTasks()) == 0
+    assert task_manager.getCurrentTask() is None
+
+
+def test_has_task_chain(task_manager):
+    assert not task_manager.hasTaskChain(TASK_CHAIN_NAME)
+    task_manager.setupTaskChain(TASK_CHAIN_NAME)
+    assert task_manager.hasTaskChain(TASK_CHAIN_NAME)
+
+
+def test_done(task_manager):
     # run-once task
     # run-once task
+    tm = task_manager
     l = []
     l = []
 
 
     def _testDone(task, l=l):
     def _testDone(task, l=l):
@@ -27,28 +87,31 @@ def test_TaskManager():
     assert len(l) == 1
     assert len(l) == 1
     tm.step()
     tm.step()
     assert len(l) == 1
     assert len(l) == 1
-    _testDone = None
-    tm._checkMemLeaks()
 
 
+
+def test_remove_by_name(task_manager):
     # remove by name
     # remove by name
+    tm = task_manager
     def _testRemoveByName(task):
     def _testRemoveByName(task):
         return task.done
         return task.done
     tm.add(_testRemoveByName, 'testRemoveByName')
     tm.add(_testRemoveByName, 'testRemoveByName')
     assert tm.remove('testRemoveByName') == 1
     assert tm.remove('testRemoveByName') == 1
     assert tm.remove('testRemoveByName') == 0
     assert tm.remove('testRemoveByName') == 0
-    _testRemoveByName = None
-    tm._checkMemLeaks()
 
 
+
+def test_duplicate_named_tasks(task_manager):
     # duplicate named tasks
     # duplicate named tasks
+    tm = task_manager
     def _testDupNamedTasks(task):
     def _testDupNamedTasks(task):
         return task.done
         return task.done
     tm.add(_testDupNamedTasks, 'testDupNamedTasks')
     tm.add(_testDupNamedTasks, 'testDupNamedTasks')
     tm.add(_testDupNamedTasks, 'testDupNamedTasks')
     tm.add(_testDupNamedTasks, 'testDupNamedTasks')
     assert tm.remove('testRemoveByName') == 0
     assert tm.remove('testRemoveByName') == 0
-    _testDupNamedTasks = None
-    tm._checkMemLeaks()
 
 
+
+def test_continued_task(task_manager):
     # continued task
     # continued task
+    tm = task_manager
     l = []
     l = []
 
 
     def _testCont(task, l = l):
     def _testCont(task, l = l):
@@ -60,10 +123,11 @@ def test_TaskManager():
     tm.step()
     tm.step()
     assert len(l) == 2
     assert len(l) == 2
     tm.remove('testCont')
     tm.remove('testCont')
-    _testCont = None
-    tm._checkMemLeaks()
 
 
+
+def test_continue_until_done(task_manager):
     # continue until done task
     # continue until done task
+    tm = task_manager
     l = []
     l = []
 
 
     def _testContDone(task, l = l):
     def _testContDone(task, l = l):
@@ -80,20 +144,22 @@ def test_TaskManager():
     tm.step()
     tm.step()
     assert len(l) == 2
     assert len(l) == 2
     assert not tm.hasTaskNamed('testContDone')
     assert not tm.hasTaskNamed('testContDone')
-    _testContDone = None
-    tm._checkMemLeaks()
 
 
+
+def test_has_task_named(task_manager):
     # hasTaskNamed
     # hasTaskNamed
+    tm = task_manager
     def _testHasTaskNamed(task):
     def _testHasTaskNamed(task):
         return task.done
         return task.done
     tm.add(_testHasTaskNamed, 'testHasTaskNamed')
     tm.add(_testHasTaskNamed, 'testHasTaskNamed')
     assert tm.hasTaskNamed('testHasTaskNamed')
     assert tm.hasTaskNamed('testHasTaskNamed')
     tm.step()
     tm.step()
     assert not tm.hasTaskNamed('testHasTaskNamed')
     assert not tm.hasTaskNamed('testHasTaskNamed')
-    _testHasTaskNamed = None
-    tm._checkMemLeaks()
 
 
+
+def test_task_sort(task_manager):
     # task sort
     # task sort
+    tm = task_manager
     l = []
     l = []
 
 
     def _testPri1(task, l = l):
     def _testPri1(task, l = l):
@@ -113,11 +179,11 @@ def test_TaskManager():
     assert l == [1, 2, 1, 2,]
     assert l == [1, 2, 1, 2,]
     tm.remove('testPri1')
     tm.remove('testPri1')
     tm.remove('testPri2')
     tm.remove('testPri2')
-    _testPri1 = None
-    _testPri2 = None
-    tm._checkMemLeaks()
 
 
+
+def test_extra_args(task_manager):
     # task extraArgs
     # task extraArgs
+    tm = task_manager
     l = []
     l = []
 
 
     def _testExtraArgs(arg1, arg2, l=l):
     def _testExtraArgs(arg1, arg2, l=l):
@@ -127,10 +193,11 @@ def test_TaskManager():
     tm.step()
     tm.step()
     assert len(l) == 2
     assert len(l) == 2
     assert l == [4, 5,]
     assert l == [4, 5,]
-    _testExtraArgs = None
-    tm._checkMemLeaks()
 
 
+
+def test_append_task(task_manager):
     # task appendTask
     # task appendTask
+    tm = task_manager
     l = []
     l = []
 
 
     def _testAppendTask(arg1, arg2, task, l=l):
     def _testAppendTask(arg1, arg2, task, l=l):
@@ -140,10 +207,11 @@ def test_TaskManager():
     tm.step()
     tm.step()
     assert len(l) == 2
     assert len(l) == 2
     assert l == [4, 5,]
     assert l == [4, 5,]
-    _testAppendTask = None
-    tm._checkMemLeaks()
 
 
+
+def test_task_upon_death(task_manager):
     # task uponDeath
     # task uponDeath
+    tm = task_manager
     l = []
     l = []
 
 
     def _uponDeathFunc(task, l=l):
     def _uponDeathFunc(task, l=l):
@@ -155,11 +223,11 @@ def test_TaskManager():
     tm.step()
     tm.step()
     assert len(l) == 1
     assert len(l) == 1
     assert l == ['testUponDeath']
     assert l == ['testUponDeath']
-    _testUponDeath = None
-    _uponDeathFunc = None
-    tm._checkMemLeaks()
 
 
+
+def test_task_owner(task_manager):
     # task owner
     # task owner
+    tm = task_manager
     class _TaskOwner:
     class _TaskOwner:
         def _addTask(self, task):
         def _addTask(self, task):
             self.addedTaskName = task.name
             self.addedTaskName = task.name
@@ -175,11 +243,10 @@ def test_TaskManager():
     tm.step()
     tm.step()
     assert getattr(to, 'addedTaskName', None) == 'testOwner'
     assert getattr(to, 'addedTaskName', None) == 'testOwner'
     assert getattr(to, 'clearedTaskName', None) == 'testOwner'
     assert getattr(to, 'clearedTaskName', None) == 'testOwner'
-    _testOwner = None
-    del to
-    _TaskOwner = None
-    tm._checkMemLeaks()
 
 
+
+def test_do_laters(task_manager):
+    tm = task_manager
     doLaterTests = [0,]
     doLaterTests = [0,]
 
 
     # doLater
     # doLater
@@ -205,8 +272,6 @@ def test_TaskManager():
     _testDoLater1 = None
     _testDoLater1 = None
     _testDoLater2 = None
     _testDoLater2 = None
     _monitorDoLater = None
     _monitorDoLater = None
-    # don't check until all the doLaters are finished
-    #tm._checkMemLeaks()
 
 
     # doLater sort
     # doLater sort
     l = []
     l = []
@@ -231,8 +296,6 @@ def test_TaskManager():
     _testDoLaterPri1 = None
     _testDoLaterPri1 = None
     _testDoLaterPri2 = None
     _testDoLaterPri2 = None
     _monitorDoLaterPri = None
     _monitorDoLaterPri = None
-    # don't check until all the doLaters are finished
-    #tm._checkMemLeaks()
 
 
     # doLater extraArgs
     # doLater extraArgs
     l = []
     l = []
@@ -252,8 +315,6 @@ def test_TaskManager():
     tm.add(_monitorDoLaterExtraArgs, 'monitorDoLaterExtraArgs', sort=10)
     tm.add(_monitorDoLaterExtraArgs, 'monitorDoLaterExtraArgs', sort=10)
     _testDoLaterExtraArgs = None
     _testDoLaterExtraArgs = None
     _monitorDoLaterExtraArgs = None
     _monitorDoLaterExtraArgs = None
-    # don't check until all the doLaters are finished
-    #tm._checkMemLeaks()
 
 
     # doLater appendTask
     # doLater appendTask
     l = []
     l = []
@@ -275,8 +336,6 @@ def test_TaskManager():
     tm.add(_monitorDoLaterAppendTask, 'monitorDoLaterAppendTask', sort=10)
     tm.add(_monitorDoLaterAppendTask, 'monitorDoLaterAppendTask', sort=10)
     _testDoLaterAppendTask = None
     _testDoLaterAppendTask = None
     _monitorDoLaterAppendTask = None
     _monitorDoLaterAppendTask = None
-    # don't check until all the doLaters are finished
-    #tm._checkMemLeaks()
 
 
     # doLater uponDeath
     # doLater uponDeath
     l = []
     l = []
@@ -302,8 +361,6 @@ def test_TaskManager():
     _testUponDeathFunc = None
     _testUponDeathFunc = None
     _testDoLaterUponDeath = None
     _testDoLaterUponDeath = None
     _monitorDoLaterUponDeath = None
     _monitorDoLaterUponDeath = None
-    # don't check until all the doLaters are finished
-    #tm._checkMemLeaks()
 
 
     # doLater owner
     # doLater owner
     class _DoLaterOwner:
     class _DoLaterOwner:
@@ -335,15 +392,15 @@ def test_TaskManager():
     _monitorDoLaterOwner = None
     _monitorDoLaterOwner = None
     del doLaterOwner
     del doLaterOwner
     _DoLaterOwner = None
     _DoLaterOwner = None
-    # don't check until all the doLaters are finished
-    #tm._checkMemLeaks()
 
 
     # run the doLater tests
     # run the doLater tests
     while doLaterTests[0] > 0:
     while doLaterTests[0] > 0:
         tm.step()
         tm.step()
     del doLaterTests
     del doLaterTests
-    tm._checkMemLeaks()
 
 
+
+def test_get_tasks(task_manager):
+    tm = task_manager
     # getTasks
     # getTasks
     def _testGetTasks(task):
     def _testGetTasks(task):
         return task.cont
         return task.cont
@@ -361,9 +418,10 @@ def test_TaskManager():
     tm.remove('testGetTasks1')
     tm.remove('testGetTasks1')
     tm.remove('testGetTasks3')
     tm.remove('testGetTasks3')
     assert len(tm.getTasks()) == 0
     assert len(tm.getTasks()) == 0
-    _testGetTasks = None
-    tm._checkMemLeaks()
 
 
+
+def test_get_do_laters(task_manager):
+    tm = task_manager
     # getDoLaters
     # getDoLaters
     def _testGetDoLaters():
     def _testGetDoLaters():
         pass
         pass
@@ -379,9 +437,18 @@ def test_TaskManager():
     tm.remove('testDoLater1')
     tm.remove('testDoLater1')
     tm.remove('testDoLater3')
     tm.remove('testDoLater3')
     assert len(tm.getDoLaters()) == 0
     assert len(tm.getDoLaters()) == 0
-    _testGetDoLaters = None
-    tm._checkMemLeaks()
 
 
+
+def test_get_all_tasks(task_manager):
+    active_task = task_manager.add(DUMMY_FUNCTION, delay=None)
+    sleeping_task = task_manager.add(DUMMY_FUNCTION, delay=1)
+    assert task_manager.getTasks() == [active_task]
+    assert task_manager.getDoLaters() == [sleeping_task]
+    assert task_manager.getAllTasks() in ([active_task, sleeping_task], [sleeping_task, active_task])
+
+
+def test_duplicate_named_do_laters(task_manager):
+    tm = task_manager
     # duplicate named doLaters removed via taskMgr.remove
     # duplicate named doLaters removed via taskMgr.remove
     def _testDupNameDoLaters():
     def _testDupNameDoLaters():
         pass
         pass
@@ -391,9 +458,10 @@ def test_TaskManager():
     assert len(tm.getDoLaters()) == 2
     assert len(tm.getDoLaters()) == 2
     tm.remove('testDupNameDoLater')
     tm.remove('testDupNameDoLater')
     assert len(tm.getDoLaters()) == 0
     assert len(tm.getDoLaters()) == 0
-    _testDupNameDoLaters = None
-    tm._checkMemLeaks()
 
 
+
+def test_duplicate_named_do_laters_remove(task_manager):
+    tm = task_manager
     # duplicate named doLaters removed via remove()
     # duplicate named doLaters removed via remove()
     def _testDupNameDoLatersRemove():
     def _testDupNameDoLatersRemove():
         pass
         pass
@@ -405,10 +473,10 @@ def test_TaskManager():
     assert len(tm.getDoLaters()) == 1
     assert len(tm.getDoLaters()) == 1
     dl1.remove()
     dl1.remove()
     assert len(tm.getDoLaters()) == 0
     assert len(tm.getDoLaters()) == 0
-    _testDupNameDoLatersRemove = None
-    # nameDict etc. isn't cleared out right away with task.remove()
-    tm._checkMemLeaks()
 
 
+
+def test_get_tasks_named(task_manager):
+    tm = task_manager
     # getTasksNamed
     # getTasksNamed
     def _testGetTasksNamed(task):
     def _testGetTasksNamed(task):
         return task.cont
         return task.cont
@@ -421,9 +489,20 @@ def test_TaskManager():
     assert len(tm.getTasksNamed('testGetTasksNamed')) == 3
     assert len(tm.getTasksNamed('testGetTasksNamed')) == 3
     tm.remove('testGetTasksNamed')
     tm.remove('testGetTasksNamed')
     assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
     assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
-    _testGetTasksNamed = None
-    tm._checkMemLeaks()
 
 
+
+def test_get_tasks_matching(task_manager):
+    task_manager.add(DUMMY_FUNCTION, 'task_1')
+    task_manager.add(DUMMY_FUNCTION, 'task_2')
+    task_manager.add(DUMMY_FUNCTION, 'another_task')
+
+    assert len(task_manager.getTasksMatching('task_?')) == 2
+    assert len(task_manager.getTasksMatching('*_task')) == 1
+    assert len(task_manager.getTasksMatching('*task*')) == 3
+
+
+def test_remove_tasks_matching(task_manager):
+    tm = task_manager
     # removeTasksMatching
     # removeTasksMatching
     def _testRemoveTasksMatching(task):
     def _testRemoveTasksMatching(task):
         return task.cont
         return task.cont
@@ -445,9 +524,10 @@ def test_TaskManager():
     tm.removeTasksMatching('testRemoveTasksMatching?a')
     tm.removeTasksMatching('testRemoveTasksMatching?a')
     assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 0
     assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 0
     assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 0
     assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 0
-    _testRemoveTasksMatching = None
-    tm._checkMemLeaks()
 
 
+
+def test_task_obj(task_manager):
+    tm = task_manager
     # create Task object and add to mgr
     # create Task object and add to mgr
     l = []
     l = []
 
 
@@ -463,9 +543,10 @@ def test_TaskManager():
     tm.remove('testTaskObj')
     tm.remove('testTaskObj')
     tm.step()
     tm.step()
     assert len(l) == 2
     assert len(l) == 2
-    _testTaskObj = None
-    tm._checkMemLeaks()
 
 
+
+def test_task_remove(task_manager):
+    tm = task_manager
     # remove Task via task.remove()
     # remove Task via task.remove()
     l = []
     l = []
 
 
@@ -482,9 +563,10 @@ def test_TaskManager():
     tm.step()
     tm.step()
     assert len(l) == 2
     assert len(l) == 2
     del t
     del t
-    _testTaskObjRemove = None
-    tm._checkMemLeaks()
 
 
+
+def test_task_get_sort(task_manager):
+    tm = task_manager
     # set/get Task sort
     # set/get Task sort
     l = []
     l = []
     def _testTaskObjSort(arg, task, l=l):
     def _testTaskObjSort(arg, task, l=l):
@@ -508,11 +590,3 @@ def test_TaskManager():
     t2.remove()
     t2.remove()
     tm.step()
     tm.step()
     assert len(l) == 4
     assert len(l) == 4
-    del t1
-    del t2
-    _testTaskObjSort = None
-    tm._checkMemLeaks()
-
-    del l
-    tm.destroy()
-    del tm