TaskNew.py 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
  1. """ This module defines a Python-level wrapper around the C++
  2. AsyncTaskManager interface. It replaces the old full-Python
  3. implementation of the Task system. """
  4. __all__ = ['Task', 'TaskManager',
  5. 'cont', 'done', 'again', 'pickup', 'exit',
  6. 'sequence', 'loop', 'pause']
  7. from direct.directnotify.DirectNotifyGlobal import *
  8. from direct.showbase import ExceptionVarDump
  9. import signal
  10. import types
  11. import time
  12. from pandac.PandaModules import *
  13. def print_exc_plus():
  14. """
  15. Print the usual traceback information, followed by a listing of all the
  16. local variables in each frame.
  17. """
  18. import sys
  19. import traceback
  20. tb = sys.exc_info()[2]
  21. while 1:
  22. if not tb.tb_next:
  23. break
  24. tb = tb.tb_next
  25. stack = []
  26. f = tb.tb_frame
  27. while f:
  28. stack.append(f)
  29. f = f.f_back
  30. stack.reverse()
  31. traceback.print_exc()
  32. print "Locals by frame, innermost last"
  33. for frame in stack:
  34. print
  35. print "Frame %s in %s at line %s" % (frame.f_code.co_name,
  36. frame.f_code.co_filename,
  37. frame.f_lineno)
  38. for key, value in frame.f_locals.items():
  39. print "\t%20s = " % key,
  40. #We have to be careful not to cause a new error in our error
  41. #printer! Calling str() on an unknown object could cause an
  42. #error we don't want.
  43. try:
  44. print value
  45. except:
  46. print "<ERROR WHILE PRINTING VALUE>"
  47. # For historical purposes, we remap the C++-defined enumeration to
  48. # these Python names, and define them both at the module level, here,
  49. # and at the class level (below). The preferred access is via the
  50. # class level.
  51. done = AsyncTask.DSDone
  52. cont = AsyncTask.DSCont
  53. again = AsyncTask.DSAgain
  54. pickup = AsyncTask.DSPickup
  55. exit = AsyncTask.DSExit
  56. # Alias PythonTask to Task for historical purposes.
  57. Task = PythonTask
  58. # Copy the module-level enums above into the class level. This funny
  59. # syntax is necessary because it's a C++-wrapped extension type, not a
  60. # true Python class.
  61. Task.DtoolClassDict['done'] = done
  62. Task.DtoolClassDict['cont'] = cont
  63. Task.DtoolClassDict['again'] = again
  64. Task.DtoolClassDict['pickup'] = pickup
  65. Task.DtoolClassDict['exit'] = exit
  66. # Alias the AsyncTaskPause constructor as Task.pause().
  67. pause = AsyncTaskPause
  68. Task.DtoolClassDict['pause'] = staticmethod(pause)
  69. def sequence(*taskList):
  70. seq = AsyncTaskSequence('sequence')
  71. for task in taskList:
  72. seq.addTask(task)
  73. return seq
  74. Task.DtoolClassDict['sequence'] = staticmethod(sequence)
  75. def loop(*taskList):
  76. seq = AsyncTaskSequence('loop')
  77. for task in taskList:
  78. seq.addTask(task)
  79. seq.setRepeatCount(-1)
  80. return seq
  81. Task.DtoolClassDict['loop'] = staticmethod(loop)
  82. class TaskManager:
  83. notify = directNotify.newCategory("TaskManager")
  84. extendedExceptions = False
  85. MaxEpochSpeed = 1.0/30.0
  86. def __init__(self):
  87. self.mgr = AsyncTaskManager.getGlobalPtr()
  88. self.resumeFunc = None
  89. self.globalClock = self.mgr.getClock()
  90. self.stepping = False
  91. self.running = False
  92. self.fKeyboardInterrupt = False
  93. self.interruptCount = 0
  94. def destroy(self):
  95. self.mgr.cleanup()
  96. def setClock(self, clockObject):
  97. self.mgr.setClock(clockObject)
  98. self.globalClock = clockObject
  99. def invokeDefaultHandler(self, signalNumber, stackFrame):
  100. print '*** allowing mid-frame keyboard interrupt.'
  101. # Restore default interrupt handler
  102. signal.signal(signal.SIGINT, signal.default_int_handler)
  103. # and invoke it
  104. raise KeyboardInterrupt
  105. def keyboardInterruptHandler(self, signalNumber, stackFrame):
  106. self.fKeyboardInterrupt = 1
  107. self.interruptCount += 1
  108. if self.interruptCount == 1:
  109. print '* interrupt by keyboard'
  110. elif self.interruptCount == 2:
  111. print '** waiting for end of frame before interrupting...'
  112. # The user must really want to interrupt this process
  113. # Next time around invoke the default handler
  114. signal.signal(signal.SIGINT, self.invokeDefaultHandler)
  115. def setupTaskChain(self, chainName, numThreads = None, tickClock = None,
  116. threadPriority = None, frameBudget = None,
  117. timeslicePriority = None):
  118. """Defines a new task chain. Each task chain executes tasks
  119. potentially in parallel with all of the other task chains (if
  120. numThreads is more than zero). When a new task is created, it
  121. may be associated with any of the task chains, by name (or you
  122. can move a task to another task chain with
  123. task.setTaskChain()). You can have any number of task chains,
  124. but each must have a unique name.
  125. numThreads is the number of threads to allocate for this task
  126. chain. If it is 1 or more, then the tasks on this task chain
  127. will execute in parallel with the tasks on other task chains.
  128. If it is greater than 1, then the tasks on this task chain may
  129. execute in parallel with themselves (within tasks of the same
  130. sort value).
  131. If tickClock is True, then this task chain will be responsible
  132. for ticking the global clock each frame (and thereby
  133. incrementing the frame counter). There should be just one
  134. task chain responsible for ticking the clock, and usually it
  135. is the default, unnamed task chain.
  136. threadPriority specifies the priority level to assign to
  137. threads on this task chain. It may be one of TPLow, TPNormal,
  138. TPHigh, or TPUrgent. This is passed to the underlying
  139. threading system to control the way the threads are scheduled.
  140. frameBudget is the maximum amount of time (in seconds) to
  141. allow this task chain to run per frame. Set it to -1 to mean
  142. no limit (the default). It's not directly related to
  143. threadPriority.
  144. timeslicePriority is False in the default mode, in which each
  145. task runs exactly once each frame, round-robin style,
  146. regardless of the task's priority value; or True to change the
  147. meaning of priority so that certain tasks are run less often,
  148. in proportion to their time used and to their priority value.
  149. See AsyncTaskManager.setTimeslicePriority() for more.
  150. """
  151. chain = self.mgr.makeTaskChain(chainName)
  152. if numThreads is not None:
  153. chain.setNumThreads(numThreads)
  154. if tickClock is not None:
  155. chain.setTickClock(tickClock)
  156. if threadPriority is not None:
  157. chain.setThreadPriority(threadPriority)
  158. if frameBudget is not None:
  159. chain.setFrameBudget(frameBudget)
  160. if timeslicePriority is not None:
  161. chain.setTimeslicePriority(timeslicePriority)
  162. def hasTaskNamed(self, taskName):
  163. """Returns true if there is at least one task, active or
  164. sleeping, with the indicated name. """
  165. return bool(self.mgr.findTask(taskName))
  166. def getTasksNamed(self, taskName):
  167. """Returns a list of all tasks, active or sleeping, with the
  168. indicated name. """
  169. return self.__makeTaskList(self.mgr.findTasks(taskName))
  170. def getAllTasks(self):
  171. """Returns list of all tasks, active and sleeping, in
  172. arbitrary order. """
  173. return self.__makeTaskList(self.mgr.getTasks())
  174. def getTasks(self):
  175. """Returns list of all active tasks in arbitrary order. """
  176. return self.__makeTaskList(self.mgr.getActiveTasks())
  177. def getDoLaters(self):
  178. """Returns list of all sleeping tasks in arbitrary order. """
  179. return self.__makeTaskList(self.mgr.getSleepingTasks())
  180. def __makeTaskList(self, taskCollection):
  181. l = []
  182. for i in range(taskCollection.getNumTasks()):
  183. l.append(taskCollection.getTask(i))
  184. return l
  185. def doMethodLater(self, delayTime, funcOrTask, name, extraArgs = None,
  186. sort = None, priority = None, taskChain = None,
  187. uponDeath = None, appendTask = False, owner = None):
  188. """Adds a task to be performed at some time in the future.
  189. This is identical to add(), except that the specified
  190. delayTime is applied to the Task object first, which means
  191. that the task will not begin executing until at least the
  192. indicated delayTime (in seconds) has elapsed.
  193. After delayTime has elapsed, the task will become active, and
  194. will run in the soonest possible frame thereafter. If you
  195. wish to specify a task that will run in the next frame, use a
  196. delayTime of 0.
  197. """
  198. if delayTime < 0:
  199. assert self.notify.warning('doMethodLater: added task: %s with negative delay: %s' % (name, delayTime))
  200. task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
  201. task.setDelay(delayTime)
  202. self.mgr.add(task)
  203. return task
  204. def add(self, funcOrTask, name = None, sort = None, extraArgs = None,
  205. priority = None, uponDeath = None, appendTask = False,
  206. taskChain = None, owner = None):
  207. """
  208. Add a new task to the taskMgr. The task will begin executing
  209. immediately, or next frame if its sort value has already
  210. passed this frame.
  211. The parameters are:
  212. funcOrTask - either an existing Task object (not already added
  213. to the task manager), or a callable function object. If this
  214. is a function, a new Task object will be created and returned.
  215. name - the name to assign to the Task. Required, unless you
  216. are passing in a Task object that already has a name.
  217. extraArgs - the list of arguments to pass to the task
  218. function. If this is omitted, the list is just the task
  219. object itself.
  220. appendTask - a boolean flag. If this is true, then the task
  221. object itself will be appended to the end of the extraArgs
  222. list before calling the function.
  223. sort - the sort value to assign the task. The default sort is
  224. 0. Within a particular task chain, it is guaranteed that the
  225. tasks with a lower sort value will all run before tasks with a
  226. higher sort value run.
  227. priority - the priority at which to run the task. The default
  228. priority is 0. Higher priority tasks are run sooner, and/or
  229. more often. For historical purposes, if you specify a
  230. priority without also specifying a sort, the priority value is
  231. understood to actually be a sort value. (Previously, there
  232. was no priority value, only a sort value, and it was called
  233. "priority".)
  234. uponDeath - a function to call when the task terminates,
  235. either because it has run to completion, or because it has
  236. been explicitly removed.
  237. taskChain - the name of the task chain to assign the task to.
  238. owner - an optional Python object that is declared as the
  239. "owner" of this task for maintenance purposes. The owner must
  240. have two methods: owner._addTask(self, task), which is called
  241. when the task begins, and owner._clearTask(self, task), which
  242. is called when the task terminates. This is all the owner
  243. means.
  244. The return value of add() is the new Task object that has been
  245. added, or the original Task object that was passed in.
  246. """
  247. task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
  248. self.mgr.add(task)
  249. return task
  250. def __setupTask(self, funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath):
  251. if isinstance(funcOrTask, AsyncTask):
  252. task = funcOrTask
  253. elif callable(funcOrTask):
  254. task = PythonTask(funcOrTask)
  255. else:
  256. self.notify.error(
  257. 'add: Tried to add a task that was not a Task or a func')
  258. if hasattr(task, 'setArgs'):
  259. # It will only accept arguments if it's a PythonTask.
  260. if extraArgs is None:
  261. extraArgs = []
  262. appendTask = True
  263. task.setArgs(extraArgs, appendTask)
  264. elif extraArgs is not None:
  265. self.notify.error(
  266. 'Task %s does not accept arguments.' % (repr(task)))
  267. if name is not None:
  268. assert isinstance(name, types.StringTypes), 'Name must be a string type'
  269. task.setName(name)
  270. assert task.hasName()
  271. # For historical reasons, if priority is specified but not
  272. # sort, it really means sort.
  273. if priority is not None and sort is None:
  274. task.setSort(priority)
  275. else:
  276. if priority is not None:
  277. task.setPriority(priority)
  278. if sort is not None:
  279. task.setSort(sort)
  280. if taskChain is not None:
  281. task.setTaskChain(taskChain)
  282. if owner is not None:
  283. task.setOwner(owner)
  284. if uponDeath is not None:
  285. task.setUponDeath(uponDeath)
  286. return task
  287. def remove(self, taskOrName):
  288. """Removes a task from the task manager. The task is stopped,
  289. almost as if it had returned task.done. (But if the task is
  290. currently executing, it will finish out its current frame
  291. before being removed.) You may specify either an explicit
  292. Task object, or the name of a task. If you specify a name,
  293. all tasks with the indicated name are removed. Returns the
  294. number of tasks removed. """
  295. if isinstance(taskOrName, types.StringTypes):
  296. tasks = self.mgr.findTasks(taskOrName)
  297. return self.mgr.remove(tasks)
  298. elif isinstance(taskOrName, AsyncTask):
  299. return self.mgr.remove(taskOrName)
  300. else:
  301. self.notify.error('remove takes a string or a Task')
  302. def removeTasksMatching(self, taskPattern):
  303. """Removes all tasks whose names match the pattern, which can
  304. include standard shell globbing characters like *, ?, and [].
  305. See also remove().
  306. Returns the number of tasks removed.
  307. """
  308. tasks = self.mgr.findTasksMatching(GlobPattern(taskPattern))
  309. return self.mgr.remove(tasks)
  310. def step(self):
  311. """Invokes the task manager for one frame, and then returns.
  312. Normally, this executes each task exactly once, though task
  313. chains that are in sub-threads or that have frame budgets
  314. might execute their tasks differently. """
  315. # Replace keyboard interrupt handler during task list processing
  316. # so we catch the keyboard interrupt but don't handle it until
  317. # after task list processing is complete.
  318. self.fKeyboardInterrupt = 0
  319. self.interruptCount = 0
  320. signal.signal(signal.SIGINT, self.keyboardInterruptHandler)
  321. startFrameTime = self.globalClock.getRealTime()
  322. self.mgr.poll()
  323. # This is the spot for an internal yield function
  324. nextTaskTime = self.mgr.getNextWakeTime()
  325. self.doYield(startFrameTime, nextTaskTime)
  326. # Restore default interrupt handler
  327. signal.signal(signal.SIGINT, signal.default_int_handler)
  328. if self.fKeyboardInterrupt:
  329. raise KeyboardInterrupt
  330. def run(self):
  331. """Starts the task manager running. Does not return until an
  332. exception is encountered (including KeyboardInterrupt). """
  333. # Set the clock to have last frame's time in case we were
  334. # Paused at the prompt for a long time
  335. t = self.globalClock.getFrameTime()
  336. timeDelta = t - self.globalClock.getRealTime()
  337. self.globalClock.setRealTime(t)
  338. messenger.send("resetClock", [timeDelta])
  339. if self.resumeFunc != None:
  340. self.resumeFunc()
  341. if self.stepping:
  342. self.step()
  343. else:
  344. self.running = True
  345. while self.running:
  346. try:
  347. self.step()
  348. except KeyboardInterrupt:
  349. self.stop()
  350. except IOError, ioError:
  351. code, message = self._unpackIOError(ioError)
  352. # Since upgrading to Python 2.4.1, pausing the execution
  353. # often gives this IOError during the sleep function:
  354. # IOError: [Errno 4] Interrupted function call
  355. # So, let's just handle that specific exception and stop.
  356. # All other IOErrors should still get raised.
  357. # Only problem: legit IOError 4s will be obfuscated.
  358. if code == 4:
  359. self.stop()
  360. else:
  361. raise
  362. except Exception, e:
  363. if self.extendedExceptions:
  364. self.stop()
  365. print_exc_plus()
  366. else:
  367. if (ExceptionVarDump.wantVariableDump and
  368. ExceptionVarDump.dumpOnExceptionInit):
  369. ExceptionVarDump._varDump__print(e)
  370. raise
  371. except:
  372. if self.extendedExceptions:
  373. self.stop()
  374. print_exc_plus()
  375. else:
  376. raise
  377. self.mgr.stopThreads()
  378. def _unpackIOError(self, ioError):
  379. # IOError unpack from http://www.python.org/doc/essays/stdexceptions/
  380. # this needs to be in its own method, exceptions that occur inside
  381. # a nested try block are not caught by the inner try block's except
  382. try:
  383. (code, message) = ioError
  384. except:
  385. code = 0
  386. message = ioError
  387. return code, message
  388. def stop(self):
  389. # Set a flag so we will stop before beginning next frame
  390. self.running = False
  391. def __tryReplaceTaskMethod(self, task, oldMethod, newFunction):
  392. if not isinstance(task, PythonTask):
  393. return
  394. method = task.getFunction()
  395. if (type(method) == types.MethodType):
  396. function = method.im_func
  397. else:
  398. function = method
  399. if (function == oldMethod):
  400. import new
  401. newMethod = new.instancemethod(newFunction,
  402. method.im_self,
  403. method.im_class)
  404. task.setFunction(newMethod)
  405. # Found a match
  406. return 1
  407. return 0
  408. def replaceMethod(self, oldMethod, newFunction):
  409. numFound = 0
  410. for task in self.getAllTasks():
  411. numFound += self.__tryReplaceTaskMethod(task, oldMethod, newFunction)
  412. return numFound
  413. def popupControls(self):
  414. from direct.tkpanels import TaskManagerPanel
  415. return TaskManagerPanel.TaskManagerPanel(self)
  416. def __repr__(self):
  417. return str(self.mgr)
  418. # In the event we want to do frame time managment, this is the
  419. # function to replace or overload.
  420. def doYield(self, frameStartTime, nextScheduledTaskTime):
  421. pass
  422. """
  423. def doYieldExample(self, frameStartTime, nextScheduledTaskTime):
  424. minFinTime = frameStartTime + self.MaxEpochSpeed
  425. if nextScheduledTaskTime > 0 and nextScheduledTaskTime < minFinTime:
  426. print ' Adjusting Time'
  427. minFinTime = nextScheduledTaskTime
  428. delta = minFinTime - self.globalClock.getRealTime()
  429. while(delta > 0.002):
  430. print ' sleep %s'% (delta)
  431. time.sleep(delta)
  432. delta = minFinTime - self.globalClock.getRealTime()
  433. """
  434. if __debug__:
  435. # to catch memory leaks during the tests at the bottom of the file
  436. def _startTrackingMemLeaks(self):
  437. pass
  438. def _stopTrackingMemLeaks(self):
  439. pass
  440. def _checkMemLeaks(self):
  441. pass
  442. def _runTests(self):
  443. if __debug__:
  444. tm = TaskManager()
  445. tm.setClock(ClockObject())
  446. tm.setupTaskChain("default", tickClock = True)
  447. # check for memory leaks after every test
  448. tm._startTrackingMemLeaks()
  449. tm._checkMemLeaks()
  450. # run-once task
  451. l = []
  452. def _testDone(task, l=l):
  453. l.append(None)
  454. return task.done
  455. tm.add(_testDone, 'testDone')
  456. tm.step()
  457. assert len(l) == 1
  458. tm.step()
  459. assert len(l) == 1
  460. _testDone = None
  461. tm._checkMemLeaks()
  462. # remove by name
  463. def _testRemoveByName(task):
  464. return task.done
  465. tm.add(_testRemoveByName, 'testRemoveByName')
  466. assert tm.remove('testRemoveByName') == 1
  467. assert tm.remove('testRemoveByName') == 0
  468. _testRemoveByName = None
  469. tm._checkMemLeaks()
  470. # duplicate named tasks
  471. def _testDupNamedTasks(task):
  472. return task.done
  473. tm.add(_testDupNamedTasks, 'testDupNamedTasks')
  474. tm.add(_testDupNamedTasks, 'testDupNamedTasks')
  475. assert tm.remove('testRemoveByName') == 0
  476. _testDupNamedTasks = None
  477. tm._checkMemLeaks()
  478. # continued task
  479. l = []
  480. def _testCont(task, l = l):
  481. l.append(None)
  482. return task.cont
  483. tm.add(_testCont, 'testCont')
  484. tm.step()
  485. assert len(l) == 1
  486. tm.step()
  487. assert len(l) == 2
  488. tm.remove('testCont')
  489. _testCont = None
  490. tm._checkMemLeaks()
  491. # continue until done task
  492. l = []
  493. def _testContDone(task, l = l):
  494. l.append(None)
  495. if len(l) >= 2:
  496. return task.done
  497. else:
  498. return task.cont
  499. tm.add(_testContDone, 'testContDone')
  500. tm.step()
  501. assert len(l) == 1
  502. tm.step()
  503. assert len(l) == 2
  504. tm.step()
  505. assert len(l) == 2
  506. assert not tm.hasTaskNamed('testContDone')
  507. _testContDone = None
  508. tm._checkMemLeaks()
  509. # hasTaskNamed
  510. def _testHasTaskNamed(task):
  511. return task.done
  512. tm.add(_testHasTaskNamed, 'testHasTaskNamed')
  513. assert tm.hasTaskNamed('testHasTaskNamed')
  514. tm.step()
  515. assert not tm.hasTaskNamed('testHasTaskNamed')
  516. _testHasTaskNamed = None
  517. tm._checkMemLeaks()
  518. # task sort
  519. l = []
  520. def _testPri1(task, l = l):
  521. l.append(1)
  522. return task.cont
  523. def _testPri2(task, l = l):
  524. l.append(2)
  525. return task.cont
  526. tm.add(_testPri1, 'testPri1', sort = 1)
  527. tm.add(_testPri2, 'testPri2', sort = 2)
  528. tm.step()
  529. assert len(l) == 2
  530. assert l == [1, 2,]
  531. tm.step()
  532. assert len(l) == 4
  533. assert l == [1, 2, 1, 2,]
  534. tm.remove('testPri1')
  535. tm.remove('testPri2')
  536. _testPri1 = None
  537. _testPri2 = None
  538. tm._checkMemLeaks()
  539. # task extraArgs
  540. l = []
  541. def _testExtraArgs(arg1, arg2, l=l):
  542. l.extend([arg1, arg2,])
  543. return done
  544. tm.add(_testExtraArgs, 'testExtraArgs', extraArgs=[4,5])
  545. tm.step()
  546. assert len(l) == 2
  547. assert l == [4, 5,]
  548. _testExtraArgs = None
  549. tm._checkMemLeaks()
  550. # task appendTask
  551. l = []
  552. def _testAppendTask(arg1, arg2, task, l=l):
  553. l.extend([arg1, arg2,])
  554. return task.done
  555. tm.add(_testAppendTask, '_testAppendTask', extraArgs=[4,5], appendTask=True)
  556. tm.step()
  557. assert len(l) == 2
  558. assert l == [4, 5,]
  559. _testAppendTask = None
  560. tm._checkMemLeaks()
  561. # task uponDeath
  562. l = []
  563. def _uponDeathFunc(task, l=l):
  564. l.append(task.name)
  565. def _testUponDeath(task):
  566. return done
  567. tm.add(_testUponDeath, 'testUponDeath', uponDeath=_uponDeathFunc)
  568. tm.step()
  569. assert len(l) == 1
  570. assert l == ['testUponDeath']
  571. _testUponDeath = None
  572. _uponDeathFunc = None
  573. tm._checkMemLeaks()
  574. # task owner
  575. class _TaskOwner:
  576. def _addTask(self, task):
  577. self.addedTaskName = task.name
  578. def _clearTask(self, task):
  579. self.clearedTaskName = task.name
  580. to = _TaskOwner()
  581. l = []
  582. def _testOwner(task):
  583. return done
  584. tm.add(_testOwner, 'testOwner', owner=to)
  585. tm.step()
  586. assert getattr(to, 'addedTaskName', None) == 'testOwner'
  587. assert getattr(to, 'clearedTaskName', None) == 'testOwner'
  588. _testOwner = None
  589. del to
  590. _TaskOwner = None
  591. tm._checkMemLeaks()
  592. doLaterTests = [0,]
  593. # doLater
  594. l = []
  595. def _testDoLater1(task, l=l):
  596. l.append(1)
  597. def _testDoLater2(task, l=l):
  598. l.append(2)
  599. def _monitorDoLater(task, tm=tm, l=l, doLaterTests=doLaterTests):
  600. if task.time > .03:
  601. assert l == [1, 2,]
  602. doLaterTests[0] -= 1
  603. return task.done
  604. return task.cont
  605. tm.doMethodLater(.01, _testDoLater1, 'testDoLater1')
  606. tm.doMethodLater(.02, _testDoLater2, 'testDoLater2')
  607. doLaterTests[0] += 1
  608. # make sure we run this task after the doLaters if they all occur on the same frame
  609. tm.add(_monitorDoLater, 'monitorDoLater', sort=10)
  610. _testDoLater1 = None
  611. _testDoLater2 = None
  612. _monitorDoLater = None
  613. # don't check until all the doLaters are finished
  614. #tm._checkMemLeaks()
  615. # doLater sort
  616. l = []
  617. def _testDoLaterPri1(task, l=l):
  618. l.append(1)
  619. def _testDoLaterPri2(task, l=l):
  620. l.append(2)
  621. def _monitorDoLaterPri(task, tm=tm, l=l, doLaterTests=doLaterTests):
  622. if task.time > .02:
  623. assert l == [1, 2,]
  624. doLaterTests[0] -= 1
  625. return task.done
  626. return task.cont
  627. tm.doMethodLater(.01, _testDoLaterPri1, 'testDoLaterPri1', sort=1)
  628. tm.doMethodLater(.01, _testDoLaterPri2, 'testDoLaterPri2', sort=2)
  629. doLaterTests[0] += 1
  630. # make sure we run this task after the doLaters if they all occur on the same frame
  631. tm.add(_monitorDoLaterPri, 'monitorDoLaterPri', sort=10)
  632. _testDoLaterPri1 = None
  633. _testDoLaterPri2 = None
  634. _monitorDoLaterPri = None
  635. # don't check until all the doLaters are finished
  636. #tm._checkMemLeaks()
  637. # doLater extraArgs
  638. l = []
  639. def _testDoLaterExtraArgs(arg1, l=l):
  640. l.append(arg1)
  641. def _monitorDoLaterExtraArgs(task, tm=tm, l=l, doLaterTests=doLaterTests):
  642. if task.time > .02:
  643. assert l == [3,]
  644. doLaterTests[0] -= 1
  645. return task.done
  646. return task.cont
  647. tm.doMethodLater(.01, _testDoLaterExtraArgs, 'testDoLaterExtraArgs', extraArgs=[3,])
  648. doLaterTests[0] += 1
  649. # make sure we run this task after the doLaters if they all occur on the same frame
  650. tm.add(_monitorDoLaterExtraArgs, 'monitorDoLaterExtraArgs', sort=10)
  651. _testDoLaterExtraArgs = None
  652. _monitorDoLaterExtraArgs = None
  653. # don't check until all the doLaters are finished
  654. #tm._checkMemLeaks()
  655. # doLater appendTask
  656. l = []
  657. def _testDoLaterAppendTask(arg1, task, l=l):
  658. assert task.name == 'testDoLaterAppendTask'
  659. l.append(arg1)
  660. def _monitorDoLaterAppendTask(task, tm=tm, l=l, doLaterTests=doLaterTests):
  661. if task.time > .02:
  662. assert l == [4,]
  663. doLaterTests[0] -= 1
  664. return task.done
  665. return task.cont
  666. tm.doMethodLater(.01, _testDoLaterAppendTask, 'testDoLaterAppendTask',
  667. extraArgs=[4,], appendTask=True)
  668. doLaterTests[0] += 1
  669. # make sure we run this task after the doLaters if they all occur on the same frame
  670. tm.add(_monitorDoLaterAppendTask, 'monitorDoLaterAppendTask', sort=10)
  671. _testDoLaterAppendTask = None
  672. _monitorDoLaterAppendTask = None
  673. # don't check until all the doLaters are finished
  674. #tm._checkMemLeaks()
  675. # doLater uponDeath
  676. l = []
  677. def _testUponDeathFunc(task, l=l):
  678. assert task.name == 'testDoLaterUponDeath'
  679. l.append(10)
  680. def _testDoLaterUponDeath(arg1, l=l):
  681. return done
  682. def _monitorDoLaterUponDeath(task, tm=tm, l=l, doLaterTests=doLaterTests):
  683. if task.time > .02:
  684. assert l == [10,]
  685. doLaterTests[0] -= 1
  686. return task.done
  687. return task.cont
  688. tm.doMethodLater(.01, _testDoLaterUponDeath, 'testDoLaterUponDeath',
  689. uponDeath=_testUponDeathFunc)
  690. doLaterTests[0] += 1
  691. # make sure we run this task after the doLaters if they all occur on the same frame
  692. tm.add(_monitorDoLaterUponDeath, 'monitorDoLaterUponDeath', sort=10)
  693. _testUponDeathFunc = None
  694. _testDoLaterUponDeath = None
  695. _monitorDoLaterUponDeath = None
  696. # don't check until all the doLaters are finished
  697. #tm._checkMemLeaks()
  698. # doLater owner
  699. class _DoLaterOwner:
  700. def _addTask(self, task):
  701. self.addedTaskName = task.name
  702. def _clearTask(self, task):
  703. self.clearedTaskName = task.name
  704. doLaterOwner = _DoLaterOwner()
  705. l = []
  706. def _testDoLaterOwner(l=l):
  707. pass
  708. def _monitorDoLaterOwner(task, tm=tm, l=l, doLaterOwner=doLaterOwner,
  709. doLaterTests=doLaterTests):
  710. if task.time > .02:
  711. assert getattr(doLaterOwner, 'addedTaskName', None) == 'testDoLaterOwner'
  712. assert getattr(doLaterOwner, 'clearedTaskName', None) == 'testDoLaterOwner'
  713. doLaterTests[0] -= 1
  714. return task.done
  715. return task.cont
  716. tm.doMethodLater(.01, _testDoLaterOwner, 'testDoLaterOwner',
  717. owner=doLaterOwner)
  718. doLaterTests[0] += 1
  719. # make sure we run this task after the doLaters if they all occur on the same frame
  720. tm.add(_monitorDoLaterOwner, 'monitorDoLaterOwner', sort=10)
  721. _testDoLaterOwner = None
  722. _monitorDoLaterOwner = None
  723. del doLaterOwner
  724. _DoLaterOwner = None
  725. # don't check until all the doLaters are finished
  726. #tm._checkMemLeaks()
  727. # run the doLater tests
  728. while doLaterTests[0] > 0:
  729. tm.step()
  730. del doLaterTests
  731. tm._checkMemLeaks()
  732. # getTasks
  733. def _testGetTasks(task):
  734. return task.cont
  735. # No doLaterProcessor in the new world.
  736. assert len(tm.getTasks()) == 0
  737. tm.add(_testGetTasks, 'testGetTasks1')
  738. assert len(tm.getTasks()) == 1
  739. assert (tm.getTasks()[0].name == 'testGetTasks1' or
  740. tm.getTasks()[1].name == 'testGetTasks1')
  741. tm.add(_testGetTasks, 'testGetTasks2')
  742. tm.add(_testGetTasks, 'testGetTasks3')
  743. assert len(tm.getTasks()) == 3
  744. tm.remove('testGetTasks2')
  745. assert len(tm.getTasks()) == 2
  746. tm.remove('testGetTasks1')
  747. tm.remove('testGetTasks3')
  748. assert len(tm.getTasks()) == 0
  749. _testGetTasks = None
  750. tm._checkMemLeaks()
  751. # getDoLaters
  752. def _testGetDoLaters():
  753. pass
  754. assert len(tm.getDoLaters()) == 0
  755. tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater1')
  756. assert len(tm.getDoLaters()) == 1
  757. assert tm.getDoLaters()[0].name == 'testDoLater1'
  758. tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater2')
  759. tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater3')
  760. assert len(tm.getDoLaters()) == 3
  761. tm.remove('testDoLater2')
  762. assert len(tm.getDoLaters()) == 2
  763. tm.remove('testDoLater1')
  764. tm.remove('testDoLater3')
  765. assert len(tm.getDoLaters()) == 0
  766. _testGetDoLaters = None
  767. tm._checkMemLeaks()
  768. # duplicate named doLaters removed via taskMgr.remove
  769. def _testDupNameDoLaters():
  770. pass
  771. # the doLaterProcessor is always running
  772. tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
  773. tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
  774. assert len(tm.getDoLaters()) == 2
  775. tm.remove('testDupNameDoLater')
  776. assert len(tm.getDoLaters()) == 0
  777. _testDupNameDoLaters = None
  778. tm._checkMemLeaks()
  779. # duplicate named doLaters removed via remove()
  780. def _testDupNameDoLatersRemove():
  781. pass
  782. # the doLaterProcessor is always running
  783. dl1 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
  784. dl2 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
  785. assert len(tm.getDoLaters()) == 2
  786. dl2.remove()
  787. assert len(tm.getDoLaters()) == 1
  788. dl1.remove()
  789. assert len(tm.getDoLaters()) == 0
  790. _testDupNameDoLatersRemove = None
  791. # nameDict etc. isn't cleared out right away with task.remove()
  792. tm._checkMemLeaks()
  793. # getTasksNamed
  794. def _testGetTasksNamed(task):
  795. return task.cont
  796. assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
  797. tm.add(_testGetTasksNamed, 'testGetTasksNamed')
  798. assert len(tm.getTasksNamed('testGetTasksNamed')) == 1
  799. assert tm.getTasksNamed('testGetTasksNamed')[0].name == 'testGetTasksNamed'
  800. tm.add(_testGetTasksNamed, 'testGetTasksNamed')
  801. tm.add(_testGetTasksNamed, 'testGetTasksNamed')
  802. assert len(tm.getTasksNamed('testGetTasksNamed')) == 3
  803. tm.remove('testGetTasksNamed')
  804. assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
  805. _testGetTasksNamed = None
  806. tm._checkMemLeaks()
  807. # removeTasksMatching
  808. def _testRemoveTasksMatching(task):
  809. return task.cont
  810. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching')
  811. assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 1
  812. tm.removeTasksMatching('testRemoveTasksMatching')
  813. assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 0
  814. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1')
  815. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2')
  816. assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 1
  817. assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 1
  818. tm.removeTasksMatching('testRemoveTasksMatching*')
  819. assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 0
  820. assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 0
  821. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1a')
  822. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2a')
  823. assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 1
  824. assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 1
  825. tm.removeTasksMatching('testRemoveTasksMatching?a')
  826. assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 0
  827. assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 0
  828. _testRemoveTasksMatching = None
  829. tm._checkMemLeaks()
  830. # create Task object and add to mgr
  831. l = []
  832. def _testTaskObj(task, l=l):
  833. l.append(None)
  834. return task.cont
  835. t = Task(_testTaskObj)
  836. tm.add(t, 'testTaskObj')
  837. tm.step()
  838. assert len(l) == 1
  839. tm.step()
  840. assert len(l) == 2
  841. tm.remove('testTaskObj')
  842. tm.step()
  843. assert len(l) == 2
  844. _testTaskObj = None
  845. tm._checkMemLeaks()
  846. # remove Task via task.remove()
  847. l = []
  848. def _testTaskObjRemove(task, l=l):
  849. l.append(None)
  850. return task.cont
  851. t = Task(_testTaskObjRemove)
  852. tm.add(t, 'testTaskObjRemove')
  853. tm.step()
  854. assert len(l) == 1
  855. tm.step()
  856. assert len(l) == 2
  857. t.remove()
  858. tm.step()
  859. assert len(l) == 2
  860. del t
  861. _testTaskObjRemove = None
  862. tm._checkMemLeaks()
  863. """
  864. # this test fails, and it's not clear what the correct behavior should be.
  865. # sort passed to Task.__init__ is always overridden by taskMgr.add()
  866. # even if no sort is specified, and calling Task.setSort() has no
  867. # effect on the taskMgr's behavior.
  868. # set/get Task sort
  869. l = []
  870. def _testTaskObjSort(arg, task, l=l):
  871. l.append(arg)
  872. return task.cont
  873. t1 = Task(_testTaskObjSort, sort=1)
  874. t2 = Task(_testTaskObjSort, sort=2)
  875. tm.add(t1, 'testTaskObjSort1', extraArgs=['a',], appendTask=True)
  876. tm.add(t2, 'testTaskObjSort2', extraArgs=['b',], appendTask=True)
  877. tm.step()
  878. assert len(l) == 2
  879. assert l == ['a', 'b']
  880. assert t1.getSort() == 1
  881. assert t2.getSort() == 2
  882. t1.setSort(3)
  883. assert t1.getSort() == 3
  884. tm.step()
  885. assert len(l) == 4
  886. assert l == ['a', 'b', 'b', 'a',]
  887. t1.remove()
  888. t2.remove()
  889. tm.step()
  890. assert len(l) == 4
  891. del t1
  892. del t2
  893. _testTaskObjSort = None
  894. tm._checkMemLeaks()
  895. """
  896. del l
  897. tm.destroy()
  898. del tm