Exemple #1
0
 def _inv(a, b):
     a_range = VersionRange(a)
     b_range = VersionRange(b)
     self.assertTrue(~a_range == b_range)
     self.assertTrue(~b_range == a_range)
     self.assertTrue(a_range | b_range == VersionRange())
     self.assertTrue(a_range & b_range is None)
Exemple #2
0
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)
Exemple #3
0
        def _and(a, b, c):
            _print("'%s' & '%s' == '%s'" % (a, b, c))
            a_range = VersionRange(a)
            b_range = VersionRange(b)
            c_range = None if c is None else VersionRange(c)
            self.assertTrue(a_range & b_range == c_range)
            self.assertTrue(b_range & a_range == c_range)

            a_or_b = a_range | b_range
            a_and_b = a_range & b_range
            a_sub_b = a_range - b_range
            b_sub_a = b_range - a_range
            ranges = [a_and_b, a_sub_b, b_sub_a]
            ranges = [x for x in ranges if x]
            self.assertTrue(ranges[0].union(ranges[1:]) == a_or_b)
Exemple #4
0
    def set_packages(self, packages):
        package_paths = self.context_model.packages_path

        self.help_1 = PackageHelp(self.variant.name, paths=package_paths)
        self.tab.setTabEnabled(0, self.help_1.success)
        if self.help_1.success:
            self._apply_help(self.help_1, 0)
            label = "latest help (%s)" % self.help_1.package.qualified_name
            self.tab.setTabText(0, label)

        exact_range = VersionRange.from_version(self.variant.version, "==")
        self.help_2 = PackageHelp(self.variant.name,
                                  exact_range,
                                  paths=package_paths)
        self.tab.setTabEnabled(1, self.help_2.success)
        label = None
        if self.help_2.success:
            self._apply_help(self.help_2, 1)
            label = "help for %s"
        if label:
            self.tab.setTabText(1, label % self.help_2.package.qualified_name)

        if self.help_1.success or self.help_2.success:
            self.tab.show()
        else:
            self.no_help_label.show()
    def set_variant(self, variant):
        self.tab.setCurrentIndex(0)
        self.stop_loading_packages()
        self.clear()

        self.variant = variant
        if self.variant is None:
            return

        package_paths = self.context_model.packages_path
        if self.variant.wrapped.location not in package_paths:
            txt = "not on the package search path"
            self.label.setText(txt)
            return

        self.setEnabled(True)

        range_ = None
        if self.reference_variant and self.reference_variant.name == variant.name:
            versions = sorted(
                [variant.version, self.reference_variant.version])
            range_ = VersionRange.as_span(*versions)

        self.load_packages(package_paths=package_paths,
                           package_name=variant.name,
                           range_=range_,
                           package_attributes=("timestamp", ))
    def set_variant(self, variant):
        self.tab.setCurrentIndex(0)
        self.stop_loading_packages()
        self.clear()

        self.variant = variant
        if self.variant is None:
            return

        package_paths = self.context_model.packages_path
        if self.variant.wrapped.location not in package_paths:
            txt = "not on the package search path"
            self.label.setText(txt)
            return

        self.setEnabled(True)

        range_ = None
        if self.reference_variant and self.reference_variant.name == variant.name:
            versions = sorted([variant.version, self.reference_variant.version])
            range_ = VersionRange.as_span(*versions)

        self.load_packages(package_paths=package_paths,
                           package_name=variant.name,
                           range_=range_,
                           package_attributes=("timestamp",))
Exemple #7
0
 def _get_dest_pkg(self, name, version):
     return get_latest_package(
         name,
         range_=VersionRange("==" + version),
         paths=[self.dest_install_root],
         error=True
     )
Exemple #8
0
def iter_packages(name, range_=None, paths=None):
    """Iterate over `Package` instances, in no particular order.

    Packages of the same name and version earlier in the search path take
    precedence - equivalent packages later in the paths are ignored. Packages
    are not returned in any specific order.

    Args:
        name (str): Name of the package, eg 'maya'.
        range_ (VersionRange or str): If provided, limits the versions returned
            to those in `range_`.
        paths (list of str, optional): paths to search for packages, defaults
            to `config.packages_path`.

    Returns:
        `Package` iterator.
    """
    entries = _get_families(name, paths)

    seen = set()
    for repo, family_resource in entries:
        for package_resource in repo.iter_packages(family_resource):
            key = (package_resource.name, package_resource.version)
            if key in seen:
                continue

            seen.add(key)
            if range_:
                if isinstance(range_, basestring):
                    range_ = VersionRange(range_)
                if package_resource.version not in range_:
                    continue

            yield Package(package_resource)
Exemple #9
0
    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
Exemple #10
0
 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
Exemple #11
0
    def construct(cls, name, range=None):
        """Create a requirement directly from an object name and VersionRange.

        Args:
            name: Object name string.
            range: VersionRange object. If None, an unversioned requirement is
                created.
        """
        other = Requirement(None)
        other.name_ = name
        other.range_ = VersionRange() if range is None else range
        return other
Exemple #12
0
def get_package(name, version, paths=None):
    """Get an exact version of a package.

    Args:
        name (str): Name of the package, eg 'maya'.
        version (Version or str): Version of the package, eg '1.0.0'
        paths (list of str, optional): paths to search for package, defaults
            to `config.packages_path`.

    Returns:
        `Package` object, or None if the package was not found.
    """
    if isinstance(version, basestring):
        range_ = VersionRange("==%s" % version)
    else:
        range_ = VersionRange.from_version(version, "==")

    it = iter_packages(name, range_, paths)
    try:
        return it.next()
    except StopIteration:
        return None
Exemple #13
0
    def __init__(self, s, invalid_bound_error=True):
        self.name_ = None
        self.range_ = None
        self.negate_ = False
        self.conflict_ = False
        self._str = None
        self.sep_ = '-'
        if s is None:
            return

        self.conflict_ = s.startswith('!')
        if self.conflict_:
            s = s[1:]
        elif s.startswith('~'):
            s = s[1:]
            self.negate_ = True
            self.conflict_ = True

        m = self.sep_regex.search(s)
        if m:
            i = m.start()
            self.name_ = s[:i]
            req_str = s[i:]
            if req_str[0] in ('-', '@', '#'):
                self.sep_ = req_str[0]
                req_str = req_str[1:]

            self.range_ = VersionRange(req_str,
                                       invalid_bound_error=invalid_bound_error)
            if self.negate_:
                self.range_ = ~self.range_
        elif self.negate_:
            self.name_ = s
            # rare case - '~foo' equates to no effect
            self.range_ = None
        else:
            self.name_ = s
            self.range_ = VersionRange()
Exemple #14
0
    def __init__(self, s, invalid_bound_error=True):
        self.name_ = None
        self.range_ = None
        self.negate_ = False
        self.conflict_ = False
        self._str = None
        self.sep_ = '-'
        if s is None:
            return

        self.conflict_ = s.startswith('!')
        if self.conflict_:
            s = s[1:]
        elif s.startswith('~'):
            s = s[1:]
            self.negate_ = True
            self.conflict_ = True

        m = self.sep_regex.search(s)
        if m:
            i = m.start()
            self.name_ = s[:i]
            req_str = s[i:]
            if req_str[0] in ('-', '@', '#'):
                self.sep_ = req_str[0]
                req_str = req_str[1:]

            self.range_ = VersionRange(
                req_str, invalid_bound_error=invalid_bound_error)
            if self.negate_:
                self.range_ = ~self.range_
        elif self.negate_:
            self.name_ = s
            # rare case - '~foo' equates to no effect
            self.range_ = None
        else:
            self.name_ = s
            self.range_ = VersionRange()
Exemple #15
0
    def __init__(self, s):
        self.name_ = None
        self.range_ = None
        self.negate_ = False
        self.conflict_ = False
        self.sep_ = "-"
        if s is None:
            return

        self.conflict_ = s.startswith("!")
        if self.conflict_:
            s = s[1:]
        elif s.startswith("~"):
            s = s[1:]
            self.negate_ = True
            self.conflict_ = True

        m = self.sep_regex.search(s)
        if m:
            i = m.start()
            self.name_ = s[:i]
            req_str = s[i:]
            if req_str[0] in ("-", "@", "#"):
                self.sep_ = req_str[0]
                req_str = req_str[1:]

            self.range_ = VersionRange(req_str)
            if self.negate_:
                self.range_ = ~self.range_
        elif self.negate_:
            self.name_ = s
            # rare case - '~foo' equates to no effect
            self.range_ = None
        else:
            self.name_ = s
            self.range_ = VersionRange()
