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

markers: add new method to reduce a marker by a Python constraint #733

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
66 changes: 66 additions & 0 deletions src/poetry/core/version/markers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from poetry.core.constraints.generic import MultiConstraint
from poetry.core.constraints.generic import UnionConstraint
from poetry.core.constraints.version import VersionConstraint
from poetry.core.constraints.version import VersionUnion
from poetry.core.constraints.version.exceptions import ParseConstraintError
from poetry.core.version.grammars import GRAMMAR_PEP_508_MARKERS
from poetry.core.version.parser import Parser
Expand Down Expand Up @@ -107,6 +108,12 @@ def exclude(self, marker_name: str) -> BaseMarker:
def only(self, *marker_names: str) -> BaseMarker:
raise NotImplementedError

@abstractmethod
def reduce_by_python_constraint(
self, python_constraint: VersionConstraint
) -> BaseMarker:
raise NotImplementedError

@abstractmethod
def invert(self) -> BaseMarker:
raise NotImplementedError
Expand Down Expand Up @@ -145,6 +152,11 @@ def exclude(self, marker_name: str) -> BaseMarker:
def only(self, *marker_names: str) -> BaseMarker:
return self

def reduce_by_python_constraint(
self, python_constraint: VersionConstraint
) -> BaseMarker:
return self

def invert(self) -> EmptyMarker:
return EmptyMarker()

Expand Down Expand Up @@ -186,6 +198,11 @@ def exclude(self, marker_name: str) -> EmptyMarker:
def only(self, *marker_names: str) -> BaseMarker:
return self

def reduce_by_python_constraint(
self, python_constraint: VersionConstraint
) -> BaseMarker:
return self

def invert(self) -> AnyMarker:
return AnyMarker()

Expand Down Expand Up @@ -283,6 +300,11 @@ def only(self, *marker_names: str) -> BaseMarker:

return self

def reduce_by_python_constraint(
self, python_constraint: VersionConstraint
) -> BaseMarker:
return self

def intersect(self, other: BaseMarker) -> BaseMarker:
if isinstance(other, SingleMarkerLike):
merged = _merge_single_markers(self, other, MultiMarker)
Expand Down Expand Up @@ -395,6 +417,18 @@ def value(self) -> str:
def _key(self) -> tuple[object, ...]:
return self._name, self._operator, self._value

def reduce_by_python_constraint(
self, python_constraint: VersionConstraint
) -> BaseMarker:
if self.name in PYTHON_VERSION_MARKERS:
assert isinstance(self._constraint, VersionConstraint)
if self._constraint.allows_all(python_constraint):
return AnyMarker()
elif not self._constraint.allows_any(python_constraint):
return EmptyMarker()

return self

def invert(self) -> BaseMarker:
if self._operator in ("===", "=="):
operator = "!="
Expand Down Expand Up @@ -670,6 +704,13 @@ def exclude(self, marker_name: str) -> BaseMarker:
def only(self, *marker_names: str) -> BaseMarker:
return self.of(*(m.only(*marker_names) for m in self._markers))

def reduce_by_python_constraint(
self, python_constraint: VersionConstraint
) -> BaseMarker:
return self.of(
*(m.reduce_by_python_constraint(python_constraint) for m in self._markers)
)

def invert(self) -> BaseMarker:
markers = [marker.invert() for marker in self._markers]

Expand Down Expand Up @@ -839,6 +880,31 @@ def exclude(self, marker_name: str) -> BaseMarker:
def only(self, *marker_names: str) -> BaseMarker:
return self.of(*(m.only(*marker_names) for m in self._markers))

def reduce_by_python_constraint(
self, python_constraint: VersionConstraint
) -> BaseMarker:
from poetry.core.packages.utils.utils import get_python_constraint_from_marker

markers: Iterable[BaseMarker] = self._markers
if isinstance(python_constraint, VersionUnion):
python_only_markers = []
other_markers = []
for m in self._markers:
if m == m.only(*PYTHON_VERSION_MARKERS):
python_only_markers.append(m)
else:
other_markers.append(m)
if get_python_constraint_from_marker(
self.of(*python_only_markers)
).allows_all(python_constraint):
if not other_markers:
return AnyMarker()
markers = other_markers

