def __init__(self,
              name,
              log=True,
              verbose=False,
              fullReport=False,
              findCycles=True,
              threaded=False,
              doneCallback=None,
              autoDestroy=False,
              priority=None,
              safeMode=False,
              delOnly=False,
              collect=True):
     # if autoDestroy is True, GarbageReport will self-destroy after logging
     # if false, caller is responsible for calling destroy()
     # if threaded is True, processing will be performed over multiple frames
     # if collect is False, we assume that the caller just did a collect and the results
     # are still in gc.garbage
     Job.__init__(self, name)
     # stick the arguments onto a ScratchPad so we can delete them all at once
     self._args = ScratchPad(name=name,
                             log=log,
                             verbose=verbose,
                             fullReport=fullReport,
                             findCycles=findCycles,
                             doneCallback=doneCallback,
                             autoDestroy=autoDestroy,
                             safeMode=safeMode,
                             delOnly=delOnly,
                             collect=collect)
     if priority is not None:
         self.setPriority(priority)
     jobMgr.add(self)
     if not threaded:
         jobMgr.finish(self)
 def getAvatarDetails(self, avatar, func, *args):
     pad = ScratchPad()
     pad.func = func
     pad.args = args
     pad.avatar = avatar
     pad.delayDelete = DelayDelete.DelayDelete(avatar, 'getAvatarDetails')
     avId = avatar.doId
     self.__queryAvatarMap[avId] = pad
     self.__sendGetAvatarDetails(avId)
    def getAvatarDetails(self, avatar, func, *args):
        avId = avatar.doId
        if avId in self.doId2do:
            func(1, self.doId2do[avId], *args)
            return

        pad = ScratchPad()
        pad.func = func
        pad.args = args
        pad.avatar = avatar
        pad.delayDelete = DelayDelete.DelayDelete(avatar, 'getAvatarDetails')
        self.__queryAvatarMap[avId] = pad
        self.__sendGetAvatarDetails(avId)
 def getAvatarDetails(self, avatar, func, *args):
     pad = ScratchPad()
     pad.func = func
     pad.args = args
     pad.avatar = avatar
     pad.delayDelete = DelayDelete.DelayDelete(avatar, 'getAvatarDetails')
     self.__queryAvatarMap[avatar.doId] = pad
     self.__sendGetAvatarDetails(avatar.doId, pet=(args[0].endswith("Pet")))
 def getAvatarDetails(self, avatar, func, *args):
     pad = ScratchPad()
     pad.func = func
     pad.args = args
     pad.avatar = avatar
     pad.delayDelete = DelayDelete.DelayDelete(avatar, 'getAvatarDetails')
     avId = avatar.doId
     self.__queryAvatarMap[avId] = pad
     self.__sendGetAvatarDetails(avId)
    def getAvatarDetails(self, avatar, func, *args):
        avId = avatar.doId
        if avId in self.doId2do:
            func(1, self.doId2do[avId], *args)
            return

        pad = ScratchPad()
        pad.func = func
        pad.args = args
        pad.avatar = avatar
        pad.delayDelete = DelayDelete.DelayDelete(avatar, 'getAvatarDetails')
        self.__queryAvatarMap[avId] = pad
        self.__sendGetAvatarDetails(avId)