Exemple #16
0
    def __str__(self):
        pre_str = '~' if self.negate_ else ('!' if self.conflict_ else '')
        range_str = ''
        sep_str = ''

        range = self.range_
        if self.negate_:
            range = ~range if range else VersionRange()

        if not range.is_any():
            range_str = str(range)
            if range_str[0] not in ('=', '<', '>'):
                sep_str = self.sep_

        return pre_str + self.name_ + sep_str + range_str
Exemple #17
0
    def __str__(self):
        if self._str is None:
            pre_str = '~' if self.negate_ else ('!' if self.conflict_ else '')
            range_str = ''
            sep_str = ''

            range_ = self.range_
            if self.negate_:
                range_ = ~range_ if range_ else VersionRange()

            if not range_.is_any():
                range_str = str(range_)
                if range_str[0] not in ('=', '<', '>'):
                    sep_str = self.sep_

            self._str = pre_str + self.name_ + sep_str + range_str
        return self._str
    def set_packages(self, packages):
        package_paths = self.context_model.packages_path

        self.help_1 = PackageHelp(self.variant.name, paths=package_paths)
        self.tab.setTabEnabled(0, self.help_1.success)
        if self.help_1.success:
            self._apply_help(self.help_1, 0)
            label = "latest help (%s)" % self.help_1.package.qualified_name
            self.tab.setTabText(0, label)

        exact_range = VersionRange.from_version(self.variant.version, "==")
        self.help_2 = PackageHelp(self.variant.name, exact_range, paths=package_paths)
        self.tab.setTabEnabled(1, self.help_2.success)
        label = None
        if self.help_2.success:
            self._apply_help(self.help_2, 1)
            label = "help for %s"
        if label:
            self.tab.setTabText(1, label % self.help_2.package.qualified_name)

        if self.help_1.success or self.help_2.success:
            self.tab.show()
        else:
            self.no_help_label.show()
Exemple #19
0
        def _eq(a, b):
            _print("'%s' == '%s'" % (a, b))
            a_range = VersionRange(a)
            b_range = VersionRange(b)

            self.assertTrue(a_range == b_range)
            self.assertTrue(a_range.issuperset(a_range))
            self.assertTrue(a_range.issuperset(b_range))
            self.assertTrue(VersionRange(str(a_range)) == a_range)
            self.assertTrue(VersionRange(str(b_range)) == a_range)
            self.assertTrue(hash(a_range) == hash(b_range))

            a_ = a.replace('.', '-')
            a_ = a_.replace("--", "..")
            a_range_ = VersionRange(a_)
            self.assertTrue(a_range_ == a_range)
            self.assertTrue(hash(a_range_) == hash(a_range))

            range_strs = a.split('|')
            ranges = [VersionRange(x) for x in range_strs]
            ranges_ = ranges[0].union(ranges[1:])
            self.assertTrue(ranges_ == a_range)

            self.assertTrue(a_range | b_range == a_range)
            self.assertTrue(a_range - b_range is None)
            self.assertTrue(b_range - a_range is None)
            self.assertTrue(VersionRange() & a_range == a_range)
            self.assertTrue(b_range.span() & a_range == a_range)

            a_inv = a_range.inverse()
            self.assertTrue(a_inv == ~b_range)

            if a_inv:
                self.assertTrue(~a_inv == a_range)
                self.assertTrue(a_range | a_inv == VersionRange())
                self.assertTrue(a_range & a_inv is None)

            a_ranges = a_range.split()
            a_range_ = a_ranges[0].union(a_ranges[1:])
            self.assertTrue(a_range_ == b_range)
    def _set_variant(self, variant, preloaded_packages=None):
        self.clear()

        hh = self.horizontalHeader()
        self.setHorizontalHeaderLabels(["path", "released"])
        hh.setResizeMode(0, QtGui.QHeaderView.Interactive)
        hh.setStretchLastSection(True)
        hh.setVisible(True)

        package_paths = self.context_model.packages_path
        package_filter = PackageFilterList.from_pod(self.context_model.package_filter)

        if variant and variant.wrapped.location in package_paths:
            self.version_index = -1
            self.reference_version_index = -1
            reference_version = None
            range_ = None

            if self.reference_variant and self.reference_variant.name == variant.name:
                reference_version = self.reference_variant.version
                versions = sorted([reference_version, variant.version])
                range_ = VersionRange.as_span(*versions)

            timestamp = self.context().timestamp

            if preloaded_packages is not None:
                if range_ is None:
                    packages = preloaded_packages
                else:
                    packages = [x for x in preloaded_packages if x.version in range_]
            else:
                it = iter_packages(name=variant.name, paths=package_paths, range_=range_)
                packages = sorted(it, key=lambda x: x.version, reverse=True)

            self.setRowCount(len(packages))
            brush = self.palette().brush(QtGui.QPalette.Active, QtGui.QPalette.Base)

            for row, package in enumerate(packages):
                version_str = str(package.version) + ' '
                item = QtGui.QTableWidgetItem(version_str)
                item.setTextAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
                self.setVerticalHeaderItem(row, item)

                if package.version == variant.version:
                    self.version_index = row
                    update_font(item, bold=True)

                if reference_version is not None \
                        and package.version == reference_version:
                    self.reference_version_index = row
                    update_font(item, bold=True, italic=True)

                def _item():
                    item_ = QtGui.QTableWidgetItem()
                    item_.setBackground(brush)  # get rid of mouse-hover coloring
                    return item_

                if package.timestamp:
                    release_str = get_timestamp_str(package.timestamp)
                    in_future = (package.timestamp > timestamp)
                else:
                    release_str = '-'
                    in_future = False

                item = _item()
                txt = package.uri + "  "

                icons = []
                if in_future:
                    icon = get_icon_widget(
                        "clock_warning", "package did not exist at time of resolve")
                    icons.append(icon)

                rule = package_filter.excludes(package)
                if rule:
                    icon = get_icon_widget(
                        "excluded", "package was excluded by rule %s" % str(rule))
                    icons.append(icon)

                if icons:
                    label = QtGui.QLabel(txt)
                    pane = create_pane(icons + [label, None], True, compact=True)
                    self.setCellWidget(row, 0, pane)
                else:
                    item.setText(txt)

                self.setItem(row, 0, item)

                item = _item()
                item.setText(release_str)
                self.setItem(row, 1, item)

            vh = self.verticalHeader()
            vh.setVisible(True)
            self.resizeColumnsToContents()
            hh.setStretchLastSection(True)
            self.update()

            self.allow_selection = True
            self.selectRow(self.version_index)
            self.allow_selection = False

        self.variant = variant
