def test_update(self): # Given packages_definition = textwrap.dedent(u"""\ dummy 1.0.1-1 dummy_with_appinst 1.0.0-1 dummy_with_entry_points 1.0.0-1 dummy_with_proxy 1.3.40-3 dummy_with_proxy_scripts 1.0.0-1 dummy_with_proxy_softlink 1.0.0-1 nose 1.2.1-1 nose 1.3.0-1 nose 1.3.0-2\ """) packages = self.packages_from_definition(packages_definition) # When repository = Repository(packages[:4]) # Then self.assertEqual(len(repository), 4) # When repository.update(packages[4:]) # Then self.assertEqual(len(repository), len(packages)) self.assertEqual(list(repository), packages)
def test_find_packages(self): # Given packages_definition = textwrap.dedent(u"""\ dummy 1.0.1-1 dummy_with_appinst 1.0.0-1 dummy_with_entry_points 1.0.0-1 dummy_with_proxy 1.3.40-3 dummy_with_proxy_scripts 1.0.0-1 dummy_with_proxy_softlink 1.0.0-1 nose 1.3.0-1 nose 1.2.1-1 nose 1.3.0-2\ """) packages = self.packages_from_definition(packages_definition) repository = Repository(packages) r_versions = (V("1.2.1-1"), V("1.3.0-1"), V("1.3.0-2")) # When packages = repository.find_packages("nose") # Then self.assertEqual(len(packages), 3) self.assertEqual(tuple(p.version for p in packages), r_versions) # When packages = repository.find_packages("non_existing_package") # Then self.assertEqual(len(packages), 0)
def setUp(self): self.repository = Repository() self.installed_repository = Repository() self._package_parser = PrettyPackageStringParser( EnpkgVersion.from_string )
def repository_factory(package_names, repository_info, reference_packages): repository = Repository() for package_name in package_names: package = reference_packages[package_name] package = RepositoryPackageMetadata(package, repository_info) repository.add_package(package) return repository
def test_add_package(self): # Given repository_info1 = RepositoryInfo("repo1") packages_definition = textwrap.dedent(u"""\ dummy 1.0.1-1 dummy_with_appinst 1.0.0-1 dummy_with_entry_points 1.0.0-1 dummy_with_proxy 1.3.40-3 dummy_with_proxy_scripts 1.0.0-1 dummy_with_proxy_softlink 1.0.0-1 nose 1.2.1-1 nose 1.3.0-1 nose 1.3.0-2\ """) packages = self.packages_from_definition(packages_definition, repository_info1) repository = Repository() # When repository.add_package(packages[0]) # Then self.assertTrue(packages[0] in repository) self.assertEqual(len(repository), 1) self.assertEqual(list(repository), packages[:1])
def _convert_upgrade_request_if_needed(request, remote_repositories, installed_repository): if len(request.jobs) == 1 and request.jobs[0].kind == JobType.upgrade: upgrade_request = attr.assoc(request, jobs=[]) remote_repository = Repository() for repository in remote_repositories: remote_repository.update(repository) latest_packages = [] for package in installed_repository: candidates = remote_repository.find_packages(package.name) # candidates may be empty (e.g. when repository configuration # changed, and an installed package is coming from a repository not # configured in the remote list) if len(candidates) > 0: latest_packages.append(candidates[-1]) for p in latest_packages: upgrade_request.install( InstallRequirement._from_string("{} == {}".format(p.name, p.version)) ) return upgrade_request else: return request
def test_regression_185(self): # Given repository_info1 = RepositoryInfo("repo1") packages_definition = textwrap.dedent(u"""\ dummy 1.0.1-1 dummy_with_appinst 1.0.0-1 dummy_with_entry_points 1.0.0-1 dummy_with_proxy 1.3.40-3 dummy_with_proxy_scripts 1.0.0-1 dummy_with_proxy_softlink 1.0.0-1 nose 1.2.1-1 nose 1.3.0-1 nose 1.3.0-2\ """) packages = self.packages_from_definition( packages_definition, repository_info1 ) repository = Repository() package = packages[0] # When repository.find_packages(package.name) repository.add_package(package) # Then self.assertTrue(packages[0] in repository) self.assertEqual(len(repository), 1) self.assertEqual(list(repository), [package])
def _solve(self, top_core, flags={}, only_matching_vlnv=False): def eq_vln(this, that): return \ this.vendor == that.vendor and \ this.library == that.library and \ this.name == that.name repo = Repository() _flags = flags.copy() cores = [x['core'] for x in self._cores.values()] for core in cores: if only_matching_vlnv: if not eq_vln(core.name, top_core): continue package_str = "{} {}-{}".format(self._package_name(core.name), core.name.version, core.name.revision) if not only_matching_vlnv: _flags['is_toplevel'] = (core.name == top_core) _depends = core.get_depends(_flags) if _depends: _s = "; depends ( {} )" package_str += _s.format(self._parse_depend(_depends)) parser = PrettyPackageStringParser(EnpkgVersion.from_string) package = parser.parse_to_package(package_str) package.core = core repo.add_package(package) request = Request() _top_dep = "{} {} {}".format(self._package_name(top_core), top_core.relation, self._package_version(top_core)) requirement = Requirement._from_string(_top_dep) request.install(requirement) installed_repository = Repository() pool = Pool([repo]) pool.add_repository(installed_repository) solver = DependencySolver(pool, [repo], installed_repository) try: transaction = solver.solve(request) except SatisfiabilityError as e: raise DependencyError(top_core.name, msg=e.unsat.to_string(pool)) except NoPackageFound as e: raise DependencyError(top_core.name) return [op.package.core for op in transaction.operations]
def simplify_requirements(packages, requirements): """ Return a reduced, but equivalent set of requirements. Parameters ---------- packages : iterable of PackageMetadata The packages available to draw from when satisfying requirements. requirements : list of Requirement The requirements used to identify relevent packages. Returns ------- tuple of Requirement The reduced requirements. """ needed_packages = packages_from_requirements(packages, requirements) pool = Pool([Repository(packages)]) R = InstallRequirement.from_constraints dependencies = set(itertools.chain.from_iterable( pool.what_provides(R(con)) for package in needed_packages for con in package.install_requires )) simple_requirements = requirements_from_packages( package for package in needed_packages if package not in dependencies ) return simple_requirements
def test_accept_anyversion_constraint_on_provides_metadata(self): # When repository = Repository(self.packages_from_definition( u"numpy 1.9.2-1; provides (A, B *)")) # Then Pool([repository])
def packages_from_requirements(packages, requirements, modifiers=None): """ Return a new tuple that only contains packages explicitly mentioned in the requirements. Parameters ---------- packages : iterable of PackageMetadata The packages available for inclusion in the result. requirements : list of Requirement The requirements used to identify relevant packages. All packages that satisfy any of the requirements will be included. modifiers : ConstraintModifiers, optional If not None, modify requirements before resolving packages. Returns ------- Tuple of PackageMetadata A tuple containing the relevant packages. """ pool = Pool((Repository(packages),), modifiers=modifiers) listed_packages = set() for requirement in requirements: listed_packages.update(pool.what_provides(requirement)) return tuple(sorted(listed_packages, key=lambda p: p._key))
def requirements_are_satisfiable(packages, requirements, modifiers=None): """ Determine if the `requirements` can be satisfied together. Parameters ---------- packages : iterable of PackageMetadata The packages available to draw from when satisfying requirements. requirements : list of Requirement The requirements used to identify relevent packages. modifiers : ConstraintModifiers, optional If not None, modify requirements before resolving packages. Returns ------- bool Return True if the `requirements` can be satisfied by the `packages`. """ modifiers = modifiers or ConstraintModifiers() Result = collections.namedtuple("Result", "is_satisfiable message") request = Request(modifiers=modifiers) for requirement in requirements: request.install(requirement) repositories = (Repository(packages),) pool = Pool(repositories, modifiers=modifiers) try: DependencySolver(pool, repositories, []).solve(request) return Result(is_satisfiable=True, message="") except SatisfiabilityError as e: return Result( is_satisfiable=False, message=e.unsat.to_string(pool=pool) )
def solve(self, top_core, tool): repo = Repository() for core in self._cores.values(): package_str = "{} {}-{}".format(self._package_name(core.name), core.name.version, core.name.revision) _depends = core.depend try: _depends += getattr(core, tool).depend except (AttributeError, KeyError): pass if _depends: _s = "; depends ( {} )" package_str += _s.format(self._parse_depend(_depends)) parser = PrettyPackageStringParser(EnpkgVersion.from_string) package = parser.parse_to_package(package_str) package.core = core repo.add_package(package) request = Request() _top_dep = "{} {} {}".format(self._package_name(top_core), top_core.relation, self._package_version(top_core)) requirement = Requirement._from_string(_top_dep) request.install(requirement) installed_repository = Repository() pool = Pool([repo]) pool.add_repository(installed_repository) solver = DependencySolver(pool, repo, installed_repository) try: transaction = solver.solve(request) except SatisfiabilityError as e: msg = "UNSATISFIABLE: {}" raise RuntimeError(msg.format(e.unsat.to_string(pool))) except NoPackageFound as e: raise DependencyError(top_core.name) return [op.package.core for op in transaction.operations]
def test_iter_package_ids(self): # Given numpy_packages = self.packages_from_definition(NUMPY_PACKAGES) repository = Repository(numpy_packages) # When pool = Pool([repository]) # Then package_ids = set(pool.iter_package_ids()) self.assertEqual(package_ids, set(pool._id_to_package_.keys()))
def test_what_provides_caret(self): # Given repository = Repository(self.packages_from_definition(NUMPY_PACKAGES)) requirement = InstallRequirement._from_string("numpy ^= 1.8.1") # When pool = Pool([repository]) candidates = pool.what_provides(requirement) # Then self.assertEqual(len(candidates), 1) self.assertEqual(candidates[0].version, V("1.8.1-1"))
def test_what_provides_casing(self): # Given repository = Repository(self.packages_from_definition(NUMPY_PACKAGES)) requirement = InstallRequirement._from_string("mkl ^= 10.2") # When pool = Pool([repository]) candidates = pool.what_provides(requirement) versions = [str(candidate.version) for candidate in candidates] # Then six.assertCountEqual(self, versions, ["10.2-1", "10.2-2"])
def test_id_to_string(self): # Given repository = Repository(self.packages_from_definition(NUMPY_PACKAGES)) requirement = InstallRequirement._from_string("numpy >= 1.8.1") # When pool = Pool([repository]) candidate = pool.what_provides(requirement)[0] package_id = pool.package_id(candidate) # Then self.assertEqual(pool.id_to_string(package_id), "+numpy-1.8.1-1") self.assertEqual(pool.id_to_string(-package_id), "-numpy-1.8.1-1")
def test_find_package(self): # Given packages_definition = textwrap.dedent(u"""\ dummy 1.0.1-1 dummy_with_appinst 1.0.0-1 dummy_with_entry_points 1.0.0-1 dummy_with_proxy 1.3.40-3 dummy_with_proxy_scripts 1.0.0-1 dummy_with_proxy_softlink 1.0.0-1 nose 1.2.1-1 nose 1.3.0-1 nose 1.3.0-2\ """) packages = self.packages_from_definition(packages_definition) repository = Repository(packages) # When package = repository.find_package("nose", V("1.3.0-1")) # Then self.assertEqual(package.name, "nose") self.assertEqual(package.version, V("1.3.0-1"))
def test_what_provides_multiple(self): # Given repository = Repository(self.packages_from_definition(NUMPY_PACKAGES)) requirement = InstallRequirement._from_string( "numpy >= 1.8.0, numpy < 1.8.1") # When pool = Pool([repository]) candidates = pool.what_provides(requirement) versions = [str(candidate.version) for candidate in candidates] # Then six.assertCountEqual( self, versions, ["1.8.0-1", "1.8.0-2", "1.8.0-3"] )
def test_modification_what_provides(self): # Given repository = Repository(self.packages_from_definition( "numpy 1.8.1-1; depends (MKL == 10.3-1)")) request = Request() request.modifiers.allow_newer.add('numpy') # When pool = Pool([repository], modifiers=request.modifiers) requirement = InstallRequirement._from_string('numpy ^= 1.7') numpy_181 = list(repository)[0] result = pool.what_provides(requirement) expected = [numpy_181] # Then self.assertEqual(result, expected)
def test_contains(self): # Given repository_info1 = RepositoryInfo("repo1") repository_info2 = RepositoryInfo("repo2") packages_definition = textwrap.dedent(u"""\ dummy 1.0.1-1 dummy_with_appinst 1.0.0-1 dummy_with_entry_points 1.0.0-1 dummy_with_proxy 1.3.40-3 dummy_with_proxy_scripts 1.0.0-1 dummy_with_proxy_softlink 1.0.0-1 nose 1.2.1-1 nose 1.3.0-1 nose 1.3.0-2\ """) packages = self.packages_from_definition(packages_definition, repository_info1) repository = Repository(packages) # When/Then for package in repository: self.assertTrue(package in repository) # Given package = RepositoryPackageMetadata._from_pretty_string( "dummy 1.0.1-1", RepositoryInfo("another")) # When/Then self.assertFalse(package in repository) # Given dummy_repository1 = self.packages_from_definition( "dummy 1.0.1-1", repository_info1)[0] dummy_repository2 = self.packages_from_definition( "dummy 1.0.1-1", repository_info2)[0] # When/Then self.assertTrue(dummy_repository1 in repository) self.assertFalse(dummy_repository2 in repository)
def test_reject_version_constraint_on_provides_metadata(self): # Given constraints = ( u"A > 1.8", u"A >= 1.8", u"A <= 1.8", u"A < 1.8", u"A ^= 1.8", u"A == 1.8-1", ) # When for constraint in constraints: repository = Repository(self.packages_from_definition( u"numpy 1.9.2-1; provides ({})".format(constraint))) constraint_re = re.escape(constraint) # Then with self.assertRaisesRegexp(InvalidConstraint, constraint_re): Pool([repository])
def test_find_unavailable_package(self): # Given packages_definition = textwrap.dedent(u"""\ dummy 1.0.1-1 dummy_with_appinst 1.0.0-1 dummy_with_entry_points 1.0.0-1 dummy_with_proxy 1.3.40-3 dummy_with_proxy_scripts 1.0.0-1 dummy_with_proxy_softlink 1.0.0-1 nose 1.2.1-1 nose 1.3.0-1 nose 1.3.0-2\ """) packages = self.packages_from_definition(packages_definition) repository = Repository(packages) # When/Then with self.assertRaises(NoPackageFound): repository.find_package("nose", V("1.4.0-1")) repository.find_package("nose", V("1.3.0-1")) with self.assertRaises(NoPackageFound): repository.find_package("nono", V("1.3.0-1"))
def satisfy_requirements(packages, requirements, modifiers=None): """ Find a collection of packages that satisfy the requirements. Parameters ---------- packages : iterable of PackageMetadata The packages available to draw from when satisfying requirements. requirements : list of Requirement The requirements used to identify relevent packages. modifiers : ConstraintModifiers, optional If not None, modify requirements before resolving packages. Returns ------- tuple of PackageMetadata Return a tuple of packages that together satisfy all of the `requirements`. The packages are in topological order. Raises ------ SatisfiabilityError If the `requirements` cannot be satisfied using the `packages`. """ request = Request(modifiers=modifiers) for requirement in requirements: request.install(requirement) repositories = (Repository(packages),) pool = Pool(repositories, modifiers=modifiers) transaction = DependencySolver(pool, repositories, []).solve(request) msg = (""" Unexpected operation in the transaction. This should never occur. Something in simplesat is broken. {!r}""") for op in transaction.operations: # Our installed repository was empty so everything should be an install # operation assert isinstance(op, InstallOperation), msg.format(op) packages = tuple(op.package for op in transaction.operations) return packages
def pool_and_repository_from_packages(packages): repository = Repository(packages_from_definition(packages)) pool = Pool([repository]) return pool, repository
def _solve(self, top_core, flags={}, only_matching_vlnv=False): def eq_vln(this, that): return ( this.vendor == that.vendor and this.library == that.library and this.name == that.name ) # Try to return a cached result solver_cache_key = (top_core, self._hash_flags_dict(flags), only_matching_vlnv) cached_solution = self._solver_cache_lookup(solver_cache_key) if cached_solution: return cached_solution repo = Repository() _flags = flags.copy() cores = [x["core"] for x in self._cores.values()] for core in cores: if only_matching_vlnv: if not eq_vln(core.name, top_core): continue package_str = "{} {}-{}".format( self._package_name(core.name), core.name.version, core.name.revision ) if not only_matching_vlnv: _flags["is_toplevel"] = core.name == top_core _depends = core.get_depends(_flags) if _depends: _s = "; depends ( {} )" package_str += _s.format(self._parse_depend(_depends)) parser = PrettyPackageStringParser(EnpkgVersion.from_string) package = parser.parse_to_package(package_str) package.core = core repo.add_package(package) request = Request() simplevlnvs = top_core.simpleVLNVs() for sv in simplevlnvs: _top_dep = "{} {} {}".format( self._package_name(top_core), top_core.relation, self._package_version(top_core), ) request.install(Requirement._from_string(_top_dep)) installed_repository = Repository() pool = Pool([repo]) pool.add_repository(installed_repository) solver = DependencySolver(pool, [repo], installed_repository) try: transaction = solver.solve(request) except SatisfiabilityError as e: raise DependencyError(top_core.name, msg=e.unsat.to_string(pool)) except NoPackageFound as e: raise DependencyError(top_core.name) objdict = {} depdict = {} if len(transaction.operations) > 1: for op in transaction.operations: objdict[op.package._name] = str(op.package.core.name) depdict[str(op.package.core.name)] = [ objdict[n[0]] for n in op.package.install_requires ] op.package.core.direct_deps = [ objdict[n[0]] for n in op.package.install_requires ] result = [op.package.core for op in transaction.operations] # Cache the solution for further lookups self._solver_cache_store(solver_cache_key, result) return result
def setUp(self): self.repository = Repository() self.installed_repository = Repository() self._package_parser = PrettyPackageStringParser( EnpkgVersion.from_string)
class TestSolver(unittest.TestCase): def setUp(self): self.repository = Repository() self.installed_repository = Repository() self._package_parser = PrettyPackageStringParser( EnpkgVersion.from_string ) def package_factory(self, s): return self._package_parser.parse_to_package(s) def resolve(self, request): pool = Pool([self.repository, self.installed_repository]) solver = DependencySolver( pool, self.repository, self.installed_repository, use_pruning=False ) return solver.solve(request) def assertEqualOperations(self, operations, r_operations): self.assertEqual(operations, r_operations) def test_simple_install(self): # Given mkl = self.package_factory(u"mkl 10.3-1") self.repository.add_package(mkl) r_operations = [InstallOperation(mkl)] request = Request() request.install(R("mkl")) # When transaction = self.resolve(request) # Then self.assertEqualOperations(transaction.operations, r_operations) def test_multiple_installs(self): # Given mkl = self.package_factory(u"mkl 10.3-1") libgfortran = self.package_factory(u"libgfortran 3.0.0-2") r_operations = [ InstallOperation(mkl), InstallOperation(libgfortran), ] self.repository.add_package(mkl) self.repository.add_package(libgfortran) request = Request() request.install(R("mkl")) request.install(R("libgfortran")) # When transaction = self.resolve(request) # Then self.assertEqualOperations(transaction.operations, r_operations) def test_simple_dependency(self): # Given mkl = self.package_factory(u"mkl 10.3-1") libgfortran = self.package_factory(u"libgfortran 3.0.0-2") numpy = self.package_factory( u"numpy 1.9.2-1; depends (mkl == 10.3-1, libgfortran ^= 3.0.0)" ) r_operations = [ InstallOperation(mkl), InstallOperation(libgfortran), InstallOperation(numpy), ] self.repository.add_package(mkl) self.repository.add_package(libgfortran) self.repository.add_package(numpy) request = Request() request.install(R("numpy")) # When transaction = self.resolve(request) # Then self.assertEqualOperations(transaction.operations, r_operations) def test_already_installed(self): # Given mkl1 = self.package_factory(u"mkl 10.3-1") mkl2 = self.package_factory(u"mkl 10.3-2") r_operations = [] self.repository.add_package(mkl1) self.repository.add_package(mkl2) self.installed_repository.add_package(mkl1) # When request = Request() request.install(R("mkl")) transaction = self.resolve(request) # Then self.assertEqualOperations(transaction.operations, r_operations) # Given r_operations = [ UpdateOperation(mkl2, mkl1), ] # When request = Request() request.install(R("mkl > 10.3-1")) # When transaction = self.resolve(request) # Then self.assertEqualOperations(transaction.operations, r_operations)