Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: tox-dev/tox
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 4.2.0
Choose a base ref
...
head repository: tox-dev/tox
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 4.2.1
Choose a head ref
  • 4 commits
  • 4 files changed
  • 3 contributors

Commits on Jan 4, 2023

  1. Update changelog.rst

    gaborbernat authored Jan 4, 2023
    Copy the full SHA
    2e977fb View commit details
  2. Update upgrading.rst

    gaborbernat authored Jan 4, 2023
    Copy the full SHA
    27c52ec View commit details
  3. update how extras are extracted to handle cases with more than 2 grou…

    …ps (#2812)
    
    Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
    Co-authored-by: Bernát Gábor <bgabor8@bloomberg.net>
    Resolves #2791
    fixes #2791
    dconathan authored Jan 4, 2023
    Copy the full SHA
    4578eaa View commit details
  4. release 4.2.1

    gaborbernat committed Jan 4, 2023
    Copy the full SHA
    43382af View commit details
Showing with 58 additions and 32 deletions.
  1. +9 −1 docs/changelog.rst
  2. +0 −8 docs/upgrading.rst
  3. +40 −22 src/tox/tox_env/python/virtual_env/package/util.py
  4. +9 −1 tests/tox_env/python/virtual_env/package/test_python_package_util.py
10 changes: 9 additions & 1 deletion docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -4,13 +4,21 @@ Release History

.. towncrier release notes start
v4.2.1 (2023-01-03)
-------------------

Bugfixes - 4.2.1
~~~~~~~~~~~~~~~~
- Fix extracting extras from markers with more than 2 extras in an or chain - by :user:`dconathan`. (:issue:`2791`)


v4.2.0 (2023-01-03)
-------------------

Features - 4.2.0
~~~~~~~~~~~~~~~~
- Packaging environments now inherit from the ``pkgenv`` section, allowing to set all your packaging options in one place,
and support the ``deps`` key to set additional dependencies that will be installed after ``pyprojec.toml`` static
and support the ``deps`` key to set additional dependencies that will be installed after ``pyproject.toml`` static
``requires`` but before backends dynamic requires - by :user:`gaborbernat`. (:issue:`2543`)

Improved Documentation - 4.2.0
8 changes: 0 additions & 8 deletions docs/upgrading.rst
Original file line number Diff line number Diff line change
@@ -119,14 +119,6 @@ CLI arguments changed
tox 3 added additional lines at the start. If you want to generate valid ini files you must not use the ``-v`` flag.
- The ``--index-url`` is now removed, use ``PIP_INDEX_URL`` in :ref:`set_env` instead.

Packaging changes
-----------------

- We use isolated builds (always) as specified by :pep:`518` and use :pep:`517` to communicate with the build backend.
- The ``--develop`` CLI flag or the :ref:`use_develop` settings now enables editable installations via the :pep:`660`
mechanism rather than the legacy ``pip install -e`` behaviour. The old functionality can still be forced by setting
the :ref:`package` setting for the run environment to ``editable-legacy``.

Output changes
--------------

62 changes: 40 additions & 22 deletions src/tox/tox_env/python/virtual_env/package/util.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from __future__ import annotations

from copy import deepcopy
from typing import Optional, Set, cast

from packaging.markers import Variable # type: ignore[attr-defined]
from packaging.markers import Marker, Op, Value, Variable # type: ignore[attr-defined]
from packaging.requirements import Requirement


@@ -29,25 +30,42 @@ def dependencies_with_extras(deps: list[Requirement], extras: set[str], package_


def extract_extra_markers(deps: list[Requirement]) -> list[tuple[Requirement, set[str | None]]]:
# extras might show up as markers, move them into extras property
result: list[tuple[Requirement, set[str | None]]] = []
for req in deps:
req = deepcopy(req)
markers: list[str | tuple[Variable, Variable, Variable]] = getattr(req.marker, "_markers", []) or []
_at: int | None = None
extra_markers = set()
for _at, (marker_key, op, marker_value) in (
(_at_marker, marker)
for _at_marker, marker in enumerate(markers)
if isinstance(marker, tuple) and len(marker) == 3
):
if marker_key.value == "extra" and op.value == "==": # pragma: no branch
extra_markers.add(marker_value.value)
del markers[_at]
_at -= 1
if _at >= 0 and (isinstance(markers[_at], str) and markers[_at] in ("and", "or")):
del markers[_at]
if len(markers) == 0:
req.marker = None
result.append((req, extra_markers or {None}))
"""
Extract extra markers from dependencies.
:param deps: the dependencies
:return: a list of requirement, extras set
"""
result = [_extract_extra_markers(d) for d in deps]
return result


def _extract_extra_markers(req: Requirement) -> tuple[Requirement, set[str | None]]:
req = deepcopy(req)
markers: list[str | tuple[Variable, Op, Variable]] = getattr(req.marker, "_markers", []) or []
new_markers: list[str | tuple[Variable, Op, Variable]] = []
extra_markers: set[str] = set() # markers that have a key of extra
marker = markers.pop(0) if markers else None
while marker:
extra = _get_extra(marker)
if extra is not None:
extra_markers.add(extra)
if new_markers and new_markers[-1] in ("and", "or"):
del new_markers[-1]
marker = markers.pop(0) if markers else None
if marker in ("and", "or"):
marker = markers.pop(0) if markers else None
else:
new_markers.append(marker)
marker = markers.pop(0) if markers else None
if new_markers:
cast(Marker, req.marker)._markers = new_markers
else:
req.marker = None
return req, cast(Set[Optional[str]], extra_markers) or {None}


def _get_extra(_marker: str | tuple[Variable, Op, Value]) -> str | None:
if isinstance(_marker, tuple) and len(_marker) == 3 and _marker[0].value == "extra" and _marker[1].value == "==":
return cast(str, _marker[2].value)
return None
Original file line number Diff line number Diff line change
@@ -67,5 +67,13 @@ def test_loads_deps_recursive_extras() -> None:

def test_load_dependency_requirement_or_extras() -> None:
requires = [Requirement('filelock<4.0.0,>=3.9.0; extra == "extras1" or extra == "extras2"')]
result = dependencies_with_extras(requires, {"extras1"}, "")
for extras in ["extras1", "extras2"]:
result = dependencies_with_extras(requires, {extras}, "")
assert [str(r) for r in result] == ["filelock<4.0.0,>=3.9.0"]


@pytest.mark.parametrize("extra", ["extras1", "extras2", "extras3"])
def test_load_dependency_requirement_many_or_extras(extra: str) -> None:
requires = [Requirement('filelock<4.0.0,>=3.9.0; extra == "extras1" or extra == "extras2" or extra == "extras3"')]
result = dependencies_with_extras(requires, {extra}, "")
assert [str(r) for r in result] == ["filelock<4.0.0,>=3.9.0"]