Exemple #7
0
    def doMagicWord(self, word, avId, zoneId):
        wordIs = self.getWordIs(word)
        print word
        if wordIs('~oobe'):
            base.oobe()
        elif wordIs('~oobeCull'):
            base.oobeCull()
        elif wordIs('~tex'):
            self.doTex(word)
        elif wordIs('~texmem'):
            base.toggleTexMem()
        elif wordIs('~verts'):
            base.toggleShowVertices()
        elif wordIs('~wire'):
            base.toggleWireframe()
        elif wordIs('~stereo'):
            base.toggleStereo()
        elif wordIs('~showfont'):
            self.showfont(word[9:])
        elif wordIs('~hidefont'):
            self.hidefont()
        elif wordIs('~guiPopup'):
            self.toggleGuiPopup()
        elif wordIs('~showCS') or wordIs('~showcs'):
            bitmask = self.getCSBitmask(word[7:])
            render.showCS(bitmask)
            self.csShown = 1
        elif wordIs('~hideCS') or wordIs('~hidecs'):
            bitmask = self.getCSBitmask(word[7:])
            render.hideCS(bitmask)
            self.csShown = 0
        elif wordIs('~cs'):
            bitmask = self.getCSBitmask(word[3:])
            if self.csShown:
                render.hideCS(bitmask)
                self.csShown = 0
            else:
                render.showCS(bitmask)
                self.csShown = 1
        elif wordIs('~showShadowCollisions'):
            self.showShadowCollisions()
        elif wordIs('~hideShadowCollisions'):
            self.hideShadowCollisions()
        elif wordIs('~showCollisions'):
            self.showCollisions()
        elif wordIs('~hideCollisions'):
            self.hideCollisions()
        elif wordIs('~showCameraCollisions'):
            self.showCameraCollisions()
        elif wordIs('~hideCameraCollisions'):
            self.hideCameraCollisions()
        elif wordIs('~collidespam'):
            n = Notify.ptr().getCategory(':collide')
            if hasattr(self, '_collideSpamSeverity'):
                n.setSeverity(self._collideSpamSeverity)
                del self._collideSpamSeverity
            else:
                self._collideSpamSeverity = n.getSeverity()
                n.setSeverity(NSSpam)
        elif wordIs('~notify'):
            args = word.split()
            n = Notify.ptr().getCategory(args[1])
            n.setSeverity({
                'error': NSError,
                'warning': NSWarning,
                'info': NSInfo,
                'debug': NSDebug,
                'spam': NSSpam }[args[2]])
        elif wordIs('~stress'):
            factor = word[7:]
            if factor:
                factor = float(factor)
                LOD.setStressFactor(factor)
                response = 'Set LOD stress factor to %s' % factor
            else:
                factor = LOD.getStressFactor()
                response = 'LOD stress factor is %s' % factor
            self.setMagicWordResponse(response)
        elif wordIs('~for'):
            self.forAnother(word, avId, zoneId)
        elif wordIs('~badname'):
            word = '~for %s ~badname' % word[9:]
            print 'word is %s' % word
            self.forAnother(word, avId, zoneId)
        elif wordIs('~avId'):
            self.setMagicWordResponse(str(localAvatar.doId))
        elif wordIs('~doId'):
            name = string.strip(word[6:])
            objs = self.identifyDistributedObjects(name)
            if len(objs) == 0:
                response = '%s is unknown.' % name
            else:
                response = ''
                for (name, obj) in objs:
                    response += '\n%s %d' % (name, obj.doId)
                
                response = response[1:]
            self.setMagicWordResponse(response)
        elif wordIs('~exec'):
            ChatManager = ChatManager
            import otp.chat
            ChatManager.ChatManager.execChat = 1
        elif wordIs('~run'):
            self.toggleRun()
        elif wordIs('~runFaster'):
            if config.GetBool('want-running', 1):
                args = word.split()
                if len(args) > 1:
                    base.debugRunningMultiplier = float(args[1])
                else:
                    base.debugRunningMultiplier = 10
                inputState.set('debugRunning', True)
            
        elif wordIs('~who'):
            avIds = []
            for av in Avatar.Avatar.ActiveAvatars:
                if hasattr(av, 'getFriendsList'):
                    avIds.append(av.doId)
                    continue
            
            self.d_setWho(avIds)
        elif wordIs('~sync'):
            tm = self.cr.timeManager
            if tm == None:
                response = 'No TimeManager.'
                self.setMagicWordResponse(response)
            else:
                tm.extraSkew = 0.0
                skew = string.strip(word[5:])
                if skew != '':
                    tm.extraSkew = float(skew)
                
                globalClockDelta.clear()
                tm.handleHotkey()
        elif wordIs('~period'):
            timeout = string.strip(word[7:])
            if timeout != '':
                seconds = int(timeout)
                self.cr.stopPeriodTimer()
                self.cr.resetPeriodTimer(seconds)
                self.cr.startPeriodTimer()
            
            if self.cr.periodTimerExpired:
                response = 'Period timer has expired.'
            elif self.cr.periodTimerStarted:
                elapsed = globalClock.getFrameTime() - self.cr.periodTimerStarted
                secondsRemaining = self.cr.periodTimerSecondsRemaining - elapsed
                response = 'Period timer expires in %s seconds.' % int(secondsRemaining)
            else:
                response = 'Period timer not set.'
            self.setMagicWordResponse(response)
        elif wordIs('~DIRECT'):
            args = word.split()
            fEnableLight = 0
            if len(args) > 1:
                if direct and args[1] == 'CAM':
                    direct.enable()
                    taskMgr.removeTasksMatching('updateSmartCamera*')
                    camera.wrtReparentTo(render)
                    direct.cameraControl.enableMouseFly()
                    self.setMagicWordResponse('Enabled DIRECT camera')
                    return None
                elif args[1] == 'LIGHT':
                    fEnableLight = 1
                
            
            base.startTk()
            DirectSession = DirectSession
            import direct.directtools
            if fEnableLight:
                direct.enableLight()
            else:
                direct.enable()
            self.setMagicWordResponse('Enabled DIRECT')
        elif wordIs('~TT'):
            if not direct:
                return None
            
            args = word.split()
            if len(args) > 1:
                if args[1] == 'CAM':
                    direct.cameraControl.disableMouseFly()
                    camera.wrtReparentTo(base.localAvatar)
                    base.localAvatar.startUpdateSmartCamera()
                    self.setMagicWordResponse('Disabled DIRECT camera')
                    return None
                
            
            direct.disable()
            camera.wrtReparentTo(base.localAvatar)
            base.localAvatar.startUpdateSmartCamera()
            self.setMagicWordResponse('Disabled DIRECT')
        elif wordIs('~net'):
            if self.cr.networkPlugPulled():
                self.cr.restoreNetworkPlug()
                self.cr.startHeartbeat()
                response = 'Network restored.'
            else:
                self.cr.pullNetworkPlug()
                self.cr.stopHeartbeat()
                response = 'Network disconnected.'
            self.setMagicWordResponse(response)
        elif wordIs('~disconnect'):
            base.cr.distributedDistrict.sendUpdate('broadcastMessage')
        elif wordIs('~model'):
            args = word.split()
            path = args[1]
            model = loader.loadModel(path)
            model.reparentTo(localAvatar)
            model.wrtReparentTo(render)
            self.setMagicWordResponse('loaded %s' % path)
        elif wordIs('~axis'):
            axis = loader.loadModel('models/misc/xyzAxis.bam')
            axis.reparentTo(render)
            axis.setPos(base.localAvatar, 0, 0, 0)
            axis.setHpr(render, 0, 0, 0)
            axis10 = loader.loadModel('models/misc/xyzAxis.bam')
            axis10.reparentTo(render)
            axis10.setPos(base.localAvatar, 0, 0, 0)
            axis10.setScale(10)
            axis10.setHpr(render, 0, 0, 0)
            axis10.setColorScale(1, 1, 1, 0.40000000000000002)
            axis10.setTransparency(1)
        elif wordIs('~clearAxes') or wordIs('~clearAxis'):
            render.findAllMatches('**/xyzAxis.egg').detach()
        elif wordIs('~myAxis'):
            if hasattr(self, 'myAxis'):
                self.myAxis.detachNode()
                del self.myAxis
            else:
                self.myAxis = loader.loadModel('models/misc/xyzAxis.bam')
                self.myAxis.reparentTo(localAvatar)
        elif wordIs('~osd'):
            onScreenDebug.enabled = not (onScreenDebug.enabled)
        elif wordIs('~osdScale'):
            args = word.split()
            defScale = 0.050000000000000003
            if len(args) > 1:
                scale = float(args[1])
            else:
                scale = 1.0
            onScreenDebug.onScreenText.setScale(defScale * scale)
        elif wordIs('~osdTaskMgr'):
            if taskMgr.osdEnabled():
                taskMgr.stopOsd()
            elif not onScreenDebug.enabled:
                onScreenDebug.enabled = True
            
            taskMgr.startOsd()
        elif wordIs('~fps'):
            self.doFps(word, avId, zoneId)
        elif wordIs('~sleep'):
            args = word.split()
            if len(args) > 1:
                s = float(args[1])
                base.setSleep(s)
                response = 'sleeping %s' % s
            else:
                base.setSleep(0.0)
                response = 'not sleeping'
            self.setMagicWordResponse(response)
        elif wordIs('~objects'):
            args = word.split()
            ObjectReport = ObjectReport
            import direct.showbase
            report = ObjectReport.ObjectReport('client ~objects')
            if 'all' in args:
                self.notify.info('printing full object set...')
                report.getObjectPool().printObjsByType(printReferrers = 'ref' in args)
            
            if hasattr(self, 'baselineObjReport'):
                self.notify.info('calculating diff from baseline ObjectReport...')
                self.lastDiff = self.baselineObjReport.diff(report)
                if not 'diff' in args:
                    pass
                self.lastDiff.printOut(full = 'dif' in args)
            
            if 'baseline' in args or not hasattr(self, 'baselineObjReport'):
                self.notify.info('recording baseline ObjectReport...')
                if hasattr(self, 'baselineObjReport'):
                    self.baselineObjReport.destroy()
                
                self.baselineObjReport = report
            
            self.setMagicWordResponse('objects logged')
        elif wordIs('~objectcount'):
            
            def handleObjectCountDone(objectCount):
                self.setMagicWordResponse('object count logged')

            oc = ObjectCount('~objectcount', doneCallback = handleObjectCountDone)
        elif wordIs('~objecthg'):
            import gc as gc
            objs = gc.get_objects()
            type2count = { }
            for obj in objs:
                tn = safeTypeName(obj)
                type2count.setdefault(tn, 0)
                type2count[tn] += 1
            
            count2type = invertDictLossless(type2count)
            counts = count2type.keys()
            counts.sort()
            counts.reverse()
            for count in counts:
                print '%s: %s' % (count, count2type[count])
            
            self.setMagicWordResponse('~aiobjecthg complete')
        elif wordIs('~containers'):
            args = word.split()
            limit = 30
            if 'full' in args:
                limit = None
            
            ContainerReport.ContainerReport('~containers', log = True, limit = limit, threaded = True)
        elif wordIs('~garbage'):
            args = word.split()
            full = 'full' in args
            safeMode = 'safe' in args
            delOnly = 'delonly' in args
            cycleLimit = None
            for arg in args:
                
                try:
                    cycleLimit = int(arg)
                continue
                continue

            
            GarbageReport.GarbageLogger('~garbage', fullReport = full, threaded = True, safeMode = safeMode, delOnly = delOnly, cycleLimit = cycleLimit, doneCallback = self.garbageReportDone)
        elif wordIs('~guicreates'):
            base.printGuiCreates = True
            self.setMagicWordResponse('printing gui creation stacks')
        elif wordIs('~creategarbage'):
            args = word.split()
            num = 1
            if len(args) > 1:
                num = int(args[1])
            
            GarbageReport._createGarbage(num)
        elif wordIs('~leakTask'):
            
            def leakTask(task):
                return task.cont

            taskMgr.add(leakTask, uniqueName('leakedTask'))
            leakTask = None
        elif wordIs('~leakmessage'):
            MessengerLeakDetector._leakMessengerObject()
            self.down_setMagicWordResponse(senderId, 'messenger leak object created')
        elif wordIs('~pstats'):
            args = word.split()
            hostname = None
            port = None
            if len(args) > 1:
                hostname = args[1]
            
            if len(args) > 2:
                port = int(args[2])
            
            base.wantStats = 1
            Task.TaskManager.pStatsTasks = 1
            result = base.createStats(hostname, port)
            connectionName = '%s' % hostname
            if port is not None:
                connectionName += ':%s' % port
            
            if result:
                response = 'connected client pstats to %s' % connectionName
            else:
                response = 'could not connect pstats to %s' % connectionName
            self.setMagicWordResponse(response)
        elif wordIs('~profile'):
            args = word.split()
            if len(args) > 1:
                num = int(args[1])
            else:
                num = 5
            session = taskMgr.getProfileSession('~profile')
            session.setLogAfterProfile(True)
            taskMgr.profileFrames(num, session)
            self.setMagicWordResponse('profiling %s client frames...' % num)
        elif wordIs('~frameprofile'):
            args = word.split()
            wasOn = bool(taskMgr.getProfileFrames())
            if len(args) > 1:
                setting = bool(int(args[1]))
            else:
                setting = not wasOn
            taskMgr.setProfileFrames(setting)
            self.setMagicWordResponse('frame profiling %s%s' % (choice(setting, 'ON', 'OFF'), choice(wasOn == setting, ' already', '')))
        elif wordIs('~taskprofile'):
            args = word.split()
            wasOn = bool(taskMgr.getProfileTasks())
            if len(args) > 1:
                setting = bool(int(args[1]))
            else:
                setting = not wasOn
            taskMgr.setProfileTasks(setting)
            self.setMagicWordResponse('task profiling %s%s' % (choice(setting, 'ON', 'OFF'), choice(wasOn == setting, ' already', '')))
        elif wordIs('~taskspikethreshold'):
            args = word.split()
            if len(args) > 1:
                threshold = float(args[1])
                response = 'task spike threshold set to %ss' % threshold
            else:
                threshold = TaskProfiler.GetDefaultSpikeThreshold()
                response = 'task spike threshold reset to %ss' % threshold
            TaskProfiler.SetSpikeThreshold(threshold)
            self.setMagicWordResponse(response)
        elif wordIs('~logtaskprofiles'):
            args = word.split()
            if len(args) > 1:
                name = args[1]
            else:
                name = None
            taskMgr.logTaskProfiles(name)
            response = 'logged task profiles%s' % choice(name, ' for %s' % name, '')
            self.setMagicWordResponse(response)
        elif wordIs('~taskprofileflush'):
            args = word.split()
            if len(args) > 1:
                name = args[1]
            else:
                name = None
            taskMgr.flushTaskProfiles(name)
            response = 'flushed AI task profiles%s' % choice(name, ' for %s' % name, '')
            self.setMagicWordResponse(response)
        elif wordIs('~dobjectcount'):
            base.cr.printObjectCount()
            self.setMagicWordResponse('logging client distributed object count...')
        elif wordIs('~taskmgr'):
            print taskMgr
            self.setMagicWordResponse('logging client taskMgr...')
        elif wordIs('~jobmgr'):
            print jobMgr
            self.setMagicWordResponse('logging client jobMgr...')
        elif wordIs('~jobtime'):
            args = word.split()
            if len(args) > 1:
                time = float(args[1])
            else:
                time = None
            response = ''
            if time is None:
                time = jobMgr.getDefaultTimeslice()
                response = 'reset client jobMgr timeslice to %s ms' % time
            else:
                response = 'set client jobMgr timeslice to %s ms' % time
                time = time / 1000.0
            jobMgr.setTimeslice(time)
            self.setMagicWordResponse(response)
        elif wordIs('~detectleaks'):
            started = self.cr.startLeakDetector()
            self.setMagicWordResponse(choice(started, 'leak detector started', 'leak detector already started'))
        elif wordIs('~taskthreshold'):
            args = word.split()
            if len(args) > 1.0:
                threshold = float(args[1])
            else:
                threshold = None
            response = ''
            if threshold is None:
                threshold = taskMgr.DefTaskDurationWarningThreshold
                response = 'reset task duration warning threshold to %s' % threshold
            else:
                response = 'set task duration warning threshold to %s' % threshold
            taskMgr.setTaskDurationWarningThreshold(threshold)
            self.setMagicWordResponse(response)
        elif wordIs('~messenger'):
            print messenger
            self.setMagicWordResponse('logging client messenger...')
        elif wordIs('~clientcrash'):
            DelayedCall(Functor(self.notify.error, '~clientcrash: simulating a client crash'))
        elif wordIs('~badDelete'):
            doId = 0
            while doId in base.cr.doId2do:
                doId += 1
            DelayedCall(Functor(base.cr.deleteObjectLocation, ScratchPad(doId = doId), 1, 1))
            self.setMagicWordResponse('doing bad delete')
        elif wordIs('~idTags'):
            messenger.send('nameTagShowAvId', [])
            base.idTags = 1
        elif wordIs('~nameTags'):
            messenger.send('nameTagShowName', [])
            base.idTags = 0
        elif wordIs('~hideNames'):
            if NametagGlobals.getMasterNametagsVisible():
                NametagGlobals.setMasterNametagsVisible(0)
            else:
                NametagGlobals.setMasterNametagsVisible(1)
        elif wordIs('~hideGui'):
            if aspect2d.isHidden():
                aspect2d.show()
            else:
                aspect2d.hide()
        elif wordIs('~flush'):
            base.cr.doDataCache.flush()
            base.cr.cache.flush()
            self.setMagicWordResponse('client object and data caches flushed')
        elif wordIs('~prof'):
            import time as time
            name = 'default'
            p = Point3()
            ts = time.time()
            for i in xrange(1000000):
                p.set(1, 2, 3)
            
            tf = time.time()
            dt = tf - ts
            response = 'prof(%s): %s secs' % (name, dt)
            print response
            self.setMagicWordResponse(response)
        elif wordIs('~gptc'):
            args = word.split()
            if len(args) > 1.0 and hasattr(self.cr, 'leakDetector'):
                gptcJob = self.cr.leakDetector.getPathsToContainers('~gptc', args[1], Functor(self._handleGPTCfinished, args[1]))
            else:
                self.setMagicWordResponse('error')
        elif wordIs('~gptcn'):
            args = word.split()
            if len(args) > 1.0 and hasattr(self.cr, 'leakDetector'):
                gptcnJob = self.cr.leakDetector.getPathsToContainersNamed('~gptcn', args[1], Functor(self._handleGPTCNfinished, args[1]))
            else:
                self.setMagicWordResponse('error')
        else:
            return 0
        return 1