Exemple #21
0
    def _contextChanged(self, flags=0):
        self._set_stale(self.context_model.is_stale())

        if flags & (ContextModel.PACKAGES_PATH_CHANGED |
                    ContextModel.CONTEXT_CHANGED):
            # update icons
            new_icons = []

            if self.variant.index is not None:
                package = self.variant.parent
                if package.num_variants > 1:
                    txt = "1 of %d variants" % package.num_variants
                    new_icons.append(("variant", txt))

            if self.variant.is_local:
                new_icons.append(("local", "package is local"))

            package_paths = PackageSearchPath(self.context_model.packages_path)
            package_filter = PackageFilterList.from_pod(self.context_model.package_filter)

            # TODO: move this all into a thread, it's blocking up the GUI during context load
            if self.variant in package_paths:
                # find all >= version packages, so we can determine tick type
                ge_range = VersionRange.from_version(self.variant.version, ">=")
                packages = None
                try:
                    it = package_paths.iter_packages(name=self.variant.name,
                                                     range_=ge_range)

                    packages = sorted(it, key=lambda x: x.version)
                except:
                    pass

                # apply a tick icon if appropriate
                ticked = False
                if packages:

                    # test if variant is latest package
                    latest_pkg = packages[-1]
                    if self.variant.version == latest_pkg.version:
                        new_icons.append(("green_tick", "package is latest"))
                        ticked = True

                    range_ = None
                    packages_ = None

                    # test if variant is in request, and is latest possible
                    if not ticked:
                        range_ = None
                        try:
                            request = self.context().requested_packages(True)
                            reqlist = RequirementList(request)
                            if self.variant.name in reqlist.names:
                                variant_ = reqlist.get(self.variant.name)
                                if not variant_.conflict:
                                    range_ = variant_.range
                        except:
                            pass
                        if range_ is not None:
                            packages_ = [x for x in packages if x.version in range_]
                            if packages_:
                                latest_pkg = packages_[-1]
                                if self.variant.version == latest_pkg.version:
                                    new_icons.append(("yellow_tick",
                                                      "package is latest possible"))
                                    ticked = True

                    packages2 = [x for x in (packages_ or packages)
                                 if x.version > self.variant.version]

                    # test if variant is latest within package filter
                    if (not ticked
                            and packages2
                            and package_filter):
                        if all(package_filter.excludes(x) for x in packages2):
                            new_icons.append(("yellow_tick",
                                              "package is latest possible"))
                            ticked = True

                    # test if variant was latest package at time of resolve
                    if not ticked and self.variant.timestamp:
                        untimestamped_packages = [x for x in packages
                                                  if not x.timestamp]
                        if not untimestamped_packages:
                            resolve_time = self.context().timestamp
                            old_packages = [x for x in packages
                                            if x.timestamp <= resolve_time]
                            if old_packages:
                                latest_pkg = old_packages[-1]
                                if self.variant.version == latest_pkg.version:
                                    new_icons.append(
                                        ("green_white_tick",
                                         "package was latest at time of resolve"))
                                    ticked = True

                    # test if variant is in request, and was latest possible at
                    # the time of resolve
                    if (not ticked
                            and self.variant.timestamp
                            and range_ is not None
                            and packages_ is not None):
                        untimestamped_packages = any(x for x in packages_ if not x.timestamp)
                        if not untimestamped_packages:
                            resolve_time = self.context().timestamp
                            old_packages = [x for x in packages_
                                            if x.timestamp <= resolve_time]
                            if old_packages:
                                latest_pkg = old_packages[-1]
                                if self.variant.version == latest_pkg.version:
                                    new_icons.append(
                                        ("yellow_white_tick",
                                         "package was latest possible at time of resolve"))
                                    ticked = True

                    # test if variant is within package filter, and was latest
                    # possible at the time of resolve
                    if (not ticked
                            and packages2
                            and package_filter
                            and self.variant.timestamp):
                        untimestamped_packages = any(x for x in (packages_ or packages)
                                                     if not x.timestamp)
                        if not untimestamped_packages:
                            newer_package = any(x for x in packages2
                                                if not package_filter.excludes(x)
                                                and x.timestamp <= resolve_time)
                            if not newer_package:
                                    new_icons.append(
                                        ("yellow_white_tick",
                                         "package was latest possible at time of resolve"))
                                    ticked = True

                    # bring in the old man
                    if not ticked:
                        new_icons.append(
                            ("old_man", "newer packages are/were available"))
            else:
                new_icons.append(("error", "package is not in the search path"))

            self._set_icons(new_icons)

        if (not self.hide_locks) and (flags & (ContextModel.LOCKS_CHANGED |
                                      ContextModel.CONTEXT_CHANGED)):
            # update lock icon
            lock = self.context_model.get_patch_lock(self.variant.name)
            if lock is None:
                lock = self.context_model.default_patch_lock
                icon_name = "%s_faint" % lock.name
            else:
                icon_name = lock.name

            # update lock tooltip
            if lock == PatchLock.no_lock:
                desc = lock.description
            else:
                req = self._get_lock_requirement(lock)
                if req:
                    if lock == PatchLock.lock:
                        desc = "Exact version (%s)" % str(req)
                    else:
                        unit = lock.description.split()[0]
                        desc = ("%s version updates only (%s.*)"
                                % (unit.capitalize(), str(req)))
                else:
                    desc = lock.description

            self._set_lock_icon(icon_name, desc.lower())
Exemple #22
0
    def __init__(self,
                 context_model,
                 variant_left=None,
                 variant_right=None,
                 parent=None):
        super(CompareCell, self).__init__(parent)
        self.context_model = context_model
        self.left_variant = variant_left
        self.right_variant = variant_right
        self.color = None
        self.side = None
        self.mode = None
        self.comparable = False

        package_paths = self.context_model.packages_path

        widget = None
        if self.left_variant and self.right_variant:
            self.side = "both"
            equal_versions = (
                self.left_variant.version == self.right_variant.version)
            right_variant_visible = (self.right_variant.wrapped.location
                                     in package_paths)
            self.comparable = right_variant_visible and not equal_versions

            if self.comparable:
                # determine how far apart the variant versions are
                versions = sorted(
                    [self.left_variant.version, self.right_variant.version])
                range_ = VersionRange.as_span(*versions)
                it = iter_packages(name=self.left_variant.name,
                                   paths=package_paths,
                                   range_=range_)
                diff_num = sum(1 for x in it) - 1

                unit = "version" if diff_num == 1 else "versions"
                icon_suffixes = {1: "_1", 2: "_2", 3: "_3"}
                icon_suffix = icon_suffixes.get(diff_num, "")

            if self.left_variant == self.right_variant:
                self.mode = "equal_to"
                self._set_color(0.7, 0.7, 0.7)
                widget = IconButton("equal_to", "packages are equal")
            elif self.left_variant.version == self.right_variant.version:
                # same version, but package is different. This can happen when
                # a local package is released which then hides a central package
                # of the same version
                self.mode = "equalish"
                self._set_color(1, 1, 0)
                widget = IconButton(
                    "equalish",
                    "packages versions are equal, but package is different")
            elif self.left_variant.version > self.right_variant.version:
                self.mode = "greater_than"
                self._set_color(0, 1, 0)
                if self.comparable:
                    desc = "package is %d %s ahead" % (diff_num, unit)
                    widget = IconButton("greater_than" + icon_suffix, desc)
                else:
                    widget = IconButton("greater_than", "package is newer")
            else:
                self.mode = "less_than"
                self._set_color(1, 0, 0)
                if self.comparable:
                    desc = "package is %d %s behind" % (diff_num, unit)
                    widget = IconButton("less_than" + icon_suffix, desc)
                else:
                    widget = IconButton("less_than", "package is older")
        elif self.right_variant:
            self.side = "right"
            self.mode = "missing"
            self._set_color(1, 0, 0)
            widget = IconButton("missing", "package is missing")
        elif self.left_variant:
            self.side = "left"
            self.mode = "new"
            self._set_color(0, 1, 0)
            widget = IconButton("new", "package is new")

        if widget:
            create_pane([None, widget, None],
                        True,
                        compact=True,
                        parent_widget=self)
            widget.clicked.connect(self._clicked)
Exemple #23
0
    def test_containment(self):
        # basic containment
        self.assertTrue(Version("3") in VersionRange("3+"))
        self.assertTrue(Version("5") in VersionRange("3..5"))
        self.assertTrue(Version("5_") not in VersionRange("3..5"))
        self.assertTrue(Version("3.0.0") in VersionRange("3+"))
        self.assertTrue(Version("3.0.0") not in VersionRange("3.1+"))
        self.assertTrue(Version("3") in VersionRange("<1|5|6|8|7|3|60+"))
        self.assertTrue(Version("3") in VersionRange("<1|5|6|8|7|==3|60+"))
        self.assertTrue(VersionRange("2.1+<4") in VersionRange("<4"))
        self.assertTrue(VersionRange("2.1..4") not in VersionRange("<4"))
        self.assertTrue(VersionRange("3") in VersionRange("3"))
        self.assertTrue(VersionRange("==3") in VersionRange("3"))
        self.assertTrue(VersionRange("3.5+<3_") in VersionRange("3"))
        self.assertTrue(VersionRange("3") not in VersionRange("4+<6"))
        self.assertTrue(VersionRange("3+<10") not in VersionRange("4+<6"))

        # iterating over sorted version list
        numbers = [2, 3, 5, 10, 11, 13, 14]
        versions = [Version(str(x)) for x in numbers]
        rev_versions = list(reversed(versions))
        composite_range = VersionRange.from_versions(versions)

        entries = [(VersionRange(""), 7), (VersionRange("0+"), 7),
                   (VersionRange("5+"), 5), (VersionRange("6+"), 4),
                   (VersionRange("50+"), 0), (VersionRange(">5"), 4),
                   (VersionRange("5"), 1), (VersionRange("6"), 0),
                   (VersionRange("<5"), 2), (VersionRange("<6"), 3),
                   (VersionRange("<50"), 7), (VersionRange("<=5"), 3),
                   (VersionRange("<1"), 0), (VersionRange("2|9+"), 5),
                   (VersionRange("3+<6|12+<13.5"), 3),
                   (VersionRange("<1|20+"), 0), (VersionRange(">0<20"), 7)]

        for range_, count in entries:
            # brute-force containment tests
            matches = set(x for x in versions if x in range_)
            self.assertEqual(len(matches), count)

            # more optimal containment tests
            def _test_it(it):
                matches_ = set(version for contains, version in it if contains)
                self.assertEqual(matches_, matches)

            _test_it(range_.iter_intersect_test(versions))
            _test_it(range_.iter_intersect_test(rev_versions, descending=True))

            # throw in an intersection test
            self.assertEqual(composite_range.intersects(range_), (count != 0))
            int_range = composite_range & range_
            versions_ = [] if int_range is None else int_range.to_versions()
            self.assertEqual(set(versions_), matches)

            # throw in a superset test as well
            self.assertEqual(range_.issuperset(composite_range), (count == 7))
            if count:
                self.assertTrue(composite_range.issuperset(int_range))
