From 2ac580592bd1560b49a71e285b6f56f37a133bf1 Mon Sep 17 00:00:00 2001 From: MSVSphere Packaging Team Date: Thu, 14 Nov 2024 21:44:21 +0300 Subject: [PATCH] import pyproject-rpm-macros-1.16.2-1.el10 --- SOURCES/README.md | 39 +- SOURCES/macros.aaa-pyproject-srpm | 2 +- SOURCES/macros.pyproject | 44 +- SOURCES/pyproject_buildrequires.py | 211 ++++- .../pyproject_buildrequires_testcases.yaml | 848 ++++++++++++++---- SOURCES/test_pyproject_buildrequires.py | 25 +- SPECS/pyproject-rpm-macros.spec | 29 +- 7 files changed, 990 insertions(+), 208 deletions(-) diff --git a/SOURCES/README.md b/SOURCES/README.md index f7f5d7b..192c04d 100644 --- a/SOURCES/README.md +++ b/SOURCES/README.md @@ -79,8 +79,21 @@ using the `-R` flag: %generate_buildrequires %pyproject_buildrequires -R -Alternatively, the runtime dependencies can be obtained by building the wheel and reading the metadata from the built wheel. -This can be enabled by using the `-w` flag. +Alternatively, if the project specifies its dependencies in the pyproject.toml +`[project]` table (as defined in [PEP 621](https://www.python.org/dev/peps/pep-0621/)), +the runtime dependencies can be obtained by reading that metadata. + +This can be enabled by using the `-p` flag. +This flag supports reading both the runtime dependencies, and the selected extras +(see the `-x` flag described below). + +Please note that not all build backends which use pyproject.toml support the +`[project]` table scheme. +For example, poetry-core (at least in 1.9.0) defines package metadata in the +custom `[tool.poetry]` table which is not supported by the `%pyproject_buildrequires` macro. + +Finally, the runtime dependencies can be obtained by building the wheel and reading the metadata from the built wheel. +This can be enabled with the `-w` flag and cannot be combined with `-p`. Support for building wheels with `%pyproject_buildrequires -w` is **provisional** and the behavior might change. Please subscribe to Fedora's [python-devel list] if you use the option. @@ -111,6 +124,14 @@ For example, if upstream suggests installing test dependencies with %generate_buildrequires %pyproject_buildrequires -x testing +For projects that specify test requirements using [PEP 735] dependency groups, +these can be added using the `-g` flag. +Multiple groups can be supplied by repeating the flag or as a comma separated list. +For example, if upstream uses a dependency group called `tests`, the test deps would be generated by: + + %generate_buildrequires + %pyproject_buildrequires -g tests + For projects that specify test requirements in their [tox] configuration, these can be added using the `-t` flag (default tox environment) or the `-e` flag followed by the tox environment. @@ -134,16 +155,21 @@ 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. Not all plugins are guaranteed to play well with [tox-current-env], in worst case, patch/sed the requirement out from the tox configuration. -Note that neither `-x` or `-t` can be used with `-R`, +Note that neither `-x` or `-t` can be used with `-R` or `-N`, because runtime dependencies are always required for testing. You can only use those options if the build backend supports the [prepare-metadata-for-build-wheel hook], -or together with `-w`. +or together with `-p` or `-w`. +However, using `-g` with `-R` or `-N` is supported because dependency groups don't need to be used for testing +and can be obtained by reading `pyproject.toml` only. [tox]: https://tox.readthedocs.io/ [tox-current-env]: https://github.com/fedora-python/tox-current-env/ @@ -158,7 +184,7 @@ Dependencies will be loaded from them: For packages not using build system you can use `-N` to entirely skip automatical generation of requirements and install requirements only from manually specified files. `-N` option implies `-R` and cannot be used in combination with other options mentioned above -(`-w`, `-e`, `-t`, `-x`). +(`-w`, `-e`, `-t`, `-x`, `-p`). The `%pyproject_buildrequires` macro also accepts the `-r` flag for backward compatibility; it means "include runtime dependencies" which has been the default since version 0-53. @@ -288,7 +314,7 @@ However, in Fedora packages, always list executables explicitly to avoid uninten `%pyproject_save_files` can automatically mark license files with `%license` macro and language (`*.mo`) files with `%lang` macro and appropriate language code. Only license files declared via [PEP 639] `License-File` field are detected. -[PEP 639] is still a draft and can be changed in the future. +[PEP 639] is still provisional and can be changed in the future. It is possible to use the `-l` flag to declare that a missing license should terminate the build or `-L` (the default) to explicitly disable this check. Packagers are encouraged to use the `-l` flag when the `%license` file is not manually listed in `%files` @@ -507,6 +533,7 @@ so be prepared for problems. [PEP 517]: https://www.python.org/dev/peps/pep-0517/ [PEP 518]: https://www.python.org/dev/peps/pep-0518/ [PEP 639]: https://www.python.org/dev/peps/pep-0639/ +[PEP 735]: https://www.python.org/dev/peps/pep-0735/ [pip's documentation]: https://pip.pypa.io/en/stable/cli/pip_install/#vcs-support diff --git a/SOURCES/macros.aaa-pyproject-srpm b/SOURCES/macros.aaa-pyproject-srpm index 9bfe84e..1b06ac3 100644 --- a/SOURCES/macros.aaa-pyproject-srpm +++ b/SOURCES/macros.aaa-pyproject-srpm @@ -4,7 +4,7 @@ # this macro will cause the package with the real macro to be installed. # When macros.pyproject is installed, it overrides this macro. # Note: This needs to maintain the same set of options as the real macro. -%pyproject_buildrequires(rRxtNwe:C:) echo 'pyproject-rpm-macros' && exit 0 +%pyproject_buildrequires(rRxtNwpe:g:C:) echo 'pyproject-rpm-macros' && exit 0 # Declarative buildsystem, requires RPM 4.20+ to work diff --git a/SOURCES/macros.pyproject b/SOURCES/macros.pyproject index 23ab565..449afe6 100644 --- a/SOURCES/macros.pyproject +++ b/SOURCES/macros.pyproject @@ -25,6 +25,11 @@ %_pyproject_record %{_builddir}/%{_pyproject_files_prefix}-pyproject-record %_pyproject_buildrequires %{_builddir}/%{_pyproject_files_prefix}-pyproject-buildrequires +# Internal macro, takes %%set_build_flags and strips all the exports +# TODO: Make such a list an actual source of %%set_build_flags (in redhat-rpm-config) +# Cannot use %%gsub directly to preserve EL 9 compatibility +%_pyproject_build_flags %{lua:local exports = rpm.expand('%{set_build_flags} ;'); print((exports:gsub('%s*;+%s+export%s+[%u_]+%s*;+%s*', ' ')))} + # Avoid leaking %%{_pyproject_builddir} to pytest collection # https://bugzilla.redhat.com/show_bug.cgi?id=1935212 # The value is read and used by the %%pytest and %%tox macros: @@ -33,7 +38,8 @@ %pyproject_wheel(C:) %{expand:\\\ %_set_pytest_addopts mkdir -p "%{_pyproject_builddir}" -CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS}}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}" TMPDIR="%{_pyproject_builddir}" \\\ +%{_pyproject_build_flags} \\\ +TMPDIR="%{_pyproject_builddir}" \\\ %{__python3} -Bs %{_rpmconfigdir}/redhat/pyproject_wheel.py %{?**} %{_pyproject_wheeldir} } @@ -152,13 +158,17 @@ fi %default_toxenv py%{python3_version_nodots} %toxenv %{default_toxenv} +%_pyproject_tomlidep %["%{python3_pkgversion}" == "3"\ + ? "echo '(python%{python3_pkgversion}dist(tomli) if python%{python3_pkgversion}-devel < 3.11)'"\ + : "%[v"%{python3_pkgversion}" < v"3.11"\ + ? "echo 'python%{python3_pkgversion}dist(tomli)'"\ + : "true # will use tomllib, echo nothing"\ + ]"\ + ] # Note: Keep the options in sync with this macro from macros.aaa-pyproject-srpm -%pyproject_buildrequires(rRxtNwe:C:) %{expand:\\\ +%pyproject_buildrequires(rRxtNwpe:g:C:) %{expand:\\\ %_set_pytest_addopts -# The _auto_set_build_flags feature does not do this in %%generate_buildrequires section, -# but we want to get an environment consistent with %%build: -%{?_auto_set_build_flags:%set_build_flags} # The default flags expect the package note file to exist # see https://bugzilla.redhat.com/show_bug.cgi?id=2097535 %{?_package_note_flags:%_generate_package_note_file} @@ -168,6 +178,7 @@ fi %{-e:%{error:The -R and -e options are mutually exclusive}} %{-t:%{error:The -R and -t options are mutually exclusive}} %{-w:%{error:The -R and -w options are mutually exclusive}} +%{-p:%{error:The -R and -p options are mutually exclusive}} } %{-N: %{-r:%{error:The -N and -r options are mutually exclusive}} @@ -175,25 +186,25 @@ fi %{-e:%{error:The -N and -e options are mutually exclusive}} %{-t:%{error:The -N and -t options are mutually exclusive}} %{-w:%{error:The -N and -w options are mutually exclusive}} +%{-p:%{error:The -N and -p options are mutually exclusive}} %{-C:%{error:The -N and -C options are mutually exclusive}} +%{-g:if [ -f pyproject.toml ]; then + %_pyproject_tomlidep +fi} +} +%{-w: +%{-p:%{error:The -w and -p options are mutually exclusive}} } %{-e:%{expand:%global toxenv %(%{__python3} -s %{_rpmconfigdir}/redhat/pyproject_construct_toxenv.py %{?**})}} echo 'pyproject-rpm-macros' # first stdout line matches the implementation in macros.aaa-pyproject-srpm echo 'python%{python3_pkgversion}-devel' -echo 'python%{python3_pkgversion}dist(pip) >= 19' echo 'python%{python3_pkgversion}dist(packaging)' -%{!-N:if [ -f pyproject.toml ]; then - %["%{python3_pkgversion}" == "3" - ? "echo '(python%{python3_pkgversion}dist(tomli) if python%{python3_pkgversion}-devel < 3.11)'" - : "%[v"%{python3_pkgversion}" < v"3.11" - ? "echo 'python%{python3_pkgversion}dist(tomli)'" - : "true # will use tomllib, echo nothing" - ]" - ] +%{!-N:echo 'python%{python3_pkgversion}dist(pip) >= 19' +if [ -f pyproject.toml ]; then + %_pyproject_tomlidep elif [ -f setup.py ]; then # Note: If the default requirements change, also change them in the script! echo 'python%{python3_pkgversion}dist(setuptools) >= 40.8' - echo 'python%{python3_pkgversion}dist(wheel)' else echo 'ERROR: Neither pyproject.toml nor setup.py found, consider using %%%%pyproject_buildrequires -N if this is not a Python package.' >&2 exit 1 @@ -203,7 +214,8 @@ rm -rfv *.dist-info/ >&2 if [ -f %{__python3} ]; then mkdir -p "%{_pyproject_builddir}" echo -n > %{_pyproject_buildrequires} - CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS}}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}" TMPDIR="%{_pyproject_builddir}" \\\ + %{_pyproject_build_flags} \\\ + TMPDIR="%{_pyproject_builddir}" \\\ RPM_TOXENV="%{toxenv}" HOSTNAME="rpmbuild" %{__python3} -Bs %{_rpmconfigdir}/redhat/pyproject_buildrequires.py %{?!_python_no_extras_requires:--generate-extras} --python3_pkgversion %{python3_pkgversion} --wheeldir %{_pyproject_wheeldir} --output %{_pyproject_buildrequires} %{?**} >&2 cat %{_pyproject_buildrequires} fi diff --git a/SOURCES/pyproject_buildrequires.py b/SOURCES/pyproject_buildrequires.py index 515da95..e0ef7de 100644 --- a/SOURCES/pyproject_buildrequires.py +++ b/SOURCES/pyproject_buildrequires.py @@ -10,6 +10,7 @@ import subprocess import re import tempfile import email.parser +import functools import pathlib import zipfile @@ -34,6 +35,7 @@ def print_err(*args, **kwargs): try: + from packaging.markers import Marker from packaging.requirements import Requirement, InvalidRequirement from packaging.utils import canonicalize_name except ImportError as e: @@ -99,18 +101,23 @@ class Requirements: return True return False - def add(self, requirement_str, *, package_name=None, source=None): + def add(self, requirement, *, package_name=None, source=None, extra=None): """Output a Python-style requirement string as RPM dep""" + + requirement_str = str(requirement) print_err(f'Handling {requirement_str} from {source}') - try: - requirement = Requirement(requirement_str) - except InvalidRequirement: - hint = guess_reason_for_invalid_requirement(requirement_str) - message = f'Requirement {requirement_str!r} from {source} is invalid.' - if hint: - message += f' Hint: {hint}' - raise ValueError(message) + # requirements read initially from the metadata are strings + # further on we work with them as Requirement instances + if not isinstance(requirement, Requirement): + try: + requirement = Requirement(requirement) + except InvalidRequirement: + hint = guess_reason_for_invalid_requirement(requirement) + message = f'Requirement {requirement!r} from {source} is invalid.' + if hint: + message += f' Hint: {hint}' + raise ValueError(message) if requirement.url: print_err( @@ -118,10 +125,17 @@ class Requirements: ) name = canonicalize_name(requirement.name) + + if extra is not None: + extra_str = f'extra == "{extra}"' + if requirement.marker is not None: + extra_str = f'({requirement.marker}) and {extra_str}' + requirement.marker = Marker(extra_str) + if (requirement.marker is not None and not self.evaluate_all_environments(requirement)): print_err(f'Ignoring alien requirement:', requirement_str) - self.ignored_alien_requirements.append(requirement_str) + self.ignored_alien_requirements.append(requirement) return # Handle self-referencing requirements @@ -215,7 +229,8 @@ def toml_load(opened_binary_file): return tomllib.load(opened_binary_file) -def get_backend(requirements): +@functools.cache +def load_pyproject(): try: f = open('pyproject.toml', 'rb') except FileNotFoundError: @@ -223,6 +238,11 @@ def get_backend(requirements): else: with f: pyproject_data = toml_load(f) + return pyproject_data + + +def get_backend(requirements): + pyproject_data = load_pyproject() buildsystem_data = pyproject_data.get('build-system', {}) requirements.extend( @@ -248,15 +268,6 @@ def get_backend(requirements): # with pyproject.toml without a specified build backend. # If the default requirements change, also change them in the macro! requirements.add('setuptools >= 40.8', source='default build backend') - # PEP 517 doesn't mandate depending on wheel when the default backend is used. - # Historically, it used to be assumed as necessary, but later it turned out to be wrong. - # See the removal in pip and build: - # https://github.com/pypa/pip/pull/12449 - # https://github.com/pypa/build/pull/716 - # However, the requirement *will* be generated by setuptools anyway - # as part of get_requires_for_build_wheel(). - # So we might as well keep it to skip one redundant step. - requirements.add('wheel', source='default build backend') requirements.check(source='build backend') @@ -310,7 +321,9 @@ def generate_run_requirements_hook(backend, requirements): raise ValueError( 'The build backend cannot provide build metadata ' '(incl. runtime requirements) before build. ' - 'Use the provisional -w flag to build the wheel and parse the metadata from it, ' + 'If the dependencies are specified in the pyproject.toml [project] ' + 'table, you can use the -p flag to read them.' + 'Alternatively, use the provisional -w flag to build the wheel and parse the metadata from it, ' 'or use the -R flag not to generate runtime dependencies.' ) dir_basename = prepare_metadata('.', config_settings=requirements.config_settings) @@ -368,8 +381,35 @@ def generate_run_requirements_wheel(backend, requirements, wheeldir): raise RuntimeError('Could not find *.dist-info/METADATA in built wheel.') -def generate_run_requirements(backend, requirements, *, build_wheel, wheeldir): - if build_wheel: +def generate_run_requirements_pyproject(requirements): + pyproject_data = load_pyproject() + + if not (project_table := pyproject_data.get('project', {})): + raise ValueError('Could not find the [project] table in pyproject.toml.') + + dynamic_fields = project_table.get('dynamic', []) + if 'dependencies' in dynamic_fields or 'optional-dependencies' in dynamic_fields: + raise ValueError('Could not read the dependencies or optional-dependencies ' + 'from the [project] table in pyproject.toml, as the field is dynamic.') + + dependencies = project_table.get('dependencies', []) + name = project_table.get('name') + requirements.extend(dependencies, + package_name=name, + source=f'pyproject.toml generated metadata: [dependencies] ({name})') + + optional_dependencies = project_table.get('optional-dependencies', {}) + for extra, dependencies in optional_dependencies.items(): + requirements.extend(dependencies, + package_name=name, + source=f'pyproject.toml generated metadata: [optional-dependencies] {extra} ({name})', + extra=extra) + + +def generate_run_requirements(backend, requirements, *, build_wheel, read_pyproject_dependencies, wheeldir): + if read_pyproject_dependencies: + generate_run_requirements_pyproject(requirements) + elif build_wheel: generate_run_requirements_wheel(backend, requirements, wheeldir) else: generate_run_requirements_hook(backend, requirements) @@ -419,6 +459,103 @@ 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 + + def _normalize_name(name: str) -> str: + return re.sub(r"[-_.]+", "-", name).lower() + + def _normalize_group_names(dependency_groups: dict) -> dict: + original_names = defaultdict(list) + normalized_groups = {} + + for group_name, value in dependency_groups.items(): + normed_group_name = _normalize_name(group_name) + original_names[normed_group_name].append(group_name) + normalized_groups[normed_group_name] = value + + errors = [] + for normed_name, names in original_names.items(): + if len(names) > 1: + errors.append(f"{normed_name} ({', '.join(names)})") + if errors: + raise ValueError(f"Duplicate dependency group names: {', '.join(errors)}") + + return normalized_groups + + def _resolve_dependency_group( + dependency_groups: dict, group: str, past_groups: tuple[str, ...] = () + ) -> list[str]: + if group in past_groups: + raise ValueError(f"Cyclic dependency group include: {group} -> {past_groups}") + + if group not in dependency_groups: + raise LookupError(f"Dependency group '{group}' not found") + + raw_group = dependency_groups[group] + if not isinstance(raw_group, list): + raise ValueError(f"Dependency group '{group}' is not a list") + + realized_group = [] + for item in raw_group: + if isinstance(item, str): + realized_group.append(item) + elif isinstance(item, dict): + if tuple(item.keys()) != ("include-group",): + raise ValueError(f"Invalid dependency group item: {item}") + + include_group = _normalize_name(next(iter(item.values()))) + realized_group.extend( + _resolve_dependency_group( + dependency_groups, include_group, past_groups + (group,) + ) + ) + else: + raise ValueError(f"Invalid dependency group item: {item}") + + return realized_group + + def resolve(dependency_groups: dict, group: str) -> list[str]: + if not isinstance(dependency_groups, dict): + raise TypeError("Dependency Groups table is not a dict") + return _resolve_dependency_group(dependency_groups, _normalize_name(group)) + + pyproject_data = load_pyproject() + dependency_groups_raw = pyproject_data.get("dependency-groups", {}) + dependency_groups = _normalize_group_names(dependency_groups_raw) + + for group_names in requested_groups: + for group_name in group_names.split(","): + requirements.extend( + resolve(dependency_groups, group_name), + source=f"Dependency group {group_name}", + ) + + def python3dist(name, op=None, version=None, python3_pkgversion="3"): prefix = f"python{python3_pkgversion}dist" @@ -431,9 +568,10 @@ def python3dist(name, op=None, version=None, python3_pkgversion="3"): def generate_requires( - *, include_runtime=False, build_wheel=False, wheeldir=None, toxenv=None, extras=None, + *, include_runtime=False, build_wheel=False, wheeldir=None, toxenv=None, extras=None, dependency_groups=None, get_installed_version=importlib.metadata.version, # for dep injection generate_extras=False, python3_pkgversion="3", requirement_files=None, use_build_system=True, + read_pyproject_dependencies=False, output, config_settings=None, ): """Generate the BuildRequires for the project in the current directory @@ -449,9 +587,10 @@ def generate_requires( config_settings=config_settings, ) + dependency_groups = dependency_groups or [] try: - if (include_runtime or toxenv) and not use_build_system: - raise ValueError('-N option cannot be used in combination with -r, -e, -t, -x options') + 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') if requirement_files: for req_file in requirement_files: requirements.extend( @@ -465,8 +604,12 @@ def generate_requires( if toxenv: include_runtime = True generate_tox_requirements(toxenv, requirements) + dependency_groups.extend(tox_dependency_groups(toxenv)) + if dependency_groups: + generate_dependency_groups(dependency_groups, requirements) if include_runtime: - generate_run_requirements(backend, requirements, build_wheel=build_wheel, wheeldir=wheeldir) + generate_run_requirements(backend, requirements, build_wheel=build_wheel, + read_pyproject_dependencies=read_pyproject_dependencies, wheeldir=wheeldir) except EndPass: return finally: @@ -493,7 +636,7 @@ def main(argv): help=argparse.SUPPRESS, ) parser.add_argument( - '-p', '--python3_pkgversion', metavar='PYTHON3_PKGVERSION', + '--python3_pkgversion', metavar='PYTHON3_PKGVERSION', default="3", help=argparse.SUPPRESS, ) parser.add_argument( @@ -508,6 +651,11 @@ def main(argv): help='comma separated list of "extras" for runtime requirements ' '(e.g. -x testing,feature-x) (implies --runtime, can be repeated)', ) + parser.add_argument( + '-g', '--dependency-groups', metavar='GROUPS', action='append', + help='comma separated list of dependency groups (PEP 735) for requirements ' + '(e.g. -g tests,docs) (can be repeated)', + ) parser.add_argument( '-t', '--tox', action='store_true', help=('generate test tequirements from tox environment ' @@ -523,6 +671,11 @@ def main(argv): help=('Generate run-time requirements by building the wheel ' '(useful for build backends without the prepare_metadata_for_build_wheel hook)'), ) + parser.add_argument( + '-p', '--read-pyproject-dependencies', action='store_true', default=False, + help=('Generate dependencies from [project] table of pyproject.toml ' + 'instead of calling prepare_metadata_for_build_wheel hook)'), + ) parser.add_argument( '-R', '--no-runtime', action='store_false', dest='runtime', help="Don't generate run-time requirements (implied by -N)", @@ -571,10 +724,12 @@ def main(argv): wheeldir=args.wheeldir, toxenv=args.toxenv, extras=args.extras, + dependency_groups=args.dependency_groups, generate_extras=args.generate_extras, python3_pkgversion=args.python3_pkgversion, requirement_files=args.requirement_files, use_build_system=args.use_build_system, + read_pyproject_dependencies=args.read_pyproject_dependencies, output=args.output, config_settings=parse_config_settings_args(args.config_settings), ) diff --git a/SOURCES/pyproject_buildrequires_testcases.yaml b/SOURCES/pyproject_buildrequires_testcases.yaml index 892e3c3..a374fa0 100644 --- a/SOURCES/pyproject_buildrequires_testcases.yaml +++ b/SOURCES/pyproject_buildrequires_testcases.yaml @@ -23,7 +23,6 @@ Insufficient version of setuptools: setup.py: | expected: | python3dist(setuptools) >= 40.8 - python3dist(wheel) result: 0 No pyproject.toml, empty setup.py: @@ -32,10 +31,12 @@ No pyproject.toml, empty setup.py: wheel: 1 include_runtime: false setup.py: | - expected: | - python3dist(setuptools) >= 40.8 - python3dist(wheel) - python3dist(wheel) + expected: + - | # setuptools 70+ + python3dist(setuptools) >= 40.8 + - | # setuptools < 70 + python3dist(setuptools) >= 40.8 + python3dist(wheel) result: 0 Default build system, empty setup.py: @@ -47,10 +48,12 @@ Default build system, empty setup.py: pyproject.toml: | # empty setup.py: | - expected: | - python3dist(setuptools) >= 40.8 - python3dist(wheel) - python3dist(wheel) + expected: + - | # setuptools 70+ + python3dist(setuptools) >= 40.8 + - | # setuptools < 70 + python3dist(setuptools) >= 40.8 + python3dist(wheel) result: 0 pyproject.toml with build-backend and setup.py: @@ -212,13 +215,18 @@ Default build system, build dependencies in setup.py: setup_requires=['foo', 'bar!=2', 'baz~=1.1.1'], install_requires=['inst'], ) - expected: | - python3dist(setuptools) >= 40.8 - python3dist(wheel) - python3dist(wheel) - python3dist(foo) - (python3dist(bar) < 2 or python3dist(bar) > 2) - (python3dist(baz) >= 1.1.1 with python3dist(baz) < 1.2) + expected: + - | # setuptools 70+ + python3dist(setuptools) >= 40.8 + python3dist(foo) + (python3dist(bar) < 2 or python3dist(bar) > 2) + (python3dist(baz) >= 1.1.1 with python3dist(baz) < 1.2) + - | # setuptools < 70 + python3dist(setuptools) >= 40.8 + python3dist(wheel) + python3dist(foo) + (python3dist(bar) < 2 or python3dist(bar) > 2) + (python3dist(baz) >= 1.1.1 with python3dist(baz) < 1.2) result: 0 Default build system, run dependencies in setup.py: @@ -234,13 +242,18 @@ Default build system, run dependencies in setup.py: setup_requires=['pyyaml'], # nb. setuptools will try to install this install_requires=['inst > 1', 'inst2 < 3'], ) - expected: | - python3dist(setuptools) >= 40.8 - python3dist(wheel) - python3dist(wheel) - python3dist(pyyaml) - python3dist(inst) > 1.0 - python3dist(inst2) < 3~~ + expected: + - | # setuptools 70+ + python3dist(setuptools) >= 40.8 + python3dist(pyyaml) + python3dist(inst) > 1.0 + python3dist(inst2) < 3~~ + - | # setuptools < 70 + python3dist(setuptools) >= 40.8 + python3dist(wheel) + python3dist(pyyaml) + python3dist(inst) > 1.0 + python3dist(inst2) < 3~~ result: 0 Run dependencies with extras (not selected): @@ -287,18 +300,28 @@ Run dependencies with extras (not selected): if __name__ == "__main__": main() - expected: | - python3dist(setuptools) >= 40.8 - python3dist(wheel) - python3dist(wheel) - python3dist(setuptools) >= 40 - python3dist(py) >= 1.5 - python3dist(six) >= 1.10 - python3dist(setuptools) - python3dist(attrs) >= 17.4 - python3dist(atomicwrites) >= 1 - python3dist(pluggy) >= 0.11 - python3dist(more-itertools) >= 4 + expected: + - | # setuptools 70+ + python3dist(setuptools) >= 40.8 + python3dist(setuptools) >= 40 + python3dist(py) >= 1.5 + python3dist(six) >= 1.10 + python3dist(setuptools) + python3dist(attrs) >= 17.4 + python3dist(atomicwrites) >= 1 + python3dist(pluggy) >= 0.11 + python3dist(more-itertools) >= 4 + - | # setuptools < 70 + python3dist(setuptools) >= 40.8 + python3dist(wheel) + python3dist(setuptools) >= 40 + python3dist(py) >= 1.5 + python3dist(six) >= 1.10 + python3dist(setuptools) + python3dist(attrs) >= 17.4 + python3dist(atomicwrites) >= 1 + python3dist(pluggy) >= 0.11 + python3dist(more-itertools) >= 4 result: 0 Run dependencies with extras (selected): @@ -310,22 +333,36 @@ Run dependencies with extras (selected): extras: - testing setup.py: *pytest_setup_py - expected: | - python3dist(setuptools) >= 40.8 - python3dist(wheel) - python3dist(wheel) - python3dist(setuptools) >= 40 - python3dist(py) >= 1.5 - python3dist(six) >= 1.10 - python3dist(setuptools) - python3dist(attrs) >= 17.4 - python3dist(atomicwrites) >= 1 - python3dist(pluggy) >= 0.11 - python3dist(more-itertools) >= 4 - python3dist(argcomplete) - python3dist(hypothesis) >= 3.56 - python3dist(nose) - python3dist(requests) + expected: + - | # setuptools 70+ + python3dist(setuptools) >= 40.8 + python3dist(setuptools) >= 40 + python3dist(py) >= 1.5 + python3dist(six) >= 1.10 + python3dist(setuptools) + python3dist(attrs) >= 17.4 + python3dist(atomicwrites) >= 1 + python3dist(pluggy) >= 0.11 + python3dist(more-itertools) >= 4 + python3dist(argcomplete) + python3dist(hypothesis) >= 3.56 + python3dist(nose) + python3dist(requests) + - | # setuptools < 70 + python3dist(setuptools) >= 40.8 + python3dist(wheel) + python3dist(setuptools) >= 40 + python3dist(py) >= 1.5 + python3dist(six) >= 1.10 + python3dist(setuptools) + python3dist(attrs) >= 17.4 + python3dist(atomicwrites) >= 1 + python3dist(pluggy) >= 0.11 + python3dist(more-itertools) >= 4 + python3dist(argcomplete) + python3dist(hypothesis) >= 3.56 + python3dist(nose) + python3dist(requests) result: 0 Run dependencies with multiple extras: @@ -348,16 +385,24 @@ Run dependencies with multiple extras: 'cool-feature': ['dep4[FOO,BAR]'], }, ) - expected: | - python3dist(setuptools) >= 40.8 - python3dist(wheel) - python3dist(wheel) - python3dist(dep4) - python3dist(dep4[bar]) - python3dist(dep4[foo]) - python3dist(dep3) - python3dist(dep2) - python3dist(dep1) + expected: + - | # setuptools 70+ + python3dist(setuptools) >= 40.8 + python3dist(dep4) + python3dist(dep4[bar]) + python3dist(dep4[foo]) + python3dist(dep3) + python3dist(dep2) + python3dist(dep1) + - | # setuptools < 70 + python3dist(setuptools) >= 40.8 + python3dist(wheel) + python3dist(dep4) + python3dist(dep4[bar]) + python3dist(dep4[foo]) + python3dist(dep3) + python3dist(dep2) + python3dist(dep1) result: 0 Run dependencies with extras and build wheel option: @@ -371,23 +416,38 @@ Run dependencies with extras and build wheel option: extras: - testing setup.py: *pytest_setup_py - expected: | - python3dist(setuptools) >= 40.8 - python3dist(wheel) - python3dist(wheel) - python3dist(setuptools) >= 40 - python3dist(pip) >= 19 - python3dist(py) >= 1.5 - python3dist(six) >= 1.10 - python3dist(setuptools) - python3dist(attrs) >= 17.4 - python3dist(atomicwrites) >= 1 - python3dist(pluggy) >= 0.11 - python3dist(more-itertools) >= 4 - python3dist(argcomplete) - python3dist(hypothesis) >= 3.56 - python3dist(nose) - python3dist(requests) + expected: + - | # setuptools 70+ + python3dist(setuptools) >= 40.8 + python3dist(setuptools) >= 40 + python3dist(pip) >= 19 + python3dist(py) >= 1.5 + python3dist(six) >= 1.10 + python3dist(setuptools) + python3dist(attrs) >= 17.4 + python3dist(atomicwrites) >= 1 + python3dist(pluggy) >= 0.11 + python3dist(more-itertools) >= 4 + python3dist(argcomplete) + python3dist(hypothesis) >= 3.56 + python3dist(nose) + python3dist(requests) + - | # setuptools < 70 + python3dist(setuptools) >= 40.8 + python3dist(wheel) + python3dist(setuptools) >= 40 + python3dist(pip) >= 19 + python3dist(py) >= 1.5 + python3dist(six) >= 1.10 + python3dist(setuptools) + python3dist(attrs) >= 17.4 + python3dist(atomicwrites) >= 1 + python3dist(pluggy) >= 0.11 + python3dist(more-itertools) >= 4 + python3dist(argcomplete) + python3dist(hypothesis) >= 3.56 + python3dist(nose) + python3dist(requests) result: 0 stderr_contains: "Reading metadata from {wheeldir}/pytest-6.6.6-py3-none-any.whl" @@ -416,17 +476,22 @@ tox dependencies: commands = true expected: - - | # tox 3 + - | # tox 3 with setuptools < 70 python3dist(setuptools) >= 40.8 python3dist(wheel) - python3dist(wheel) python3dist(tox-current-env) >= 0.0.6 python3dist(toxdep1) python3dist(toxdep2) python3dist(inst) - - | # tox 4 + - | # tox 4 with setuptools 70+ + python3dist(setuptools) >= 40.8 + python3dist(tox-current-env) >= 0.0.6 + python3dist(tox) + python3dist(toxdep1) + python3dist(toxdep2) + python3dist(inst) + - | # tox 4 with setuptools < 70 python3dist(setuptools) >= 40.8 - python3dist(wheel) python3dist(wheel) python3dist(tox-current-env) >= 0.0.6 python3dist(tox) @@ -468,10 +533,9 @@ tox extras: commands = true expected: - - | # tox 3 + - | # tox 3 with setuptools < 70 python3dist(setuptools) >= 40.8 python3dist(wheel) - python3dist(wheel) python3dist(tox-current-env) >= 0.0.6 python3dist(toxdep) python3dist(inst) @@ -482,9 +546,21 @@ tox extras: python3dist(dep23) python3dist(extra-dep) python3dist(extra-dep[extra_dep]) - - | # tox 4 + - | # tox 4 with setuptools 70+ + python3dist(setuptools) >= 40.8 + python3dist(tox-current-env) >= 0.0.6 + python3dist(tox) + python3dist(toxdep) + python3dist(inst) + python3dist(dep11) > 11.0 + python3dist(dep12) + python3dist(dep21) + python3dist(dep22) + python3dist(dep23) + python3dist(extra-dep) + python3dist(extra-dep[extra_dep]) + - | # tox 4 with setuptools < 70 python3dist(setuptools) >= 40.8 - python3dist(wheel) python3dist(wheel) python3dist(tox-current-env) >= 0.0.6 python3dist(tox) @@ -525,17 +601,22 @@ tox provision unsatisfied: toxdep1 toxdep2 expected: - - | # tox 3 + - | # tox 3 with setuptools < 70 python3dist(setuptools) >= 40.8 python3dist(wheel) - python3dist(wheel) python3dist(tox-current-env) >= 0.0.6 python3dist(tox) >= 3.999 python3dist(setuptools) > 40.0 python3dist(wheel) > 2.0 - - | # tox 4 + - | # tox 4 with setuptools 70+ + python3dist(setuptools) >= 40.8 + python3dist(tox-current-env) >= 0.0.6 + python3dist(tox) >= 3.999 + python3dist(setuptools) > 40.0 + python3dist(wheel) > 2.0 + python3dist(tox) >= 3.999 + - | # tox 4 with setuptools < 70 python3dist(setuptools) >= 40.8 - python3dist(wheel) python3dist(wheel) python3dist(tox-current-env) >= 0.0.6 python3dist(tox) >= 3.999 @@ -569,19 +650,25 @@ tox provision satisfied: toxdep1 toxdep2 expected: - - | # tox 3 + - | # tox 3 with setuptools < 70 python3dist(setuptools) >= 40.8 python3dist(wheel) - python3dist(wheel) python3dist(tox-current-env) >= 0.0.6 python3dist(tox) >= 3.5 python3dist(setuptools) > 40.0 python3dist(toxdep1) python3dist(toxdep2) python3dist(inst) - - | # tox 4 + - | # tox 4 with setuptools 70+ + python3dist(setuptools) >= 40.8 + python3dist(tox-current-env) >= 0.0.6 + python3dist(setuptools) > 40.0 + python3dist(tox) >= 3.5 + python3dist(toxdep1) + python3dist(toxdep2) + python3dist(inst) + - | # tox 4 with setuptools < 70 python3dist(setuptools) >= 40.8 - python3dist(wheel) python3dist(wheel) python3dist(tox-current-env) >= 0.0.6 python3dist(setuptools) > 40.0 @@ -611,16 +698,20 @@ tox provision no minversion: setuptools > 40 wheel > 2 expected: - - | # tox 3 + - | # tox 3 with setuptools < 70 python3dist(setuptools) >= 40.8 python3dist(wheel) - python3dist(wheel) python3dist(tox-current-env) >= 0.0.6 python3dist(setuptools) > 40.0 python3dist(wheel) > 2.0 - - | # tox 4 + - | # tox 4 with setuptools 70+ + python3dist(setuptools) >= 40.8 + python3dist(tox-current-env) >= 0.0.6 + python3dist(setuptools) > 40.0 + python3dist(wheel) > 2.0 + python3dist(tox) + - | # tox 4 with setuptools < 70 python3dist(setuptools) >= 40.8 - python3dist(wheel) python3dist(wheel) python3dist(tox-current-env) >= 0.0.6 python3dist(setuptools) > 40.0 @@ -677,15 +768,22 @@ Default build system, met deps in requirements file: SQLAlchemy>=1.0.10,<1.1.0 # Zebra protocol service requirement_files: - requirements.txt - expected: | - ((python3dist(lxml) < 3.7 or python3dist(lxml) > 3.7) with python3dist(lxml) >= 2.3) - python3dist(ncclient) - (python3dist(cryptography) < 1.5.2 or python3dist(cryptography) > 1.5.2) - python3dist(paramiko) - (python3dist(sqlalchemy) < 1.1~~ with python3dist(sqlalchemy) >= 1.0.10) - python3dist(setuptools) >= 40.8 - python3dist(wheel) - python3dist(wheel) + expected: + - | # setuptools 70+ + ((python3dist(lxml) < 3.7 or python3dist(lxml) > 3.7) with python3dist(lxml) >= 2.3) + python3dist(ncclient) + (python3dist(cryptography) < 1.5.2 or python3dist(cryptography) > 1.5.2) + python3dist(paramiko) + (python3dist(sqlalchemy) < 1.1~~ with python3dist(sqlalchemy) >= 1.0.10) + python3dist(setuptools) >= 40.8 + - | # setuptools < 70 + ((python3dist(lxml) < 3.7 or python3dist(lxml) > 3.7) with python3dist(lxml) >= 2.3) + python3dist(ncclient) + (python3dist(cryptography) < 1.5.2 or python3dist(cryptography) > 1.5.2) + python3dist(paramiko) + (python3dist(sqlalchemy) < 1.1~~ with python3dist(sqlalchemy) >= 1.0.10) + python3dist(setuptools) >= 40.8 + python3dist(wheel) result: 0 With pyproject.toml, requirements file and with -N option: @@ -850,11 +948,16 @@ Pre-releases are accepted: "cffi", ] build-backend = "setuptools.build_meta" - expected: | - python3dist(setuptools) - python3dist(wheel) - python3dist(cffi) - python3dist(wheel) + expected: + - | # setuptools 70+ + python3dist(setuptools) + python3dist(wheel) + python3dist(cffi) + - | # setuptools < 70 + python3dist(setuptools) + python3dist(wheel) + python3dist(cffi) + python3dist(wheel) stderr_contains: "Requirement satisfied: cffi" result: 0 @@ -869,10 +972,12 @@ Stdout from wrapped subprocess does not appear in output: os.system('echo LEAK?') from setuptools import setup setup(name='test', version='0.1') - expected: | - python3dist(setuptools) >= 40.8 - python3dist(wheel) - python3dist(wheel) + expected: + - | # setuptools 70+ + python3dist(setuptools) >= 40.8 + - | # setuptools < 70 + python3dist(setuptools) >= 40.8 + python3dist(wheel) result: 0 pyproject.toml with runtime dependencies: @@ -892,10 +997,14 @@ pyproject.toml with runtime dependencies: "foo", 'importlib-metadata; python_version<"3.8"', ] - expected: | - python3dist(setuptools) - python3dist(wheel) - python3dist(foo) + expected: + - | # setuptools 70+ + python3dist(setuptools) + python3dist(foo) + - | # setuptools < 70 + python3dist(setuptools) + python3dist(wheel) + python3dist(foo) result: 0 pyproject.toml with runtime dependencies and partially selected extras: @@ -906,7 +1015,7 @@ pyproject.toml with runtime dependencies and partially selected extras: tomli: 1 extras: - tests - pyproject.toml: | + pyproject.toml: &pyproject_with_extras | [build-system] requires = ["setuptools"] build-backend = "setuptools.build_meta" @@ -920,12 +1029,18 @@ pyproject.toml with runtime dependencies and partially selected extras: [project.optional-dependencies] tests = ["pytest>=5", "pytest-mock"] docs = ["sphinx", "python-docs-theme"] - expected: | - python3dist(setuptools) - python3dist(wheel) - python3dist(foo) - python3dist(pytest) >= 5 - python3dist(pytest-mock) + expected: + - | # setuptools 70+ + python3dist(setuptools) + python3dist(foo) + python3dist(pytest) >= 5 + python3dist(pytest-mock) + - | # setuptools < 70 + python3dist(setuptools) + python3dist(wheel) + python3dist(foo) + python3dist(pytest) >= 5 + python3dist(pytest-mock) result: 0 Self-referencing extras (sooner): @@ -951,14 +1066,22 @@ Self-referencing extras (sooner): tests = pytest>=5; pytest-mock docs = sphinx; python-docs-theme dev = my_package[docs,tests] - expected: | - python3dist(setuptools) - python3dist(wheel) - python3dist(foo) - python3dist(sphinx) - python3dist(python-docs-theme) - python3dist(pytest) >= 5 - python3dist(pytest-mock) + expected: + - | # setuptools 70+ + python3dist(setuptools) + python3dist(foo) + python3dist(sphinx) + python3dist(python-docs-theme) + python3dist(pytest) >= 5 + python3dist(pytest-mock) + - | # setuptools < 70 + python3dist(setuptools) + python3dist(wheel) + python3dist(foo) + python3dist(sphinx) + python3dist(python-docs-theme) + python3dist(pytest) >= 5 + python3dist(pytest-mock) result: 0 Self-referencing extras (later): @@ -984,14 +1107,22 @@ Self-referencing extras (later): tests = pytest>=5; pytest-mock docs = sphinx; python-docs-theme xdev = my_package[docs,tests] - expected: | - python3dist(setuptools) - python3dist(wheel) - python3dist(foo) - python3dist(sphinx) - python3dist(python-docs-theme) - python3dist(pytest) >= 5 - python3dist(pytest-mock) + expected: + - | # setuptools 70+ + python3dist(setuptools) + python3dist(foo) + python3dist(sphinx) + python3dist(python-docs-theme) + python3dist(pytest) >= 5 + python3dist(pytest-mock) + - | # setuptools < 70 + python3dist(setuptools) + python3dist(wheel) + python3dist(foo) + python3dist(sphinx) + python3dist(python-docs-theme) + python3dist(pytest) >= 5 + python3dist(pytest-mock) result: 0 Self-referencing extras (maze): @@ -1016,14 +1147,22 @@ Self-referencing extras (maze): forward = my_package[backward]; forwarddep backward = my_package[left,right]; backwarddep never = my_package[forward]; neverdep - expected: | - python3dist(setuptools) - python3dist(wheel) - python3dist(backwarddep) - python3dist(forwarddep) - python3dist(leftdep) - python3dist(rightdep) - python3dist(startdep) + expected: + - | # setuptools 70+ + python3dist(setuptools) + python3dist(backwarddep) + python3dist(forwarddep) + python3dist(leftdep) + python3dist(rightdep) + python3dist(startdep) + - | # setuptools < 70 + python3dist(setuptools) + python3dist(wheel) + python3dist(backwarddep) + python3dist(forwarddep) + python3dist(leftdep) + python3dist(rightdep) + python3dist(startdep) result: 0 config_settings_control: @@ -1062,3 +1201,410 @@ config_settings: expected: | python3dist(test-config-setting) result: 0 + +pyproject.toml with runtime dependencies read from it: + skipif: not SETUPTOOLS_60 + read_pyproject_dependencies: true + installed: + setuptools: 50 + wheel: 1 + tomli: 1 + pyproject.toml: | + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + [project] + name = "my_package" + version = "0.1" + dependencies = [ + "foo", + 'importlib-metadata; python_version<"3.8"', + ] + expected: + - | # setuptools 70+ + python3dist(setuptools) + python3dist(foo) + - | # setuptools < 70 + python3dist(setuptools) + python3dist(wheel) + python3dist(foo) + result: 0 + +pyproject.toml with extras - only runtime dependencies read from it: + skipif: not SETUPTOOLS_60 + read_pyproject_dependencies: true + installed: + setuptools: 50 + wheel: 1 + tomli: 1 + pyproject.toml: *pyproject_with_extras + expected: + - | # setuptools 70+ + python3dist(setuptools) + python3dist(foo) + - | # setuptools < 70 + python3dist(setuptools) + python3dist(wheel) + python3dist(foo) + result: 0 + +pyproject.toml with runtime dependencies and partially selected extras read from it: + skipif: not SETUPTOOLS_60 + read_pyproject_dependencies: true + installed: + setuptools: 50 + wheel: 1 + tomli: 1 + extras: + - tests + pyproject.toml: *pyproject_with_extras + expected: + - | # setuptools 70+ + python3dist(setuptools) + python3dist(foo) + python3dist(pytest) >= 5 + python3dist(pytest-mock) + - | # setuptools < 70 + python3dist(setuptools) + python3dist(wheel) + python3dist(foo) + python3dist(pytest) >= 5 + python3dist(pytest-mock) + result: 0 + +pyproject.toml with runtime dependencies and all extras read from it: + skipif: not SETUPTOOLS_60 + read_pyproject_dependencies: true + installed: + setuptools: 50 + wheel: 1 + tomli: 1 + extras: + - tests + - docs + pyproject.toml: *pyproject_with_extras + expected: + - | # setuptools 70+ + python3dist(setuptools) + python3dist(foo) + python3dist(pytest) >= 5 + python3dist(pytest-mock) + python3dist(sphinx) + python3dist(python-docs-theme) + - | # setuptools < 70 + python3dist(setuptools) + python3dist(wheel) + python3dist(foo) + python3dist(pytest) >= 5 + python3dist(pytest-mock) + python3dist(sphinx) + python3dist(python-docs-theme) + result: 0 + +pyproject.toml without dependencies: + skipif: not SETUPTOOLS_60 + read_pyproject_dependencies: true + installed: + setuptools: 50 + wheel: 1 + tomli: 1 + pyproject.toml: | + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + [project] + name = "my_package" + version = "0.1" + expected: + - | # setuptools 70+ + python3dist(setuptools) + - | # setuptools < 70 + python3dist(setuptools) + python3dist(wheel) + result: 0 + +pyproject.toml without project table: + skipif: not SETUPTOOLS_60 + read_pyproject_dependencies: true + installed: + setuptools: 50 + wheel: 1 + pyproject.toml: | + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + except: ValueError + +no pyproject.toml: + read_pyproject_dependencies: true + installed: + setuptools: 50 + wheel: 1 + except: FileNotFoundError + +pyproject.toml with dynamic dependencies: + skipif: not SETUPTOOLS_60 + read_pyproject_dependencies: true + installed: + setuptools: 50 + wheel: 1 + tomli: 1 + pyproject.toml: | + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + [project] + name = "my_package" + version = "0.1" + dynamic = ["dependencies"] + [tool.setuptools.dynamic] + dependencies = { file = ["deps.txt"] } + deps.txt: | + foo < 7.0 + sphinx + except: ValueError + +pyproject.toml with dynamic optional dependencies: + skipif: not SETUPTOOLS_60 + read_pyproject_dependencies: true + installed: + setuptools: 50 + wheel: 1 + tomli: 1 + extras: + - docs + pyproject.toml: | + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + [project] + name = "my_package" + version = "0.1" + dynamic = ["optional-dependencies"] + [tool.setuptools.dynamic.optional-dependencies.docs] + file = ["deps.txt"] + deps.txt: | + sphinx~=7.0.1 + except: ValueError + +pyproject.toml with dynamic table and no deps: + skipif: not SETUPTOOLS_60 + read_pyproject_dependencies: true + installed: + setuptools: 50 + wheel: 1 + tomli: 1 + pyproject.toml: | + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + [project] + name = "my_package" + version = "0.1" + dynamic = ["readme"] + [tool.setuptools.dynamic] + readme = { file = ["readme.txt"] } + readme.txt: | + nothing interesting here + expected: + - | # setuptools 70+ + python3dist(setuptools) + - | # setuptools < 70 + python3dist(setuptools) + python3dist(wheel) + result: 0 + +pyproject.toml with self-referencing extras: + skipif: not SETUPTOOLS_60 + read_pyproject_dependencies: true + installed: + setuptools: 50 + wheel: 1 + tomli: 1 + extras: + - test + pyproject.toml: | + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + [project] + name = "contourpy" + version = "0.1" + dependencies = ["numpy >= 1.23"] + [project.optional-dependencies] + bokeh = ["bokeh", "selenium"] + test = ["contourpy[test-no-images]", "matplotlib", "Pillow"] + test-no-images = ["pytest", "pytest-rerunfailures", "wurlitzer"] + expected: + - | # setuptools 70+ + python3dist(setuptools) + python3dist(numpy) >= 1.23 + python3dist(matplotlib) + python3dist(pillow) + python3dist(pytest) + python3dist(pytest-rerunfailures) + python3dist(wurlitzer) + - | # setuptools < 70 + python3dist(setuptools) + python3dist(wheel) + python3dist(numpy) >= 1.23 + python3dist(matplotlib) + python3dist(pillow) + python3dist(pytest) + python3dist(pytest-rerunfailures) + python3dist(wurlitzer) + result: 0 + +pyproject.toml with dependency-groups not requested: + use_build_system: false + pyproject.toml: | + [dependency-groups] + tests = ["pytest>=5", "pytest-mock"] + docs = ["sphinx", "python-docs-theme"] + expected: "\n" + result: 0 + +pyproject.toml with dependency-groups and build system: + skipif: not SETUPTOOLS_60 + use_build_system: true + installed: + setuptools: 50 + wheel: 1 + tomli: 1 + dependency_groups: + - tests + 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"] + expected: + - | # setuptools 70+ + python3dist(setuptools) + python3dist(pytest) >= 5 + python3dist(pytest-mock) + - | # setuptools < 70 + python3dist(setuptools) + python3dist(wheel) + python3dist(pytest) >= 5 + python3dist(pytest-mock) + result: 0 + +pyproject.toml with dependency-groups one requested: + use_build_system: false + dependency_groups: + - tests + pyproject.toml: | + [dependency-groups] + tests = ["pytest>=5", "pytest-mock"] + docs = ["sphinx", "python-docs-theme"] + expected: | + python3dist(pytest) >= 5 + python3dist(pytest-mock) + result: 0 + +pyproject.toml with dependency-groups two requested: + use_build_system: false + dependency_groups: + - tests + - docs + pyproject.toml: | + [dependency-groups] + tests = ["pytest>=5", "pytest-mock"] + docs = ["sphinx", "python-docs-theme"] + expected: | + python3dist(pytest) >= 5 + python3dist(pytest-mock) + python3dist(sphinx) + python3dist(python-docs-theme) + result: 0 + +pyproject.toml with dependency-groups two requested via comma: + use_build_system: false + dependency_groups: + - tests,docs + pyproject.toml: | + [dependency-groups] + tests = ["pytest>=5", "pytest-mock"] + docs = ["sphinx", "python-docs-theme"] + expected: | + python3dist(pytest) >= 5 + python3dist(pytest-mock) + python3dist(sphinx) + python3dist(python-docs-theme) + result: 0 + +pyproject.toml with include-group: + use_build_system: false + dependency_groups: + - tests_docs + pyproject.toml: | + [dependency-groups] + tests = ["pytest>=5", "pytest-mock"] + docs = ["sphinx", "python-docs-theme"] + typing = ["mypy"] + tests-docs = [{include-group = "tests"}, {include-group = "docs"}, "pytest-sphinx"] + expected: | + python3dist(pytest) >= 5 + python3dist(pytest-mock) + python3dist(sphinx) + python3dist(python-docs-theme) + python3dist(pytest-sphinx) + result: 0 + +pyproject.toml with dependency-groups nonexisting requested: + use_build_system: false + dependency_groups: + - typing + pyproject.toml: | + [dependency-groups] + 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: + - | # setuptools 70+ + python3dist(setuptools) + python3dist(tox-current-env) >= 0.0.6 + python3dist(tox) >= 4.22 + python3dist(tox) + python3dist(pytest) >= 5 + python3dist(pytest-mock) + - | # setuptools < 70 + 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/SOURCES/test_pyproject_buildrequires.py b/SOURCES/test_pyproject_buildrequires.py index 0fa07db..815c916 100644 --- a/SOURCES/test_pyproject_buildrequires.py +++ b/SOURCES/test_pyproject_buildrequires.py @@ -6,16 +6,36 @@ import pytest import setuptools import yaml -from pyproject_buildrequires import generate_requires +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) +@pytest.fixture(autouse=True) +def clear_pyproject_data(): + """ + Clear pyproject data before each test. + In reality we build one RPM package at a time, so we can keep the once-loaded + pyproject.toml contents. + When testing, the cached data would leak the once-loaded data to all the + following test cases. + """ + load_pyproject.cache_clear() + + @pytest.mark.parametrize('case_name', testcases) def test_data(case_name, capfd, tmp_path, monkeypatch): case = testcases[case_name] @@ -51,6 +71,7 @@ def test_data(case_name, capfd, tmp_path, monkeypatch): requirement_files = case.get('requirement_files', []) requirement_files = [open(f) for f in requirement_files] use_build_system = case.get('use_build_system', True) + read_pyproject_dependencies = case.get('read_pyproject_dependencies', False) try: generate_requires( get_installed_version=get_installed_version, @@ -58,10 +79,12 @@ def test_data(case_name, capfd, tmp_path, monkeypatch): build_wheel=case.get('build_wheel', False), wheeldir=str(wheeldir), extras=case.get('extras', []), + dependency_groups=case.get('dependency_groups', []), toxenv=case.get('toxenv', None), generate_extras=case.get('generate_extras', False), requirement_files=requirement_files, use_build_system=use_build_system, + read_pyproject_dependencies=read_pyproject_dependencies, output=output, config_settings=case.get('config_settings'), ) diff --git a/SPECS/pyproject-rpm-macros.spec b/SPECS/pyproject-rpm-macros.spec index d923285..de6ef3c 100644 --- a/SPECS/pyproject-rpm-macros.spec +++ b/SPECS/pyproject-rpm-macros.spec @@ -14,8 +14,8 @@ License: MIT # Increment Y and reset Z when new macros or features are added # Increment Z when this is a bugfix or a cosmetic change # Dropping support for EOL Fedoras is *not* considered a breaking change -Version: 1.14.0 -Release: 2%{?dist} +Version: 1.16.2 +Release: 1%{?dist} # Macro files Source001: macros.pyproject @@ -173,9 +173,28 @@ export HOSTNAME="rpmbuild" # to speedup tox in network-less mock, see rhbz#1856 %changelog -* Tue Oct 29 2024 Troy Dawson - 1.14.0-2 -- Bump release for October 2024 mass rebuild: - Resolves: RHEL-64018 +* Wed Nov 13 2024 Miro Hrončok - 1.16.2-1 +- Fix one remaining test for setuptools 70+ + +* Thu Nov 07 2024 Miro Hrončok - 1.16.1-1 +- Support for setuptools 70+ +- wheel is no longer generated as a dependency of the default build system + +* 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 +- Fix handling of self-referencing extras when reading pyproject.toml + +* Tue Sep 17 2024 Python Maint - 1.15.0-1 +- Add a possibility to read runtime requirements from pyproject.toml [project] table +- Fixes: rhbz#2261939 +- Don't generate a dependency on pip when %%pyproject_buildrequires -N is used +- Fixes: rhbz#2294510 +- Even when %%_auto_set_build_flags is disabled, set all compiler flags when building wheels +- Fixes: rhbz#2293616 * Tue Jul 23 2024 Miro Hrončok - 1.14.0-1 - Add a provisional RPM Declarative Buildsystem (RPM 4.20+)