Exemple #8
0
class Job(DirectObject):
    """Base class for cpu-intensive or non-time-critical operations that
    are run through the :class:`.JobManager`.

    To use, subclass and override the `run()` method.
    """

    #: Yielded from the `run()` generator method when the job is done.
    Done = object()

    #: ``yield None`` is acceptable in place of ``yield Job.Continue``
    Continue = None

    #: Yield any remaining time for this job until next frame.
    Sleep = object()

    # These priorities determine how many timeslices a job gets relative to other
    # jobs. A job with priority of 1000 will run 10 times more often than a job
    # with priority of 100.
    Priorities = ScratchPad(Min=1, Low=100, Normal=1000, High=10000)
    _SerialGen = SerialNumGen()

    def __init__(self, name):
        self._name = name
        self._generator = None
        self._id = Job._SerialGen.next()
        self._printing = False
        self._priority = Job.Priorities.Normal
        self._finished = False
        if __debug__:
            self._pstats = PStatCollector("App:Show code:jobManager:%s" %
                                          self._name)

    def destroy(self):
        del self._name
        del self._generator
        del self._printing

    def getFinishedEvent(self):
        return 'job-finished-%s' % self._id

    def run(self):
        """This should be overridden with a generator that does the
        needful processing.

        yield `Job.Continue` when possible/reasonable, and try not to run
        longer than the JobManager's timeslice between yields.

        When done, yield `Job.Done`.
        """
        raise NotImplementedError("don't call down")

    def getPriority(self):
        return self._priority

    def setPriority(self, priority):
        self._priority = priority

    def printingBegin(self):
        self._printing = True

    def printingEnd(self):
        self._printing = False

    def resume(self):
        """Called every time JobManager is going to start running this job."""
        #if self._printing:
        #    # we may be suspended/resumed multiple times per frame, that gets spammy
        #    # if we need to pick out the output of a job, put a prefix onto each line
        #    # of the output
        #    print('JOB:%s:RESUME' % self._name)

    def suspend(self):
        """Called when JobManager is going to stop running this job for a
        while.
        """

        #if self._printing:
        #    #print('JOB:%s:SUSPEND' % self._name)
        #    pass
        #    """

    def _setFinished(self):
        self._finished = True
        self.finished()

    def isFinished(self):
        return self._finished

    def finished(self):
        # called when the job finishes and has been removed from the JobManager
        pass

    def getJobName(self):
        return self._name

    def _getJobId(self):
        return self._id

    def _getGenerator(self):
        if self._generator is None:
            self._generator = self.run()
        return self._generator

    def _cleanupGenerator(self):
        if self._generator is not None:
            self._generator = None
