From 35f8482574cd0e393791feb1b23cbaa725218322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Tue, 5 Nov 2024 19:59:31 +0100 Subject: [PATCH] %pyproject_buildrequires: Add support for dependency groups (PEP 735), via tox configuration (cherry picked from commit 8baa94160c750a6238cd824d924b88bef6fb8724) --- README.md | 3 +++ pyproject-rpm-macros.spec | 1 + pyproject_buildrequires.py | 27 +++++++++++++++++++- pyproject_buildrequires_testcases.yaml | 34 ++++++++++++++++++++++++++ test_pyproject_buildrequires.py | 8 ++++++ 5 files changed, 72 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0fb73bd..192c04d 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,9 @@ The `-e` option redefines `%{toxenv}` for further reuse. Use `%{default_toxenv}` to get the default value. The `-t`/`-e` option uses [tox-current-env]'s `--print-deps-to-file` behind the scenes. +It generates dependencies listed directly in `deps`, +dependencies defined through `extras`, +and on tox 4.22+ also dependencies defined through `dependency_groups`. If your package specifies some tox plugins in `tox.requires`, such plugins will be BuildRequired as well. diff --git a/pyproject-rpm-macros.spec b/pyproject-rpm-macros.spec index af1c711..2b7f401 100644 --- a/pyproject-rpm-macros.spec +++ b/pyproject-rpm-macros.spec @@ -198,6 +198,7 @@ export HOSTNAME="rpmbuild" # to speedup tox in network-less mock, see rhbz#1856 %changelog * Mon Nov 04 2024 Miro HronĨok - 1.16.0-1 - %%pyproject_buildrequires: Add support for dependency groups (PEP 735), via the -g flag +- This is implied when used tox testenvs depend on dependency groups (requires tox 4.22+) - Fixes: rhbz#2318849 * Thu Oct 03 2024 Karolina Surma - 1.15.1-1 diff --git a/pyproject_buildrequires.py b/pyproject_buildrequires.py index 4a371a7..a67dcf2 100644 --- a/pyproject_buildrequires.py +++ b/pyproject_buildrequires.py @@ -468,6 +468,29 @@ def generate_tox_requirements(toxenv, requirements): source=f'tox --print-deps-only: {toxenv}') +def tox_dependency_groups(toxenv): + # We call this command separately instead of folding it into the previous one + # becasue --print-dependency-groups-to only works with tox 4.22+ and tox-current-env 0.0.14+. + # We handle failure gracefully: upstreams using dependency_groups should require tox >= 4.22. + toxenv = ','.join(toxenv) + with tempfile.NamedTemporaryFile('r') as groups: + r = subprocess.run( + [sys.executable, '-m', 'tox', + '--print-dependency-groups-to', groups.name, + '-q', '-e', toxenv], + check=False, + encoding='utf-8', + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + if r.returncode == 0: + if r.stdout: + print_err(r.stdout, end='') + if output := groups.read().strip(): + return output.splitlines() + return [] + + def generate_dependency_groups(requested_groups, requirements): """Adapted from https://peps.python.org/pep-0735/#reference-implementation (public domain)""" from collections import defaultdict @@ -573,6 +596,7 @@ def generate_requires( config_settings=config_settings, ) + dependency_groups = dependency_groups or [] try: if (include_runtime or toxenv or read_pyproject_dependencies) and not use_build_system: raise ValueError('-N option cannot be used in combination with -r, -e, -t, -x, -p options') @@ -588,7 +612,8 @@ def generate_requires( generate_build_requirements(backend, requirements) if toxenv: include_runtime = True - generate_tox_requirements(toxenv, requirements) # TODO extend dependency_groups + generate_tox_requirements(toxenv, requirements) + dependency_groups.extend(tox_dependency_groups(toxenv)) if dependency_groups: generate_dependency_groups(dependency_groups, requirements) if include_runtime: diff --git a/pyproject_buildrequires_testcases.yaml b/pyproject_buildrequires_testcases.yaml index eccc228..bc96144 100644 --- a/pyproject_buildrequires_testcases.yaml +++ b/pyproject_buildrequires_testcases.yaml @@ -1385,3 +1385,37 @@ pyproject.toml with dependency-groups nonexisting requested: tests = ["pytest>=5", "pytest-mock"] docs = ["sphinx", "python-docs-theme"] except: LookupError + +tox with dependency_groups: + skipif: not (SETUPTOOLS_60 and TOX_4_22) + installed: + setuptools: 50 + wheel: 1 + tox: 4.22 + tox-current-env: 0.0.14 + toxenv: + - py3 + pyproject.toml: | + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + [project] + name = "my_package" + version = "0.1" + [dependency-groups] + tests = ["pytest>=5", "pytest-mock"] + docs = ["sphinx", "python-docs-theme"] + [tool.tox] + requires = ["tox>=4.22"] + [tool.tox.env_run_base] + dependency_groups = ["tests"] + commands = [["pytest"]] + expected: | + python3dist(setuptools) + python3dist(wheel) + python3dist(tox-current-env) >= 0.0.6 + python3dist(tox) >= 4.22 + python3dist(tox) + python3dist(pytest) >= 5 + python3dist(pytest-mock) + result: 0 diff --git a/test_pyproject_buildrequires.py b/test_pyproject_buildrequires.py index b20ace0..815c916 100644 --- a/test_pyproject_buildrequires.py +++ b/test_pyproject_buildrequires.py @@ -11,6 +11,14 @@ from pyproject_buildrequires import generate_requires, load_pyproject SETUPTOOLS_VERSION = packaging.version.parse(setuptools.__version__) SETUPTOOLS_60 = SETUPTOOLS_VERSION >= packaging.version.parse('60') +try: + import tox +except ImportError: + TOX_4_22 = False +else: + TOX_VERSION = packaging.version.parse(tox.__version__) + TOX_4_22 = TOX_VERSION >= packaging.version.parse('4.22') + testcases = {} with Path(__file__).parent.joinpath('pyproject_buildrequires_testcases.yaml').open() as f: testcases = yaml.safe_load(f)