#297 Initial stripped down macros for epel8
Closed 2 years ago by churchyard. Opened 2 years ago by gotmax23.
rpms/ gotmax23/pyproject-rpm-macros epel8  into  rawhide

file modified
+9 -5
@@ -11,10 +11,14 @@ 

  

  from pathlib import PosixPath

  

- from pyproject_buildrequires import print_err

+ # from pyproject_buildrequires import print_err

# from pyproject_buildrequires import print_err

Suggestion: Don't comment out code, delete it.

  from pyproject_save_files import prepend_mandirs, MANPAGE_EXTENSIONS

  

- 

+ # nb: we don't use functools.partial to be able to use pytest's capsys

nb: we don't use functools.partial to be able to use pytest's capsys

I don't think this is relevant in compare_mandata.py

+ # see https://github.com/pytest-dev/pytest/issues/8900

+ def print_err(*args, **kwargs):

+     kwargs.setdefault('file', sys.stderr)

+     print(*args, **kwargs)

  

  def read_brp_compress(filename):

  
@@ -27,9 +31,9 @@ 

  

      # Get rid of ${PREFIX} when extracting the manpage directories

      mandirs = [

-         entry.replace('.${PREFIX}', '/PREFIX')

+         entry

          for entry in contents.split()

-         if entry.startswith('.${PREFIX}')

+         if entry.startswith("./usr")

      ]

  

      return manpage_exts, sorted(mandirs)
