未验证 提交 85cda2c0 编写于 作者: F Frost Ming 提交者: GitHub

Merge pull request #108 from frostming/extra-marker

fix marker evaluation
Fix a bug that environment markers cannot be evaluated correctly if extra's are connected with "or".
import copy
import operator
from functools import reduce
from typing import Any, Iterable, Optional, Tuple, Union
from typing import Any, Optional, Sequence, Tuple, Union
from pip._vendor.packaging.markers import Marker as PackageMarker
......@@ -85,29 +85,33 @@ def get_marker(marker: Union[PackageMarker, Marker, None]) -> Optional[Marker]:
return Marker(str(marker)) if marker else None
def split_marker_element(
text: str, element: str
) -> Tuple[Iterable[Tuple[str, str]], Optional[Marker]]:
def split_marker_extras(
marker: PackageMarker,
) -> Tuple[Sequence[str], Optional[Marker]]:
"""An element can be stripped from the marker only if all parts are connected
with `and` operater. The rest part are returned as a string or `None` if all are
stripped.
:param text: the input marker string
:param element: the element to be stripped
:param marker: the input marker string
:returns: an iterable of (op, value) pairs together with the stripped part.
"""
if text is None:
return [], text
marker = Marker(text)
if "or" in marker._markers:
return [], marker
if "and" in marker._markers or any(
not isinstance(p, tuple) or p[0].value != "extra"
for p in marker._markers
if p != "or"
):
return [], marker
result = []
bare_markers = [m for m in marker._markers if m != "and"]
bare_markers = [m for m in marker._markers if m not in ("and", "or")]
for m in bare_markers[:]:
if not isinstance(m, tuple):
continue
if m[0].value == element:
result.append(tuple(e.value for e in m[1:]))
if m[0].value == "extra":
if m[1].value == "==":
result.append(m[2].value)
elif m[1].value == "in":
result.extend(v.strip() for v in m[2].value.split(","))
bare_markers.remove(m)
new_markers = join_list_with(bare_markers, "and")
if not new_markers:
......
......@@ -13,7 +13,7 @@ from pip_shims import path_to_url, url_to_path
from pdm._types import RequirementDict
from pdm.exceptions import ExtrasError, RequirementError
from pdm.models.markers import Marker, get_marker, split_marker_element
from pdm.models.markers import Marker, get_marker, split_marker_extras
from pdm.models.readers import SetupReader
from pdm.models.specifiers import PySpecSet, get_specifier
from pdm.utils import (
......@@ -380,12 +380,10 @@ def filter_requirements_with_extras(
if not _r.marker:
result.append(req)
else:
elements, rest = split_marker_element(str(_r.marker), "extra")
extras_in_meta.extend(e[1] for e in elements)
elements, rest = split_marker_extras(_r.marker)
extras_in_meta.extend(e for e in elements)
_r.marker = rest
if not elements or any(
extra == e[1] for extra in extras for e in elements
):
if not elements or set(extras) & set(elements):
result.append(_r.as_line())
extras_not_found = [e for e in extras if e not in extras_in_meta]
......
......@@ -24,7 +24,7 @@ from pdm.iostream import stream
from pdm.models.candidates import Candidate
from pdm.models.environment import Environment
from pdm.models.repositories import BaseRepository
from pdm.models.requirements import Requirement
from pdm.models.requirements import Requirement, filter_requirements_with_extras
from pdm.models.specifiers import PySpecSet
from pdm.project import Project
from pdm.project.config import Config
......@@ -103,6 +103,7 @@ class TestRepository(BaseRepository):
deps = pypi_data.get("dependencies", [])
for extra in candidate.req.extras or ():
deps.extend(pypi_data.get("extras_require", {}).get(extra, []))
deps = filter_requirements_with_extras(deps, candidate.req.extras or ())
return deps, pypi_data.get("requires_python", ""), ""
def dependency_generators(self) -> Iterable[Callable[[Candidate], CandidateInfo]]:
......
......@@ -210,3 +210,13 @@ def test_resolve_package_with_dummy_upbound(project, repository):
repository.add_candidate("foo", "0.1.0", ">=3.6,<4.0")
result = resolve_requirements(repository, ["foo"], ">=3.5")
assert "foo" in result
def test_resolve_dependency_with_extra_marker(project, repository):
repository.add_candidate("foo", "0.1.0")
repository.add_dependencies("foo", "0.1.0", ["pytz; extra=='tz' or extra=='all'"])
result = resolve_requirements(repository, ["foo"])
assert "pytz" not in result
result = resolve_requirements(repository, ["foo[tz]"])
assert "pytz" in result
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册