def _processRedirects(self, csSource, uJob, jobSet, transitiveClosure,
                          recurse):
        """
        Looks for redirects in the change set, and returns a list of troves
        which need to be included in the update.  This returns redirectHack,
        which maps targets of redirections to the sources of those
        redirections.
        """

        redirectHack = {}

        jobsToRemove = []
        jobsToAdd = []
        transitiveClosureToRemove = []

        # We don't have to worry about non-primaries recursively included
        # from the job because groups can't include redirects, so any redirect
        # must either be a primary or have a parent who is a primary.
        #
        # All of this itertools stuff lets us iterate through the jobSet
        # with isPrimary set to True and then iterate through the jobs we
        # create here with isPrimary set to False.

        # The outer loop is to allow redirects to point to redirects. The
        # inner loop handles one set of troves.

        initialSet = itertools.izip(itertools.repeat(True), jobSet)
        while initialSet:
            alreadyHandled = set()
            nextSet = set()
            toDoSet = util.IterableQueue()
            for isPrimary, job in itertools.chain(initialSet, toDoSet):
                (name, (oldVersion, oldFlavor), (newVersion, newFlavor),
                    isAbsolute) = job
                item = (name, newVersion, newFlavor)

                if item in alreadyHandled:
                    continue
                alreadyHandled.add(item)

                if newVersion is None:
                    # Erasures don't involve redirects so they aren't
                    # interesting.
                    continue

                trv = uJob.getTroveSource().getTrove(name, newVersion,
                                                     newFlavor,
                                                     withFiles = False)

                if not trv.isRedirect():
                    continue

                if item in redirectHack:
                    # this was from a redirect to a redirect -- the list
                    # of what needs to be removed as part of this redirect
                    # needs to move to the new target
                    redirectSourceList = redirectHack[item]
                    del redirectHack[item]
                else:
                    redirectSourceList = []

                if isPrimary:
                    # Don't install a redirect
                    jobsToRemove.append(job)
                else:
                    transitiveClosureToRemove.append(job)

                if not recurse:
                    raise UpdateError, \
                        "Redirect found with --no-recurse set: %s=%s[%s]" % item

                allTargets = [ (x[0], str(x[1].label()), x[2])
                                        for x in trv.iterRedirects() ]
                for troveSpec in allTargets:
                    if (troveSpec[0] == trv.getName()
                       and troveSpec[1] == str(trv.getVersion().trailingLabel())
                       and troveSpec[2] is None):
                        # this is an pre-1.2 redirect from one branch to
                        # another on the same label.  It only makes sense
                        # with the branch information attached.
                        allTargets = list(trv.iterRedirects())
                        break

                matches = self.repos.findTroves([], allTargets, self.cfg.flavor,
                                                affinityDatabase = self.db)
                if not matches:
                    # this is a remove redirect
                    assert(not allTargets)
                    l = redirectHack.setdefault(None, redirectSourceList)
                    l.append(item)
                else:
                    for matchList in matches.itervalues():
                        for match in matchList:
                            if match in redirectSourceList:
                                raise UpdateError, \
                                    "Redirect loop found which includes " \
                                    "troves %s, %s" % (item[0],
                                    ", ".join(x[0] for x in redirectSourceList))
                            assert(match not in redirectSourceList)
                            l = redirectHack.setdefault(match,
                                                        redirectSourceList[:])
                            l.append(item)
                            redirectJob = (match[0], (None, None),
                                                     match[1:], True)
                            nextSet.add((isPrimary, redirectJob))
                            if isPrimary:
                                jobsToAdd.append(redirectJob)

                for info in trv.iterTroveList(strongRefs = True):
                    toDoSet.add((False,
                                 (info[0], (None, None), info[1:], True)))

            # The targets of redirects need to be loaded - but only
            # if they're not already in the job.
            nextSet = list(nextSet)
            hasTroves = uJob.getTroveSource().hasTroves([
                            (x[1][0], x[1][2][0], x[1][2][1]) for x in nextSet])
            nextSet = [ x[0] for x in itertools.izip(nextSet, hasTroves) \
                                                                  if not x[1] ]

            redirectCs, notFound = csSource.createChangeSet(
                    [ x[1] for x in nextSet ],
                    withFiles = False, recurse = True)

            uJob.getTroveSource().addChangeSet(redirectCs)
            transitiveClosure.update(redirectCs.getJobSet(primaries = False))

            initialSet = nextSet

        # We may remove some jobs which we add due to redirection chains.
        jobSet.update(jobsToAdd)
        jobSet.difference_update(jobsToRemove)

        # remove redirects from transitive closure
        transitiveClosure.difference_update(transitiveClosureToRemove)
        transitiveClosure.difference_update(jobsToRemove)

        for l in redirectHack.itervalues():
            outdated = self.db.outdatedTroves(l)
            del l[:]
            for (name, newVersion, newFlavor), \
                  (oldName, oldVersion, oldFlavor) in outdated.iteritems():
                if oldVersion is not None:
                    l.append((oldName, oldVersion, oldFlavor))

        return redirectHack
dataset/ETHPy150Open sassoftware/conary/conary/conaryclient/update.py/ClientUpdate._processRedirects/original