Exemple #24
0
    def test_version_range(self):
        def _eq(a, b):
            _print("'%s' == '%s'" % (a, b))
            a_range = VersionRange(a)
            b_range = VersionRange(b)

            self.assertTrue(a_range == b_range)
            self.assertTrue(a_range.issuperset(a_range))
            self.assertTrue(a_range.issuperset(b_range))
            self.assertTrue(VersionRange(str(a_range)) == a_range)
            self.assertTrue(VersionRange(str(b_range)) == a_range)
            self.assertTrue(hash(a_range) == hash(b_range))

            a_ = a.replace('.', '-')
            a_ = a_.replace("--", "..")
            a_range_ = VersionRange(a_)
            self.assertTrue(a_range_ == a_range)
            self.assertTrue(hash(a_range_) == hash(a_range))

            range_strs = a.split('|')
            ranges = [VersionRange(x) for x in range_strs]
            ranges_ = ranges[0].union(ranges[1:])
            self.assertTrue(ranges_ == a_range)

            self.assertTrue(a_range | b_range == a_range)
            self.assertTrue(a_range - b_range is None)
            self.assertTrue(b_range - a_range is None)
            self.assertTrue(VersionRange() & a_range == a_range)
            self.assertTrue(b_range.span() & a_range == a_range)

            a_inv = a_range.inverse()
            self.assertTrue(a_inv == ~b_range)

            if a_inv:
                self.assertTrue(~a_inv == a_range)
                self.assertTrue(a_range | a_inv == VersionRange())
                self.assertTrue(a_range & a_inv is None)

            a_ranges = a_range.split()
            a_range_ = a_ranges[0].union(a_ranges[1:])
            self.assertTrue(a_range_ == b_range)

        def _and(a, b, c):
            _print("'%s' & '%s' == '%s'" % (a, b, c))
            a_range = VersionRange(a)
            b_range = VersionRange(b)
            c_range = None if c is None else VersionRange(c)
            self.assertTrue(a_range & b_range == c_range)
            self.assertTrue(b_range & a_range == c_range)

            a_or_b = a_range | b_range
            a_and_b = a_range & b_range
            a_sub_b = a_range - b_range
            b_sub_a = b_range - a_range
            ranges = [a_and_b, a_sub_b, b_sub_a]
            ranges = [x for x in ranges if x]
            self.assertTrue(ranges[0].union(ranges[1:]) == a_or_b)

        def _inv(a, b):
            a_range = VersionRange(a)
            b_range = VersionRange(b)
            self.assertTrue(~a_range == b_range)
            self.assertTrue(~b_range == a_range)
            self.assertTrue(a_range | b_range == VersionRange())
            self.assertTrue(a_range & b_range is None)

        # simple cases
        _print()
        _eq("", "")
        _eq("1", "1")
        _eq("1.0.0", "1.0.0")
        _eq("3+<3_", "3")
        _eq("_+<__", "_")
        _eq("1.2+<=2.0", "1.2..2.0")
        _eq("10+,<20", "10+<20")
        _eq("1+<1.0", "1+<1.0")
        _eq(">=2", "2+")

        # optimised cases
        _eq("3|3", "3")
        _eq("3|1", "1|3")
        _eq("5|3|1", "1|3|5")
        _eq("1|1_", "1+<1__")
        _eq("1|1_|1__", "1+,<1___")
        _eq("|", "")
        _eq("||", "||||||||")
        _eq("1|1_+", "1+")
        _eq("<1|1", "<1_")
        _eq("1+<3|3+<5", "1+<5")
        _eq(">4<6|1+<3", "1+<3|>4,<6")
        _eq("4+<6|1+<3|", "")
        _eq("4|2+", "2+")
        _eq("3|<5", "<5")
        _eq("<3|>3", ">3|<3")
        _eq("3+|<3", "")
        _eq("3+|<4", "")
        _eq("2+<=6|3+<5", "2..6")
        _eq("3+,<5|2+<=6", "2+<=6")
        _eq("2|2+", "2+")
        _eq("2|2.1+", "2+")
        _eq("2|<2.1", "<2_")
        _eq("3..3", "==3")
        _eq(">=3,<=3", "==3")

        # AND'ing
        _and("3", "3", "3")
        _and("1", "==1", "==1")
        _and("", "==1", "==1")
        _and("3", "4", None)
        _and("<3", "5+", None)
        _and("4+<6", "6+<8", None)
        _and("2+", "<=4", "2..4")
        _and("1", "1.0", "1.0")
        _and("4..6", "6+<8", "==6")

        # inverse
        _inv("3+", "<3")
        _inv("<=3", ">3")
        _inv("3.5", "<3.5|3.5_+")
        self.assertTrue(~VersionRange() is None)

        # odd (but valid) cases
        _eq(">", ">")       # greater than the empty version
        _eq("+", "")        # greater or equal to empty version (is all vers)
        _eq(">=", "")       # equivalent to above
        _eq("<=", "==")     # less or equal to empty version (is only empty)
        _eq("..", "==")     # from empty version to empty version
        _eq("+<=", "==")    # equivalent to above

        invalid_range = [
            "4+<2",         # lower bound greater than upper
            ">3<3",         # both greater and less than same version
            ">3<=3",        # greater and less or equal to same version
            "3+<3"          # greater and equal to, and less than, same version
        ]

        for s in invalid_range:
            self.assertRaises(VersionError, VersionRange, s)

        invalid_syntax = [
            "<",            # less than the empty version
            "><",           # both greater and less than empty version
            ">3>4",         # both are lower bounds
            "<3<4",         # both are upper bounds
            "<4>3",         # upper bound before lower
            ",<4",          # leading comma
            "4+,",          # trailing comma
            "1>=",          # pre-lower-op in post
            "+1",           # post-lower-op in pre
            "4<",           # pre-upper-op in post
            "1+<2<3"        # more than two bounds
        ]

        for s in invalid_syntax:
            self.assertRaises(VersionError, VersionRange, s)

        # test simple logic
        self.assertTrue(VersionRange("").is_any())
        self.assertTrue(VersionRange("2+<4").bounded())
        self.assertTrue(VersionRange("2+").lower_bounded())
        self.assertTrue(not VersionRange("2+").upper_bounded())
        self.assertTrue(not VersionRange("2+").bounded())
        self.assertTrue(VersionRange("<2").upper_bounded())
        self.assertTrue(not VersionRange("<2").lower_bounded())
        self.assertTrue(not VersionRange("<2").bounded())

        # test range from version(s)
        v = Version("3")
        self.assertTrue(VersionRange.from_version(v, "eq") == VersionRange("==3"))
        self.assertTrue(VersionRange.from_version(v, "gt") == VersionRange(">3"))
        self.assertTrue(VersionRange.from_version(v, "gte") == VersionRange("3+"))
        self.assertTrue(VersionRange.from_version(v, "lt") == VersionRange("<3"))
        self.assertTrue(VersionRange.from_version(v, "lte") == VersionRange("<=3"))

        range1 = VersionRange.from_version(Version("2"), "gte")
        range2 = VersionRange.from_version(Version("4"), "lte")
        _eq(str(range1 & range2), "2..4")

        v2 = Version("6.0")
        v3 = Version("4")
        self.assertTrue(VersionRange.from_versions([v, v2, v3])
                        == VersionRange("==3|==4|==6.0"))

        # test behaviour in sets
        def _eq2(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(a == b)

        a = VersionRange("1+<=2.5")
        b = VersionRange("1..2.5")
        c = VersionRange(">=5")
        d = VersionRange(">6.1.0")
        e = VersionRange("3.2")

        _eq2(set([a]) - set([a]), set())
        _eq2(set([a]) - set([b]), set())
        _eq2(set([a, a]) - set([a]), set())
        _eq2(set([b, c, d, e]) - set([a]), set([c, d, e]))
        _eq2(set([b, c, e]) | set([c, d]), set([b, c, d, e]))
        _eq2(set([b, c]) & set([c, d]), set([c]))
Exemple #25
0
    def _set_variant(self, variant, preloaded_packages=None):
        self.clear()

        hh = self.horizontalHeader()
        self.setHorizontalHeaderLabels(["path", "released"])
        hh.setResizeMode(0, QtGui.QHeaderView.Interactive)
        hh.setStretchLastSection(True)
        hh.setVisible(True)

        package_paths = self.context_model.packages_path
        package_filter = PackageFilterList.from_pod(
            self.context_model.package_filter)

        if variant and variant.wrapped.location in package_paths:
            self.version_index = -1
            self.reference_version_index = -1
            reference_version = None
            range_ = None

            if self.reference_variant and self.reference_variant.name == variant.name:
                reference_version = self.reference_variant.version
                versions = sorted([reference_version, variant.version])
                range_ = VersionRange.as_span(*versions)

            timestamp = self.context().timestamp

            if preloaded_packages is not None:
                if range_ is None:
                    packages = preloaded_packages
                else:
                    packages = [
                        x for x in preloaded_packages if x.version in range_
                    ]
            else:
                it = iter_packages(name=variant.name,
                                   paths=package_paths,
                                   range_=range_)
                packages = sorted(it, key=lambda x: x.version, reverse=True)

            self.setRowCount(len(packages))
            brush = self.palette().brush(QtGui.QPalette.Active,
                                         QtGui.QPalette.Base)

            for row, package in enumerate(packages):
                version_str = str(package.version) + ' '
                item = QtGui.QTableWidgetItem(version_str)
                item.setTextAlignment(QtCore.Qt.AlignRight
                                      | QtCore.Qt.AlignVCenter)
                self.setVerticalHeaderItem(row, item)

                if package.version == variant.version:
                    self.version_index = row
                    update_font(item, bold=True)

                if reference_version is not None \
                        and package.version == reference_version:
                    self.reference_version_index = row
                    update_font(item, bold=True, italic=True)

                def _item():
                    item_ = QtGui.QTableWidgetItem()
                    item_.setBackground(
                        brush)  # get rid of mouse-hover coloring
                    return item_

                if package.timestamp:
                    release_str = get_timestamp_str(package.timestamp)
                    in_future = (package.timestamp > timestamp)
                else:
                    release_str = '-'
                    in_future = False

                item = _item()
                txt = package.uri + "  "

                icons = []
                if in_future:
                    icon = get_icon_widget(
                        "clock_warning",
                        "package did not exist at time of resolve")
                    icons.append(icon)

                rule = package_filter.excludes(package)
                if rule:
                    icon = get_icon_widget(
                        "excluded",
                        "package was excluded by rule %s" % str(rule))
                    icons.append(icon)

                if icons:
                    label = QtGui.QLabel(txt)
                    pane = create_pane(icons + [label, None],
                                       True,
                                       compact=True)
                    self.setCellWidget(row, 0, pane)
                else:
                    item.setText(txt)

                self.setItem(row, 0, item)

                item = _item()
                item.setText(release_str)
                self.setItem(row, 1, item)

            vh = self.verticalHeader()
            vh.setVisible(True)
            self.resizeColumnsToContents()
            hh.setStretchLastSection(True)
            self.update()

            self.allow_selection = True
            self.selectRow(self.version_index)
            self.allow_selection = False

        self.variant = variant
Exemple #26
0
    def test_version_range(self):
        def _eq(a, b):
            _print("'%s' == '%s'" % (a, b))
            a_range = VersionRange(a)
            b_range = VersionRange(b)

            self.assertTrue(a_range == b_range)
            self.assertTrue(a_range.issuperset(a_range))
            self.assertTrue(a_range.issuperset(b_range))
            self.assertTrue(VersionRange(str(a_range)) == a_range)
            self.assertTrue(VersionRange(str(b_range)) == a_range)
            self.assertTrue(hash(a_range) == hash(b_range))

            a_ = a.replace('.', '-')
            a_ = a_.replace("--", "..")
            a_range_ = VersionRange(a_)
            self.assertTrue(a_range_ == a_range)
            self.assertTrue(hash(a_range_) == hash(a_range))

            range_strs = a.split('|')
            ranges = [VersionRange(x) for x in range_strs]
            ranges_ = ranges[0].union(ranges[1:])
            self.assertTrue(ranges_ == a_range)

            self.assertTrue(a_range | b_range == a_range)
            self.assertTrue(a_range - b_range is None)
            self.assertTrue(b_range - a_range is None)
            self.assertTrue(VersionRange() & a_range == a_range)
            self.assertTrue(b_range.span() & a_range == a_range)

            a_inv = a_range.inverse()
            self.assertTrue(a_inv == ~b_range)

            if a_inv:
                self.assertTrue(~a_inv == a_range)
                self.assertTrue(a_range | a_inv == VersionRange())
                self.assertTrue(a_range & a_inv is None)

            a_ranges = a_range.split()
            a_range_ = a_ranges[0].union(a_ranges[1:])
            self.assertTrue(a_range_ == b_range)

        def _and(a, b, c):
            _print("'%s' & '%s' == '%s'" % (a, b, c))
            a_range = VersionRange(a)
            b_range = VersionRange(b)
            c_range = None if c is None else VersionRange(c)
            self.assertTrue(a_range & b_range == c_range)
            self.assertTrue(b_range & a_range == c_range)

            a_or_b = a_range | b_range
            a_and_b = a_range & b_range
            a_sub_b = a_range - b_range
            b_sub_a = b_range - a_range
            ranges = [a_and_b, a_sub_b, b_sub_a]
            ranges = [x for x in ranges if x]
            self.assertTrue(ranges[0].union(ranges[1:]) == a_or_b)

        def _inv(a, b):
            a_range = VersionRange(a)
            b_range = VersionRange(b)
            self.assertTrue(~a_range == b_range)
            self.assertTrue(~b_range == a_range)
            self.assertTrue(a_range | b_range == VersionRange())
            self.assertTrue(a_range & b_range is None)

        # simple cases
        _print()
        _eq("", "")
        _eq("1", "1")
        _eq("1.0.0", "1.0.0")
        _eq("3+<3_", "3")
        _eq("_+<__", "_")
        _eq("1.2+<=2.0", "1.2..2.0")
        _eq("10+,<20", "10+<20")
        _eq("1+<1.0", "1+<1.0")
        _eq(">=2", "2+")

        # optimised cases
        _eq("3|3", "3")
        _eq("3|1", "1|3")
        _eq("5|3|1", "1|3|5")
        _eq("1|1_", "1+<1__")
        _eq("1|1_|1__", "1+,<1___")
        _eq("|", "")
        _eq("||", "||||||||")
        _eq("1|1_+", "1+")
        _eq("<1|1", "<1_")
        _eq("1+<3|3+<5", "1+<5")
        _eq(">4<6|1+<3", "1+<3|>4,<6")
        _eq("4+<6|1+<3|", "")
        _eq("4|2+", "2+")
        _eq("3|<5", "<5")
        _eq("<3|>3", ">3|<3")
        _eq("3+|<3", "")
        _eq("3+|<4", "")
        _eq("2+<=6|3+<5", "2..6")
        _eq("3+,<5|2+<=6", "2+<=6")
        _eq("2|2+", "2+")
        _eq("2|2.1+", "2+")
        _eq("2|<2.1", "<2_")
        _eq("3..3", "==3")
        _eq(">=3,<=3", "==3")

        # AND'ing
        _and("3", "3", "3")
        _and("1", "==1", "==1")
        _and("", "==1", "==1")
        _and("3", "4", None)
        _and("<3", "5+", None)
        _and("4+<6", "6+<8", None)
        _and("2+", "<=4", "2..4")
        _and("1", "1.0", "1.0")
        _and("4..6", "6+<8", "==6")

        # inverse
        _inv("3+", "<3")
        _inv("<=3", ">3")
        _inv("3.5", "<3.5|3.5_+")
        self.assertTrue(~VersionRange() is None)

        # odd (but valid) cases
        _eq(">", ">")  # greater than the empty version
        _eq("+", "")  # greater or equal to empty version (is all vers)
        _eq(">=", "")  # equivalent to above
        _eq("<=", "==")  # less or equal to empty version (is only empty)
        _eq("..", "==")  # from empty version to empty version
        _eq("+<=", "==")  # equivalent to above

        invalid_range = [
            "4+<2",  # lower bound greater than upper
            ">3<3",  # both greater and less than same version
            ">3<=3",  # greater and less or equal to same version
            "3+<3"  # greater and equal to, and less than, same version
        ]

        for s in invalid_range:
            self.assertRaises(VersionError, VersionRange, s)

        invalid_syntax = [
            "<",  # less than the empty version
            "><",  # both greater and less than empty version
            ">3>4",  # both are lower bounds
            "<3<4",  # both are upper bounds
            "<4>3",  # upper bound before lower
            ",<4",  # leading comma
            "4+,",  # trailing comma
            "1>=",  # pre-lower-op in post
            "+1",  # post-lower-op in pre
            "4<",  # pre-upper-op in post
            "1+<2<3"  # more than two bounds
        ]

        for s in invalid_syntax:
            self.assertRaises(VersionError, VersionRange, s)

        # test simple logic
        self.assertTrue(VersionRange("").is_any())
        self.assertTrue(VersionRange("2+<4").bounded())
        self.assertTrue(VersionRange("2+").lower_bounded())
        self.assertTrue(not VersionRange("2+").upper_bounded())
        self.assertTrue(not VersionRange("2+").bounded())
        self.assertTrue(VersionRange("<2").upper_bounded())
        self.assertTrue(not VersionRange("<2").lower_bounded())
        self.assertTrue(not VersionRange("<2").bounded())

        # test range from version(s)
        v = Version("3")
        self.assertTrue(
            VersionRange.from_version(v, "eq") == VersionRange("==3"))
        self.assertTrue(
            VersionRange.from_version(v, "gt") == VersionRange(">3"))
        self.assertTrue(
            VersionRange.from_version(v, "gte") == VersionRange("3+"))
        self.assertTrue(
            VersionRange.from_version(v, "lt") == VersionRange("<3"))
        self.assertTrue(
            VersionRange.from_version(v, "lte") == VersionRange("<=3"))

        range1 = VersionRange.from_version(Version("2"), "gte")
        range2 = VersionRange.from_version(Version("4"), "lte")
        _eq(str(range1 & range2), "2..4")

        v2 = Version("6.0")
        v3 = Version("4")
        self.assertTrue(
            VersionRange.from_versions([v, v2, v3]) == VersionRange(
                "==3|==4|==6.0"))

        # test behaviour in sets
        def _eq2(a, b):
            _print("'%s' == '%s'" % (a, b))
            self.assertTrue(a == b)

        a = VersionRange("1+<=2.5")
        b = VersionRange("1..2.5")
        c = VersionRange(">=5")
        d = VersionRange(">6.1.0")
        e = VersionRange("3.2")

        _eq2(set([a]) - set([a]), set())
        _eq2(set([a]) - set([b]), set())
        _eq2(set([a, a]) - set([a]), set())
        _eq2(set([b, c, d, e]) - set([a]), set([c, d, e]))
        _eq2(set([b, c, e]) | set([c, d]), set([b, c, d, e]))
        _eq2(set([b, c]) & set([c, d]), set([c]))
Exemple #27
0
 def assertPipRezEquivalent(pip_spec_str, rez_req_str):
     pip_spec = SpecifierSet(pip_spec_str)
     self.assertEqual(
         rez.utils.pip.pip_specifier_to_rez_requirement(pip_spec),
         VersionRange(rez_req_str))
Exemple #28
0
    def test_containment(self):
        # basic containment
        self.assertTrue(Version("3") in VersionRange("3+"))
        self.assertTrue(Version("5") in VersionRange("3..5"))
        self.assertTrue(Version("5_") not in VersionRange("3..5"))
        self.assertTrue(Version("3.0.0") in VersionRange("3+"))
        self.assertTrue(Version("3.0.0") not in VersionRange("3.1+"))
        self.assertTrue(Version("3") in VersionRange("<1|5|6|8|7|3|60+"))
        self.assertTrue(Version("3") in VersionRange("<1|5|6|8|7|==3|60+"))
        self.assertTrue(VersionRange("2.1+<4") in VersionRange("<4"))
        self.assertTrue(VersionRange("2.1..4") not in VersionRange("<4"))
        self.assertTrue(VersionRange("3") in VersionRange("3"))
        self.assertTrue(VersionRange("==3") in VersionRange("3"))
        self.assertTrue(VersionRange("3.5+<3_") in VersionRange("3"))
        self.assertTrue(VersionRange("3") not in VersionRange("4+<6"))
        self.assertTrue(VersionRange("3+<10") not in VersionRange("4+<6"))

        # iterating over sorted version list
        numbers = [2, 3, 5, 10, 11, 13, 14]
        versions = [Version(str(x)) for x in numbers]
        rev_versions = list(reversed(versions))
        composite_range = VersionRange.from_versions(versions)

        entries = [(VersionRange(""), 7),
                   (VersionRange("0+"), 7),
                   (VersionRange("5+"), 5),
                   (VersionRange("6+"), 4),
                   (VersionRange("50+"), 0),
                   (VersionRange(">5"), 4),
                   (VersionRange("5"), 1),
                   (VersionRange("6"), 0),
                   (VersionRange("<5"), 2),
                   (VersionRange("<6"), 3),
                   (VersionRange("<50"), 7),
                   (VersionRange("<=5"), 3),
                   (VersionRange("<1"), 0),
                   (VersionRange("2|9+"), 5),
                   (VersionRange("3+<6|12+<13.5"), 3),
                   (VersionRange("<1|20+"), 0),
                   (VersionRange(">0<20"), 7)]

        for range_, count in entries:
            # brute-force containment tests
            matches = set(x for x in versions if x in range_)
            self.assertEqual(len(matches), count)

            # more optimal containment tests
            def _test_it(it):
                matches_ = set(version for contains, version in it if contains)
                self.assertEqual(matches_, matches)

            _test_it(range_.iter_intersect_test(versions))
            _test_it(range_.iter_intersect_test(rev_versions, descending=True))

            # throw in an intersection test
            self.assertEqual(composite_range.intersects(range_), (count != 0))
            int_range = composite_range & range_
            versions_ = [] if int_range is None else int_range.to_versions()
            self.assertEqual(set(versions_), matches)

            # throw in a superset test as well
            self.assertEqual(range_.issuperset(composite_range), (count == 7))
            if count:
                self.assertTrue(composite_range.issuperset(int_range))
Exemple #29
0
class Requirement(_Common):
    """Requirement for a versioned object.

    Examples of valid requirement strings:

        foo-1.0
        [email protected]
        foo#1.0
        foo-1+
        foo-1+<4.3
        foo<3
        foo==1.0.1

    Defines a requirement for an object. For example, "foo-5+" means that you
    require any version of "foo", version 5 or greater. An unversioned
    requirement can also be used ("foo"), this means you require any version of
    foo. You can drop the hyphen between object name and version range if the
    version range starts with a non-alphanumeric character - eg "foo<2".

    There are two different prefixes that can be applied to a requirement:

    - "!": The conflict requirement. This means that you require this version
      range of an object NOT to be present. To conflict with all versions of an
      object, use "!foo".

    - "~": This is known as a "weak reference", and means, "I do not require this
      object, but if present, it must be within this range." It is equivalent to
      the *conflict of the inverse* of the given version range.

    There is one subtle case to be aware of. "~foo" is a requirement that has no
    effect - ie, it means "I do not require foo, but if foo is present, it can
    be any version." This statement is still valid, but will produce a
    Requirement object with a None range.
    """
    sep_regex = re.compile(r'[-@#=<>]')

    def __init__(self, s):
        self.name_ = None
        self.range_ = None
        self.negate_ = False
        self.conflict_ = False
        self._str = None
        self.sep_ = '-'
        if s is None:
            return

        self.conflict_ = s.startswith('!')
        if self.conflict_:
            s = s[1:]
        elif s.startswith('~'):
            s = s[1:]
            self.negate_ = True
            self.conflict_ = True

        m = self.sep_regex.search(s)
        if m:
            i = m.start()
            self.name_ = s[:i]
            req_str = s[i:]
            if req_str[0] in ('-', '@', '#'):
                self.sep_ = req_str[0]
                req_str = req_str[1:]

            self.range_ = VersionRange(req_str)
            if self.negate_:
                self.range_ = ~self.range_
        elif self.negate_:
            self.name_ = s
            # rare case - '~foo' equates to no effect
            self.range_ = None
        else:
            self.name_ = s
            self.range_ = VersionRange()

    @classmethod
    def construct(cls, name, range=None):
        """Create a requirement directly from an object name and VersionRange.

        Args:
            name: Object name string.
            range: VersionRange object. If None, an unversioned requirement is
                created.
        """
        other = Requirement(None)
        other.name_ = name
        other.range_ = VersionRange() if range is None else range
        return other

    @property
    def name(self):
        """Name of the required object."""
        return self.name_

    @property
    def range(self):
        """VersionRange of the requirement."""
        return self.range_

    @property
    def conflict(self):
        """True if the requirement is a conflict requirement, eg "!foo", "~foo-1".
        """
        return self.conflict_

    @property
    def weak(self):
        """True if the requirement is weak, eg "~foo".

        Note that weak requirements are also conflict requirements, but not
        necessarily the other way around.
        """
        return self.negate_

    def safe_str(self):
        """Return a string representation that is safe for the current filesystem,
        and guarantees that no two different Requirement objects will encode to
        the same value."""
        return str(self)

    def conflicts_with(self, other):
        """Returns True if this requirement conflicts with another `Requirement`
        or `VersionedObject`."""
        if isinstance(other, Requirement):
            if (self.name_ != other.name_) or (self.range is None) \
                    or (other.range is None):
                return False
            elif self.conflict:
                return False if other.conflict \
                    else self.range_.issuperset(other.range_)
            elif other.conflict:
                return other.range_.issuperset(self.range_)
            else:
                return not self.range_.intersects(other.range_)
        else:  # VersionedObject
            if (self.name_ != other.name_) or (self.range is None):
                return False
            if self.conflict:
                return (other.version_ in self.range_)
            else:
                return (other.version_ not in self.range_)

    def merged(self, other):
        """Returns the merged result of two requirements.

        Two requirements can be in conflict and if so, this function returns
        None. For example, requests for "foo-4" and "foo-6" are in conflict,
        since both cannot be satisfied with a single version of foo.

        Some example successful requirements merges are:
        - "foo-3+" and "!foo-5+" == "foo-3+<5"
        - "foo-1" and "foo-1.5" == "foo-1.5"
        - "!foo-2" and "!foo-5" == "!foo-2|5"
        """
        if self.name_ != other.name_:
            return None  # cannot merge across object names

        def _r(r_):
            r = Requirement(None)
            r.name_ = r_.name_
            r.negate_ = r_.negate_
            r.conflict_ = r_.conflict_
            r.sep_ = r_.sep_
            return r

        if self.range is None:
            return other
        elif other.range is None:
            return self
        elif self.conflict:
            if other.conflict:
                r = _r(self)
                r.range_ = self.range_ | other.range_
                r.negate_ = (self.negate_ and other.negate_
                             and not r.range_.is_any())
                return r
            else:
                range_ = other.range - self.range
                if range_ is None:
                    return None
                else:
                    r = _r(other)
                    r.range_ = range_
                    return r
        elif other.conflict:
            range_ = self.range_ - other.range_
            if range_ is None:
                return None
            else:
                r = _r(self)
                r.range_ = range_
                return r
        else:
            range_ = self.range_ & other.range_
            if range_ is None:
                return None
            else:
                r = _r(self)
                r.range_ = range_
                return r

    def __eq__(self, other):
        return (isinstance(other, Requirement)
                and (self.name_ == other.name_)
                and (self.range_ == other.range_)
                and (self.conflict_ == other.conflict_))

    def __hash__(self):
        return hash(str(self))

    def __str__(self):
        if self._str is None:
            pre_str = '~' if self.negate_ else ('!' if self.conflict_ else '')
            range_str = ''
            sep_str = ''

            range_ = self.range_
            if self.negate_:
                range_ = ~range_ if range_ else VersionRange()

            if not range_.is_any():
                range_str = str(range_)
                if range_str[0] not in ('=', '<', '>'):
                    sep_str = self.sep_

            self._str = pre_str + self.name_ + sep_str + range_str
        return self._str
Exemple #30
0
def pip_specifier_to_rez_requirement(specifier):
    """Convert PEP440 version specifier to rez equivalent.

    See https://www.python.org/dev/peps/pep-0440/#version-specifiers

    Note that version numbers in the specifier are converted to rez equivalents
    at the same time. Thus a specifier like '<1.ALPHA2' becomes '<1.a2'.

    Note that the conversion is not necessarily exact - there are cases in
    PEP440 that have no equivalent in rez versioning. Most of these are
    specifiers that involve pre/post releases, which don't exist in rez (or
    rather, they do exist in the sense that '1.0.post1' is a valid rez version
    number, but it has no special meaning).

    Note also that the specifier is being converted into rez format, but in a
    way that still expresses how _pip_ interprets the specifier. For example,
    '==1' is a valid version range in rez, but '==1' has a different meaning to
    pip than it does to rez ('1.0' matches '==1' in pip, but not in rez). This
    is why '==1' is converted to '1+<1.1' in rez, rather than '==1'.

    Example conversions:

        |   PEP440    |     rez     |
        |-------------|-------------|
        | ==1         | 1+<1.1      |
        | ==1.*       | 1           |
        | >1          | 1.1+        |
        | <1          | <1          |
        | >=1         | 1+          |
        | <=1         | <1.1        |
        | ~=1.2       | 1.2+<2      |
        | ~=1.2.3     | 1.2.3+<1.3  |
        | !=1         | <1|1.1+     |
        | !=1.2       | <1.2|1.2.1+ |
        | !=1.*       | <1|2+       |
        | !=1.2.*     | <1.2|1.3+   |

    Args:
        specifier (`package.SpecifierSet`): Pip specifier.

    Returns:
        `VersionRange`: Equivalent rez version range.
    """
    def is_release(rez_ver):
        parts = rez_ver.split('.')
        try:
            _ = int(parts[-1])  # noqa
            return True
        except:
            return False

    # 1 --> 2; 1.2 --> 1.3; 1.a2 -> 1.0
    def next_ver(rez_ver):
        parts = rez_ver.split('.')
        if is_release(rez_ver):
            parts = parts[:-1] + [str(int(parts[-1]) + 1)]
        else:
            parts = parts[:-1] + ["0"]
        return '.'.join(parts)

    # 1 --> 1.1; 1.2 --> 1.2.1; 1.a2 --> 1.0
    def adjacent_ver(rez_ver):
        if is_release(rez_ver):
            return rez_ver + ".1"
        else:
            parts = rez_ver.split('.')
            parts = parts[:-1] + ["0"]
            return '.'.join(parts)

    def convert_spec(spec):
        def parsed_rez_ver():
            v = spec.version.replace(".*", "")
            return pip_to_rez_version(v)

        def fmt(txt):
            v = parsed_rez_ver()
            vnext = next_ver(v)
            vadj = adjacent_ver(v)
            return txt.format(V=v, VNEXT=vnext, VADJ=vadj)

        # ==1.* --> 1
        if spec.operator == "==" and spec.version.endswith(".*"):
            return fmt("{V}")

        # ==1 --> 1+<1.1
        if spec.operator == "==":
            return fmt("{V}+<{VADJ}")

        # >=1 --> 1+
        if spec.operator == ">=":
            return fmt("{V}+")

        # >1 --> 1.1+
        if spec.operator == ">":
            return fmt("{VADJ}+")

        # <= 1 --> <1.1
        if spec.operator == "<=":
            return fmt("<{VADJ}")

        # <1 --> <1
        if spec.operator == "<":
            return fmt("<{V}")

        # ~=1.2 --> 1.2+<2; ~=1.2.3 --> 1.2.3+<1.3
        if spec.operator == "~=":
            v = Version(parsed_rez_ver())
            v = v.trim(len(v) - 1)
            v_next = next_ver(str(v))
            return fmt("{V}+<" + v_next)

        # !=1.* --> <1|2+; !=1.2.* --> <1.2|1.3+
        if spec.operator == "!=" and spec.version.endswith(".*"):
            return fmt("<{V}|{VNEXT}+")

        # !=1 --> <1|1.1+; !=1.2 --> <1.2|1.2.1+
        if spec.operator == "!=":
            return fmt("<{V}|{VADJ}+")

        raise PackageRequestError(
            "Don't know how to convert PEP440 specifier %r "
            "into rez equivalent" % specifier)

    # convert each spec into rez equivalent
    ranges = list(map(convert_spec, specifier))

    # AND together ranges
    total_range = VersionRange(ranges[0])

    for range_ in ranges[1:]:
        range_ = VersionRange(range_)
        total_range = total_range.intersection(range_)

        if total_range is None:
            raise PackageRequestError(
                "PEP440 specifier %r converts to a non-intersecting rez "
                "version range" % specifier)

    return total_range
    def __init__(self, context_model, variant_left=None, variant_right=None,
                 parent=None):
        super(CompareCell, self).__init__(parent)
        self.context_model = context_model
        self.left_variant = variant_left
        self.right_variant = variant_right
        self.color = None
        self.side = None
        self.mode = None
        self.comparable = False

        package_paths = self.context_model.packages_path

        widget = None
        if self.left_variant and self.right_variant:
            self.side = "both"
            equal_versions = (self.left_variant.version == self.right_variant.version)
            right_variant_visible = (self.right_variant.wrapped.location in package_paths)
            self.comparable = right_variant_visible and not equal_versions

            if self.comparable:
                # determine how far apart the variant versions are
                versions = sorted([self.left_variant.version,
                                   self.right_variant.version])
                range_ = VersionRange.as_span(*versions)
                it = iter_packages(name=self.left_variant.name,
                                   paths=package_paths, range_=range_)
                diff_num = sum(1 for x in it) - 1

                unit = "version" if diff_num == 1 else "versions"
                icon_suffixes = {1: "_1", 2: "_2", 3: "_3"}
                icon_suffix = icon_suffixes.get(diff_num, "")

            if self.left_variant == self.right_variant:
                self.mode = "equal_to"
                self._set_color(0.7, 0.7, 0.7)
                widget = IconButton("equal_to", "packages are equal")
            elif self.left_variant.version == self.right_variant.version:
                # same version, but package is different. This can happen when
                # a local package is released which then hides a central package
                # of the same version
                self.mode = "equalish"
                self._set_color(1, 1, 0)
                widget = IconButton(
                    "equalish", "packages versions are equal, but package is different")
            elif self.left_variant.version > self.right_variant.version:
                self.mode = "greater_than"
                self._set_color(0, 1, 0)
                if self.comparable:
                    desc = "package is %d %s ahead" % (diff_num, unit)
                    widget = IconButton("greater_than" + icon_suffix, desc)
                else:
                    widget = IconButton("greater_than", "package is newer")
            else:
                self.mode = "less_than"
                self._set_color(1, 0, 0)
                if self.comparable:
                    desc = "package is %d %s behind" % (diff_num, unit)
                    widget = IconButton("less_than" + icon_suffix, desc)
                else:
                    widget = IconButton("less_than", "package is older")
        elif self.right_variant:
            self.side = "right"
            self.mode = "missing"
            self._set_color(1, 0, 0)
            widget = IconButton("missing", "package is missing")
        elif self.left_variant:
            self.side = "left"
            self.mode = "new"
            self._set_color(0, 1, 0)
            widget = IconButton("new", "package is new")

        if widget:
            create_pane([None, widget, None], True, compact=True,
                        parent_widget=self)
            widget.clicked.connect(self._clicked)
Exemple #32
0
class Requirement(_Common):
    """Requirement for a versioned object.

    Examples of valid requirement strings:

        foo-1.0
        [email protected]
        foo#1.0
        foo-1+
        foo-1+<4.3
        foo<3
        foo==1.0.1

    Defines a requirement for an object. For example, "foo-5+" means that you
    require any version of "foo", version 5 or greater. An unversioned
    requirement can also be used ("foo"), this means you require any version of
    foo. You can drop the hyphen between object name and version range if the
    version range starts with a non-alphanumeric character - eg "foo<2".

    There are two different prefixes that can be applied to a requirement:

    - "!": The conflict requirement. This means that you require this version
      range of an object NOT to be present. To conflict with all versions of an
      object, use "!foo".

    - "~": This is known as a "weak reference", and means, "I do not require this
      object, but if present, it must be within this range." It is equivalent to
      the *conflict of the inverse* of the given version range.

    There is one subtle case to be aware of. "~foo" is a requirement that has no
    effect - ie, it means "I do not require foo, but if foo is present, it can
    be any version." This statement is still valid, but will produce a
    Requirement object with a None range.
    """
    sep_regex = re.compile(r'[-@#=<>]')

    def __init__(self, s, invalid_bound_error=True):
        self.name_ = None
        self.range_ = None
        self.negate_ = False
        self.conflict_ = False
        self._str = None
        self.sep_ = '-'
        if s is None:
            return

        self.conflict_ = s.startswith('!')
        if self.conflict_:
            s = s[1:]
        elif s.startswith('~'):
            s = s[1:]
            self.negate_ = True
            self.conflict_ = True

        m = self.sep_regex.search(s)
        if m:
            i = m.start()
            self.name_ = s[:i]
            req_str = s[i:]
            if req_str[0] in ('-', '@', '#'):
                self.sep_ = req_str[0]
                req_str = req_str[1:]

            self.range_ = VersionRange(req_str,
                                       invalid_bound_error=invalid_bound_error)
            if self.negate_:
                self.range_ = ~self.range_
        elif self.negate_:
            self.name_ = s
            # rare case - '~foo' equates to no effect
            self.range_ = None
        else:
            self.name_ = s
            self.range_ = VersionRange()

    @classmethod
    def construct(cls, name, range=None):
        """Create a requirement directly from an object name and VersionRange.

        Args:
            name: Object name string.
            range: VersionRange object. If None, an unversioned requirement is
                created.
        """
        other = Requirement(None)
        other.name_ = name
        other.range_ = VersionRange() if range is None else range
        return other

    @property
    def name(self):
        """Name of the required object."""
        return self.name_

    @property
    def range(self):
        """VersionRange of the requirement."""
        return self.range_

    @property
    def conflict(self):
        """True if the requirement is a conflict requirement, eg "!foo", "~foo-1".
        """
        return self.conflict_

    @property
    def weak(self):
        """True if the requirement is weak, eg "~foo".

        Note that weak requirements are also conflict requirements, but not
        necessarily the other way around.
        """
        return self.negate_

    def safe_str(self):
        """Return a string representation that is safe for the current filesystem,
        and guarantees that no two different Requirement objects will encode to
        the same value."""
        return str(self)

    def conflicts_with(self, other):
        """Returns True if this requirement conflicts with another `Requirement`
        or `VersionedObject`."""
        if isinstance(other, Requirement):
            if (self.name_ != other.name_) or (self.range is None) \
                    or (other.range is None):
                return False
            elif self.conflict:
                return False if other.conflict \
                    else self.range_.issuperset(other.range_)
            elif other.conflict:
                return other.range_.issuperset(self.range_)
            else:
                return not self.range_.intersects(other.range_)
        else:  # VersionedObject
            if (self.name_ != other.name_) or (self.range is None):
                return False
            if self.conflict:
                return (other.version_ in self.range_)
            else:
                return (other.version_ not in self.range_)

    def merged(self, other):
        """Returns the merged result of two requirements.

        Two requirements can be in conflict and if so, this function returns
        None. For example, requests for "foo-4" and "foo-6" are in conflict,
        since both cannot be satisfied with a single version of foo.

        Some example successful requirements merges are:
        - "foo-3+" and "!foo-5+" == "foo-3+<5"
        - "foo-1" and "foo-1.5" == "foo-1.5"
        - "!foo-2" and "!foo-5" == "!foo-2|5"
        """
        if self.name_ != other.name_:
            return None  # cannot merge across object names

        def _r(r_):
            r = Requirement(None)
            r.name_ = r_.name_
            r.negate_ = r_.negate_
            r.conflict_ = r_.conflict_
            r.sep_ = r_.sep_
            return r

        if self.range is None:
            return other
        elif other.range is None:
            return self
        elif self.conflict:
            if other.conflict:
                r = _r(self)
                r.range_ = self.range_ | other.range_
                r.negate_ = (self.negate_ and other.negate_
                             and not r.range_.is_any())
                return r
            else:
                range_ = other.range - self.range
                if range_ is None:
                    return None
                else:
                    r = _r(other)
                    r.range_ = range_
                    return r
        elif other.conflict:
            range_ = self.range_ - other.range_
            if range_ is None:
                return None
            else:
                r = _r(self)
                r.range_ = range_
                return r
        else:
            range_ = self.range_ & other.range_
            if range_ is None:
                return None
            else:
                r = _r(self)
                r.range_ = range_
                return r

    def __eq__(self, other):
        return (isinstance(other, Requirement) and (self.name_ == other.name_)
                and (self.range_ == other.range_)
                and (self.conflict_ == other.conflict_))

    def __hash__(self):
        return hash(str(self))

    def __str__(self):
        if self._str is None:
            pre_str = '~' if self.negate_ else ('!' if self.conflict_ else '')
            range_str = ''
            sep_str = ''

            range_ = self.range_
            if self.negate_:
                range_ = ~range_ if range_ else VersionRange()

            if not range_.is_any():
                range_str = str(range_)
                if range_str[0] not in ('=', '<', '>'):
                    sep_str = self.sep_

            self._str = pre_str + self.name_ + sep_str + range_str
        return self._str
Exemple #33
0
        def _eq(a, b):
            _print("'%s' == '%s'" % (a, b))
            a_range = VersionRange(a)
            b_range = VersionRange(b)

            self.assertTrue(a_range == b_range)
            self.assertTrue(a_range.issuperset(a_range))
            self.assertTrue(a_range.issuperset(b_range))
            self.assertTrue(VersionRange(str(a_range)) == a_range)
            self.assertTrue(VersionRange(str(b_range)) == a_range)
            self.assertTrue(hash(a_range) == hash(b_range))

            a_ = a.replace('.', '-')
            a_ = a_.replace("--", "..")
            a_range_ = VersionRange(a_)
            self.assertTrue(a_range_ == a_range)
            self.assertTrue(hash(a_range_) == hash(a_range))

            range_strs = a.split('|')
            ranges = [VersionRange(x) for x in range_strs]
            ranges_ = ranges[0].union(ranges[1:])
            self.assertTrue(ranges_ == a_range)

            self.assertTrue(a_range | b_range == a_range)
            self.assertTrue(a_range - b_range is None)
            self.assertTrue(b_range - a_range is None)
            self.assertTrue(VersionRange() & a_range == a_range)
            self.assertTrue(b_range.span() & a_range == a_range)

            a_inv = a_range.inverse()
            self.assertTrue(a_inv == ~b_range)

            if a_inv:
                self.assertTrue(~a_inv == a_range)
                self.assertTrue(a_range | a_inv == VersionRange())
                self.assertTrue(a_range & a_inv is None)

            a_ranges = a_range.split()
            a_range_ = a_ranges[0].union(a_ranges[1:])
            self.assertTrue(a_range_ == b_range)