Skip to content

Commit

Permalink
drop extra from markers
Browse files Browse the repository at this point in the history
  • Loading branch information
orsinium committed Apr 6, 2021
1 parent bc24362 commit 3b63aa9
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 1 deletion.
10 changes: 9 additions & 1 deletion piptools/scripts/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@
from ..repositories import LocalRequirementsRepository, PyPIRepository
from ..repositories.base import BaseRepository
from ..resolver import Resolver
from ..utils import UNSAFE_PACKAGES, dedup, is_pinned_requirement, key_from_ireq
from ..utils import (
UNSAFE_PACKAGES,
dedup,
drop_extras,
is_pinned_requirement,
key_from_ireq,
)
from ..writer import OutputWriter

DEFAULT_REQUIREMENTS_FILE = "requirements.in"
Expand Down Expand Up @@ -402,6 +408,8 @@ def cli(
)

constraints = [req for req in constraints if req.match_markers(extras)]
for req in constraints:
drop_extras(req)

log.debug("Using indexes:")
with log.indentation():
Expand Down
51 changes: 51 additions & 0 deletions piptools/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
Dict,
Iterable,
Iterator,
List,
Optional,
Set,
Tuple,
Expand Down Expand Up @@ -210,6 +211,56 @@ def dedup(iterable: Iterable[_T]) -> Iterable[_T]:
return iter(dict.fromkeys(iterable))


def drop_extras(ireq: InstallRequirement) -> None:
"""Remove "extra" markers (PEP-508) from requirement."""
if ireq.markers is None:
return
ireq.markers._markers = _drop_extras(ireq.markers._markers)
if not ireq.markers._markers:
ireq.markers = None


def _drop_extras(markers: List[_T]) -> List[_T]:
# drop `extra` tokens
to_remove: List[int] = []
for i, token in enumerate(markers):
# operator (and/or)
if isinstance(token, str):
continue
# sub-expression (inside braces)
if isinstance(token, list):
markers[i] = _drop_extras(token) # type: ignore
if not markers[i]:
to_remove.append(i)
continue
# test expression (like `extra == "dev"`)
assert isinstance(token, tuple)
if token[0].value == "extra":
to_remove.append(i)
for i in reversed(to_remove):
markers.pop(i)

# drop duplicate bool operators (and/or)
to_remove = []
for i, (token1, token2) in enumerate(zip(markers, markers[1:])):
if not isinstance(token1, str):
continue
if not isinstance(token2, str):
continue
if token1 == "and":
to_remove.append(i)
else:
to_remove.append(i + 1)
for i in reversed(to_remove):
markers.pop(i)
if markers and isinstance(markers[0], str):
markers.pop(0)
if markers and isinstance(markers[-1], str):
markers.pop(-1)

return markers


def get_hashes_from_ireq(ireq: InstallRequirement) -> Set[str]:
"""
Given an InstallRequirement, return a set of string hashes in the format
Expand Down
3 changes: 3 additions & 0 deletions tests/test_cli_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -1767,6 +1767,7 @@ def test_input_formats(fake_dists, runner, make_module, fname, content):
assert "small-fake-d" not in out.stderr
assert "small-fake-e" not in out.stderr
assert "small-fake-f" not in out.stderr
assert "extra ==" not in out.stderr


@pytest.mark.network
Expand All @@ -1786,6 +1787,7 @@ def test_one_extra(fake_dists, runner, make_module, fname, content):
assert "small-fake-d==0.4" in out.stderr
assert "small-fake-e" not in out.stderr
assert "small-fake-f" not in out.stderr
assert "extra ==" not in out.stderr


@pytest.mark.network
Expand Down Expand Up @@ -1815,6 +1817,7 @@ def test_multiple_extras(fake_dists, runner, make_module, fname, content):
assert "small-fake-d==0.4" in out.stderr
assert "small-fake-e==0.5" in out.stderr
assert "small-fake-f==0.6" in out.stderr
assert "extra ==" not in out.stderr


def test_extras_fail_with_requirements_in(runner, tmpdir):
Expand Down
52 changes: 52 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from piptools.utils import (
as_tuple,
dedup,
drop_extras,
flat_map,
format_requirement,
format_specifier,
Expand Down Expand Up @@ -367,3 +368,54 @@ def test_lookup_table_from_tuples_with_empty_values():

def test_lookup_table_with_empty_values():
assert lookup_table((), operator.itemgetter(0)) == {}


@pytest.mark.parametrize(
("given", "expected"),
(
("", None),
("extra == 'dev'", None),
("extra == 'dev' or extra == 'test'", None),
("os_name == 'nt' and extra == 'dev'", "os_name == 'nt'"),
("extra == 'dev' and os_name == 'nt'", "os_name == 'nt'"),
("os_name == 'nt' or extra == 'dev'", "os_name == 'nt'"),
("extra == 'dev' or os_name == 'nt'", "os_name == 'nt'"),
("(extra == 'dev') or os_name == 'nt'", "os_name == 'nt'"),
("os_name == 'nt' and (extra == 'dev' or extra == 'test')", "os_name == 'nt'"),
("os_name == 'nt' or (extra == 'dev' or extra == 'test')", "os_name == 'nt'"),
("(extra == 'dev' or extra == 'test') or os_name == 'nt'", "os_name == 'nt'"),
("(extra == 'dev' or extra == 'test') and os_name == 'nt'", "os_name == 'nt'"),
(
"os_name == 'nt' or (os_name == 'unix' and extra == 'test')",
"os_name == 'nt' or os_name == 'unix'",
),
(
"(os_name == 'unix' and extra == 'test') or os_name == 'nt'",
"os_name == 'unix' or os_name == 'nt'",
),
(
"(os_name == 'unix' or extra == 'test') and os_name == 'nt'",
"os_name == 'unix' and os_name == 'nt'",
),
(
"(os_name == 'unix' and extra == 'test' or python_version < '3.5')"
" or os_name == 'nt'",
"(os_name == 'unix' or python_version < '3.5') or os_name == 'nt'",
),
(
"os_name == 'unix' and extra == 'test' or os_name == 'nt'",
"os_name == 'unix' or os_name == 'nt'",
),
(
"os_name == 'unix' or extra == 'test' and os_name == 'nt'",
"os_name == 'unix' or os_name == 'nt'",
),
),
)
def test_drop_extras(from_line, given, expected):
ireq = from_line(f"test;{given}")
drop_extras(ireq)
if expected is None:
assert ireq.markers is None
else:
assert str(ireq.markers).replace("'", '"') == expected.replace("'", '"')

0 comments on commit 3b63aa9

Please sign in to comment.