@@ -41,7 +45,7 @@ 

      stored in pyproject_save_files.py

      '''

  

-     pyp_save_files_mandirs = sorted(prepend_mandirs(prefix='/PREFIX'))

+     pyp_save_files_mandirs = sorted(prepend_mandirs(prefix='./usr'))

      if brp_compress_mandirs == pyp_save_files_mandirs:

          return True

      else:

file modified
-65
@@ -118,68 +118,3 @@ 

  fi

  %py3_check_import -f "%{_pyproject_modules}" %{?**}

  }

- 

- 

- %default_toxenv py%{python3_version_nodots}

- %toxenv %{default_toxenv}

- 

- 

- %pyproject_buildrequires(rRxtNwe:) %{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}

- %{-R:

- %{-r:%{error:The -R and -r options are mutually exclusive}}

- %{-w:%{error:The -R and -w options are mutually exclusive}}

- }

- %{-N:

- %{-r:%{error:The -N and -r options are mutually exclusive}}

- %{-x:%{error:The -N and -x options are mutually exclusive}}

- %{-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}}

- }

- %{-e:%{expand:%global toxenv %(%{__python3} -s %{_rpmconfigdir}/redhat/pyproject_construct_toxenv.py %{?**})}}

- echo 'pyproject-rpm-macros'  # we already have this installed, but this way, it's repoqueryable

- 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(toml) if python%{python3_pkgversion}-devel < 3.11)'"

-     : "%[v"%{python3_pkgversion}" < v"3.11"

-        ? "echo 'python%{python3_pkgversion}dist(toml)'"

-        : "true # will use tomllib, echo nothing"

-     ]"

-   ]

- 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 <requirements-file> if this is not a Python package.' >&2

-   exit 1

- fi}

- # setuptools assumes no pre-existing dist-info

- rm -rfv *.dist-info/ >&2

- if [ -f %{__python3} ]; then

-   mkdir -p "%{_pyproject_builddir}"

-   CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS}}" LDFLAGS="${LDFLAGS:-${RPM_LD_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} %{?**}

- fi

- }

- 

- 

- %tox(e:) %{expand:\\\

- TOX_TESTENV_PASSENV="${TOX_TESTENV_PASSENV:-*}" \\

- PYTHONDONTWRITEBYTECODE=1 \\

- PATH="%{buildroot}%{_bindir}:$PATH" \\

- PYTHONPATH="${PYTHONPATH:-%{buildroot}%{python3_sitearch}:%{buildroot}%{python3_sitelib}}" \\

- %{?__pytest_addopts:PYTEST_ADDOPTS="${PYTEST_ADDOPTS:-} %{__pytest_addopts}"} \\

- HOSTNAME="rpmbuild" \\

- %{__python3} -m tox --current-env -q --recreate -e "%{-e:%{-e*}}%{!-e:%{toxenv}}" %{?*}

- }

file modified
+56 -34
@@ -1,8 +1,11 @@ 

+ %global python3_pkgversion 38

+ 

  Name:           pyproject-rpm-macros

  Summary:        RPM macros for PEP 517 Python packages

  License:        MIT

  

  %bcond_without tests

+ %bcond_with tox

  

  # The idea is to follow the spirit of semver

  # Given version X.Y.Z:
@@ -11,28 +14,21 @@ 

  #   Increment Z when this is a bugfix or a cosmetic change

  # Dropping support for EOL Fedoras is *not* considered a breaking change

  Version:        1.3.2

- Release:        1%{?dist}

+ Release:        2%{?dist}

  

  # Macro files

  Source001:      macros.pyproject

  

  # Implementation files

- Source101:      pyproject_buildrequires.py

  Source102:      pyproject_save_files.py

- Source103:      pyproject_convert.py

  Source104:      pyproject_preprocess_record.py

- Source105:      pyproject_construct_toxenv.py

- Source106:      pyproject_requirements_txt.py

  Source107:      pyproject_wheel.py

  

  # Tests

- Source201:      test_pyproject_buildrequires.py

  Source202:      test_pyproject_save_files.py

- Source203:      test_pyproject_requirements_txt.py

  Source204:      compare_mandata.py

  

  # Test data

- Source301:      pyproject_buildrequires_testcases.yaml

  Source302:      pyproject_save_files_test_data.yaml

  Source303:      test_RECORD

  
@@ -44,25 +40,51 @@ 

  

  BuildArch:      noarch

  

+ BuildRequires:  python%{python3_pkgversion}-devel

+ 

  %if %{with tests}

- BuildRequires: python3dist(pytest)

- BuildRequires: python3dist(pyyaml)

- BuildRequires: python3dist(packaging)

- BuildRequires: python3dist(pip)

- BuildRequires: python3dist(setuptools)

- BuildRequires: python3dist(tox-current-env) >= 0.0.6

- BuildRequires: python3dist(wheel)

- BuildRequires: (python3dist(toml) if python3-devel < 3.11)

+ BuildRequires:  %{py3_dist pytest}

+ BuildRequires:  %{py3_dist pyyaml}

+ BuildRequires:  %{py3_dist packaging}

packaging is only required by %pyproject_buildrequires (and some of the other removed parts), right?

packaging is only required by %pyproject_buildrequires (and some of the other removed parts), right?

I think so, yes.

+ BuildRequires:  %{py3_dist pip}

+ BuildRequires:  %{py3_dist setuptools}

+ %if %{with tox}

+ BuildRequires:  %{py3_dist tox-current-env} >= 0.0.6

+ %endif

+ BuildRequires:  %{py3_dist wheel}

+ BuildRequires:  (%{py3_dist toml} if python%{python3_pkgversion}-devel < 3.11)

  %endif

  

  # We build on top of those:

- Requires:      python-rpm-macros

- Requires:      python-srpm-macros

- Requires:      python3-rpm-macros

+ Requires:       python-rpm-macros

+ Requires:       python-srpm-macros

+ Requires:       python3-rpm-macros

+ # This package is incompatible with python36.

+ # We use rich dependencies to avoid creating implicit conflicts between

+ # python3{8,9}-devel.

+ Requires:       (python38-rpm-macros or python39-rpm-macros)

+ Suggests:       (python38-rpm-macros if python38-devel)

+ Suggests:       (python39-rpm-macros if python39-devel)

  

  # We use the following tools outside of coreutils

- Requires:      /usr/bin/find

- Requires:      /usr/bin/sed

+ Requires:       /usr/bin/find

+ Requires:       /usr/bin/sed

+ 

+ # These packages are pulled in unconditionally by %%pyproject_buildrequires

+ # on Fedora, so we Require them here.

+ Requires:       (python38-devel if python38-rpm-macros)

+ Requires:       (python39-devel if python39-rpm-macros)

+ 

+ Requires:       (python3.8dist(pip) if python38-rpm-macros)

+ Requires:       (python3.9dist(pip) if python39-rpm-macros)

+ 

+ Requires:       (python3.8dist(packaging) if python38-rpm-macros)

+ Requires:       (python3.9dist(packaging) if python39-rpm-macros)

+ 

+ # Needed by pip wheel

What does "needed by pip wheel" mean? wheel should only be needed with the default build backend.

+ Requires:       (python3.8dist(wheel) if python38-rpm-macros)

+ Requires:       (python3.9dist(wheel) if python39-rpm-macros)

+ 

  

  %description

  These macros allow projects that follow the Python packaging specifications
@@ -78,6 +100,11 @@ 

  These macros replace %%py3_build and %%py3_install,

  which only work with setup.py.

  

+ This is a slimmed down version of the macros that are available in Fedora,

+ in order to be compatible with el8's RPM version.

+ Notably, %%tox and %%pyproject_buildrequires are absent, so buildrequirements

+ must be manually specified. It is only compatible with python38 and python39.

+ 

  

  %prep

  # Not strictly necessary but allows working on file names instead
@@ -91,19 +118,15 @@ 

  %install

  mkdir -p %{buildroot}%{_rpmmacrodir}

  mkdir -p %{buildroot}%{_rpmconfigdir}/redhat

- install -m 644 macros.pyproject %{buildroot}%{_rpmmacrodir}/

- install -m 644 pyproject_buildrequires.py %{buildroot}%{_rpmconfigdir}/redhat/

- install -m 644 pyproject_convert.py %{buildroot}%{_rpmconfigdir}/redhat/

- install -m 644 pyproject_save_files.py  %{buildroot}%{_rpmconfigdir}/redhat/

- install -m 644 pyproject_preprocess_record.py %{buildroot}%{_rpmconfigdir}/redhat/

- install -m 644 pyproject_construct_toxenv.py %{buildroot}%{_rpmconfigdir}/redhat/

- install -m 644 pyproject_requirements_txt.py %{buildroot}%{_rpmconfigdir}/redhat/

- install -m 644 pyproject_wheel.py %{buildroot}%{_rpmconfigdir}/redhat/

+ install -pm 644 macros.pyproject %{buildroot}%{_rpmmacrodir}/

+ install -pm 644 pyproject_save_files.py %{buildroot}%{_rpmconfigdir}/redhat/

+ install -pm 644 pyproject_preprocess_record.py %{buildroot}%{_rpmconfigdir}/redhat/

+ install -pm 644 pyproject_wheel.py %{buildroot}%{_rpmconfigdir}/redhat/

  

  %if %{with tests}

  %check

  export HOSTNAME="rpmbuild"  # to speedup tox in network-less mock, see rhbz#1856356

- %{python3} -m pytest -vv --doctest-modules

+ %{pytest} -vv --doctest-modules

This should probably be a separate commit or not included in this PR at all. What do you prefer?

This should probably be a separate commit or not included in this PR at all. What do you prefer?

If this works in Fedora, let's submit it as a PR there? No need to bump the release.

  

  # brp-compress is provided as an argument to get the right directory macro expansion

  %{python3} compare_mandata.py -f %{_rpmconfigdir}/brp-compress
@@ -112,18 +135,17 @@ 

  

  %files

  %{_rpmmacrodir}/macros.pyproject

- %{_rpmconfigdir}/redhat/pyproject_buildrequires.py

- %{_rpmconfigdir}/redhat/pyproject_convert.py

  %{_rpmconfigdir}/redhat/pyproject_save_files.py

  %{_rpmconfigdir}/redhat/pyproject_preprocess_record.py

- %{_rpmconfigdir}/redhat/pyproject_construct_toxenv.py

- %{_rpmconfigdir}/redhat/pyproject_requirements_txt.py

  %{_rpmconfigdir}/redhat/pyproject_wheel.py

  

  %doc README.md

  %license LICENSE

  

  %changelog

+ * Sat Jun 25 2022 Maxwell G <gotmax@e.email> - 1.3.2-2

+ - Initial stripped down macros for epel8

+ 

  * Wed Jun 15 2022 Benjamin A. Beasley <code@musicinmybrain.net> - 1.3.2-1

  - Update %%pyproject_build_lib to support setuptools 62.1.0 and later

  - Fixes: rhbz#2097158

@@ -1,519 +0,0 @@ 

- import glob

- import io

- import os

- import sys

- import importlib.metadata

- import argparse

- import traceback

- import contextlib

- from io import StringIO

- import json

- import subprocess

- import re

- import tempfile

- import email.parser

- import pathlib

- import zipfile

- 

- from pyproject_requirements_txt import convert_requirements_txt

- 

- 

- # Some valid Python version specifiers are not supported.

- # Allow only the forms we know we can handle.

- VERSION_RE = re.compile(r'[a-zA-Z0-9.-]+(\.\*)?')

- 

- 

- class EndPass(Exception):

-     """End current pass of generating requirements"""

- 

- 

- # nb: we don't use functools.partial to be able to use pytest's capsys

- # see https://github.com/pytest-dev/pytest/issues/8900

- def print_err(*args, **kwargs):

-     kwargs.setdefault('file', sys.stderr)

-     print(*args, **kwargs)

- 

- 

- try:

-     from packaging.requirements import Requirement, InvalidRequirement

-     from packaging.utils import canonicalize_name

- except ImportError as e:

-     print_err('Import error:', e)

-     # already echoed by the %pyproject_buildrequires macro

-     sys.exit(0)

- 

- # uses packaging, needs to be imported after packaging is verified to be present

- from pyproject_convert import convert

- 

- 

- @contextlib.contextmanager

- def hook_call():

-     captured_out = StringIO()

-     with contextlib.redirect_stdout(captured_out):

-         yield

-     for line in captured_out.getvalue().splitlines():

-         print_err('HOOK STDOUT:', line)

- 

- 

- def guess_reason_for_invalid_requirement(requirement_str):

-     if ':' in requirement_str:

-         message = (

-             'It might be an URL. '

-             '%pyproject_buildrequires cannot handle all URL-based requirements. '

-             'Add PackageName@ (see PEP 508) to the URL to at least require any version of PackageName.'

-         )

-         if '@' in requirement_str:

-             message += ' (but note that URLs might not work well with other features)'

-         return message

-     if '/' in requirement_str:

-         return (

-             'It might be a local path. '

-             '%pyproject_buildrequires cannot handle local paths as requirements. '

-             'Use an URL with PackageName@ (see PEP 508) to at least require any version of PackageName.'

-         )

-     # No more ideas

-     return None

- 

- 

- class Requirements:

-     """Requirement printer"""

-     def __init__(self, get_installed_version, extras=None,

-                  generate_extras=False, python3_pkgversion='3'):

-         self.get_installed_version = get_installed_version

-         self.extras = set()

- 

-         if extras:

-             for extra in extras:

-                 self.add_extras(*extra.split(','))

- 

-         self.missing_requirements = False

- 

-         self.generate_extras = generate_extras

-         self.python3_pkgversion = python3_pkgversion

- 

-     def add_extras(self, *extras):

-         self.extras |= set(e.strip() for e in extras)

- 

-     @property

-     def marker_envs(self):

-         if self.extras:

-             return [{'extra': e} for e in sorted(self.extras)]

-         return [{'extra': ''}]

- 

-     def evaluate_all_environamnets(self, requirement):

-         for marker_env in self.marker_envs:

-             if requirement.marker.evaluate(environment=marker_env):

-                 return True

-         return False

- 

-     def add(self, requirement_str, *, source=None):

-         """Output a Python-style requirement string as RPM dep"""

-         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)

- 

-         if requirement.url:

-             print_err(

-                 f'WARNING: Simplifying {requirement_str!r} to {requirement.name!r}.'

-             )

- 

-         name = canonicalize_name(requirement.name)

-         if (requirement.marker is not None and

-                 not self.evaluate_all_environamnets(requirement)):

-             print_err(f'Ignoring alien requirement:', requirement_str)

-             return

- 

-         # We need to always accept pre-releases as satisfying the requirement

-         # Otherwise e.g. installed cffi version 1.15.0rc2 won't even satisfy the requirement for "cffi"

-         # https://bugzilla.redhat.com/show_bug.cgi?id=2014639#c3

-         requirement.specifier.prereleases = True

- 

-         try:

-             # TODO: check if requirements with extras are satisfied

-             installed = self.get_installed_version(requirement.name)

-         except importlib.metadata.PackageNotFoundError:

-             print_err(f'Requirement not satisfied: {requirement_str}')

-             installed = None

-         if installed and installed in requirement.specifier:

-             print_err(f'Requirement satisfied: {requirement_str}')

-             print_err(f'   (installed: {requirement.name} {installed})')

-             if requirement.extras:

-                 print_err(f'   (extras are currently not checked)')

-         else:

-             self.missing_requirements = True

- 

-         if self.generate_extras:

-             extra_names = [f'{name}[{extra.lower()}]' for extra in sorted(requirement.extras)]

-         else:

-             extra_names = []

- 

-         for name in [name] + extra_names:

-             together = []

-             for specifier in sorted(

-                 requirement.specifier,

-                 key=lambda s: (s.operator, s.version),

-             ):

-                 if not VERSION_RE.fullmatch(str(specifier.version)):

-                     raise ValueError(

-                         f'Unknown character in version: {specifier.version}. '

-                         + '(This might be a bug in pyproject-rpm-macros.)',

-                     )

-                 together.append(convert(python3dist(name, python3_pkgversion=self.python3_pkgversion),

-                                         specifier.operator, specifier.version))

-             if len(together) == 0:

-                 print(python3dist(name,

-                                   python3_pkgversion=self.python3_pkgversion))

-             elif len(together) == 1:

-                 print(together[0])

-             else:

-                 print(f"({' with '.join(together)})")

- 

-     def check(self, *, source=None):

-         """End current pass if any unsatisfied dependencies were output"""

-         if self.missing_requirements:

-             print_err(f'Exiting dependency generation pass: {source}')

-             raise EndPass(source)

- 

-     def extend(self, requirement_strs, **kwargs):

-         """add() several requirements"""

-         for req_str in requirement_strs:

-             self.add(req_str, **kwargs)

- 

- 

- def toml_load(opened_binary_file):

-     try:

-         # tomllib is in the standard library since 3.11.0b1

-         import tomllib as toml_module

-         load_from = opened_binary_file

-     except ImportError:

-         try:

-             # note: we could use tomli here,

-             # but for backwards compatibility with RHEL 9, we use toml instead

-             import toml as toml_module

-             load_from = io.TextIOWrapper(opened_binary_file, encoding='utf-8')

-         except ImportError as e:

-             print_err('Import error:', e)

-             # already echoed by the %pyproject_buildrequires macro

-             sys.exit(0)

-     return toml_module.load(load_from)

- 

- 

- def get_backend(requirements):

-     try:

-         f = open('pyproject.toml', 'rb')

-     except FileNotFoundError:

-         pyproject_data = {}

-     else:

-         with f:

-             pyproject_data = toml_load(f)

- 

-     buildsystem_data = pyproject_data.get('build-system', {})

-     requirements.extend(

-         buildsystem_data.get('requires', ()),

-         source='build-system.requires',

-     )

- 

-     backend_name = buildsystem_data.get('build-backend')

-     if not backend_name:

-         # https://www.python.org/dev/peps/pep-0517/:

-         # If the pyproject.toml file is absent, or the build-backend key is

-         # missing, the source tree is not using this specification, and tools

-         # should revert to the legacy behaviour of running setup.py

-         # (either directly, or by implicitly invoking the [following] backend).

-         # If setup.py is also not present program will mimick pip's behavior

-         # and end with an error.

-         if not os.path.exists('setup.py'):

-             raise FileNotFoundError('File "setup.py" not found for legacy project.')

-         backend_name = 'setuptools.build_meta:__legacy__'

- 

-         # Note: For projects without pyproject.toml, this was already echoed

-         # by the %pyproject_buildrequires macro, but this also handles cases

-         # 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')

-         requirements.add('wheel', source='default build backend')

- 

-     requirements.check(source='build backend')

- 

-     backend_path = buildsystem_data.get('backend-path')

-     if backend_path:

-         # PEP 517 example shows the path as a list, but some projects don't follow that

-         if isinstance(backend_path, str):

-             backend_path = [backend_path]

-         sys.path = backend_path + sys.path

- 

-     module_name, _, object_name = backend_name.partition(":")

-     backend_module = importlib.import_module(module_name)

- 

-     if object_name:

-         return getattr(backend_module, object_name)

- 

-     return backend_module

- 

- 

- def generate_build_requirements(backend, requirements):

-     get_requires = getattr(backend, 'get_requires_for_build_wheel', None)

-     if get_requires:

-         with hook_call():

-             new_reqs = get_requires()

-         requirements.extend(new_reqs, source='get_requires_for_build_wheel')

-         requirements.check(source='get_requires_for_build_wheel')

- 

- 

- def requires_from_metadata_file(metadata_file):

-     message = email.parser.Parser().parse(metadata_file, headersonly=True)

-     return {k: message.get_all(k, ()) for k in ('Requires', 'Requires-Dist')}

- 

- 

- def generate_run_requirements_hook(backend, requirements):

-     hook_name = 'prepare_metadata_for_build_wheel'

-     prepare_metadata = getattr(backend, hook_name, None)

-     if not prepare_metadata:

-         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, '

-             'or use the -R flag not to generate runtime dependencies.'

-         )

-     with hook_call():

-         dir_basename = prepare_metadata('.')

-     with open(dir_basename + '/METADATA') as metadata_file:

-         for key, requires in requires_from_metadata_file(metadata_file).items():

-             requirements.extend(requires, source=f'hook generated metadata: {key}')

- 

- 

- def find_built_wheel(wheeldir):

-     wheels = glob.glob(os.path.join(wheeldir, '*.whl'))

-     if not wheels:

-         return None

-     if len(wheels) > 1:

-         raise RuntimeError('Found multiple wheels in %{_pyproject_wheeldir}, '

-                            'this is not supported with %pyproject_buildrequires -w.')

-     return wheels[0]

- 

- 

- def generate_run_requirements_wheel(backend, requirements, wheeldir):

-     # Reuse the wheel from the previous round of %pyproject_buildrequires (if it exists)

-     wheel = find_built_wheel(wheeldir)

-     if not wheel:

-         import pyproject_wheel

-         returncode = pyproject_wheel.build_wheel(wheeldir=wheeldir, stdout=sys.stderr)

-         if returncode != 0:

-             raise RuntimeError('Failed to build the wheel for %pyproject_buildrequires -w.')

-         wheel = find_built_wheel(wheeldir)

-     if not wheel:

-         raise RuntimeError('Cannot locate the built wheel for %pyproject_buildrequires -w.')

- 

-     print_err(f'Reading metadata from {wheel}')

-     with zipfile.ZipFile(wheel) as wheelfile:

-         for name in wheelfile.namelist():

-             if name.count('/') == 1 and name.endswith('.dist-info/METADATA'):

-                 with io.TextIOWrapper(wheelfile.open(name), encoding='utf-8') as metadata_file:

-                     for key, requires in requires_from_metadata_file(metadata_file).items():

-                         requirements.extend(requires, source=f'built wheel metadata: {key}')

-                 break

-         else:

-             raise RuntimeError('Could not find *.dist-info/METADATA in built wheel.')

- 

- 

- def generate_run_requirements(backend, requirements, *, build_wheel, wheeldir):

-     if build_wheel:

-         generate_run_requirements_wheel(backend, requirements, wheeldir)

-     else:

-         generate_run_requirements_hook(backend, requirements)

- 

- 

- def generate_tox_requirements(toxenv, requirements):

-     toxenv = ','.join(toxenv)

-     requirements.add('tox-current-env >= 0.0.6', source='tox itself')

-     requirements.check(source='tox itself')

-     with tempfile.NamedTemporaryFile('r') as deps, \

-         tempfile.NamedTemporaryFile('r') as extras, \

-             tempfile.NamedTemporaryFile('r') as provision:

-         r = subprocess.run(

-             [sys.executable, '-m', 'tox',

-              '--print-deps-to', deps.name,

-              '--print-extras-to', extras.name,

-              '--no-provision', provision.name,

-              '-q', '-r', '-e', toxenv],

-             check=False,

-             encoding='utf-8',

-             stdout=subprocess.PIPE,

-             stderr=subprocess.STDOUT,

-         )

-         if r.stdout:

-             print_err(r.stdout, end='')

- 

-         provision_content = provision.read()

-         if provision_content and r.returncode != 0:

-             provision_requires = json.loads(provision_content)

-             if 'minversion' in provision_requires:

-                 requirements.add(f'tox >= {provision_requires["minversion"]}',

-                                  source='tox provision (minversion)')

-             if 'requires' in provision_requires:

-                 requirements.extend(provision_requires["requires"],

-                                     source='tox provision (requires)')

-             requirements.check(source='tox provision')  # this terminates the script

-             raise RuntimeError(

-                 'Dependencies requested by tox provisioning appear installed, '

-                 'but tox disagreed.')

-         else:

-             r.check_returncode()

- 

-         deplines = deps.read().splitlines()

-         packages = convert_requirements_txt(deplines)

-         requirements.add_extras(*extras.read().splitlines())

-         requirements.extend(packages,

-                             source=f'tox --print-deps-only: {toxenv}')

- 

- 

- def python3dist(name, op=None, version=None, python3_pkgversion="3"):

-     prefix = f"python{python3_pkgversion}dist"

- 

-     if op is None:

-         if version is not None:

-             raise AssertionError('op and version go together')

-         return f'{prefix}({name})'

-     else:

-         return f'{prefix}({name}) {op} {version}'

- 

- 

- def generate_requires(

-     *, include_runtime=False, build_wheel=False, wheeldir=None, toxenv=None, extras=None,

-     get_installed_version=importlib.metadata.version,  # for dep injection

-     generate_extras=False, python3_pkgversion="3", requirement_files=None, use_build_system=True

- ):

-     """Generate the BuildRequires for the project in the current directory

- 

-     This is the main Python entry point.

-     """

-     requirements = Requirements(

-         get_installed_version, extras=extras or [],

-         generate_extras=generate_extras,

-         python3_pkgversion=python3_pkgversion

-     )

- 

-     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 requirement_files:

-             for req_file in requirement_files:

-                 requirements.extend(

-                     convert_requirements_txt(req_file, pathlib.Path(req_file.name)),

-                     source=f'requirements file {req_file.name}'

-                 )

-             requirements.check(source='all requirements files')

-         if use_build_system:

-             backend = get_backend(requirements)

-             generate_build_requirements(backend, requirements)

-         if toxenv:

-             include_runtime = True

-             generate_tox_requirements(toxenv, requirements)

-         if include_runtime:

-             generate_run_requirements(backend, requirements, build_wheel=build_wheel, wheeldir=wheeldir)

-     except EndPass:

-         return

- 

- 

- def main(argv):

-     parser = argparse.ArgumentParser(

-         description='Generate BuildRequires for a Python project.'

-     )

-     parser.add_argument(

-         '-r', '--runtime', action='store_true', default=True,

-         help='Generate run-time requirements (default, disable with -R)',

-     )

-     parser.add_argument(

-         '-w', '--wheel', action='store_true', default=False,

-         help=('Generate run-time requirements by building the wheel '

-               '(useful for build backends without the prepare_metadata_for_build_wheel hook)'),

-     )

-     parser.add_argument(

-         '--wheeldir', metavar='PATH', default=None,

-         help='The directory with wheel, used when -w.',

-     )

-     parser.add_argument(

-         '-R', '--no-runtime', action='store_false', dest='runtime',

-         help="Don't generate run-time requirements (implied by -N)",

-     )

-     parser.add_argument(

-         '-e', '--toxenv', metavar='TOXENVS', action='append',

-         help=('specify tox environments (comma separated and/or repeated)'

-               '(implies --tox)'),

-     )

-     parser.add_argument(

-         '-t', '--tox', action='store_true',

-         help=('generate test tequirements from tox environment '

-               '(implies --runtime)'),

-     )

-     parser.add_argument(

-         '-x', '--extras', metavar='EXTRAS', action='append',

-         help='comma separated list of "extras" for runtime requirements '

-              '(e.g. -x testing,feature-x) (implies --runtime, can be repeated)',

-     )

-     parser.add_argument(

-         '--generate-extras', action='store_true',

-         help='Generate build requirements on Python Extras',

-     )

-     parser.add_argument(

-         '-p', '--python3_pkgversion', metavar='PYTHON3_PKGVERSION',

-         default="3", help=('Python version for pythonXdist()'

-                            'or pythonX.Ydist() requirements'),

-     )

-     parser.add_argument(

-         '-N', '--no-use-build-system', dest='use_build_system',

-         action='store_false', help='Use -N to indicate that project does not use any build system',

-     )

-     parser.add_argument(

-        'requirement_files', nargs='*', type=argparse.FileType('r'),

-         help=('Add buildrequires from file'),

-     )

- 

-     args = parser.parse_args(argv)

- 

-     if not args.use_build_system:

-         args.runtime = False

- 

-     if args.wheel:

-         if not args.wheeldir:

-             raise ValueError('--wheeldir must be set when -w.')

- 

-     if args.toxenv:

-         args.tox = True

- 

-     if args.tox:

-         args.runtime = True

-         if not args.toxenv:

-             _default = f'py{sys.version_info.major}{sys.version_info.minor}'

-             args.toxenv = [os.getenv('RPM_TOXENV', _default)]

- 

-     if args.extras:

-         args.runtime = True

- 

-     try:

-         generate_requires(

-             include_runtime=args.runtime,

-             build_wheel=args.wheel,

-             wheeldir=args.wheeldir,

-             toxenv=args.toxenv,

-             extras=args.extras,

-             generate_extras=args.generate_extras,

-             python3_pkgversion=args.python3_pkgversion,

-             requirement_files=args.requirement_files,

-             use_build_system=args.use_build_system,

-         )

-     except Exception:

-         # Log the traceback explicitly (it's useful debug info)

-         traceback.print_exc()

-         exit(1)

- 

- 

- if __name__ == '__main__':

-     main(sys.argv[1:])

@@ -1,773 +0,0 @@ 

- No pyproject.toml, nothing installed:

-   installed:

-     # empty

-   except: FileNotFoundError

- 

- Nothing installed yet:

-   installed:

-     # empty

-   pyproject.toml: |

-     # empty

-   expected: |

-     python3dist(setuptools) >= 40.8

-     python3dist(wheel)

-   except: FileNotFoundError

- 

- Insufficient version of setuptools:

-   installed:

-     setuptools: 5

-     wheel: 1

-     toml: 1

-   pyproject.toml: |

-     # empty

-   setup.py: |

-   expected: |

-     python3dist(setuptools) >= 40.8

-     python3dist(wheel)

-   result: 0

- 

- No pyproject.toml, empty setup.py:

-   installed:

-     setuptools: 50

-     wheel: 1

-   include_runtime: false

-   setup.py: |

-   expected: |

-     python3dist(setuptools) >= 40.8

-     python3dist(wheel)

-     python3dist(wheel)

-   result: 0

- 

- Default build system, empty setup.py:

-   installed:

-     setuptools: 50

-     wheel: 1

-     toml: 1

-   include_runtime: false

-   pyproject.toml: |

-     # empty

-   setup.py: |

-   expected: |

-     python3dist(setuptools) >= 40.8

-     python3dist(wheel)

-     python3dist(wheel)

-   result: 0

- 

- pyproject.toml with build-backend and setup.py:

-   generate_extras: true

-   installed:

-     setuptools: 50

-     wheel: 1

-     toml: 1

-   setup.py: |

-     # empty

-   pyproject.toml: |

-     [build-system]

-     requires = [

-         "foo",

-     ]

-     build-backend = "foo.build"

-   expected: |

-     python3dist(foo)

-   result: 0

- 

- Erroring setup.py:

-   installed:

-     setuptools: 50

-     wheel: 1

-   setup.py: |

-     exit(77)

-   result: 77

- 

- Bad character in version:

-   installed:

-     toml: 1

-   pyproject.toml: |

-     [build-system]

-     requires = ["pkg == 0.$.^.*"]

-   except: ValueError

- 

- Single value version with unsupported compatible operator:

-   installed:

-     toml: 1

-   pyproject.toml: |

-     [build-system]

-     requires = ["pkg ~= 42", "foo"]

-     build-backend = "foo.build"

-   except: ValueError

- 

- Asterisk in version with unsupported compatible operator:

-   installed:

-     toml: 1

-   pyproject.toml: |

-     [build-system]

-     requires = ["pkg ~= 0.1.*", "foo"]

-     build-backend = "foo.build"

-   except: ValueError

- 

- Local path as requirement:

-   installed:

-     toml: 1

-   pyproject.toml: |

-     [build-system]

-     requires = ["./pkg-1.2.3.tar.gz", "foo"]

-     build-backend = "foo.build"

-   except: ValueError

- 

- Pip's egg=pkgName requirement not in requirements file:

-   installed:

-     toml: 1

-   pyproject.toml: |

-     [build-system]

-     requires = ["git+https://github.com/monty/spam.git@master#egg=spam", "foo"]

-     build-backend = "foo.build"

-   except: ValueError

- 

- URL without egg fragment as requirement:

-   installed:

-     toml: 1

-   pyproject.toml: |

-     [build-system]

-     requires = ["git+https://github.com/pkg-dev/pkg.git@96dbe5e3", "foo"]

-     build-backend = "foo.build"

-   except: ValueError

- 

- Build system dependencies in pyproject.toml with extras:

-   generate_extras: true

-   installed:

-     setuptools: 50

-     wheel: 1

-     toml: 1

-   pyproject.toml: |

-     [build-system]

-     requires = [

-         "foo",

-         "bar[bAz] > 5",

-         "ne!=1",

-         "ge>=1.2.0",

-         "le <= 1.2.3",

-         "lt < 1.2.3.4      ",

-         "    gt > 1.2.3.4.5",

-         "compatible ~= 0.4.0",

-         "equal == 0.5.0",

-         "arbitrary_equal === 0.6.0",

-         "asterisk_equal == 0.6.*",

-         "appdirs@https://github.com/ActiveState/appdirs/archive/8eacfa312d77aba28d483fbfb6f6fc54099622be.zip",

-         "multi[Extras1,Extras2] == 6.0",

-         "combo >2, <5, != 3.0.0",

-         "py2 ; python_version < '2.7'",

-         "py3 ; python_version > '3.0'",

-     ]

-     build-backend = "foo.build"

-   expected: |

-     python3dist(foo)

-     python3dist(bar) > 5.0

-     python3dist(bar[baz]) > 5.0

-     (python3dist(ne) < 1 or python3dist(ne) > 1)

-     python3dist(ge) >= 1.2

-     python3dist(le) <= 1.2.3

-     python3dist(lt) < 1.2.3.4~~

-     python3dist(gt) > 1.2.3.4.5.0

-     (python3dist(compatible) >= 0.4 with python3dist(compatible) < 0.5)

-     python3dist(equal) = 0.5

-     python3dist(arbitrary-equal) = 0.6

-     (python3dist(asterisk-equal) >= 0.6 with python3dist(asterisk-equal) < 0.7)

-     python3dist(appdirs)

-     python3dist(multi) = 6

-     python3dist(multi[extras1]) = 6

-     python3dist(multi[extras2]) = 6

-     ((python3dist(combo) < 3 or python3dist(combo) > 3) with python3dist(combo) < 5~~ with python3dist(combo) > 2.0)

-     python3dist(py3)

-   stderr_contains: "WARNING: Simplifying 'appdirs@https://github.com/ActiveState/appdirs/archive/8eacfa312d77aba28d483fbfb6f6fc54099622be.zip' to 'appdirs'."

-   result: 0

- 

- Build system dependencies in pyproject.toml without extras:

-   generate_extras: false

-   installed:

-     setuptools: 50

-     wheel: 1

-     toml: 1

-   pyproject.toml: |

-     [build-system]

-     requires = [

-         "bar[Baz] > 5",

-         "multi[extras1,extras2] == 6.0",

-     ]

-     build-backend = "foo.build"

-   expected: |

-     python3dist(bar) > 5.0

-     python3dist(multi) = 6

-   result: 0

- 

- Default build system, build dependencies in setup.py:

-   installed:

-     setuptools: 50

-     wheel: 1

-   include_runtime: false

-   setup.py: |

-     from setuptools import setup

-     setup(

-         name='test',

-         version='0.1',

-         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)

-   result: 0

- 

- Default build system, run dependencies in setup.py:

-   installed:

-     setuptools: 50

-     wheel: 1

-     pyyaml: 1

-   setup.py: |

-     from setuptools import setup

-     setup(

-         name='test',

-         version='0.1',

-         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~~

-   result: 0

- 

- Run dependencies with extras (not selected):

-   installed:

-     setuptools: 50

-     wheel: 1

-     pyyaml: 1

-   setup.py: &pytest_setup_py |

-         # slightly abriged copy of pytest's setup.py

-         from setuptools import setup

- 

-         INSTALL_REQUIRES = [

-             "py>=1.5.0",

-             "six>=1.10.0",

-             "setuptools",

-             "attrs>=17.4.0",

-             'more-itertools>=4.0.0,<6.0.0;python_version<="2.7"',

-             'more-itertools>=4.0.0;python_version>"2.7"',

-             "atomicwrites>=1.0",

-             'funcsigs>=1.0;python_version<"3.0"',

-             'pathlib2>=2.2.0;python_version<"3.6"',

-             'colorama;sys_platform=="win32"',

-             "pluggy>=0.11",

-         ]

- 

-         def main():

-             setup(

-                 name = "pytest",

-                 version = "6.6.6",

-                 setup_requires=["setuptools>=40.0"],

-                 # fmt: off

-                 extras_require={

-                     "testing": [

-                         "argcomplete",

-                         "hypothesis>=3.56",

-                         "nose",

-                         "requests",

-                         "mock;python_version=='2.7'",

-                     ],

-                 },

-                 # fmt: on

-                 install_requires=INSTALL_REQUIRES,

-             )

- 

-         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

-   result: 0

- 

- Run dependencies with extras (selected):

-   installed:

-     setuptools: 50

-     wheel: 1

-     pyyaml: 1

-   include_runtime: true

-   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)

-   result: 0

- 

- Run dependencies with multiple extras:

-   installed:

-     setuptools: 50

-     wheel: 1

-     pyyaml: 1

-   include_runtime: true

-   generate_extras: true

-   extras:

-     - testing,more-testing

-     - even-more-testing , cool-feature

-   setup.py: |

-     from setuptools import setup

-     setup(

-         extras_require={

-             'testing': ['dep1'],

-             'more-testing': ['dep2'],

-             'even-more-testing': ['dep3'],

-             '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)

-   result: 0

- 

- Run dependencies with extras and build wheel option:

-   installed:

-     setuptools: 50

-     wheel: 1

-     pyyaml: 1

-   include_runtime: true

-   build_wheel: true

-   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)

-   result: 0

-   stderr_contains: "Reading metadata from {wheeldir}/pytest-6.6.6-py3-none-any.whl"

- 

- Tox dependencies:

-   installed:

-     setuptools: 50

-     wheel: 1

-     tox: 3.5.3

-     tox-current-env: 0.0.6

-   toxenv:

-     - py3

-   setup.py: |

-     from setuptools import setup

-     setup(

-         name='test',

-         version='0.1',

-         install_requires=['inst'],

-     )

-   tox.ini: |

-     [tox]

-     envlist = py36,py37,py38

-     [testenv]

-     deps =

-         toxdep1

-         toxdep2

-     commands =

-         true

-   expected: |

-     python3dist(setuptools) >= 40.8

-     python3dist(wheel)

-     python3dist(wheel)

-     python3dist(tox-current-env) >= 0.0.6

-     python3dist(toxdep1)

-     python3dist(toxdep2)

-     python3dist(inst)

-   result: 0

- 

- Tox extras:

-   installed:

-     setuptools: 50

-     wheel: 1

-     tox: 3.5.3

-     tox-current-env: 0.0.6

-   generate_extras: true

-   toxenv:

-     - py3

-   setup.py: |

-     from setuptools import setup

-     setup(

-         name='test',

-         version='0.1',

-         install_requires=['inst'],

-         extras_require={

-             'extra1':  ['dep11 > 11', 'dep12'],

-             'extra2': ['dep21', 'dep22', 'dep23', 'extra_dep[EXTRA_DEP]'],

-             'nope': ['nopedep'],

-         }

-     )

-   tox.ini: |

-     [tox]

-     envlist = py36,py37,py38

-     [testenv]

-     deps =

-         toxdep

-     extras =

-         extra2

-         extra1

-     commands =

-         true

-   expected: |

-     python3dist(setuptools) >= 40.8

-     python3dist(wheel)

-     python3dist(wheel)

-     python3dist(tox-current-env) >= 0.0.6

-     python3dist(toxdep)

-     python3dist(inst)

-     python3dist(dep11) > 11.0

-     python3dist(dep12)

-     python3dist(dep21)

-     python3dist(dep22)

-     python3dist(dep23)

-     python3dist(extra-dep)

-     python3dist(extra-dep[extra_dep])

-   result: 0

- 

- Tox provision unsatisfied:

-   installed:

-     setuptools: 50

-     wheel: 1

-     tox: 3.5.3

-     tox-current-env: 0.0.6

-   toxenv:

-     - py3

-   setup.py: |

-     from setuptools import setup

-     setup(

-         name='test',

-         version='0.1',

-         install_requires=['inst'],

-     )

-   tox.ini: |

-     [tox]

-     minversion = 3.999

-     requires =

-         setuptools > 40

-         wheel > 2

-     [testenv]

-     deps =

-         toxdep1

-         toxdep2

-   expected: |

-     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

-   result: 0

- 

- Tox provision satisfied:

-   installed:

-     setuptools: 50

-     wheel: 1

-     tox: 3.5.3

-     tox-current-env: 0.0.6

-   toxenv:

-     - py3

-   setup.py: |

-     from setuptools import setup

-     setup(

-         name='test',

-         version='0.1',

-         install_requires=['inst'],

-     )

-   tox.ini: |

-     [tox]

-     minversion = 3.5

-     requires =

-         setuptools > 40

-     [testenv]

-     deps =

-         toxdep1

-         toxdep2

-   expected: |

-     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)

-   result: 0

- 

- Default build system, unmet deps in requirements file:

-   installed:

-     setuptools: 50

-     wheel: 1

-   setup.py: |

-     from setuptools import setup

-     setup(

-         name='test',

-         version='0.1',

-     )

-   requirements.txt: |

-     lxml

-     ncclient

-     cryptography

-     paramiko

-     SQLAlchemy

-   requirement_files:

-     - requirements.txt

-   expected: |

-     python3dist(lxml)

-     python3dist(ncclient)

-     python3dist(cryptography)

-     python3dist(paramiko)

-     python3dist(sqlalchemy)

-   result: 0

- 

- Default build system, met deps in requirements file:

-   installed:

-     setuptools: 50

-     wheel: 1

-     lxml: 3.9

-     ncclient: 1

-     cryptography: 2

-     paramiko: 1

-     SQLAlchemy: 1.0.90

-   setup.py: |

-     from setuptools import setup

-     setup(

-         name='test',

-         version='0.1',

-     )

-   requirements.txt: |

-     lxml!=3.7.0,>=2.3    # OF-Config

-     ncclient  # OF-Config

-     cryptography!=1.5.2  # Required by paramiko

-     paramiko  # NETCONF, BGP speaker (SSH console)

-     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)

-   result: 0

- 

- With pyproject.toml, requirements file and with -N option:

-   use_build_system: false

-   installed:

-     setuptools: 50

-     wheel: 1

-     toml: 1

-     lxml: 3.9

-     ncclient: 1

-     cryptography: 2

-     paramiko: 1

-     SQLAlchemy: 1.0.90

-   pyproject.toml: |

-     [build-system]

-     requires = [

-         "foo",

-     ]

-     build-backend = "foo.build"

-   requirements.txt: |

-     lxml

-     ncclient

-     cryptography

-     paramiko

-     SQLAlchemy

-     git+https://github.com/monty/spam.git@master#egg=spam

-   requirement_files:

-     - requirements.txt

-   expected: |

-     python3dist(lxml)

-     python3dist(ncclient)

-     python3dist(cryptography)

-     python3dist(paramiko)

-     python3dist(sqlalchemy)

-     python3dist(spam)

-   stderr_contains: "WARNING: Simplifying 'spam@git+https://github.com/monty/spam.git@master#egg=spam' to 'spam'."

-   result: 0

- 

- With pyproject.toml, requirements file and without -N option:

-   use_build_system: true

-   installed:

-     setuptools: 50

-     wheel: 1

-     toml: 1

-     lxml: 3.9

-     ncclient: 1

-     cryptography: 2

-     paramiko: 1

-     SQLAlchemy: 1.0.90

-     argcomplete: 1

-     hypothesis: 1

-   pyproject.toml: |

-     [build-system]

-     requires = [

-         "foo",

-     ]

-     build-backend = "foo.build"

-   requirements.txt: |

-     lxml

-     ncclient

-     cryptography

-     paramiko

-     SQLAlchemy

-   requirements1.in: |

-     argcomplete

-     hypothesis

-   requirement_files:

-     - requirements.txt

-     - requirements1.in

-   expected: |

-     python3dist(lxml)

-     python3dist(ncclient)

-     python3dist(cryptography)

-     python3dist(paramiko)

-     python3dist(sqlalchemy)

-     python3dist(argcomplete)

-     python3dist(hypothesis)

-     python3dist(foo)

-   result: 0

- 

- Value error if -N and -r arguments are present:

-   installed:

-     # empty

-   include_runtime: true

-   use_build_system: false

-   except: ValueError

- 

- Value error if -N and -e arguments are present:

-   installed:

-     # empty

-   toxenv:

-     - py3

-   use_build_system: false

-   except: ValueError

- 

- Weird and complex requirements file:

-   installed:

-     setuptools: 50

-     wheel: 1

-   setup.py: |

-     from setuptools import setup

-     setup(

-         name='test',

-         version='0.1',

-     )

-   requirements.txt: |

-     Normal_Req ~= 1.2.0

- 

-     good@git+https://github.com/monty/spam.git@master#egg=bad

-     git+https://github.com/monty/spam.git@master#egg=ugly

- 

-     this-name-is-too-\

-     long-for-this-file<\

-     =30  # even names and operators can be split

- 

-     # this is not a multi-line comment \

-     some-dep

-     other-dep  # but this *is* a multi-line coment \

-     so any garbage can be here

-     dep-a # and this comment ends with the blank line below \

- 

-     dep-b

-     -r requirements2.txt

-     ${PACKAGE}${WANTED_VERSION}

-   requirements2.txt: |

-     dep-from-included-file

-   requirement_files:

-     - requirements.txt

-   environ:

-     PACKAGE: package

-     WANTED_VERSION: -from-environ >= 1.2.3

-   expected: |

-     (python3dist(normal-req) >= 1.2 with python3dist(normal-req) < 1.3)

-     python3dist(good)

-     python3dist(ugly)

-     python3dist(this-name-is-too-long-for-this-file) <= 30

-     python3dist(some-dep)

-     python3dist(other-dep)

-     python3dist(dep-a)

-     python3dist(dep-b)

-     python3dist(dep-from-included-file)

-     python3dist(package-from-environ) >= 1.2.3

-   stderr_contains:

-   - "WARNING: Simplifying 'good@git+https://github.com/monty/spam.git@master#egg=bad' to 'good'."

-   # XXX: pyproject_requirements_txt adds a prefix that's not actually in the source;

-   # but that's good enough:

-   - "WARNING: Simplifying 'ugly@git+https://github.com/monty/spam.git@master#egg=ugly' to 'ugly'."

-   result: 0

- 

- 

- Pre-releases are accepted:

-   installed:

-     setuptools: 50

-     wheel: 1

-     toml: 1

-     cffi: 1.15.0rc2

-   pyproject.toml: |

-     [build-system]

-     requires = [

-         "setuptools",

-         "wheel",

-         "cffi",

-     ]

-     build-backend = "setuptools.build_meta"

-   expected: |

-     python3dist(setuptools)

-     python3dist(wheel)

-     python3dist(cffi)

-     python3dist(wheel)

-   stderr_contains: "Requirement satisfied: cffi"

-   result: 0

@@ -1,15 +0,0 @@ 

- import argparse

- import sys

- 

- 

- def main(argv):

-     parser = argparse.ArgumentParser(

-         description='Parse -e arguments instead of RPM getopt.'

-     )

-     parser.add_argument('-e', '--toxenv', action='append')

-     args, _ = parser.parse_known_args(argv)

-     return ','.join(args.toxenv)

- 

- 

- if __name__ == '__main__':

-     print(main(sys.argv[1:]))

file removed
-171
@@ -1,171 +0,0 @@ 

- # Copyright 2019 Gordon Messmer <gordon.messmer@gmail.com>

- #

- # Upstream: https://github.com/gordonmessmer/pyreq2rpm

- #

- # Permission is hereby granted, free of charge, to any person

- # obtaining a copy of this software and associated documentation files

- # (the "Software"), to deal in the Software without restriction,

- # including without limitation the rights to use, copy, modify, merge,

- # publish, distribute, sublicense, and/or sell copies of the Software,

- # and to permit persons to whom the Software is furnished to do so,

- # subject to the following conditions:

- #

- # The above copyright notice and this permission notice shall be

- # included in all copies or substantial portions of the Software.

- #

- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,

- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF

- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND

- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS

- # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN

- # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN

- # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE

- # SOFTWARE.

- 

- from packaging.requirements import Requirement

- from packaging.version import parse as parse_version

- 

- class RpmVersion():

-     def __init__(self, version_id):

-         version = parse_version(version_id)

-         if isinstance(version._version, str):

-             self.version = version._version

-         else:

-             self.epoch = version._version.epoch

-             self.version = list(version._version.release)

-             self.pre = version._version.pre

-             self.dev = version._version.dev

-             self.post = version._version.post

-             # version.local is ignored as it is not expected to appear

-             # in public releases

-             # https://www.python.org/dev/peps/pep-0440/#local-version-identifiers

- 

-     def is_legacy(self):

-         return isinstance(self.version, str)

- 

-     def increment(self):

-         self.version[-1] += 1

-         self.pre = None

-         self.dev = None

-         self.post = None

-         return self

- 

-     def __str__(self):

-         if self.is_legacy():

-             return self.version

-         if self.epoch:

-             rpm_epoch = str(self.epoch) + ':'

-         else:

-             rpm_epoch = ''

-         while len(self.version) > 1 and self.version[-1] == 0:

-             self.version.pop()

-         rpm_version = '.'.join(str(x) for x in self.version)

-         if self.pre:

-             rpm_suffix = '~{}'.format(''.join(str(x) for x in self.pre))

-         elif self.dev:

-             rpm_suffix = '~~{}'.format(''.join(str(x) for x in self.dev))

-         elif self.post:

-             rpm_suffix = '^post{}'.format(self.post[1])

-         else:

-             rpm_suffix = ''

-         return '{}{}{}'.format(rpm_epoch, rpm_version, rpm_suffix)

- 

- def convert_compatible(name, operator, version_id):

-     if version_id.endswith('.*'):

-         return 'Invalid version'

-     version = RpmVersion(version_id)

-     if version.is_legacy():

-         # LegacyVersions are not supported in this context

-         return 'Invalid version'

-     if len(version.version) == 1:

-         return 'Invalid version'

-     upper_version = RpmVersion(version_id)

-     upper_version.version.pop()

-     upper_version.increment()

-     return '({} >= {} with {} < {})'.format(

-         name, version, name, upper_version)

- 

- def convert_equal(name, operator, version_id):

-     if version_id.endswith('.*'):

-         version_id = version_id[:-2] + '.0'

-         return convert_compatible(name, '~=', version_id)

-     version = RpmVersion(version_id)

-     return '{} = {}'.format(name, version)

- 

- def convert_arbitrary_equal(name, operator, version_id):

-     if version_id.endswith('.*'):

-         return 'Invalid version'

-     version = RpmVersion(version_id)

-     return '{} = {}'.format(name, version)

- 

- def convert_not_equal(name, operator, version_id):

-     if version_id.endswith('.*'):

-         version_id = version_id[:-2]

-         version = RpmVersion(version_id)

-         if version.is_legacy():

-             # LegacyVersions are not supported in this context

-             return 'Invalid version'

-         version_gt = RpmVersion(version_id).increment()

-         version_gt_operator = '>='

-         # Prevent dev and pre-releases from satisfying a < requirement

-         version = '{}~~'.format(version)

-     else:

-         version = RpmVersion(version_id)

-         version_gt = version

-         version_gt_operator = '>'

-     return '({} < {} or {} {} {})'.format(

-         name, version, name, version_gt_operator, version_gt)

- 

- def convert_ordered(name, operator, version_id):

-     if version_id.endswith('.*'):

-         # PEP 440 does not define semantics for prefix matching

-         # with ordered comparisons

-         # see: https://github.com/pypa/packaging/issues/320

-         # and: https://github.com/pypa/packaging/issues/321

-         # This style of specifier is officially "unsupported",

-         # even though it is processed.  Support may be removed

-         # in version 21.0.

-         version_id = version_id[:-2]

-         version = RpmVersion(version_id)

-         if operator == '>':

-             # distutils will allow a prefix match with '>'

-             operator = '>='

-         if operator == '<=':

-             # distutils will not allow a prefix match with '<='

-             operator = '<'

-     else:

-         version = RpmVersion(version_id)

-     # For backwards compatibility, fallback to previous behavior with LegacyVersions

-     if not version.is_legacy():

-         # Prevent dev and pre-releases from satisfying a < requirement

-         if operator == '<' and not version.pre and not version.dev and not version.post:

-             version = '{}~~'.format(version)

-         # Prevent post-releases from satisfying a > requirement

-         if operator == '>' and not version.pre and not version.dev and not version.post:

-             version = '{}.0'.format(version)

-     return '{} {} {}'.format(name, operator, version)

- 

- OPERATORS = {'~=': convert_compatible,

-              '==': convert_equal,

-              '===': convert_arbitrary_equal,

-              '!=': convert_not_equal,

-              '<=': convert_ordered,

-              '<':  convert_ordered,

-              '>=': convert_ordered,

-              '>':  convert_ordered}

- 

- def convert(name, operator, version_id):

-     return OPERATORS[operator](name, operator, version_id)

- 

- def convert_requirement(req):

-     parsed_req = Requirement.parse(req)

-     reqs = []

-     for spec in parsed_req.specs:

-         reqs.append(convert(parsed_req.project_name, spec[0], spec[1]))

-     if len(reqs) == 0:

-         return parsed_req.project_name

-     if len(reqs) == 1:

-         return reqs[0]

-     else:

-         reqs.sort()

-         return '({})'.format(' with '.join(reqs))

@@ -1,103 +0,0 @@ 

- """Best-effort parser for requirements.txt files"""

- 

- import urllib.parse

- from pathlib import Path

- import sys

- import os

- import re

- 

- # `#` starts a comment only at end of line and after whitespace

- COMMENT_RE = re.compile(r'(^|\s+)#.*$')

- 

- # Assume URLs start with a scheme; don't look for "egg=" URLs otherwise

- URL_START_RE = re.compile(r'^[-_+a-zA-Z0-9]+://')

- 

- ENV_VAR_RE = re.compile(r'(?P<var>\$\{(?P<name>[A-Z0-9_]+)\})')

- PKGNAME_RE = re.compile(r'^[-_a-zA-Z0-9]+')

- 

- # The requirements.txt format evolved rather organically; expect weirdness.

- 

- def convert_requirements_txt(lines, path:Path = None):

-     """Convert lines of a requirements file to PEP 440-style requirement strs

- 

-     This does NOT handle all of requirements.txt features (only pip can do

-     that), but tries its best.

- 

-     The resulting requirements might not actually be valid (either because

-     they're wrong in the file, or because we missed a special case).

- 

-     path is the path to the requirements.txt file, used for options like `-r`.

-     """

-     requirements = []

-     lines = combine_logical_lines(lines)

-     lines = strip_comments(lines)

-     lines = expand_env_vars(lines)

-     if path:

-         filename = path.name

-     else:

-         filename = '<requirements file>'

-     for line in lines:

-         if URL_START_RE.match(line):

-             # Handle URLs with "egg=..." fragments

-             # see https://pip.pypa.io/en/stable/cli/pip_install/#vcs-support

-             parsed_url = urllib.parse.urlparse(line)

-             parsed_fragment = urllib.parse.parse_qs(parsed_url.fragment)

-             if 'egg' in parsed_fragment:

-                 # Prepend the package name to the URL.

-                 match = PKGNAME_RE.match(parsed_fragment['egg'][0])

-                 if match:

-                     pkg_name = match[0]

-                     requirements.append(f'{pkg_name}@{line}')

-                     continue

-             # If that didn't work, pass the line on;

-             # the caller will deal with invalid requirements

-             requirements.append(line)

-         elif line.startswith('-r'):

-             recursed_path = line[2:].strip()

-             if path:

-                 recursed_path = path.parent / recursed_path

-             recursed_path = Path(recursed_path)

-             with recursed_path.open() as f:

-                 requirements.extend(convert_requirements_txt(f, recursed_path))

-         elif line.startswith('-'):

-             raise ValueError(f'{filename}: unsupported requirements file option: {line}')

-         else:

-             requirements.append(line)

-     return requirements

- 

- def combine_logical_lines(lines):

-     """Combine logical lines together (backslash line-continuation)"""

-     pieces = []

-     for line in lines:

-         line = line.rstrip('\n')

-         # Whole-line comments *only* are removed before line-contionuation

-         if COMMENT_RE.match(line):

-             continue

-         if line.endswith('\\'):

-             pieces.append(line[:-1])

-         else:

-             # trailing whitespace is only removed from full logical lines

-             pieces.append(line.rstrip())

-             yield ''.join(pieces)

-             pieces = []

-     yield ''.join(pieces)

- 

- 

- def strip_comments(lines):

-     for line in lines:

-         line, *rest = COMMENT_RE.split(line, maxsplit=1)

-         line = line.strip()

-         if line:

-             yield line

- 

- 

- def expand_env_vars(lines):

-     def repl(match):

-         value = os.getenv(match['name'])

-         if value is None:

-             return match['var']

-         return value

-     for line in lines:

-         if match := ENV_VAR_RE.search(line):

-             var = match['var']

-         yield ENV_VAR_RE.sub(repl, line)

file modified
+2 -2
@@ -18,7 +18,7 @@ 

  # https://github.com/rpm-software-management/rpm/issues/1865

  # The original lists can be found here:

  # https://github.com/rpm-software-management/rpm/blob/master/scripts/brp-compress

- MANPAGE_EXTENSIONS = ['gz', 'Z', 'bz2', 'xz', 'lzma', 'zst', 'zstd']

+ MANPAGE_EXTENSIONS = ['gz', 'Z', 'bz2', 'xz', 'lzma']

  MANDIRS = [

      '/man/man*',

      '/man/*/man*',
@@ -189,7 +189,7 @@ 

          >>> normalize_manpage_filename(PosixPath('/usr'), BuildrootPath('/usr/share/doc/en/man/man1/getmac.1'))

          BuildrootPath('/usr/share/doc/en/man/man1/getmac.1*')

  

-         >>> normalize_manpage_filename(PosixPath('/usr'), BuildrootPath('/usr/share/man/man8/abc.8.zstd'))

+         >>> normalize_manpage_filename(PosixPath('/usr'), BuildrootPath('/usr/share/man/man8/abc.8.bz2'))

          BuildrootPath('/usr/share/man/man8/abc.8*')

  

          >>> normalize_manpage_filename(PosixPath('/usr'), BuildrootPath('/usr/kerberos/man/dir'))

@@ -1,78 +0,0 @@ 

- from pathlib import Path

- from textwrap import dedent

- 

- from pyproject_requirements_txt import convert_requirements_txt

- 

- 

- def test_requirements_add_pkgname():

-     reqs_txt = dedent(r"""

-         good@git+https://github.com/monty/spam.git@master#egg=bad

-         git+https://github.com/monty/spam.git@master#egg=ugly

-         https://example.com/undead.tar.gz#egg=undead ; python_version > 3.0

-     """)

-     result = convert_requirements_txt(reqs_txt.splitlines())

- 

-     expected = [

-         'good@git+https://github.com/monty/spam.git@master#egg=bad',

-         'ugly@git+https://github.com/monty/spam.git@master#egg=ugly',

-         'undead@https://example.com/undead.tar.gz#egg=undead ; python_version > 3.0',

-     ]

-     assert result == expected

- 

- 

- def test_requirements_preprocess(monkeypatch):

-     reqs_txt = dedent(r"""

-         Normal_Req ~= 1.2.0

-            whitespace-stripped < 3    <END>

- 

-         # indentation is preserved in continuations:

-         foo <=\

-             30

-         bar<=   \

-         30

-         # names and operators can be split:

-         this-was-\

-         too-long<\

-         =30  

- 

-         # this is not a multi-line comment \

-         some-dep

-              # neither is this \

-         other-dep

-         another-dep  # but this *is* a multi-line coment \

-         so any garbage can be here

-         dep-a # and this comment ends with the blank line below \

- 

-         dep-b

-         ${ENVVAR}

-         whitespace-stripped-before-substitution   ${SPACE}

-         ${MISSING_ENVVAR}

-     """.replace('<END>', ''))

-     monkeypatch.setenv('ENVVAR', 'package-from-env')

-     monkeypatch.setenv('SPACE', ' ')

-     monkeypatch.delenv('MISSING_ENVVAR', raising=False)

-     result = convert_requirements_txt(reqs_txt.splitlines())

- 

-     expected = [

-         'Normal_Req ~= 1.2.0',

-         'whitespace-stripped < 3',

-         'foo <=    30',

-         'bar<=   30',

-         'this-was-too-long<=30',

-         'some-dep',

-         'other-dep',

-         'another-dep',

-         'dep-a',

-         'dep-b',

-         'package-from-env',

-         'whitespace-stripped-before-substitution    ',

-         '${MISSING_ENVVAR}',

-     ]

-     #result = expected

-     assert result == expected

- 

-     # This test uses pip internals, so it might break in the future.

-     from pip._internal.req.req_file import preprocess

-     expected = [line for lineno, line in preprocess(reqs_txt)]

-     assert result == expected

- 

Supersedes: https://src.fedoraproject.org/rpms/pyproject-rpm-macros/pull-request/284

This brings a stripped down version of the pyproject-rpm-macros to EPEL 8. Here is a breakdown of the changes:

  • Remove the %pyproject_buildrequires and %tox macro definitions, along with their associated python code
  • Fix the compare_mandata.py to account for differences in RHEL 8's brp-compress script and replace imports from pyproject_buildrequires.py .
  • Depend on appropriate macro package for python38 or python39.
  • Adjust Requires to pull in default packages always included by %pyproject_buildrequires. This ensures that packagers don't need to manually specify additional BRs that they didn't when using the old macros.
  • Clarify that this is a stripped down version in %description.

I could help co-maintain this in EPEL 8 if you are open to this PR. I understand if you're not given the amount of changes from the version in Fedora.

This should probably be a separate commit or not included in this PR at all. What do you prefer?

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci

What does "needed by pip wheel" mean? wheel should only be needed with the default build backend.

This should probably be a separate commit or not included in this PR at all. What do you prefer?

If this works in Fedora, let's submit it as a PR there? No need to bump the release.

Note that once again I express my objection against this stripped-down version of this package. It will not allow packagers to copy-paste a Fedora spec to EPEL anyway, and it will lead to confusion.

However, if you are willing to maintain this for the next couple of years, I will not stand in your way. I do have one request though. Please, don't advertise this as version 1.x. Please, pick a version scheme that starts with 0.

nb: we don't use functools.partial to be able to use pytest's capsys

I don't think this is relevant in compare_mandata.py

# from pyproject_buildrequires import print_err

Suggestion: Don't comment out code, delete it.

# from pyproject_buildrequires import print_err

Suggestion: Don't comment out code, delete it.

Yeah, you're right :).

If this works in Fedora, let's submit it as a PR there? No need to bump the release.

Will do.

nb: we don't use functools.partial to be able to use pytest's capsys

I don't think this is relevant in compare_mandata.py

I'll delete it.

What does "needed by pip wheel" mean? wheel should only be needed with the default build backend.

By "default build backend", you mean setuptools?


Note that once again I express my objection against this stripped-down version of this package. It will not allow packagers to copy-paste a Fedora spec to EPEL anyway, and it will lead to confusion.

Thanks for your feedback. The idea is to require less specfile changes when reusing specfiles from Fedora and provide a consistent way to build projects that don't have legacy setup.pys without each one having to reinvent the wheel (pun intended). I acknowledge that it's not possible to completely copy-paste a Fedora specfile that uses pyproject-rpm-macros to EPEL, but to be fair, it's often not possible to do that in other domains. I feel that this is something EPEL packagers have come to accept.

However, I don't really want to go against your wishes, so I'll reconsider. Do you have any suggestions about how to make this less confusing to packagers, though? I added sane dependencies and I clarified the description. I suppose I could also change the summary. I did consider renaming the package to something like pyproject-rpm-macros-minimal or pyproject-rpm-macros-core, but I'm not sure that's really worth it. (It would be nice if pyproject-rpm-macros in Fedora could also Provide that if we do that)

However, if you are willing to maintain this for the next couple of years, I will not stand in your way. I do have one request though. Please, don't advertise this as version 1.x. Please, pick a version scheme that starts with 0.

Of course. If I decide to go through with it, I'll probably keep up with semver and start at 0.1.0.

packaging is only required by %pyproject_buildrequires (and some of the other removed parts), right?

packaging is only required by %pyproject_buildrequires (and some of the other removed parts), right?

I think so, yes.

By "default build backend", you mean setuptools?

I mean when no backend is specified. In that case, it's setuptools+wheel. But plain setuptools (when specified) might not need wheel.

I decided not to work on this for now

Pull-Request has been closed by churchyard

2 years ago