Skip to content

Commit

Permalink
performance: fix a performance regression introduced in python-poetry…
Browse files Browse the repository at this point in the history
  • Loading branch information
radoering committed May 12, 2023
1 parent 96d3f39 commit f93c93a
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 25 deletions.
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

0 comments on commit f93c93a

Please sign in to comment.