diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 0a58cf74e9..75436a4d64 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -3421,7 +3421,7 @@ class ShowBase(DirectObject.DirectObject): # Set fWantTk to 0 to avoid starting Tk with this call 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` 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 diff --git a/direct/src/task/Task.py b/direct/src/task/Task.py index b0aa610e40..9896b9c5ee 100644 --- a/direct/src/task/Task.py +++ b/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. """ +from __future__ import annotations + __all__ = ['Task', 'TaskManager', 'cont', 'done', 'again', 'pickup', 'exit', 'sequence', 'loop', 'pause'] @@ -13,7 +15,7 @@ __all__ = ['Task', 'TaskManager', from direct.directnotify.DirectNotifyGlobal import directNotify from direct.showbase.PythonUtil import Functor, ScratchPad 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 random import importlib @@ -21,7 +23,7 @@ import sys # 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. -signal: Optional[types.ModuleType] +signal: types.ModuleType | None if hasattr(sys, 'getandroidapilevel'): signal = None else: @@ -43,8 +45,15 @@ from panda3d.core 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 local variables in each frame. @@ -52,12 +61,13 @@ def print_exc_plus(): import traceback tb = sys.exc_info()[2] + assert tb is not None while 1: if not tb.tb_next: break tb = tb.tb_next stack = [] - f = tb.tb_frame + f: types.FrameType | None = tb.tb_frame while f: stack.append(f) f = f.f_back @@ -84,11 +94,11 @@ def print_exc_plus(): # 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 +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 = PythonTask @@ -112,7 +122,7 @@ gather = Task.gather shield = Task.shield -def sequence(*taskList): +def sequence(*taskList: AsyncTask) -> AsyncTaskSequence: seq = AsyncTaskSequence('sequence') for task in taskList: seq.addTask(task) @@ -122,7 +132,7 @@ def sequence(*taskList): Task.DtoolClassDict['sequence'] = staticmethod(sequence) -def loop(*taskList): +def loop(*taskList: AsyncTask) -> AsyncTaskSequence: seq = AsyncTaskSequence('loop') for task in taskList: seq.addTask(task) @@ -144,10 +154,10 @@ class TaskManager: __prevHandler: Any - def __init__(self): + def __init__(self) -> None: self.mgr = AsyncTaskManager.getGlobalPtr() - self.resumeFunc = None + self.resumeFunc: Callable[[], object] | None = None self.globalClock = self.mgr.getClock() self.stepping = False self.running = False @@ -157,12 +167,12 @@ class TaskManager: if signal: 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 - self._profileFrames = None + self._profileFrames: Any = None self._frameProfiler = None - self._profileTasks = None + self._profileTasks: Any = None self._taskProfiler = None self._taskProfileInfo = ScratchPad( taskId = None, @@ -170,7 +180,7 @@ class TaskManager: session = None, ) - def finalInit(self): + def finalInit(self) -> None: # This function should be called once during startup, after # most things are imported. from direct.fsm.StatePush import StateVar @@ -179,7 +189,7 @@ class TaskManager: self._profileFrames = StateVar(False) self.setProfileFrames(ConfigVariableBool('profile-frames', 0).getValue()) - def destroy(self): + def destroy(self) -> None: # This should be safe to call multiple times. self.running = False self.notify.info("TaskManager.destroy()") @@ -187,10 +197,10 @@ class TaskManager: self._frameProfileQueue.clear() self.mgr.cleanup() - def __getClock(self): + def __getClock(self) -> ClockObject: return self.mgr.getClock() - def setClock(self, clockObject): + def setClock(self, clockObject: ClockObject) -> None: self.mgr.setClock(clockObject) self.globalClock = clockObject @@ -215,13 +225,13 @@ class TaskManager: # Next time around invoke the default handler signal.signal(signal.SIGINT, self.invokeDefaultHandler) - def getCurrentTask(self): + def getCurrentTask(self) -> AsyncTask | None: """ Returns the task currently executing on this thread, or None if this is being called outside of the task manager. """ 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 already been defined, or false otherwise. Note that setupTaskChain() will implicitly define a task chain if it has @@ -231,9 +241,16 @@ class TaskManager: 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 potentially in parallel with all of the other task chains (if numThreads is more than zero). When a new task is created, it @@ -297,40 +314,50 @@ class TaskManager: if timeslicePriority is not None: chain.setTimeslicePriority(timeslicePriority) - def hasTaskNamed(self, taskName): + def hasTaskNamed(self, taskName: str) -> bool: """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): + def getTasksNamed(self, taskName: str) -> list[AsyncTask]: """Returns a list of all tasks, active or sleeping, with the indicated name. """ 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 name that matches the pattern, which can include standard shell globbing characters like \\*, ?, and []. """ return list(self.mgr.findTasksMatching(GlobPattern(taskPattern))) - def getAllTasks(self): + def getAllTasks(self) -> list[AsyncTask]: """Returns list of all tasks, active and sleeping, in arbitrary order. """ return list(self.mgr.getTasks()) - def getTasks(self): + def getTasks(self) -> list[AsyncTask]: """Returns list of all active tasks in arbitrary order. """ return list(self.mgr.getActiveTasks()) - def getDoLaters(self): + def getDoLaters(self) -> list[AsyncTask]: """Returns list of all sleeping tasks in arbitrary order. """ 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. This is identical to `add()`, except that the specified delayTime is applied to the Task object first, which means @@ -353,9 +380,19 @@ class TaskManager: 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 immediately, or next frame if its sort value has already @@ -422,7 +459,18 @@ class TaskManager: self.mgr.add(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 if isinstance(funcOrTask, AsyncTask): task = funcOrTask @@ -480,7 +528,7 @@ class TaskManager: 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, almost as if it had returned task.done. (But if the task is currently executing, it will finish out its current frame @@ -492,13 +540,15 @@ class TaskManager: if isinstance(taskOrName, AsyncTask): return self.mgr.remove(taskOrName) elif isinstance(taskOrName, list): + count = 0 for task in taskOrName: - self.remove(task) + count += self.remove(task) + return count else: tasks = self.mgr.findTasks(taskOrName) 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 include standard shell globbing characters like \\*, ?, and []. See also :meth:`remove()`. @@ -508,7 +558,7 @@ class TaskManager: tasks = self.mgr.findTasksMatching(GlobPattern(taskPattern)) return self.mgr.remove(tasks) - def step(self): + def step(self) -> None: """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 @@ -519,7 +569,7 @@ class TaskManager: # 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.fKeyboardInterrupt = False self.interruptCount = 0 if signal: @@ -541,7 +591,7 @@ class TaskManager: if self.fKeyboardInterrupt: raise KeyboardInterrupt - def run(self): + def run(self) -> None: """Starts the task manager running. Does not return until an exception is encountered (including KeyboardInterrupt). """ @@ -567,11 +617,11 @@ class TaskManager: if len(self._frameProfileQueue) > 0: numFrames, session, callback = self._frameProfileQueue.pop(0) - def _profileFunc(numFrames=numFrames): + def _profileFunc(numFrames: int = numFrames) -> None: self._doProfiledFrames(numFrames) session.setFunc(_profileFunc) session.run() - _profileFunc = None + del _profileFunc if callback: callback() session.release() @@ -624,7 +674,7 @@ class TaskManager: message = ioError return code, message - def stop(self): + def stop(self) -> None: # Set a flag so we will stop before beginning next frame self.running = False @@ -789,12 +839,12 @@ class TaskManager: task = tasks.getTask(i) return task - def __repr__(self): + def __repr__(self) -> str: 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): + def doYield(self, frameStartTime: float, nextScheduledTaskTime: float) -> None: pass #def doYieldExample(self, frameStartTime, nextScheduledTaskTime): diff --git a/tests/task/test_Task.py b/tests/task/test_Task.py index 46710362b1..dbfaba3d28 100644 --- a/tests/task/test_Task.py +++ b/tests/task/test_Task.py @@ -1,22 +1,82 @@ +import pytest from panda3d import core 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 + +@pytest.fixture +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 + tm = task_manager l = [] def _testDone(task, l=l): @@ -27,28 +87,31 @@ def test_TaskManager(): assert len(l) == 1 tm.step() assert len(l) == 1 - _testDone = None - tm._checkMemLeaks() + +def test_remove_by_name(task_manager): # remove by name + tm = task_manager def _testRemoveByName(task): return task.done tm.add(_testRemoveByName, 'testRemoveByName') assert tm.remove('testRemoveByName') == 1 assert tm.remove('testRemoveByName') == 0 - _testRemoveByName = None - tm._checkMemLeaks() + +def test_duplicate_named_tasks(task_manager): # duplicate named tasks + tm = task_manager def _testDupNamedTasks(task): return task.done tm.add(_testDupNamedTasks, 'testDupNamedTasks') tm.add(_testDupNamedTasks, 'testDupNamedTasks') assert tm.remove('testRemoveByName') == 0 - _testDupNamedTasks = None - tm._checkMemLeaks() + +def test_continued_task(task_manager): # continued task + tm = task_manager l = [] def _testCont(task, l = l): @@ -60,10 +123,11 @@ def test_TaskManager(): tm.step() assert len(l) == 2 tm.remove('testCont') - _testCont = None - tm._checkMemLeaks() + +def test_continue_until_done(task_manager): # continue until done task + tm = task_manager l = [] def _testContDone(task, l = l): @@ -80,20 +144,22 @@ def test_TaskManager(): tm.step() assert len(l) == 2 assert not tm.hasTaskNamed('testContDone') - _testContDone = None - tm._checkMemLeaks() + +def test_has_task_named(task_manager): # hasTaskNamed + tm = task_manager 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() + +def test_task_sort(task_manager): # task sort + tm = task_manager l = [] def _testPri1(task, l = l): @@ -113,11 +179,11 @@ def test_TaskManager(): assert l == [1, 2, 1, 2,] tm.remove('testPri1') tm.remove('testPri2') - _testPri1 = None - _testPri2 = None - tm._checkMemLeaks() + +def test_extra_args(task_manager): # task extraArgs + tm = task_manager l = [] def _testExtraArgs(arg1, arg2, l=l): @@ -127,10 +193,11 @@ def test_TaskManager(): tm.step() assert len(l) == 2 assert l == [4, 5,] - _testExtraArgs = None - tm._checkMemLeaks() + +def test_append_task(task_manager): # task appendTask + tm = task_manager l = [] def _testAppendTask(arg1, arg2, task, l=l): @@ -140,10 +207,11 @@ def test_TaskManager(): tm.step() assert len(l) == 2 assert l == [4, 5,] - _testAppendTask = None - tm._checkMemLeaks() + +def test_task_upon_death(task_manager): # task uponDeath + tm = task_manager l = [] def _uponDeathFunc(task, l=l): @@ -155,11 +223,11 @@ def test_TaskManager(): tm.step() assert len(l) == 1 assert l == ['testUponDeath'] - _testUponDeath = None - _uponDeathFunc = None - tm._checkMemLeaks() + +def test_task_owner(task_manager): # task owner + tm = task_manager class _TaskOwner: def _addTask(self, task): self.addedTaskName = task.name @@ -175,11 +243,10 @@ def test_TaskManager(): tm.step() assert getattr(to, 'addedTaskName', 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,] # doLater @@ -205,8 +272,6 @@ def test_TaskManager(): _testDoLater1 = None _testDoLater2 = None _monitorDoLater = None - # don't check until all the doLaters are finished - #tm._checkMemLeaks() # doLater sort l = [] @@ -231,8 +296,6 @@ def test_TaskManager(): _testDoLaterPri1 = None _testDoLaterPri2 = None _monitorDoLaterPri = None - # don't check until all the doLaters are finished - #tm._checkMemLeaks() # doLater extraArgs l = [] @@ -252,8 +315,6 @@ def test_TaskManager(): tm.add(_monitorDoLaterExtraArgs, 'monitorDoLaterExtraArgs', sort=10) _testDoLaterExtraArgs = None _monitorDoLaterExtraArgs = None - # don't check until all the doLaters are finished - #tm._checkMemLeaks() # doLater appendTask l = [] @@ -275,8 +336,6 @@ def test_TaskManager(): tm.add(_monitorDoLaterAppendTask, 'monitorDoLaterAppendTask', sort=10) _testDoLaterAppendTask = None _monitorDoLaterAppendTask = None - # don't check until all the doLaters are finished - #tm._checkMemLeaks() # doLater uponDeath l = [] @@ -302,8 +361,6 @@ def test_TaskManager(): _testUponDeathFunc = None _testDoLaterUponDeath = None _monitorDoLaterUponDeath = None - # don't check until all the doLaters are finished - #tm._checkMemLeaks() # doLater owner class _DoLaterOwner: @@ -335,15 +392,15 @@ def test_TaskManager(): _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() + +def test_get_tasks(task_manager): + tm = task_manager # getTasks def _testGetTasks(task): return task.cont @@ -361,9 +418,10 @@ def test_TaskManager(): tm.remove('testGetTasks1') tm.remove('testGetTasks3') assert len(tm.getTasks()) == 0 - _testGetTasks = None - tm._checkMemLeaks() + +def test_get_do_laters(task_manager): + tm = task_manager # getDoLaters def _testGetDoLaters(): pass @@ -379,9 +437,18 @@ def test_TaskManager(): tm.remove('testDoLater1') tm.remove('testDoLater3') 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 def _testDupNameDoLaters(): pass @@ -391,9 +458,10 @@ def test_TaskManager(): assert len(tm.getDoLaters()) == 2 tm.remove('testDupNameDoLater') 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() def _testDupNameDoLatersRemove(): pass @@ -405,10 +473,10 @@ def test_TaskManager(): 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() + +def test_get_tasks_named(task_manager): + tm = task_manager # getTasksNamed def _testGetTasksNamed(task): return task.cont @@ -421,9 +489,20 @@ def test_TaskManager(): assert len(tm.getTasksNamed('testGetTasksNamed')) == 3 tm.remove('testGetTasksNamed') 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 def _testRemoveTasksMatching(task): return task.cont @@ -445,9 +524,10 @@ def test_TaskManager(): tm.removeTasksMatching('testRemoveTasksMatching?a') assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 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 l = [] @@ -463,9 +543,10 @@ def test_TaskManager(): tm.remove('testTaskObj') tm.step() assert len(l) == 2 - _testTaskObj = None - tm._checkMemLeaks() + +def test_task_remove(task_manager): + tm = task_manager # remove Task via task.remove() l = [] @@ -482,9 +563,10 @@ def test_TaskManager(): tm.step() assert len(l) == 2 del t - _testTaskObjRemove = None - tm._checkMemLeaks() + +def test_task_get_sort(task_manager): + tm = task_manager # set/get Task sort l = [] def _testTaskObjSort(arg, task, l=l): @@ -508,11 +590,3 @@ def test_TaskManager(): t2.remove() tm.step() assert len(l) == 4 - del t1 - del t2 - _testTaskObjSort = None - tm._checkMemLeaks() - - del l - tm.destroy() - del tm