return self.of(
*(m.reduce_by_python_constraint(python_constraint) for m in markers)
)

def invert(self) -> BaseMarker:
markers = [marker.invert() for marker in self._markers]
return MultiMarker(*markers)
Expand Down
83 changes: 83 additions & 0 deletions tests/version/test_markers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from poetry.core.constraints.generic import UnionConstraint
from poetry.core.constraints.generic import parse_constraint as parse_generic_constraint
from poetry.core.constraints.version import parse_constraint as parse_version_constraint
from poetry.core.version.markers import AnyMarker
from poetry.core.version.markers import AtomicMarkerUnion
from poetry.core.version.markers import EmptyMarker
Expand Down Expand Up @@ -1175,6 +1176,88 @@ def test_only(marker: str, only: list[str], expected: str) -> None:
assert str(m.only(*only)) == expected


@pytest.mark.parametrize(
("marker", "constraint", "expected"),
[
("", "~3.8", ""),
("<empty>", "~3.8", "<empty>"),
('sys_platform == "linux"', "~3.8", 'sys_platform == "linux"'),
('python_version >= "3.8"', "~3.8", ""),
('python_version > "3.8"', "~3.8", 'python_version > "3.8"'),
('python_version >= "3.9"', "~3.8", "<empty>"),
('python_full_version >= "3.8.0"', "~3.8", ""),
('python_full_version >= "3.8.1"', "~3.8", 'python_full_version >= "3.8.1"'),
('python_full_version < "3.8.0"', "~3.8", "<empty>"),
('python_version >= "3.8" and python_version < "3.9"', "~3.8", ""),
('python_version >= "3.7" and python_version < "4.0"', "~3.8", ""),
(
'python_full_version >= "3.8.1" and python_version < "3.9"',
"~3.8",
'python_full_version >= "3.8.1"',
),
(
'python_version >= "3.8" and python_full_version < "3.8.2"',
"~3.8",
'python_full_version < "3.8.2"',
),
(
'python_version >= "3.8" and sys_platform == "linux" and python_version < "3.9"',
"~3.8",
'sys_platform == "linux"',
),
('python_version < "3.8" or python_version >= "3.9"', "~3.9", ""),
(
'python_version < "3.8" or python_version >= "3.9"',
">=3.7",
'python_version < "3.8" or python_version >= "3.9"',
),
('python_version < "3.8" or python_version >= "3.9"', "~3.7", ""),
(
'python_version < "3.8" or python_version >= "3.9"',
"<=3.10",
'python_version < "3.8" or python_version >= "3.9"',
),
(
(
'python_version < "3.8"'
' or python_version >= "3.9" and sys_platform == "linux"'
),
"~3.9",
'sys_platform == "linux"',
),
('python_version < "3.8" or python_version >= "3.9"', "~3.7 || ~3.9", ""),
(
'python_version < "3.8" or python_version >= "3.9"',
"~3.6 || ~3.8",
'python_version < "3.8"',
),
(
(
'python_version < "3.8" or sys_platform == "linux"'
' or python_version >= "3.9"'
),
"~3.7 || ~3.9",
'sys_platform == "linux"',
),
(
(
'python_version < "3.8" or sys_platform == "linux"'
' or python_version >= "3.9" or sys_platform == "win32"'
),
"~3.7 || ~3.9",
'sys_platform == "linux" or sys_platform == "win32"',
),
],
)
def test_reduce_by_python_constraint(
marker: str, constraint: str, expected: str
) -> None:
m = parse_marker(marker)
c = parse_version_constraint(constraint)

assert str(m.reduce_by_python_constraint(c)) == expected


def test_union_of_a_single_marker_is_the_single_marker() -> None:
union = MarkerUnion.of(SingleMarker("python_version", ">= 2.7"))

Expand Down
Loading