Exemple #9
0
        self._handlePotentialStateChange(args[self._index])


class AttrSetter(StateChangeNode):
    def __init__(self, source, object, attrName):
        self._object = object
        self._attrName = attrName
        StateChangeNode.__init__(self, source)
        self._handleStateChange()

    def _handleStateChange(self):
        setattr(self._object, self._attrName, self._value)
        StateChangeNode._handleStateChange(self)


if __debug__:
    from direct.showbase.PythonUtil import ScratchPad
    o = ScratchPad()
    svar = StateVar(0)
    aset = AttrSetter(svar, o, 'testAttr')
    assert hasattr(o, 'testAttr')
    assert o.testAttr == 0
    svar.set('red')
    assert o.testAttr == 'red'
    aset.destroy()
    svar.destroy()
    o.destroy()
    del aset
    del svar
    del o
Exemple #10
0
    def doMagicWord(self, word, avId, zoneId):
        wordIs = self.getWordIs(word)

        print word
        if wordIs("~oobe"):
            base.oobe()
        elif wordIs("~oobeCull"):
            base.oobeCull()
        elif wordIs("~tex"):
            self.doTex(word)
        elif wordIs("~texmem"):
            base.toggleTexMem()
        elif wordIs("~verts"):
            base.toggleShowVertices()
        elif wordIs("~wire"):
            base.toggleWireframe()
        elif wordIs("~stereo"):
            base.toggleStereo()
        elif wordIs("~showfont"):
            self.showfont(word[9:])
        elif wordIs("~hidefont"):
            self.hidefont()
        elif wordIs("~guiPopup"):
            self.toggleGuiPopup()

        elif wordIs("~showCS") or wordIs("~showcs"):
            bitmask = self.getCSBitmask(word[7:])
            render.showCS(bitmask)
            self.csShown = 1

        elif wordIs("~hideCS") or wordIs("~hidecs"):
            bitmask = self.getCSBitmask(word[7:])
            render.hideCS(bitmask)
            self.csShown = 0

        elif wordIs("~cs"):
            # Toggle hide/show collision solids:
            # (Also a shorthand for ~hideCS and ~showCS).
            bitmask = self.getCSBitmask(word[3:])
            if self.csShown:
                render.hideCS(bitmask)
                self.csShown = 0
            else:
                render.showCS(bitmask)
                self.csShown = 1

        elif wordIs("~showShadowCollisions"):
            self.showShadowCollisions()

        elif wordIs("~hideShadowCollisions"):
            self.hideShadowCollisions()

        elif wordIs("~showCollisions"):
            self.showCollisions()

        elif wordIs("~hideCollisions"):
            self.hideCollisions()

        elif wordIs("~showCameraCollisions"):
            self.showCameraCollisions()

        elif wordIs("~hideCameraCollisions"):
            self.hideCameraCollisions()

        elif wordIs("~collidespam"):
            n = Notify.ptr().getCategory(':collide')
            if hasattr(self, '_collideSpamSeverity'):
                n.setSeverity(self._collideSpamSeverity)
                del self._collideSpamSeverity
            else:
                self._collideSpamSeverity = n.getSeverity()
                n.setSeverity(NSSpam)

        elif wordIs("~notify"):
            args = word.split()
            n = Notify.ptr().getCategory(args[1])
            n.setSeverity({
                'error': NSError,
                'warning': NSWarning,
                'info': NSInfo,
                'debug': NSDebug,
                'spam': NSSpam,
            }[args[2]])

        # MPG we probably need generic versions of these
        #elif wordIs("~listen"):
        #    base.localAvatar.garbleChat = 0

        #elif wordIs("~nochat") or wordIs("~chat") or wordIs("~superchat"):
        #    base.localAvatar.garbleChat = 1

        elif wordIs("~stress"):
            factor = word[7:]
            if factor:
                factor = float(factor)
                LOD.setStressFactor(factor)
                response = "Set LOD stress factor to %s" % (factor)
            else:
                factor = LOD.getStressFactor()
                response = "LOD stress factor is %s" % (factor)

            self.setMagicWordResponse(response)

        elif wordIs("~for"):
            self.forAnother(word, avId, zoneId)

        elif wordIs("~badname"):
            # ~badname with an argument becomes ~for ... ~badname
            word = "~for %s ~badname" % (word[9:])
            print "word is %s" % (word)
            self.forAnother(word, avId, zoneId)

        elif wordIs('~avId'):
            self.setMagicWordResponse(str(localAvatar.doId))

        elif wordIs("~doId"):
            name = string.strip(word[6:])

            objs = self.identifyDistributedObjects(name)
            if (len(objs) == 0):
                response = "%s is unknown." % (name)
            else:
                response = ""
                for name, obj in objs:
                    response += "\n%s %d" % (name, obj.doId)
                response = response[1:]

            self.setMagicWordResponse(response)

        # MPG - need generic versions of these
        #elif wordIs("~collisions_on"):
        #    base.localAvatar.collisionsOn()

        #elif wordIs("~collisions_off"):
        #    base.localAvatar.collisionsOff()

        #elif wordIs('~addCameraPosition'):
        #    base.localAvatar.addCameraPosition()

        #elif wordIs('~removeCameraPosition'):
        #    base.localAvatar.removeCameraPosition()

        #elif wordIs('~printCameraPosition'):
        #    base.localAvatar.printCameraPosition(
        #        base.localAvatar.cameraIndex)

        #elif wordIs('~printCameraPositions'):
        #    base.localAvatar.printCameraPositions()

        elif wordIs("~exec"):
            # Enable execChat.
            from otp.chat import ChatManager
            ChatManager.ChatManager.execChat = 1

        elif wordIs("~run"):
            self.toggleRun()

        elif wordIs("~runFaster"):
            if (config.GetBool("want-running", 1)):
                args = word.split()
                if (len(args) > 1):
                    base.debugRunningMultiplier = float(args[1])
                else:
                    base.debugRunningMultiplier = 10
                inputState.set("debugRunning", True)

        elif wordIs("~who"):
            # Get all the nearby avIds and send them to the AI.
            avIds = []
            for av in Avatar.Avatar.ActiveAvatars:
                # If the avatar has a friends list, it's probably a
                # real avatar and not an NPC.
                if hasattr(av, "getFriendsList"):
                    avIds.append(av.doId)
            self.d_setWho(avIds)

        elif wordIs("~sync"):
            # Sync with the AI, like F6, but rather than accumulating
            # sync informatoin, throw away whatever information was
            # there from before.  If a second parameter is supplied,
            # it is a number of seconds of temporary extra skew to
            # apply; the default is 0.

            tm = self.cr.timeManager
            if tm == None:
                response = "No TimeManager."
                self.setMagicWordResponse(response)
            else:
                tm.extraSkew = 0.0
                skew = string.strip(word[5:])
                if skew != "":
                    tm.extraSkew = float(skew)
                globalClockDelta.clear()
                tm.handleHotkey()

        elif wordIs("~period"):
            # Reset the period timer to expire in the indicated number
            # of seconds, or with no parameter, report the number of
            # seconds remaining.

            timeout = string.strip(word[7:])
            if timeout != "":
                seconds = int(timeout)
                self.cr.stopPeriodTimer()
                self.cr.resetPeriodTimer(seconds)
                self.cr.startPeriodTimer()

            # Now report the number of seconds remaining.
            if self.cr.periodTimerExpired:
                response = "Period timer has expired."

            elif self.cr.periodTimerStarted:
                elapsed = globalClock.getFrameTime(
                ) - self.cr.periodTimerStarted
                secondsRemaining = self.cr.periodTimerSecondsRemaining - elapsed
                response = "Period timer expires in %s seconds." % (
                    int(secondsRemaining))
            else:
                response = "Period timer not set."

            self.setMagicWordResponse(response)

        elif wordIs("~DIRECT"):
            args = word.split()
            fEnableLight = 0
            if len(args) > 1:
                if direct and (args[1] == 'CAM'):
                    direct.enable()
                    taskMgr.removeTasksMatching('updateSmartCamera*')
                    camera.wrtReparentTo(render)
                    direct.cameraControl.enableMouseFly()
                    self.setMagicWordResponse("Enabled DIRECT camera")
                    return
                elif args[1] == 'LIGHT':
                    fEnableLight = 1
            # Start up DIRECT
            base.startTk()
            from direct.directtools import DirectSession
            if fEnableLight:
                direct.enableLight()
            else:
                direct.enable()
            self.setMagicWordResponse("Enabled DIRECT")

        elif wordIs("~TT"):
            if not direct:
                return
            args = word.split()
            if len(args) > 1:
                if (args[1] == 'CAM'):
                    direct.cameraControl.disableMouseFly()
                    camera.wrtReparentTo(base.localAvatar)
                    base.localAvatar.startUpdateSmartCamera()
                    self.setMagicWordResponse("Disabled DIRECT camera")
                    return
            # Return to toontown mode
            direct.disable()
            camera.wrtReparentTo(base.localAvatar)
            base.localAvatar.startUpdateSmartCamera()
            self.setMagicWordResponse("Disabled DIRECT")

        elif wordIs("~net"):
            # Simulate pulling or restoring the network plug.
            if self.cr.networkPlugPulled():
                self.cr.restoreNetworkPlug()
                self.cr.startHeartbeat()
                response = "Network restored."
            else:
                self.cr.pullNetworkPlug()
                self.cr.stopHeartbeat()
                response = "Network disconnected."
            self.setMagicWordResponse(response)

        elif wordIs('~disconnect'):
            # force a simulated disconnect
            # you can also do this from the OTP webpage
            base.cr.distributedDistrict.sendUpdate('broadcastMessage')

        elif wordIs("~model"):
            # load a model into the scene graph at the location of localAvatar
            args = word.split()
            path = args[1]
            model = loader.loadModel(path)
            model.reparentTo(localAvatar)
            model.wrtReparentTo(render)
            self.setMagicWordResponse('loaded %s' % path)

        elif wordIs("~axis"):
            # Show a 10 foot and 100 foot axis at the spot of the avatar
            # axis aligned to render
            axis = loader.loadModel("models/misc/xyzAxis.bam")
            axis.reparentTo(render)
            axis.setPos(base.localAvatar, 0, 0, 0)
            axis.setHpr(render, 0, 0, 0)
            axis10 = loader.loadModel("models/misc/xyzAxis.bam")
            axis10.reparentTo(render)
            axis10.setPos(base.localAvatar, 0, 0, 0)
            axis10.setScale(10)
            axis10.setHpr(render, 0, 0, 0)
            axis10.setColorScale(1, 1, 1, 0.4)
            axis10.setTransparency(1)

        elif (wordIs("~clearAxes") or wordIs("~clearAxis")):
            # Remove the effects of ~axis calls
            render.findAllMatches("**/xyzAxis.egg").detach()

        elif wordIs("~myAxis"):
            if hasattr(self, 'myAxis'):
                self.myAxis.detachNode()
                del self.myAxis
            else:
                self.myAxis = loader.loadModel("models/misc/xyzAxis.bam")
                self.myAxis.reparentTo(localAvatar)

        elif (wordIs("~osd")):
            onScreenDebug.enabled = not onScreenDebug.enabled

        elif wordIs("~osdScale"):
            args = word.split()
            defScale = .05
            if len(args) > 1:
                scale = float(args[1])
            else:
                scale = 1.
            onScreenDebug.onScreenText.setScale(defScale * scale)

        elif wordIs('~osdTaskMgr'):
            if taskMgr.osdEnabled():
                taskMgr.stopOsd()
            else:
                if not onScreenDebug.enabled:
                    onScreenDebug.enabled = True
                taskMgr.startOsd()

        elif wordIs("~fps"):
            self.doFps(word, avId, zoneId)

        elif wordIs("~sleep"):
            args = word.split()
            if len(args) > 1:
                s = float(args[1])
                base.setSleep(s)
                response = 'sleeping %s' % s
            else:
                base.setSleep(0.0)
                response = 'not sleeping'
            self.setMagicWordResponse(response)

        elif wordIs('~objects'):
            args = word.split()
            from direct.showbase import ObjectReport
            report = ObjectReport.ObjectReport('client ~objects')

            if 'all' in args:
                self.notify.info('printing full object set...')
                report.getObjectPool().printObjsByType(
                    printReferrers='ref' in args)

            if hasattr(self, 'baselineObjReport'):
                self.notify.info(
                    'calculating diff from baseline ObjectReport...')
                self.lastDiff = self.baselineObjReport.diff(report)
                self.lastDiff.printOut(full=('diff' in args or 'dif' in args))

            if 'baseline' in args or not hasattr(self, 'baselineObjReport'):
                self.notify.info('recording baseline ObjectReport...')
                if hasattr(self, 'baselineObjReport'):
                    self.baselineObjReport.destroy()
                self.baselineObjReport = report

            self.setMagicWordResponse('objects logged')

        elif wordIs('~objecthg'):
            import gc
            objs = gc.get_objects()
            type2count = {}
            for obj in objs:
                tn = safeTypeName(obj)
                type2count.setdefault(tn, 0)
                type2count[tn] += 1
            count2type = invertDictLossless(type2count)
            counts = count2type.keys()
            counts.sort()
            counts.reverse()
            for count in counts:
                print '%s: %s' % (count, count2type[count])
            self.setMagicWordResponse('~aiobjecthg complete')

        elif wordIs('~containers'):
            args = word.split()
            limit = 30
            if 'full' in args:
                limit = None
            ContainerReport.ContainerReport('~containers',
                                            log=True,
                                            limit=limit,
                                            threaded=True)

        elif wordIs('~garbage'):
            args = word.split()
            # it can take a LOOONG time to print out the garbage referrers and referents
            # by reference (as opposed to by number)
            full = 'full' in args
            safeMode = 'safe' in args
            delOnly = 'delonly' in args
            # This does a garbage collection and dumps the list of leaked (uncollectable) objects to the log.
            GarbageReport.GarbageLogger('~garbage',
                                        fullReport=full,
                                        threaded=True,
                                        safeMode=safeMode,
                                        delOnly=delOnly,
                                        doneCallback=self.garbageReportDone)
            # this is coming back from the AI
            #self.setMagicWordResponse('garbage logged')

        elif wordIs('~guicreates'):
            base.printGuiCreates = True
            self.setMagicWordResponse('printing gui creation stacks')

        elif wordIs("~creategarbage"):
            GarbageReport._createGarbage()
            # this is coming back from the AI
            #self.setMagicWordResponse(senderId, 'leaked garbage created')

        elif wordIs('~leakTask'):

            def leakTask(task):
                return task.cont

            taskMgr.add(leakTask, uniqueName('leakedTask'))
            leakTask = None
            # this is coming back from the AI
            #self.setMagicWordResponse(senderId, 'leaked task created')

        elif wordIs('~leakmessage'):
            MessengerLeakDetector._leakMessengerObject()
            self.down_setMagicWordResponse(senderId,
                                           'messenger leak object created')

        elif wordIs('~pstats'):
            args = word.split()
            hostname = None
            port = None
            if len(args) > 1:
                hostname = args[1]
            if len(args) > 2:
                port = int(args[2])
            # make sure pstats is enabled
            base.wantStats = 1
            Task.TaskManager.pStatsTasks = 1
            result = base.createStats(hostname, port)
            connectionName = '%s' % hostname
            if port is not None:
                connectionName += ':%s' % port
            if result:
                response = 'connected client pstats to %s' % connectionName
            else:
                response = 'could not connect pstats to %s' % connectionName
            self.setMagicWordResponse(response)

        elif wordIs('~profile'):
            args = word.split()
            if len(args) > 1:
                num = int(args[1])
            else:
                num = 5
            session = taskMgr.getProfileSession('~profile')
            session.setLogAfterProfile(True)
            taskMgr.profileFrames(num, session)
            self.setMagicWordResponse('profiling %s client frames...' % num)

        elif wordIs('~frameprofile'):
            args = word.split()
            wasOn = bool(taskMgr.getProfileFrames())
            if len(args) > 1:
                setting = bool(int(args[1]))
            else:
                setting = not wasOn
            taskMgr.setProfileFrames(setting)
            self.setMagicWordResponse(
                'frame profiling %s%s' %
                (choice(setting, 'ON',
                        'OFF'), choice(wasOn == setting, ' already', '')))

        elif wordIs('~taskprofile'):
            args = word.split()
            wasOn = bool(taskMgr.getProfileTasks())
            if len(args) > 1:
                setting = bool(int(args[1]))
            else:
                setting = not wasOn
            taskMgr.setProfileTasks(setting)
            self.setMagicWordResponse(
                'task profiling %s%s' %
                (choice(setting, 'ON',
                        'OFF'), choice(wasOn == setting, ' already', '')))

        elif wordIs('~taskspikethreshold'):
            args = word.split()
            if len(args) > 1:
                threshold = float(args[1])
                response = 'task spike threshold set to %ss' % threshold
            else:
                threshold = TaskProfiler.GetDefaultSpikeThreshold()
                response = 'task spike threshold reset to %ss' % threshold
            TaskProfiler.SetSpikeThreshold(threshold)
            self.setMagicWordResponse(response)

        elif wordIs('~logtaskprofiles'):
            args = word.split()
            if len(args) > 1:
                name = args[1]
            else:
                name = None
            taskMgr.logTaskProfiles(name)
            response = 'logged task profiles%s' % choice(
                name, ' for %s' % name, '')
            self.setMagicWordResponse(response)

        elif wordIs('~taskprofileflush'):
            args = word.split()
            if len(args) > 1:
                name = args[1]
            else:
                name = None
            taskMgr.flushTaskProfiles(name)
            response = 'flushed AI task profiles%s' % choice(
                name, ' for %s' % name, '')
            self.setMagicWordResponse(response)

        elif wordIs('~objectcount'):
            base.cr.printObjectCount()
            self.setMagicWordResponse(
                'logging client distributed object count...')

        elif wordIs('~taskmgr'):
            print taskMgr
            self.setMagicWordResponse('logging client taskMgr...')

        elif wordIs('~jobmgr'):
            print jobMgr
            self.setMagicWordResponse('logging client jobMgr...')

        elif wordIs('~jobtime'):
            args = word.split()
            if len(args) > 1:
                time = float(args[1])
            else:
                time = None
            response = ''
            if time is None:
                time = jobMgr.getDefaultTimeslice()
                response = 'reset client jobMgr timeslice to %s ms' % time
            else:
                response = 'set client jobMgr timeslice to %s ms' % time
                time = time / 1000.
            jobMgr.setTimeslice(time)
            self.setMagicWordResponse(response)

        elif wordIs('~detectleaks'):
            started = self.cr.startLeakDetector()
            self.setMagicWordResponse(
                choice(
                    started,
                    'leak detector started',
                    'leak detector already started',
                ))

        elif wordIs('~taskthreshold'):
            args = word.split()
            if len(args) > 1.:
                threshold = float(args[1])
            else:
                threshold = None
            response = ''
            if threshold is None:
                threshold = taskMgr.DefTaskDurationWarningThreshold
                response = 'reset task duration warning threshold to %s' % threshold
            else:
                response = 'set task duration warning threshold to %s' % threshold
            taskMgr.setTaskDurationWarningThreshold(threshold)
            self.setMagicWordResponse(response)

        elif wordIs('~messenger'):
            print messenger
            self.setMagicWordResponse('logging client messenger...')

        elif wordIs('~clientcrash'):
            # if we call notify.error directly, the magic word mgr will catch it
            # self.notify.error doesn't seem to work either
            DelayedCall(
                Functor(self.notify.error,
                        '~clientcrash: simulating a client crash'))

        elif wordIs('~badDelete'):
            doId = 0
            while doId in base.cr.doId2do:
                doId += 1
            # location (0,0) is special, pass in (1,1)
            # deleteObjectLocation expects a DO, pass in a ScratchPad instead
            # we must delay the call because magicWordMgr is in a big try/except block
            DelayedCall(
                Functor(base.cr.deleteObjectLocation, ScratchPad(doId=doId), 1,
                        1))
            self.setMagicWordResponse('doing bad delete')

        elif wordIs("~idTags"):
            messenger.send('nameTagShowAvId', [])
            base.idTags = 1

        elif wordIs("~nameTags"):
            messenger.send('nameTagShowName', [])
            base.idTags = 0

        elif wordIs("~hideNames"):
            # note do ~hideNames before ~hideGui if you want both off
            if NametagGlobals.getMasterNametagsVisible():
                NametagGlobals.setMasterNametagsVisible(0)
            else:
                NametagGlobals.setMasterNametagsVisible(1)

        elif wordIs("~hideGui"):
            if aspect2d.isHidden():
                aspect2d.show()
            else:
                aspect2d.hide()

        elif wordIs('~flush'):
            base.cr.doDataCache.flush()
            base.cr.cache.flush()
            self.setMagicWordResponse('client object and data caches flushed')

        elif wordIs('~prof'):
            import time

            ### set up ###
            name = 'default'
            p = Point3()
            ##############

            ts = time.time()
            for i in xrange(1000000):

                ### code to be timed ###
                p.set(1, 2, 3)
                ########################

            tf = time.time()
            dt = tf - ts
            response = 'prof(%s): %s secs' % (name, dt)
            print response
            self.setMagicWordResponse(response)

        elif wordIs('~gptc'):
            args = word.split()
            if len(args) > 1. and hasattr(self.cr, 'leakDetector'):
                gptcJob = self.cr.leakDetector.getPathsToContainers(
                    '~gptc', args[1], Functor(self._handleGPTCfinished,
                                              args[1]))
            else:
                self.setMagicWordResponse('error')

        elif wordIs('~gptcn'):
            args = word.split()
            if len(args) > 1. and hasattr(self.cr, 'leakDetector'):
                gptcnJob = self.cr.leakDetector.getPathsToContainersNamed(
                    '~gptcn', args[1],
                    Functor(self._handleGPTCNfinished, args[1]))
            else:
                self.setMagicWordResponse('error')

        else:
            # Not a magic word I know!
            return 0

        return 1
