def _updateFromTroveSetGraph(self, uJob, troveSet, troveCache,
                            split = True, fromChangesets = [],
                            criticalUpdateInfo=None, applyCriticalOnly = False,
                            restartInfo = None, callback = None,
                            ignoreMissingDeps = False):

        def _updateJob(job, addedTroves):
            for newTup in addedTroves:
                # First look for an exact erase or update in the old job that
                # would remove this trove
                erases = [x for x in job
                        if (x[0], x[1][0], x[1][1]) == newTup]
                if erases:
                    if len(erases) > 1 or erases[0][2][0] is not None:
                        # Corner case, fall back to doing a full diff
                        return False
                    # We're adding back a trove that the job would have erased
                    # and the two annihilate each other
                    job.remove(erases[0])
                    continue

                # Then look for a name-only match against an erase
                erases = [x for x in job
                        if x[0] == newTup[0] and x[2][0] is None]
                if erases:
                    if len(erases) > 1:
                        # Corner case
                        return False
                    # Convert this erasure into an update
                    job.remove(erases[0])
                    oldVF = erases[0][1]
                    job.append( (newTup[0], oldVF, newTup[1:3], False) )
                    continue

                # No match, it's a new install
                job.append( (newTup[0], (None, None), newTup[1:3], True) )
            return True

        if criticalUpdateInfo is None:
            criticalUpdateInfo = update.CriticalUpdateInfo()

        searchPath = troveSet.searchPath

        if callback:
            callback.executingSystemModel()

        #depSearch = CMLSearchPath([ preFetch, searchPath ],
                                               #graph = preFetch.g)
        depSearch = searchPath
        troveSet.g.realize(CMLActionData(troveCache,
                                              self.cfg.flavor[0],
                                              self.repos, self.cfg))

        existsTrv = trove.Trove("@model", versions.NewVersion(),
                                deps.Flavor(), None)
        targetTrv = trove.Trove("@model", versions.NewVersion(),
                                deps.Flavor(), None)

        pins = set()
        phantomsByName = {}
        for tup, pinned in self.db.iterAllTroves(withPins = True):
            existsTrv.addTrove(*tup)
            if pinned:
                pins.add(tup)
                targetTrv.addTrove(*tup)
            if tup[1].onPhantomLabel():
                phantomsByName.setdefault(tup[0], set()).add(tup)

        for tup, inInstall, explicit in \
                                troveSet._walk(troveCache, recurse = True):
            if inInstall and tup[0:3] not in pins:
                targetTrv.addTrove(*tup[0:3])

        self._closePackages(troveCache, targetTrv)

        if phantomsByName and self.cfg.syncCapsuleDatabase == 'update':
            # Allow phantom troves to be updated to a real trove, but preserve
            # ones that would be erased outright.
            for tup in targetTrv.iterTroveList(strongRefs=True):
                name = tup[0]
                if existsTrv.hasTrove(*tup):
                    # This particular trove is not replacing anything
                    continue
                if name in phantomsByName:
                    # Could be replacing a phantom trove, so keep the latter in
                    # the old set so it will be updated.
                    del phantomsByName[name]
            # Discard any unmatched phantom troves from the old set so that
            # they will be left alone.
            for tups in phantomsByName.itervalues():
                for tup in tups:
                    existsTrv.delTrove(missingOkay=False, *tup)

        job = targetTrv.diff(existsTrv, absolute = False)[2]

        if callback:
            callback.resolvingDependencies()

        # don't resolve against local troves (we can do this because either
        # they're installed and show up in the unresolveable list or they
        # aren't installed and we don't know about them) or troves which are
        # in the install set (since they're already in the install set,
        # adding them to the install set won't help)
        depDb = deptable.DependencyDatabase()
        depResolveSource = depSearch._getResolveSource(
                        depDb = depDb,
                        filterFn = lambda n, v, f :
                            (v.isOnLocalHost() or
                             targetTrv.isStrongReference(n,v,f)))
        resolveMethod = depResolveSource.getResolveMethod()

        uJob.setSearchSource(self.getSearchSource())
        # this is awful
        jobTroveSource = uJob.getTroveSource()
        jobTroveSource.addChangeSets(fromChangesets,
                                     includesFileContents = True)

        pathHashCache = {}

        resolveDeps = split = True
        if resolveDeps or split:
            check = self.db.getDepStateClass(troveCache,
               findOrdering = split,
               ignoreDepClasses = self.cfg.ignoreDependencies)

            linkedJobs = self._findOverlappingJobs(job, troveCache,
                                      pathHashCache = pathHashCache)

            criticalJobs = criticalUpdateInfo.findCriticalJobs(job)
            finalJobs = criticalUpdateInfo.findFinalJobs(job)
            criticalOnly = criticalUpdateInfo.isCriticalOnlyUpdate()

            log.info("resolving dependencies")
            result = check.depCheck(job,
                                    linkedJobs = linkedJobs,
                                    criticalJobs = criticalJobs,
                                    finalJobs = finalJobs,
                                    criticalOnly = criticalOnly)

            suggMap = {}
            while True:
                added = set()
                for (needingTup, neededDeps, neededTupList) in \
                                                result.unresolveableList:
                    for neededTup in neededTupList:
                        if (neededTup not in added and
                                searchPath.hasOptionalTrove(neededTup)):
                            log.info("keeping installed trove for deps %s",
                                     neededTup)
                            added.add(neededTup)

                if not added:
                    unsatisfied = result.unsatisfiedList
                    unsatisfied += [ x[0:2] for x in result.unresolveableList ]

                    while (resolveMethod.prepareForResolution(unsatisfied) and
                           not added):
                        sugg = resolveMethod.resolveDependencies()
                        newJob = resolveMethod.filterSuggestions(
                                            result.unsatisfiedList, sugg, suggMap)
                        for (name, oldInfo, newInfo, isAbsolute) in newJob:
                            assert(isAbsolute)
                            log.info("adding for dependency %s", name)
                            added.add((name, newInfo[0], newInfo[1]))

                if not added:
                    break

                for troveTup in added:
                    targetTrv.addTrove(*troveTup)

                added.update(self._closePackages(troveCache, targetTrv,
                                                 newTroves = added))

                # try to avoid a diff here
                if not _updateJob(job, added):
                    job = targetTrv.diff(existsTrv, absolute = False)[2]

                log.info("resolving dependencies (job length %d)", len(job))
                criticalJobs = criticalUpdateInfo.findCriticalJobs(job)
                finalJobs = criticalUpdateInfo.findFinalJobs(job)
                criticalOnly = criticalUpdateInfo.isCriticalOnlyUpdate()

                linkedJobs = self._findOverlappingJobs(job, troveCache,
                                          pathHashCache = pathHashCache)

                result = check.depCheck(job,
                                        linkedJobs = linkedJobs,
                                        criticalJobs = criticalJobs,
                                        finalJobs = finalJobs,
                                        criticalOnly = criticalOnly)

            check.done()
            log.info("job dependency closed; %s jobs resulted", len(job))

            # if any of the things we're about to install or remove use
            # capsules we cannot split the job
            if not split:
                splitJob = [ job ]
                criticalUpdates = []
            else:
                splitJob = result.getChangeSetList()
                criticalUpdates = [ splitJob[x] for x in
                                        result.getCriticalUpdates() ]

            if result.unsatisfiedList and (not ignoreMissingDeps):
                raise update.DepResolutionFailure(
                            self.cfg, result.unsatisfiedList,
                            suggMap, result.unresolveableList, splitJob,
                            criticalUpdates)
            elif result.unresolveableList and (not ignoreMissingDeps):
                # this can't happen because dep resolution empties
                # the unresolveableList into the unsatisfiedList to try
                # and find matches
                assert(0)
        else:
            (depList, suggMap, cannotResolve, splitJob, keepList,
             criticalUpdates) = ( [], {}, [], [ job ], [], [] )

        # this prevents us from using the changesetList as a searchSource
        log.info("processing job list")
        self._processCMLJobList(job, uJob, troveCache)
        log.info("gathering group defined path conflicts")
        self._findAcceptablePathConflicts(job, uJob, troveCache)
        log.info("combining jobs")
        self._combineJobs(uJob, splitJob, criticalUpdates)
        uJob.reorderPreScripts(criticalUpdateInfo)
        uJob.setTransactionCounter(self.db.getTransactionCounter())

        # remove things from the suggMap which are in the already installed
        # set
        for neededSet in suggMap.itervalues():
            for troveTup in set(neededSet):
                if existsTrv.hasTrove(*troveTup):
                    neededSet.remove(troveTup)

        for needingTroveTup, neededSet in suggMap.items():
            if not neededSet:
                del suggMap[needingTroveTup]

        if callback:
            callback.done()

        return suggMap
Populates an update job based on a set of trove update and erase
dataset/ETHPy150Open sassoftware/conary/conary/conaryclient/modelupdate.py/CMLClient._updateFromTroveSetGraph