def test_intersects_request(self): """Test intersects with request object""" # request.get request = RequirementsBinding([Requirement("foo.bar-1")]) bar_on = intersects(request.get("foo.bar", "0"), "1") self.assertTrue(bar_on) request = RequirementsBinding([]) bar_on = intersects(request.get("foo.bar", "0"), "1") self.assertTrue(bar_on) # should be False, but for backward compat request = RequirementsBinding([]) bar_on = intersects(request.get("foo.bar", "foo.bar-0"), "1") self.assertFalse(bar_on) # workaround, see PR nerdvegas/rez#1030 # request.get_range request = RequirementsBinding([Requirement("foo.bar-1")]) bar_on = intersects(request.get_range("foo.bar", "0"), "1") self.assertTrue(bar_on) request = RequirementsBinding([]) bar_on = intersects(request.get_range("foo.bar", "0"), "1") self.assertFalse(bar_on) request = RequirementsBinding([]) foo = intersects(request.get_range("foo", "==1.2.3"), "1.2") self.assertTrue(foo) request = RequirementsBinding([]) foo = intersects(request.get_range("foo", "==1.2.3"), "1.4") self.assertFalse(foo) request = RequirementsBinding([Requirement("foo-1.4.5")]) foo = intersects(request.get_range("foo", "==1.2.3"), "1.4") self.assertTrue(foo)
def _confl(reqs, a, b): _print("requirements(%s) == %s <--!--> %s" % (' '.join(reqs), a, b)) reqs_ = [Requirement(x) for x in reqs] reqlist = RequirementList(reqs_) _print("result: %s" % str(reqlist)) a_req = Requirement(a) b_req = Requirement(b) self.assertTrue(reqlist.conflict == (a_req, b_req))
def _eq(reqs, expected_reqs): _print("requirements(%s) == requirements(%s)" % (' '.join(reqs), ' '.join(expected_reqs))) reqs_ = [Requirement(x) for x in reqs] reqlist = RequirementList(reqs_) _print("result: %s" % str(reqlist)) exp_reqs_ = [Requirement(x) for x in expected_reqs] self.assertTrue(reqlist.requirements == exp_reqs_)
def test_intersects_ephemerals(self): """Test intersects with ephemerals object""" # ephemerals.get ephemerals = EphemeralsBinding([Requirement(".foo.bar-1")]) bar_on = intersects(ephemerals.get("foo.bar", "0"), "1") self.assertTrue(bar_on) ephemerals = EphemeralsBinding([]) bar_on = intersects(ephemerals.get("foo.bar", "0"), "1") self.assertTrue(bar_on) # should be False, but for backward compat ephemerals = EphemeralsBinding([]) bar_on = intersects(ephemerals.get("foo.bar", "foo.bar-0"), "1") self.assertFalse( bar_on ) # workaround, see https://github.com/nerdvegas/rez/pull/1030 ephemerals = EphemeralsBinding([]) self.assertRaises( RuntimeError, # no default intersects, ephemerals.get("foo.bar"), "0") # ephemerals.get_range ephemerals = EphemeralsBinding([Requirement(".foo.bar-1")]) bar_on = intersects(ephemerals.get_range("foo.bar", "0"), "1") self.assertTrue(bar_on) ephemerals = EphemeralsBinding([]) bar_on = intersects(ephemerals.get_range("foo.bar", "0"), "1") self.assertFalse(bar_on) ephemerals = EphemeralsBinding([]) foo = intersects(ephemerals.get_range("foo", "==1.2.3"), "1.2") self.assertTrue(foo) ephemerals = EphemeralsBinding([]) foo = intersects(ephemerals.get_range("foo", "==1.2.3"), "1.4") self.assertFalse(foo) ephemerals = EphemeralsBinding([Requirement(".foo-1.4.5")]) foo = intersects(ephemerals.get_range("foo", "==1.2.3"), "1.4") self.assertTrue(foo) ephemerals = EphemeralsBinding([]) self.assertRaises( RuntimeError, # no default intersects, ephemerals.get_range("foo.bar"), "0")
def test_packaging_req_to_rez_req(self): """ """ self.assertEqual( rez.utils.pip.packaging_req_to_rez_req( packaging_Requirement("package>1")), Requirement("package-1.1+")) self.assertEqual( rez.utils.pip.packaging_req_to_rez_req( packaging_Requirement("package")), Requirement("package")) self.assertEqual( rez.utils.pip.packaging_req_to_rez_req( packaging_Requirement("package[extra]")), Requirement("package"))
def _eq(reqs, expected_reqs): _print("requirements(%s) == requirements(%s)" % (' '.join(reqs), ' '.join(expected_reqs))) reqs_ = [Requirement(x) for x in reqs] reqlist = RequirementList(reqs_) _print("result: %s" % str(reqlist)) exp_reqs_ = [Requirement(x) for x in expected_reqs] self.assertTrue(reqlist.requirements == exp_reqs_) exp_names = set(x.name for x in exp_reqs_ if not x.conflict) self.assertTrue(reqlist.names == exp_names) exp_confl_names = set(x.name for x in exp_reqs_ if x.conflict) self.assertTrue(reqlist.conflict_names == exp_confl_names)
def _update_status(self): def _ok(): self._set_style() self.setToolTip("") def _err(msg, color="red"): self._set_style("QLineEdit { border : 2px solid %s;}" % color) self.setToolTip(msg) txt = str(self.text()) if not txt: _ok() return try: req = Requirement(str(txt)) except Exception as e: _err(str(e)) return _ok() if not req.conflict: try: it = iter_packages(name=req.name, range_=req.range, paths=self._paths) pkg = sorted(it, key=lambda x: x.version)[-1] except Exception: _err("cannot find package: %r" % txt, "orange") return if pkg.description: self.setToolTip(pkg.description)
def _solve(self, packages, expected_resolve): print() reqs = [Requirement(x) for x in packages] s1, s2, s_perms = self._create_solvers(reqs) s1.solve() self.assertEqual(s1.status, SolverStatus.solved) # ephemeral order doesn't matter, hence the sort resolve = ([str(x) for x in s1.resolved_packages] + sorted(str(x) for x in s1.resolved_ephemerals)) print() print("request: %s" % ' '.join(packages)) print("expecting: %s" % ' '.join(expected_resolve)) print("result: %s" % ' '.join(str(x) for x in resolve)) self.assertEqual(resolve, expected_resolve) print("checking that unoptimised solve matches optimised...") s2.solve() self.assertEqual(s2.status, SolverStatus.solved) resolve2 = ([str(x) for x in s2.resolved_packages] + sorted(str(x) for x in s2.resolved_ephemerals)) self.assertEqual(resolve2, resolve) print("checking that permutations also succeed...") for s in s_perms: s.solve() self.assertEqual(s.status, SolverStatus.solved) return s1
def get_package(self): """Get the target package. Returns: `Package`: Package to run tests on. """ if self.package is not None: return self.package if self.use_current_env: # get package from current context, or return None current_context = ResolvedContext.get_current() if current_context is None: return None req = Requirement(self.package_request) variant = current_context.get_resolved_package(req.name) if variant is None: return None package = variant.parent if not req.range.contains_version(package.version): return None else: # find latest package within request package = get_latest_package_from_string(str(self.package_request), self.package_paths) if package is None: raise PackageNotFoundError("Could not find package to test: %s" % str(self.package_request)) self.package = package return self.package
def _solve(self, packages, expected_resolve): print reqs = [Requirement(x) for x in packages] s1, s2, s_perms = self._create_solvers(reqs) s1.solve() self.assertEqual(s1.status, SolverStatus.solved) resolve = [str(x) for x in s1.resolved_packages] print print "request: %s" % ' '.join(packages) print "expecting: %s" % ' '.join(expected_resolve) print "result: %s" % ' '.join(str(x) for x in resolve) self.assertEqual(resolve, expected_resolve) print "checking that unoptimised solve matches optimised..." s2.solve() self.assertEqual(s2.status, SolverStatus.solved) resolve2 = [str(x) for x in s2.resolved_packages] self.assertEqual(resolve2, resolve) print "checking that permutations also succeed..." for s in s_perms: s.solve() self.assertEqual(s.status, SolverStatus.solved) return s1
def intersects(obj, range_): """Test if an object intersects with the given version range. Examples: # in package.py def commands(): # test a request if intersects(request.maya, '2019+'): info('requested maya allows >=2019.*') # tests if a resolved version intersects with given range if intersects(resolve.maya, '2019+') ... # same as above if intersects(resolve.maya.version, '2019+') ... # disable my cli tools if .foo.cli-0 was specified def commands(): if intersects(ephemerals.get('foo.cli', '1'), '1'): env.PATH.append('{root}/bin') Args: obj (VariantBinding or str): Object to test, either a variant, or requirement string (eg 'foo-1.2.3+'). range_ (str): Version range, eg '1.2+<2' Returns: bool: True if the object intersects the given range. """ range1 = VersionRange(range_) # eg 'if intersects(request.maya, ...)' if isinstance(obj, basestring): req = Requirement(obj) if req.conflict: return False range2 = req.range # eg 'if intersects(ephemerals.get_range('foo.cli', '1'), ...)' elif isinstance(obj, VersionRange): range2 = obj # eg 'if intersects(resolve.maya, ...)' elif isinstance(obj, VariantBinding): range2 = VersionRange(str(obj.version)) # eg 'if intersects(resolve.maya.version, ...)' elif isinstance(obj, VersionBinding): range2 = VersionRange(str(obj)) else: raise RuntimeError( "Invalid type %s passed as first arg to 'intersects'" % type(obj) ) return range1.intersects(range2)
def get_range(self, name, default=None): """Returns ephemeral version range object""" req_str = self._data.get(name) if req_str: return Requirement(req_str).range elif default is not None: return VersionRange(default) else: return None
def _apply_resolve(self, context_model, column, reference_column, hide_locks=False, read_only=False, reference_column_is_variants=False): context = context_model.context() resolved = context.resolved_packages[:] consumed_rows = set() # match variants up with matching request/variant in source column for row, widget in self._iter_column_widgets( reference_column, (PackageSelectWidget, VariantCellWidget)): request_str = str(widget.text()) if not request_str: continue package_name = Requirement(request_str).name matches = [x for x in resolved if x.name == package_name] if matches: variant = matches[0] resolved = [x for x in resolved if x.name != package_name] reference_variant = None if reference_column_is_variants and isinstance( widget, VariantCellWidget): reference_variant = widget.variant self._set_variant_cell(row, column, context_model, variant, reference_variant=reference_variant, hide_locks=hide_locks, read_only=read_only) consumed_rows.add(row) # append variants that don't match reference requests/variants if reference_column_is_variants: hide_locks = True row = 0 while resolved: variant = resolved[0] resolved = resolved[1:] while row in consumed_rows: row += 1 self._set_variant_cell(row, column, context_model, variant, hide_locks=hide_locks, read_only=read_only) row += 1
def _parse_request(cls, resources_request): name_pattern = resources_request or '*' version_range = None try: req = Requirement(name_pattern) name_pattern = req.name if not req.range.is_any(): version_range = req.range except: pass return name_pattern, version_range
def test_range_filter(self): """Test the range filter. """ fltr = PackageFilter() fltr.add_exclusion(RangeRule(Requirement("timestamped-1.1+"))) self._test( fltr, "timestamped", [ "1.0.5", "1.0.6" ] )
def set_package_text(self, txt): try: req = Requirement(str(txt)) package_name = req.name version_range = req.range except: package_name = str(txt) version_range = None self.edit.setText(package_name) self._set_package_name(package_name) if version_range is not None: self.versions_table.select_version(version_range)
def _fail(self, *packages): reqs = [Requirement(x) for x in packages] s1, s2, s_perms = self._create_solvers(reqs) s1.solve() self.assertEqual(s1.status, SolverStatus.failed) s2.solve() self.assertEqual(s2.status, SolverStatus.failed) self.assertEqual(s1.failure_reason(), s2.failure_reason()) for s in s_perms: s.solve() self.assertEqual(s.status, SolverStatus.failed) return s1
def _solve(self, packages, expected_resolve): reqs = [Requirement(x) for x in packages] s1, s2, s_perms = self._create_solvers(reqs) s1.solve() self.assertEqual(s1.status, SolverStatus.solved) resolve = [str(x) for x in s1.resolved_packages] self.assertEqual(resolve, expected_resolve) s2.solve() self.assertEqual(s2.status, SolverStatus.solved) resolve2 = [str(x) for x in s2.resolved_packages] self.assertEqual(resolve2, resolve) for s in s_perms: s.solve() self.assertEqual(s.status, SolverStatus.solved) return s1
def _set_result(self, solver_dict): self.status_ = solver_dict.get("status") self.graph_ = solver_dict.get("graph") self.solve_time = solver_dict.get("solve_time") self.load_time = solver_dict.get("load_time") self.failure_description = solver_dict.get("failure_description") self.resolved_packages_ = None self.resolved_ephemerals_ = None if self.status_ == ResolverStatus.solved: # convert solver.Variants to packages.Variants self.resolved_packages_ = [] for variant_handle in solver_dict.get("variant_handles", []): variant = self._get_variant(variant_handle) self.resolved_packages_.append(variant) self.resolved_ephemerals_ = [] for req_str in solver_dict.get("ephemerals", []): req = Requirement(req_str) self.resolved_ephemerals_.append(req)
def resolve(self): # validate the request before opening dialog for req_str in self.context_model.request: try: Requirement(req_str) except Exception as e: title = "Invalid package request - %r" % req_str QtGui.QMessageBox.critical(self, title, str(e)) return self._reset() if not self.advanced: self._start_resolve() self.exec_() if self.started: self.resolver.stop() self.thread.quit() self.thread.wait() return self.resolver.success() return False
def expand_requirement(request): """Expands a requirement string like 'python-2.*' Only trailing wildcards are supported; they will be replaced with the latest package version found within the range. If none are found, the wildcards will just be stripped. Example: >>> print expand_requirement('python-2.*') python-2.7 Args: request (str): Request to expand, eg 'python-2.*' Returns: str: Expanded request string. """ if '*' not in request: return request from rez.vendor.version.requirement import VersionedObject, Requirement from rez.packages_ import get_latest_package txt = request.replace('*', '_') obj = VersionedObject(txt) rank = len(obj.version) request_ = request while request_.endswith('*'): request_ = request_[:-2] # strip sep + * req = Requirement(request_) package = get_latest_package(name=req.name, range_=req.range_) if package is None: return request_ obj.version_ = package.version.trim(rank) return str(obj)
def packaging_req_to_rez_req(packaging_req): """Convert packaging requirement object to equivalent rez requirement. Note that environment markers are ignored. Args: packaging_req (`packaging.requirements.Requirement`): Packaging requirement. Returns: `Requirement`: Equivalent rez requirement object. """ if packaging_req.extras: print_warning("Ignoring extras requested on %r - " "this is not yet supported" % str(packaging_req)) rez_req_str = pip_to_rez_package_name(packaging_req.name) if packaging_req.specifier: range_ = pip_specifier_to_rez_requirement(packaging_req.specifier) rez_req_str += '-' + str(range_) return Requirement(rez_req_str)
def _fail(self, *packages): print reqs = [Requirement(x) for x in packages] s1, s2, s_perms = self._create_solvers(reqs) s1.solve() print print "request: %s" % ' '.join(packages) print "expecting failure" self.assertEqual(s1.status, SolverStatus.failed) print "result: %s" % str(s1.failure_reason()) print "checking that unoptimised solve fail matches optimised..." s2.solve() self.assertEqual(s2.status, SolverStatus.failed) self.assertEqual(s1.failure_reason(), s2.failure_reason()) print "checking that permutations also fail..." for s in s_perms: s.solve() self.assertEqual(s.status, SolverStatus.failed) return s1
def _parse(cls, txt): _, txt = Rule._parse_label(txt) return cls(Requirement(txt))
def command(opts, parser, extra_arg_groups=None): from rez.config import config from rez.exceptions import RezError from rez.utils.formatting import get_epoch_time_from_str, expand_abbreviations from rez.utils.logging_ import print_error from rez.packages_ import iter_package_families, iter_packages from rez.vendor.version.requirement import Requirement import os.path import fnmatch import sys error_class = None if opts.debug else RezError before_time = 0 after_time = 0 if opts.before: before_time = get_epoch_time_from_str(opts.before) if opts.after: after_time = get_epoch_time_from_str(opts.after) if after_time and before_time and (after_time >= before_time): parser.error("non-overlapping --before and --after") if opts.paths is None: pkg_paths = config.nonlocal_packages_path if opts.no_local else None else: pkg_paths = (opts.paths or "").split(os.pathsep) pkg_paths = [os.path.expanduser(x) for x in pkg_paths if x] name_pattern = opts.PKG or '*' version_range = None if opts.PKG: try: req = Requirement(opts.PKG) name_pattern = req.name if not req.range.is_any(): version_range = req.range except: pass type_ = opts.type if opts.errors or (type_ == "auto" and version_range): type_ = "package" # turn some of the nastier rez-1 warnings into errors config.override("error_package_name_mismatch", True) config.override("error_version_mismatch", True) config.override("error_nonstring_version", True) if opts.no_warnings: config.override("warn_none", True) # families found = False family_names = [] families = iter_package_families(paths=pkg_paths) if opts.sort: families = sorted(families, key=lambda x: x.name) for family in families: if family.name not in family_names and \ fnmatch.fnmatch(family.name, name_pattern): family_names.append(family.name) if type_ == "auto": type_ = "package" if family.name == name_pattern else "family" if type_ == "family": print family.name found = True def _handle(e): print_error(str(e)) def _print_resource(r): if opts.validate: try: r.validate_data() except error_class as e: _handle(e) return if opts.format: txt = expand_abbreviations(opts.format, fields) lines = txt.split("\\n") for line in lines: try: line_ = r.format(line) except error_class as e: _handle(e) break if opts.no_newlines: line_ = line_.replace('\n', "\\n") print line_ else: print r.qualified_name # packages/variants if type_ in ("package", "variant"): for name in family_names: packages = iter_packages(name, version_range, paths=pkg_paths) if opts.sort or opts.latest: packages = sorted(packages, key=lambda x: x.version) if opts.latest and packages: packages = [packages[-1]] for package in packages: if ((before_time or after_time) and package.timestamp and (before_time and package.timestamp >= before_time or after_time and package.timestamp <= after_time)): continue if opts.errors: try: package.validate_data() except error_class as e: _handle(e) found = True elif type_ == "package": _print_resource(package) found = True elif type_ == "variant": try: package.validate_data() except error_class as e: _handle(e) continue try: for variant in package.iter_variants(): _print_resource(variant) found = True except error_class as e: _handle(e) continue if not found: if opts.errors: print "no erroneous packages found" else: print "no matches found" sys.exit(1)
def get_patched_request(requires, patchlist): """Apply patch args to a request. For example, consider: >>> print get_patched_request(["foo-5", "bah-8.1"], ["foo-6"]) ["foo-6", "bah-8.1"] >>> print get_patched_request(["foo-5", "bah-8.1"], ["^bah"]) ["foo-5"] The following rules apply wrt how normal/conflict/weak patches override (note though that the new request is always added, even if it doesn't override an existing request): PATCH OVERRIDES: foo !foo ~foo ----- ---------- --- ---- ----- foo Y Y Y !foo N N N ~foo N N Y ^foo Y Y Y Args: requires (list of str or `version.Requirement`): Request. patchlist (list of str): List of patch requests. Returns: List of `version.Requirement`: Patched request. """ # rules from table in docstring above rules = { '': (True, True, True), '!': (False, False, False), '~': (False, False, True), '^': (True, True, True) } requires = [ Requirement(x) if not isinstance(x, Requirement) else x for x in requires ] appended = [] for patch in patchlist: if patch and patch[0] in ('!', '~', '^'): ch = patch[0] name = Requirement(patch[1:]).name else: ch = '' name = Requirement(patch).name rule = rules[ch] replaced = (ch == '^') for i, req in enumerate(requires): if req is None or req.name != name: continue if not req.conflict: replace = rule[0] # foo elif not req.weak: replace = rule[1] # !foo else: replace = rule[2] # ~foo if replace: if replaced: requires[i] = None else: requires[i] = Requirement(patch) replaced = True if not replaced: appended.append(Requirement(patch)) result = [x for x in requires if x is not None] + appended return result
def expand_requirement(request, paths=None): """Expands a requirement string like 'python-2.*', 'foo-2.*+<*', etc. Wildcards are expanded to the latest version that matches. There is also a special wildcard '**' that will expand to the full version, but it cannot be used in combination with '*'. Wildcards MUST placehold a whole version token, not partial - while 'foo-2.*' is valid, 'foo-2.v*' is not. Wildcards MUST appear at the end of version numbers - while 'foo-1.*.*' is valid, 'foo-1.*.0' is not. It is possible that an expansion will result in an invalid request string (such as 'foo-2+<2'). The appropriate exception will be raised if this happens. Examples: >>> print(expand_requirement('python-2.*')) python-2.7 >>> print(expand_requirement('python==2.**')) python==2.7.12 >>> print(expand_requirement('python<**')) python<3.0.5 Args: request (str): Request to expand, eg 'python-2.*' paths (list of str, optional): paths to search for package families, defaults to `config.packages_path`. Returns: str: Expanded request string. """ if '*' not in request: return request from rez.vendor.version.version import VersionRange from rez.vendor.version.requirement import Requirement from rez.packages_ import get_latest_package from uuid import uuid4 wildcard_map = {} expanded_versions = {} request_ = request # replace wildcards with valid version tokens that can be replaced again # afterwards. This produces a horrendous, but both valid and temporary, # version string. # while "**" in request_: uid = "_%s_" % uuid4().hex request_ = request_.replace("**", uid, 1) wildcard_map[uid] = "**" while '*' in request_: uid = "_%s_" % uuid4().hex request_ = request_.replace('*', uid, 1) wildcard_map[uid] = '*' # create the requirement, then expand wildcards # req = Requirement(request_, invalid_bound_error=False) def expand_version(version): rank = len(version) wildcard_found = False while version and str(version[-1]) in wildcard_map: token = wildcard_map[str(version[-1])] version = version.trim(len(version) - 1) if token == "**": if wildcard_found: # catches bad syntax '**.*' return None else: wildcard_found = True rank = 0 break wildcard_found = True if not wildcard_found: return None range_ = VersionRange(str(version)) package = get_latest_package(name=req.name, range_=range_, paths=paths) if package is None: return version if rank: return package.version.trim(rank) else: return package.version def visit_version(version): # requirements like 'foo-1' are actually represented internally as # 'foo-1+<1_' - '1_' is the next possible version after '1'. So we have # to detect this case and remap the uid-ified wildcard back here too. # for v, expanded_v in expanded_versions.items(): if version == next(v): return next(expanded_v) version_ = expand_version(version) if version_ is None: return None expanded_versions[version] = version_ return version_ if req.range_ is not None: req.range_.visit_versions(visit_version) result = str(req) # do some cleanup so that long uids aren't left in invalid wildcarded strings for uid, token in wildcard_map.items(): result = result.replace(uid, token) # cast back to a Requirement again, then back to a string. This will catch # bad verison ranges, but will also put OR'd version ranges into the correct # order expanded_req = Requirement(result) return str(expanded_req)