class GarbageReport(Job):
    """Detects leaked Python objects (via gc.collect()) and reports on garbage
    items, garbage-to-garbage references, and garbage cycles.
    If you just want to dump the report to the log, use GarbageLogger."""
    notify = directNotify.newCategory("GarbageReport")

    def __init__(self,
                 name,
                 log=True,
                 verbose=False,
                 fullReport=False,
                 findCycles=True,
                 threaded=False,
                 doneCallback=None,
                 autoDestroy=False,
                 priority=None,
                 safeMode=False,
                 delOnly=False,
                 collect=True):
        # if autoDestroy is True, GarbageReport will self-destroy after logging
        # if false, caller is responsible for calling destroy()
        # if threaded is True, processing will be performed over multiple frames
        # if collect is False, we assume that the caller just did a collect and the results
        # are still in gc.garbage
        Job.__init__(self, name)
        # stick the arguments onto a ScratchPad so we can delete them all at once
        self._args = ScratchPad(name=name,
                                log=log,
                                verbose=verbose,
                                fullReport=fullReport,
                                findCycles=findCycles,
                                doneCallback=doneCallback,
                                autoDestroy=autoDestroy,
                                safeMode=safeMode,
                                delOnly=delOnly,
                                collect=collect)
        if priority is not None:
            self.setPriority(priority)
        jobMgr.add(self)
        if not threaded:
            jobMgr.finish(self)

    def run(self):
        # do the garbage collection
        oldFlags = gc.get_debug()

        if self._args.delOnly:
            # do a collect without SAVEALL, to identify the instances that are involved in
            # cycles with instances that define __del__
            # cycles that do not involve any instances that define __del__ are cleaned up
            # automatically by Python, but they also appear in gc.garbage when SAVEALL is set
            gc.set_debug(0)
            if self._args.collect:
                gc.collect()
            garbageInstances = gc.garbage[:]
            del gc.garbage[:]
            # only yield if there's more time-consuming work to do,
            # if there's no garbage, give instant feedback
            if len(garbageInstances) > 0:
                yield None
            # don't repr the garbage list if we don't have to
            if self.notify.getDebug():
                self.notify.debug('garbageInstances == %s' %
                                  fastRepr(garbageInstances))

            self.numGarbageInstances = len(garbageInstances)
            # grab the ids of the garbage instances (objects with __del__)
            self.garbageInstanceIds = set()
            for i in range(len(garbageInstances)):
                self.garbageInstanceIds.add(id(garbageInstances[i]))
                if i % 20 == 0:
                    yield None
            # then release the list of instances so that it doesn't interfere with the gc.collect() below
            del garbageInstances
        else:
            self.garbageInstanceIds = set()

        # do a SAVEALL pass so that we have all of the objects involved in legitimate garbage cycles
        # without SAVEALL, gc.garbage only contains objects with __del__ methods
        gc.set_debug(gc.DEBUG_SAVEALL)
        if self._args.collect:
            gc.collect()
        self.garbage = gc.garbage[:]
        del gc.garbage[:]
        # only yield if there's more time-consuming work to do,
        # if there's no garbage, give instant feedback
        if len(self.garbage) > 0:
            yield None
        # don't repr the garbage list if we don't have to
        if self.notify.getDebug():
            self.notify.debug('self.garbage == %s' % fastRepr(self.garbage))
        gc.set_debug(oldFlags)

        self.numGarbage = len(self.garbage)
        # only yield if there's more time-consuming work to do,
        # if there's no garbage, give instant feedback
        if self.numGarbage > 0:
            yield None

        if self._args.verbose:
            self.notify.info('found %s garbage items' % self.numGarbage)

        # print the types of the garbage first, in case the repr of an object
        # causes a crash
        #if self.numGarbage > 0:
        #    self.notify.info('TYPES ONLY (this is only needed if a crash occurs before GarbageReport finishes):')
        #    for result in printNumberedTypesGen(self.garbage):
        #        yield None

        # Py obj id -> garbage list index
        self._id2index = {}

        self.referrersByReference = {}
        self.referrersByNumber = {}

        self.referentsByReference = {}
        self.referentsByNumber = {}

        self._id2garbageInfo = {}

        self.cycles = []
        self.cyclesBySyntax = []
        self.uniqueCycleSets = set()
        self.cycleIds = set()

        # make the id->index table to speed up the next steps
        for i in range(self.numGarbage):
            self._id2index[id(self.garbage[i])] = i
            if i % 20 == 0:
                yield None

        # grab the referrers (pointing to garbage)
        if self._args.fullReport and (self.numGarbage != 0):
            if self._args.verbose:
                self.notify.info('getting referrers...')
            for i in range(self.numGarbage):
                yield None
                for result in self._getReferrers(self.garbage[i]):
                    yield None
                byNum, byRef = result
                self.referrersByNumber[i] = byNum
                self.referrersByReference[i] = byRef

        # grab the referents (pointed to by garbage)
        if self.numGarbage > 0:
            if self._args.verbose:
                self.notify.info('getting referents...')
            for i in range(self.numGarbage):
                yield None
                for result in self._getReferents(self.garbage[i]):
                    yield None
                byNum, byRef = result
                self.referentsByNumber[i] = byNum
                self.referentsByReference[i] = byRef

        for i in range(self.numGarbage):
            if hasattr(self.garbage[i], '_garbageInfo') and callable(
                    self.garbage[i]._garbageInfo):
                try:
                    info = self.garbage[i]._garbageInfo()
                except Exception as e:
                    info = str(e)
                self._id2garbageInfo[id(self.garbage[i])] = info
                yield None
            else:
                if i % 20 == 0:
                    yield None

        # find the cycles
        if self._args.findCycles and self.numGarbage > 0:
            if self._args.verbose:
                self.notify.info('calculating cycles...')
            for i in range(self.numGarbage):
                yield None
                for newCycles in self._getCycles(i, self.uniqueCycleSets):
                    yield None
                self.cycles.extend(newCycles)
                # create a representation of the cycle in human-readable form
                newCyclesBySyntax = []
                for cycle in newCycles:
                    cycleBySyntax = ''
                    objs = []
                    # leave off the last index, it's a repeat of the first index
                    for index in cycle[:-1]:
                        objs.append(self.garbage[index])
                        yield None
                    # make the list repeat so we can safely iterate off the end
                    numObjs = len(objs) - 1
                    objs.extend(objs)

                    # state variables for our loop below
                    numToSkip = 0
                    objAlreadyRepresented = False

                    # if cycle starts off with an instance dict, start with the instance instead
                    startIndex = 0
                    # + 1 to include a reference back to the first object
                    endIndex = numObjs + 1
                    if inspect.isclass(objs[-1]) and type(objs[0]) is dict:
                        startIndex -= 1
                        endIndex -= 1

                    for index in range(startIndex, endIndex):
                        if numToSkip:
                            numToSkip -= 1
                            continue
                        obj = objs[index]
                        if inspect.isclass(obj):
                            if not objAlreadyRepresented:
                                cycleBySyntax += '%s' % obj.__class__.__name__
                            cycleBySyntax += '.'
                            # skip past the instance dict and get the member obj
                            numToSkip += 1
                            member = objs[index + 2]
                            for key, value in obj.__dict__.items():
                                if value is member:
                                    break
                                yield None
                            else:
                                key = '<unknown member name>'
                            cycleBySyntax += '%s' % key
                            objAlreadyRepresented = True
                        elif type(obj) is dict:
                            cycleBySyntax += '{'
                            # get object referred to by dict
                            val = objs[index + 1]
                            for key, value in obj.items():
                                if value is val:
                                    break
                                yield None
                            else:
                                key = '<unknown key>'
                            cycleBySyntax += '%s}' % fastRepr(key)
                            objAlreadyRepresented = True
                        elif type(obj) in (tuple, list):
                            brackets = {
                                tuple: '()',
                                list: '[]',
                            }[type(obj)]
                            # get object being referenced by container
                            nextObj = objs[index + 1]
                            cycleBySyntax += brackets[0]
                            for index in range(len(obj)):
                                if obj[index] is nextObj:
                                    index = str(index)
                                    break
                                yield None
                            else:
                                index = '<unknown index>'
                            cycleBySyntax += '%s%s' % (index, brackets[1])
                            objAlreadyRepresented = True
                        else:
                            cycleBySyntax += '%s --> ' % itype(obj)
                            objAlreadyRepresented = False
                    newCyclesBySyntax.append(cycleBySyntax)
                    yield None
                self.cyclesBySyntax.extend(newCyclesBySyntax)
                # if we're not doing a full report, add this cycle's IDs to the master set
                if not self._args.fullReport:
                    for cycle in newCycles:
                        yield None
                        self.cycleIds.update(set(cycle))

        self.numCycles = len(self.cycles)

        if self._args.findCycles:
            s = [
                '===== GarbageReport: \'%s\' (%s %s) =====' %
                (self._args.name, self.numCycles,
                 ('cycle' if self.numCycles == 1 else 'cycles'))
            ]
        else:
            s = ['===== GarbageReport: \'%s\' =====' % (self._args.name)]
        if self.numGarbage > 0:
            # make a list of the ids we will actually be printing
            if self._args.fullReport:
                garbageIndices = range(self.numGarbage)
            else:
                garbageIndices = list(self.cycleIds)
                garbageIndices.sort()
            numGarbage = len(garbageIndices)

            # log each individual item with a number in front of it
            if not self._args.fullReport:
                abbrev = '(abbreviated) '
            else:
                abbrev = ''
            s.append('===== Garbage Items %s=====' % abbrev)
            digits = 0
            n = numGarbage
            while n > 0:
                yield None
                digits += 1
                n /= 10
            digits = digits
            format = '%0' + '%s' % digits + 'i:%s \t%s'

            for i in range(numGarbage):
                yield None
                idx = garbageIndices[i]
                if self._args.safeMode:
                    # in safe mode, don't try to repr any of the objects
                    objStr = repr(itype(self.garbage[idx]))
                else:
                    objStr = fastRepr(self.garbage[idx])
                maxLen = 5000
                if len(objStr) > maxLen:
                    snip = '<SNIP>'
                    objStr = '%s%s' % (objStr[:(maxLen - len(snip))], snip)
                s.append(format % (idx, itype(self.garbage[idx]), objStr))

            # also log the types of the objects
            s.append('===== Garbage Item Types %s=====' % abbrev)
            for i in range(numGarbage):
                yield None
                idx = garbageIndices[i]
                objStr = str(deeptype(self.garbage[idx]))
                maxLen = 5000
                if len(objStr) > maxLen:
                    snip = '<SNIP>'
                    objStr = '%s%s' % (objStr[:(maxLen - len(snip))], snip)
                s.append(format % (idx, itype(self.garbage[idx]), objStr))

            if self._args.findCycles:
                s.append('===== Garbage Cycles (Garbage Item Numbers) =====')
                ac = AlphabetCounter()
                for i in range(self.numCycles):
                    yield None
                    s.append('%s:%s' % (ac.next(), self.cycles[i]))

            if self._args.findCycles:
                s.append('===== Garbage Cycles (Python Syntax) =====')
                ac = AlphabetCounter()
                for i in range(len(self.cyclesBySyntax)):
                    yield None
                    s.append('%s:%s' % (ac.next(), self.cyclesBySyntax[i]))

            if len(self._id2garbageInfo) > 0:
                s.append('===== Garbage Custom Info =====')
                ac = AlphabetCounter()
                for i in range(len(self.cyclesBySyntax)):
                    yield None
                    counter = ac.next()
                    _id = id(self.garbage[i])
                    if _id in self._id2garbageInfo:
                        s.append('%s:%s' %
                                 (counter, self._id2garbageInfo[_id]))

            if self._args.fullReport:
                format = '%0' + '%s' % digits + 'i:%s'
                s.append(
                    '===== Referrers By Number (what is referring to garbage item?) ====='
                )
                for i in range(numGarbage):
                    yield None
                    s.append(format % (i, self.referrersByNumber[i]))
                s.append(
                    '===== Referents By Number (what is garbage item referring to?) ====='
                )
                for i in range(numGarbage):
                    yield None
                    s.append(format % (i, self.referentsByNumber[i]))
                s.append(
                    '===== Referrers (what is referring to garbage item?) ====='
                )
                for i in range(numGarbage):
                    yield None
                    s.append(format % (i, self.referrersByReference[i]))
                s.append(
                    '===== Referents (what is garbage item referring to?) ====='
                )
                for i in range(numGarbage):
                    yield None
                    s.append(format % (i, self.referentsByReference[i]))

        self._report = s

        if self._args.log:
            self.printingBegin()
            for i in range(len(self._report)):
                if self.numGarbage > 0:
                    yield None
                self.notify.info(self._report[i])
            self.notify.info('===== Garbage Report Done =====')
            self.printingEnd()

        yield Job.Done

    def finished(self):
        if self._args.doneCallback:
            self._args.doneCallback(self)
        if self._args.autoDestroy:
            self.destroy()

    def destroy(self):
        #print 'GarbageReport.destroy'
        del self._args
        del self.garbage
        # don't get rid of these, we might need them
        #del self.numGarbage
        #del self.numCycles
        del self.referrersByReference
        del self.referrersByNumber
        del self.referentsByReference
        del self.referentsByNumber
        if hasattr(self, 'cycles'):
            del self.cycles
        del self._report
        if hasattr(self, '_reportStr'):
            del self._reportStr
        Job.destroy(self)

    def getNumCycles(self):
        # if the job hasn't run yet, we don't have a numCycles yet
        return self.numCycles

    def getDesc2numDict(self):
        # dict of python-syntax leak -> number of that type of leak
        desc2num = {}
        for cycleBySyntax in self.cyclesBySyntax:
            desc2num.setdefault(cycleBySyntax, 0)
            desc2num[cycleBySyntax] += 1
        return desc2num

    def getGarbage(self):
        return self.garbage

    def getReport(self):
        if not hasattr(self, '_reportStr'):
            self._reportStr = ''
            for str in self._report:
                self._reportStr += '\n' + str
        return self._reportStr

    def _getReferrers(self, obj):
        # referrers (pointing to garbage)
        # returns two lists, first by index into gc.garbage, second by
        # direct reference
        yield None
        byRef = gc.get_referrers(obj)
        yield None
        # look to see if each referrer is another garbage item
        byNum = []
        for i in range(len(byRef)):
            if i % 20 == 0:
                yield None
            referrer = byRef[i]
            num = self._id2index.get(id(referrer), None)
            byNum.append(num)
        yield byNum, byRef

    def _getReferents(self, obj):
        # referents (pointed to by garbage)
        # returns two lists, first by index into gc.garbage, second by
        # direct reference
        yield None
        byRef = gc.get_referents(obj)
        yield None
        # look to see if each referent is another garbage item
        byNum = []
        for i in range(len(byRef)):
            if i % 20 == 0:
                yield None
            referent = byRef[i]
            num = self._id2index.get(id(referent), None)
            byNum.append(num)
        yield byNum, byRef

    def _getNormalizedCycle(self, cycle):
        # returns a representation of a cycle (list of indices) that will be
        # reliably derived from a unique cycle regardless of ordering
        # this lets us detect duplicate cycles that appear different because of
        # which element appears first
        if len(cycle) == 0:
            return cycle
        min = 1 << 30
        minIndex = None
        for i in range(len(cycle)):
            elem = cycle[i]
            if elem < min:
                min = elem
                minIndex = i
        return cycle[minIndex:] + cycle[:minIndex]

    def _getCycles(self, index, uniqueCycleSets=None):
        # detect garbage cycles for a particular item of garbage
        assert self.notify.debugCall()
        # returns list of lists, sublists are garbage reference cycles
        cycles = []
        # this lets us eliminate duplicate cycles
        if uniqueCycleSets is None:
            uniqueCycleSets = set()
        stateStack = Stack()
        rootId = index
        # check if the root object is one of the garbage instances (has __del__)
        objId = id(self.garbage[rootId])
        numDelInstances = int(objId in self.garbageInstanceIds)
        stateStack.push(([rootId], rootId, numDelInstances, 0))
        while True:
            yield None
            if len(stateStack) == 0:
                break
            candidateCycle, curId, numDelInstances, resumeIndex = stateStack.pop(
            )
            if self.notify.getDebug():
                if self._args.delOnly:
                    print(
                        'restart: %s root=%s cur=%s numDelInstances=%s resume=%s'
                        % (candidateCycle, rootId, curId, numDelInstances,
                           resumeIndex))
                else:
                    print('restart: %s root=%s cur=%s resume=%s' %
                          (candidateCycle, rootId, curId, resumeIndex))
            for index in range(resumeIndex,
                               len(self.referentsByNumber[curId])):
                yield None
                refId = self.referentsByNumber[curId][index]
                if self.notify.getDebug():
                    print('       : %s -> %s' % (curId, refId))
                if refId == rootId:
                    # we found a cycle! mark it down and move on to the next refId
                    normCandidateCycle = self._getNormalizedCycle(
                        candidateCycle)
                    normCandidateCycleTuple = tuple(normCandidateCycle)
                    if not normCandidateCycleTuple in uniqueCycleSets:
                        # cycles with no instances that define __del__ will be
                        # cleaned up by Python
                        if (not self._args.delOnly) or numDelInstances >= 1:
                            if self.notify.getDebug():
                                print(
                                    '  FOUND: ', normCandidateCycle + [
                                        normCandidateCycle[0],
                                    ])
                            cycles.append(normCandidateCycle + [
                                normCandidateCycle[0],
                            ])
                            uniqueCycleSets.add(normCandidateCycleTuple)
                elif refId in candidateCycle:
                    pass
                elif refId is not None:
                    # check if this object is one of the garbage instances (has __del__)
                    objId = id(self.garbage[refId])
                    numDelInstances += int(objId in self.garbageInstanceIds)
                    # this refId does not complete a cycle. Mark down
                    # where we are in this list of referents, then
                    # start looking through the referents of the new refId
                    stateStack.push((list(candidateCycle), curId,
                                     numDelInstances, index + 1))
                    stateStack.push((list(candidateCycle) + [refId], refId,
                                     numDelInstances, 0))
                    break
        yield cycles