Exemplo n.º 1
0
    def intersect(self, other):  # type: (VersionConstraint) -> VersionConstraint
        from pipgrip.libs.semver.version import Version

        if other.is_empty():
            return other

        if isinstance(other, VersionUnion):
            return other.intersect(self)

        # A range and a Version just yields the version if it's in the range.
        if isinstance(other, Version):
            if self.allows(other):
                return other

            return EmptyConstraint()

        if not isinstance(other, VersionRange):
            raise ValueError("Unknown VersionConstraint type {}.".format(other))

        if self.allows_lower(other):
            if self.is_strictly_lower(other):
                return EmptyConstraint()

            intersect_min = other.min
            intersect_include_min = other.include_min
        else:
            if other.is_strictly_lower(self):
                return EmptyConstraint()

            intersect_min = self._min
            intersect_include_min = self._include_min

        if self.allows_higher(other):
            intersect_max = other.max
            intersect_include_max = other.include_max
        else:
            intersect_max = self._max
            intersect_include_max = self._include_max

        if intersect_min is None and intersect_max is None:
            return VersionRange()

        # If the range is just a single version.
        if intersect_min == intersect_max:
            # Because we already verified that the lower range isn't strictly
            # lower, there must be some overlap.
            assert intersect_include_min and intersect_include_max

            return intersect_min

        # If we got here, there is an actual range.
        return VersionRange(
            intersect_min, intersect_max, intersect_include_min, intersect_include_max
        )
Exemplo n.º 2
0
    def of(cls, *ranges):
        from pipgrip.libs.semver.version_range import VersionRange

        flattened = []
        for constraint in ranges:
            if constraint.is_empty():
                continue

            if isinstance(constraint, VersionUnion):
                flattened += constraint.ranges
                continue

            flattened.append(constraint)

        if not flattened:
            return EmptyConstraint()

        if any(constraint.is_any() for constraint in flattened):
            return VersionRange()

        # Only allow Versions and VersionRanges here so we can more easily reason
        # about everything in flattened. _EmptyVersions and VersionUnions are
        # filtered out above.
        for constraint in flattened:
            if isinstance(constraint, VersionRange):
                continue

            raise ValueError(
                "Unknown VersionConstraint type {}.".format(constraint))

        flattened.sort()

        merged = []
        for constraint in flattened:
            # Merge this constraint with the previous one, but only if they touch.
            if not merged or (not merged[-1].allows_any(constraint)
                              and not merged[-1].is_adjacent_to(constraint)):
                merged.append(constraint)
            else:
                merged[-1] = merged[-1].union(constraint)

        if len(merged) == 1:
            return merged[0]

        return VersionUnion(*merged)
Exemplo n.º 3
0
    def difference(self, other):  # type: (VersionConstraint) -> VersionConstraint
        from pipgrip.libs.semver.version import Version

        if other.is_empty():
            return self

        if isinstance(other, Version):
            if not self.allows(other):
                return self

            if other == self.min:
                if not self.include_min:
                    return self

                return VersionRange(self.min, self.max, False, self.include_max)

            if other == self.max:
                if not self.include_max:
                    return self

                return VersionRange(self.min, self.max, self.include_min, False)

            return VersionUnion.of(
                VersionRange(self.min, other, self.include_min, False),
                VersionRange(other, self.max, False, self.include_max),
            )
        elif isinstance(other, VersionRange):
            if not self.allows_any(other):
                return self

            if not self.allows_lower(other):
                before = None
            elif self.min == other.min:
                before = self.min
            else:
                before = VersionRange(
                    self.min, other.min, self.include_min, not other.include_min
                )

            if not self.allows_higher(other):
                after = None
            elif self.max == other.max:
                after = self.max
            else:
                after = VersionRange(
                    other.max, self.max, not other.include_max, self.include_max
                )

            if before is None and after is None:
                return EmptyConstraint()

            if before is None:
                return after

            if after is None:
                return before

            return VersionUnion.of(before, after)
        elif isinstance(other, VersionUnion):
            ranges = []  # type: List[VersionRange]
            current = self

            for range_ in other.ranges:
                # Skip any ranges that are strictly lower than [current].
                if range_.is_strictly_lower(current):
                    continue

                # If we reach a range strictly higher than [current], no more ranges
                # will be relevant so we can bail early.
                if range_.is_strictly_higher(current):
                    break

                difference = current.difference(range_)
                if difference.is_empty():
                    return EmptyConstraint()
                elif isinstance(difference, VersionUnion):
                    # If [range] split [current] in half, we only need to continue
                    # checking future ranges against the latter half.
                    ranges.append(difference.ranges[0])
                    current = difference.ranges[-1]
                else:
                    current = difference

            if not ranges:
                return current

            return VersionUnion.of(*(ranges + [current]))

        raise ValueError("Unknown VersionConstraint type {}.".format(other))
Exemplo n.º 4
0
    def difference(self,
                   other):  # type: (VersionConstraint) -> VersionConstraint
        if other.allows(self):
            return EmptyConstraint()

        return self
Exemplo n.º 5
0
    def intersect(self,
                  other):  # type: (VersionConstraint) -> VersionConstraint
        if other.allows(self):
            return self

        return EmptyConstraint()
Exemplo n.º 6
0
    def difference(self,
                   other):  # type: (VersionConstraint) -> VersionConstraint
        our_ranges = iter(self._ranges)
        their_ranges = iter(self._ranges_for(other))
        new_ranges = []

        state = {
            "current": next(our_ranges, None),
            "their_range": next(their_ranges, None),
        }

        def their_next_range():
            state["their_range"] = next(their_ranges, None)
            if state["their_range"]:
                return True

            new_ranges.append(state["current"])
            our_current = next(our_ranges, None)
            while our_current:
                new_ranges.append(our_current)
                our_current = next(our_ranges, None)

            return False

        def our_next_range(include_current=True):
            if include_current:
                new_ranges.append(state["current"])

            our_current = next(our_ranges, None)
            if not our_current:
                return False

            state["current"] = our_current

            return True

        while True:
            if state["their_range"] is None:
                break

            if state["their_range"].is_strictly_lower(state["current"]):
                if not their_next_range():
                    break

                continue

            if state["their_range"].is_strictly_higher(state["current"]):
                if not our_next_range():
                    break

                continue

            difference = state["current"].difference(state["their_range"])
            if isinstance(difference, VersionUnion):
                assert len(difference.ranges) == 2
                new_ranges.append(difference.ranges[0])
                state["current"] = difference.ranges[-1]

                if not their_next_range():
                    break
            elif difference.is_empty():
                if not our_next_range(False):
                    break
            else:
                state["current"] = difference

                if state["current"].allows_higher(state["their_range"]):
                    if not their_next_range():
                        break
                else:
                    if not our_next_range():
                        break

        if not new_ranges:
            return EmptyConstraint()

        if len(new_ranges) == 1:
            return new_ranges[0]

        return VersionUnion.of(*new_ranges)