Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

performance: fix a performance regression introduced in #402 #582

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/poetry/core/constraints/version/version_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
VersionRangeConstraint,
)
from poetry.core.constraints.version.version_union import VersionUnion
from poetry.core.utils._compat import cached_property


if TYPE_CHECKING:
Expand Down Expand Up @@ -356,14 +357,16 @@ def difference(self, other: VersionConstraint) -> VersionConstraint:
def flatten(self) -> list[VersionRangeConstraint]:
return [self]

@cached_property
def _single_wildcard_range_string(self) -> str:
if not self.is_single_wildcard_range():
if not self.is_single_wildcard_range:
raise ValueError("Not a valid wildcard range")

assert self.min is not None
assert self.max is not None
return f"=={_single_wildcard_range_string(self.min, self.max)}"

@cached_property
def is_single_wildcard_range(self) -> bool:
# e.g.
# - "1.*" equals ">=1.0.dev0, <2" (equivalent to ">=1.0.dev0, <2.0.dev0")
Expand Down Expand Up @@ -436,7 +439,7 @@ def _compare_max(self, other: VersionRangeConstraint) -> int:

def __str__(self) -> str:
with suppress(ValueError):
return self._single_wildcard_range_string()
return self._single_wildcard_range_string

text = ""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import TYPE_CHECKING

from poetry.core.constraints.version.version_constraint import VersionConstraint
from poetry.core.utils._compat import cached_property


if TYPE_CHECKING:
Expand Down Expand Up @@ -33,9 +34,6 @@ def include_max(self) -> bool:

@property
def allowed_min(self) -> Version | None:
if self.min is None:
return None

# That is a bit inaccurate because
# 1) The exclusive ordered comparison >V MUST NOT allow a post-release
# of the given version unless V itself is a post release.
Expand All @@ -47,7 +45,7 @@ def allowed_min(self) -> Version | None:
# the callers of allowed_min.
return self.min

@property
@cached_property
def allowed_max(self) -> Version | None:
if self.max is None:
return None
Expand Down
39 changes: 26 additions & 13 deletions src/poetry/core/constraints/version/version_union.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from poetry.core.constraints.version.version_range_constraint import (
VersionRangeConstraint,
)
from poetry.core.utils._compat import cached_property


if TYPE_CHECKING:
Expand Down Expand Up @@ -92,19 +93,17 @@ def is_any(self) -> bool:
return False

def is_simple(self) -> bool:
return self.excludes_single_version()
return self.excludes_single_version

def allows(self, version: Version) -> bool:
if self.excludes_single_version():
if self.excludes_single_version:
# when excluded version is local, special handling is required
# to ensure that a constraint (!=2.0+deadbeef) will allow the
# provided version (2.0)
from poetry.core.constraints.version.version import Version
from poetry.core.constraints.version.version_range import VersionRange

excluded = VersionRange().difference(self)
excluded = self._excluded_single_version

if isinstance(excluded, Version) and excluded.is_local():
if excluded.is_local():
return excluded != version

return any(constraint.allows(version) for constraint in self._ranges)
Expand Down Expand Up @@ -252,12 +251,13 @@ def our_next_range(include_current: bool = True) -> bool:
def flatten(self) -> list[VersionRangeConstraint]:
return self.ranges

@cached_property
def _exclude_single_wildcard_range_string(self) -> str:
"""
Helper method to convert this instance into a wild card range
string.
"""
if not self.excludes_single_wildcard_range():
if not self.excludes_single_wildcard_range:
raise ValueError("Not a valid wildcard range")

idx_order = (0, 1) if self._ranges[0].max else (1, 0)
Expand All @@ -268,6 +268,7 @@ def _exclude_single_wildcard_range_string(self) -> str:
assert two.min is not None
return f"!={_single_wildcard_range_string(one.max, two.min)}"

@cached_property
def excludes_single_wildcard_range(self) -> bool:
if len(self._ranges) != 2:
return False
Expand All @@ -288,11 +289,25 @@ def excludes_single_wildcard_range(self) -> bool:

return _is_wildcard_candidate(two.min, one.max, inverted=True)

@cached_property
def excludes_single_version(self) -> bool:
from poetry.core.constraints.version.version import Version

return isinstance(self._inverted, Version)

@cached_property
def _excluded_single_version(self) -> Version:
from poetry.core.constraints.version.version import Version

excluded = self._inverted
assert isinstance(excluded, Version)
return excluded

@cached_property
def _inverted(self) -> VersionConstraint:
from poetry.core.constraints.version.version_range import VersionRange

return isinstance(VersionRange().difference(self), Version)
return VersionRange().difference(self)

def __eq__(self, other: object) -> bool:
if not isinstance(other, VersionUnion):
Expand All @@ -304,12 +319,10 @@ def __hash__(self) -> int:
return reduce(op.xor, map(hash, self._ranges))

def __str__(self) -> str:
from poetry.core.constraints.version.version_range import VersionRange

if self.excludes_single_version():
return f"!={VersionRange().difference(self)}"
if self.excludes_single_version:
return f"!={self._excluded_single_version}"

try:
return self._exclude_single_wildcard_range_string()
return self._exclude_single_wildcard_range_string
except ValueError:
return " || ".join([str(r) for r in self._ranges])
4 changes: 2 additions & 2 deletions src/poetry/core/packages/dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,8 @@ def base_pep_508_name(self) -> str:
constraint = self.constraint
if isinstance(constraint, VersionUnion):
if (
constraint.excludes_single_version()
or constraint.excludes_single_wildcard_range()
constraint.excludes_single_version
or constraint.excludes_single_wildcard_range
):
# This branch is a short-circuit logic for special cases and
# avoids having to split and parse constraint again. This has
Expand Down
8 changes: 8 additions & 0 deletions src/poetry/core/utils/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@

WINDOWS = sys.platform == "win32"

if sys.version_info < (3, 8):
# no caching for python 3.7
cached_property = property
else:
import functools

cached_property = functools.cached_property

if sys.version_info < (3, 11):
# compatibility for python <3.11
import tomli as tomllib
Expand Down
4 changes: 2 additions & 2 deletions tests/constraints/version/test_version_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,7 @@ def test_is_single_wildcard_range_include_min_include_max(
version_range = VersionRange(
Version.parse("1.2.dev0"), Version.parse("1.3"), include_min, include_max
)
assert version_range.is_single_wildcard_range() is expected
assert version_range.is_single_wildcard_range is expected


@pytest.mark.parametrize(
Expand Down Expand Up @@ -721,7 +721,7 @@ def test_is_single_wildcard_range(
Version.parse(max) if max else None,
include_min=True,
)
assert version_range.is_single_wildcard_range() is expected
assert version_range.is_single_wildcard_range is expected


@pytest.mark.parametrize(
Expand Down
4 changes: 2 additions & 2 deletions tests/constraints/version/test_version_union.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
def test_excludes_single_wildcard_range_basics(
ranges: list[VersionRange], expected: bool
) -> None:
assert VersionUnion(*ranges).excludes_single_wildcard_range() is expected
assert VersionUnion(*ranges).excludes_single_wildcard_range is expected


@pytest.mark.parametrize(
Expand Down Expand Up @@ -126,7 +126,7 @@ def test_excludes_single_wildcard_range(max: str, min: str, expected: bool) -> N
VersionRange(max=Version.parse(max)),
VersionRange(Version.parse(min), include_min=True),
)
assert version_union.excludes_single_wildcard_range() is expected
assert version_union.excludes_single_wildcard_range is expected


@pytest.mark.parametrize(
Expand Down