def intersect(self, other: VersionConstraint) -> VersionConstraint: from poetry.core.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, VersionRangeConstraint): raise ValueError(f"Unknown VersionConstraint type {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 assert intersect_min is not None return intersect_min # If we got here, there is an actual range. return VersionRange( intersect_min, intersect_max, intersect_include_min, intersect_include_max )
def allows_all(self, other: VersionConstraint) -> bool: from poetry.core.semver.version import Version if other.is_empty(): return True if isinstance(other, Version): return self.allows(other) if isinstance(other, VersionUnion): return all([self.allows_all(constraint) for constraint in other.ranges]) if isinstance(other, VersionRangeConstraint): return not other.allows_lower(self) and not other.allows_higher(self) raise ValueError(f"Unknown VersionConstraint type {other}.")
def _ranges_for( self, constraint: VersionConstraint ) -> list[VersionRangeConstraint]: if constraint.is_empty(): return [] if isinstance(constraint, VersionUnion): return constraint.ranges if isinstance(constraint, VersionRangeConstraint): return [constraint] raise ValueError(f"Unknown VersionConstraint type {constraint}")
def _non_empty_term( self, constraint: VersionConstraint, is_positive: bool, other: Term ) -> Term | None: if constraint.is_empty(): return None # when creating a new term prefer direct-reference dependencies dependency = ( other.dependency if not self.dependency.is_direct_origin() and other.dependency.is_direct_origin() else self.dependency ) return Term(dependency.with_constraint(constraint), is_positive)
def union(self, other: VersionConstraint) -> VersionConstraint: from poetry.core.semver.version_range import VersionRange if other.allows(self): return other if isinstance(other, VersionRangeConstraint): if self.allows(other.min): return VersionRange( other.min, other.max, include_min=True, include_max=other.include_max, ) if self.allows(other.max): return VersionRange( other.min, other.max, include_min=other.include_min, include_max=True, ) return VersionUnion.of(self, other)
def format_python_constraint(constraint: VersionConstraint) -> str: """ This helper will help in transforming disjunctive constraint into proper constraint. """ if isinstance(constraint, Version): if constraint.precision >= 3: return f"=={str(constraint)}" # Transform 3.6 or 3 if constraint.precision == 2: # 3.6 constraint = parse_constraint(f"~{constraint.major}.{constraint.minor}") else: constraint = parse_constraint(f"^{constraint.major}.0") if not isinstance(constraint, VersionUnion): return str(constraint) formatted = [] accepted = [] for version in PYTHON_VERSION: version_constraint = parse_constraint(version) matches = constraint.allows_any(version_constraint) if not matches: formatted.append("!=" + version) else: accepted.append(version) # Checking lower bound low = accepted[0] formatted.insert(0, ">=" + ".".join(low.split(".")[:2])) return ", ".join(formatted)
def _non_empty_term(self, constraint: VersionConstraint, is_positive: bool) -> Term | None: if constraint.is_empty(): return None return Term(self.dependency.with_constraint(constraint), is_positive)
def allows_all(self, other: VersionConstraint) -> bool: return other.is_empty() or (self.allows(other) if isinstance( other, self.__class__) else other == self)
def difference(self, other: VersionConstraint) -> Version | EmptyConstraint: if other.allows(self): return EmptyConstraint() return self
def intersect(self, other: VersionConstraint) -> Version | EmptyConstraint: if other.allows(self): return self return EmptyConstraint()
def allows_any(self, other: VersionConstraint) -> bool: return other.allows(self)
def allows_all(self, other: VersionConstraint) -> bool: return other.is_empty()
def difference(self, other: VersionConstraint) -> VersionConstraint: from poetry.core.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, VersionRangeConstraint): if not self.allows_any(other): return self before: VersionConstraint | None 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 ) after: VersionConstraint | None 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: assert after is not None return after if after is None: return before return VersionUnion.of(before, after) elif isinstance(other, VersionUnion): ranges: list[VersionRangeConstraint] = [] current: VersionRangeConstraint = 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: assert isinstance(difference, VersionRangeConstraint) current = difference if not ranges: return current return VersionUnion.of(*(ranges + [current])) raise ValueError(f"Unknown VersionConstraint type {other}.")