%pyproject_save_files: Implement support for multi-package specfiles

This adds a new -D/--dist-name option for:

 - %pyproject_save_files
 - %pyproject_files
 - %pyproject_check_import
 - %pyproject_extras_subpkg

And allows the use of all of those in multi-package spec files.
See README.md changes for usage.

Assisted-By: Claude Opus 4.6

Many useful suggestions by Maxwell G <maxwell@gtmx.me> and Karolina Surma <ksurma@redhat.com>.
This commit is contained in:
Miro Hrončok 2026-05-18 13:33:15 +02:00
parent b433764db6
commit 44cabdfd57
6 changed files with 258 additions and 55 deletions

View File

@ -190,11 +190,14 @@ Is roughly equivalent to:
If you use `%pyproject_wheel` to build multiple wheels, for example like this:
%pyproject_wheel -d project_a
%pyproject_wheel -d project_b
%pyproject_wheel --directory directory1/
%pyproject_wheel --directory directory2/
You still only call `%pyproject_install` once and it will install all such wheels.
Support for `%pyproject_save_files` with multiple wheels is not implemented.
To use `%pyproject_save_files` (and optionally `%pyproject_check_import`) with multiple wheels,
use the `-D`/`--dist-name` option to select a specific package,
for details, see *Generating the %files section*
(and *Performing an import check on all importable modules*).
If you use `%pyproject_buildrequires -d ...` for multiple co-dependent packages
in one spec file, it will create one or more build time dependencies
@ -413,6 +416,17 @@ If you wish to rename, remove or otherwise change the installed files of a packa
If possible, remove/rename such files in `%prep`.
If not possible, avoid using `%pyproject_save_files` or edit/replace `%{pyproject_files}`.
When processing files from multiple Python packages
(most likely as a result of multiple `%pyproject_wheel` invocations),
use the `-D`/`--dist-name` option to select a specific package
in `%pyproject_save_files` and `%{pyproject_files}`:
%pyproject_save_files --dist-name package_a module_glob_a...
%pyproject_save_files --dist-name package_b module_glob_b...
%files -n python3-package-a -f %{pyproject_files -D package_a}
%files -n python3-package-b -f %{pyproject_files -D package_b}
Performing an import check on all importable modules
----------------------------------------------------
@ -460,6 +474,15 @@ The `%pyproject_check_import` macro also accepts positional arguments with
additional qualified module names to check, useful for example if some modules are installed manually.
Note that filtering by `-t`/`--top-level-only`/`-e`/`--exclude` also applies to the positional arguments.
When listing files from multiple Python packages via `%pyproject_save_files -D`/`--dist-name`,
pass `-D`/`--dist-name` to `%pyproject_check_import` as well.
%pyproject_save_files --dist-name package_a module_glob_a...
%pyproject_save_files --dist-name package_b module_glob_b...
%pyproject_check_import --dist-name package_a
%pyproject_check_import --dist-name package_b
Another macro, `%_pyproject_check_import_allow_no_modules` allows to pass the import check,
even if no Python modules are detected in the package.
This may be a valid case for packages containing e.g. typing stubs.
@ -496,6 +519,13 @@ These arguments are still required:
* Positional arguments: the extra name(s).
Multiple subpackages are generated when multiple names are provided.
When processing files from multiple Python packages
(see *Generating the %files section* for details),
pass `-D` to `%pyproject_extras_subpkg` as well:
%pyproject_extras_subpkg -n python3-package-a -D package_a extra1
%pyproject_extras_subpkg -n python3-package-b -D package_b extra2
Provisional: Declarative Buildsystem (RPM 4.20+)
------------------------------------------------
@ -603,19 +633,21 @@ Here is a complete reference of all options:
### `%pyproject_save_files`
| Short | Long | Description |
|-------|-----------------------|-------------------------------------------------|
| `-l` | `--assert-license` | Fail when no License-File (PEP 639) is found |
| `-L` | `--no-assert-license` | Don't fail on missing License-File (default) |
| `-M` | `--allow-no-modules` | Allow no module globs |
| `-a` | `--auto` | Include non-module files (same as `+auto`) |
| Short | Long | Description |
|------------|------------------------|--------------------------------------------------------------|
| `-l` | `--assert-license` | Fail when no License-File (PEP 639) is found |
| `-L` | `--no-assert-license` | Don't fail on missing License-File (default) |
| `-M` | `--allow-no-modules` | Allow no module globs |
| `-D NAME` | `--dist-name NAME` | Save files for a specific distribution package (multi-wheel) |
| `-a` | `--auto` | Include non-module files (same as `+auto`) |
### `%pyproject_check_import`
| Short | Long | Description |
|-----------|--------------------|------------------------------------|
| `-e GLOB` | `--exclude GLOB` | Exclude module names matching glob |
| `-t` | `--top-level-only` | Only check top-level modules |
| Short | Long | Description |
|-----------|-----------------------|---------------------------------------------------|
| `-e GLOB` | `--exclude GLOB` | Exclude module names matching glob |
| `-D NAME` | `--dist-name NAME` | Check imports for a specific distribution package |
| `-t` | `--top-level-only` | Only check top-level modules |
### `%tox`

View File

@ -20,9 +20,28 @@
# %%_pyproject_files_prefix defined in srpm macros
%pyproject_files %{_builddir}/%{_pyproject_files_prefix}-pyproject-files
%_pyproject_modules %{_builddir}/%{_pyproject_files_prefix}-pyproject-modules
%_pyproject_ghost_distinfo %{_builddir}/%{_pyproject_files_prefix}-pyproject-ghost-distinfo
# PEP 503 name normalization: lowercase, one or more [-_.] → single -
%__pyproject_normalize_name(-) %{lua:print((rpm.expand("%1"):lower():gsub("[%-_.]+", "-")))}
%_pyproject_files_base %{_builddir}/%{_pyproject_files_prefix}-pyproject-files
%_pyproject_modules_base %{_builddir}/%{_pyproject_files_prefix}-pyproject-modules
%_pyproject_ghost_distinfo_base %{_builddir}/%{_pyproject_files_prefix}-pyproject-ghost-distinfo
%pyproject_files(-) %{lua:
require("fedora.rpm.pyproject_getopt").getopt({
{short="D", long="dist-name", value=true},
})}%{_pyproject_files_base}%{?__pyproject_opt_D:-%{__pyproject_normalize_name %{__pyproject_opt_D}}}
%_pyproject_modules(-) %{lua:
require("fedora.rpm.pyproject_getopt").getopt({
{short="D", long="dist-name", value=true},
})}%{_pyproject_modules_base}%{?__pyproject_opt_D:-%{__pyproject_normalize_name %{__pyproject_opt_D}}}
%_pyproject_ghost_distinfo(-) %{lua:
require("fedora.rpm.pyproject_getopt").getopt({
{short="D", long="dist-name", value=true},
})}%{_pyproject_ghost_distinfo_base}%{?__pyproject_opt_D:-%{__pyproject_normalize_name %{__pyproject_opt_D}}}
%_pyproject_record %{_builddir}/%{_pyproject_files_prefix}-pyproject-record
%_pyproject_buildrequires %{_builddir}/%{_pyproject_files_prefix}-pyproject-buildrequires
# %%_pyproject_dep_overrides defined in srpm macros
@ -95,7 +114,7 @@ if [ -d %{buildroot}%{_bindir} ]; then
%py3_shebang_fix %{buildroot}%{_bindir}/*
rm -rfv %{buildroot}%{_bindir}/__pycache__
fi
rm -f %{_pyproject_ghost_distinfo}
rm -f %{_pyproject_ghost_distinfo_base}
site_dirs=()
# Process %%{python3_sitelib} if exists
if [ -d %{buildroot}%{python3_sitelib} ]; then
@ -108,7 +127,12 @@ fi
# Process all *.dist-info dirs in sitelib/sitearch
for site_dir in ${site_dirs[@]}; do
for distinfo in %{buildroot}$site_dir/*.dist-info; do
echo "%ghost %dir ${distinfo#%{buildroot}}" >> %{_pyproject_ghost_distinfo}
echo "%ghost %dir ${distinfo#%{buildroot}}" >> %{_pyproject_ghost_distinfo_base}
_normalized=$(PYTHONPATH=%{_rpmconfigdir}/redhat \\
%{__python3} -sBc \\
"import sys; from pyproject_save_files import canonical_name_from_distinfo; print(canonical_name_from_distinfo(sys.argv[1]))" \\
"$(basename ${distinfo})")
echo "%ghost %dir ${distinfo#%{buildroot}}" >> "%{_pyproject_ghost_distinfo_base}-${_normalized}"
sed -i 's/pip/rpm/' ${distinfo}/INSTALLER
PYTHONPATH=%{_rpmconfigdir}/redhat \\
%{__python3} -B %{_rpmconfigdir}/redhat/pyproject_preprocess_record.py \\
@ -122,16 +146,17 @@ for site_dir in ${site_dirs[@]}; do
fi
done
done
lines=$(wc -l %{_pyproject_ghost_distinfo} | cut -f1 -d" ")
lines=$(wc -l %{_pyproject_ghost_distinfo_base} | cut -f1 -d" ")
if [ $lines -ne 1 ]; then
echo -e "\\n\\nWARNING: %%%%pyproject_extras_subpkg won't work without explicit -i or -F, found $lines dist-info directories.\\n\\n" >&2
rm %{_pyproject_ghost_distinfo} # any attempt to use this will fail
echo -e "%%%%pyproject_extras_subpkg will require -D <name> or explicit -i/-F with multiple dist-info directories (found $lines)." >&2
rm %{_pyproject_ghost_distinfo_base} # any attempt to use this will fail
fi
}
# Note: the three times nested questionmarked -i -f -F pattern means: If none of those options was used -- in that case, we inject our own -f
%pyproject_extras_subpkg(n:i:f:FaA) %{expand:%{?python_extras_subpkg:%{python_extras_subpkg%{!?-i:%{!?-f:%{!?-F: -f %{_pyproject_ghost_distinfo}}}} %**}}}
# -D selects the per-package ghost distinfo file for multi-wheel installs
%pyproject_extras_subpkg(n:i:f:FaAD:) %{expand:%{?python_extras_subpkg:%{python_extras_subpkg%{!?-i:%{!?-f:%{!?-F: -f %{_pyproject_ghost_distinfo %{?-D}}}}} %{?-n} %{?-i} %{?-f} %{?-F} %{?-a} %{?-A} %*}}}
# Escaping shell-globs, percentage signs and spaces was reworked in RPM 4.19+
@ -144,12 +169,13 @@ fi
{short="L", long="no-assert-license"},
{short="M", long="allow-no-modules"},
{short="a", long="auto"},
{short="D", long="dist-name", value=true},
})}\
%{expand:\\\
%{expr:v"0%{?rpmversion}" >= v"4.18.90" ? "RPM_FILES_ESCAPE=4.19" : "RPM_FILES_ESCAPE=4.18" } \\
%{__python3} %{_rpmconfigdir}/redhat/pyproject_save_files.py \\
--output-files "%{pyproject_files}" \\
--output-modules "%{_pyproject_modules}" \\
--output-files "%{pyproject_files %{?__pyproject_opt_D:-D %{__pyproject_opt_D}}}" \\
--output-modules "%{_pyproject_modules %{?__pyproject_opt_D:-D %{__pyproject_opt_D}}}" \\
--buildroot "%{buildroot}" \\
--sitelib "%{python3_sitelib}" \\
--sitearch "%{python3_sitearch}" \\
@ -162,23 +188,32 @@ fi
%pyproject_check_import(-) \
%{lua:require("fedora.rpm.pyproject_getopt").getopt({
{short="e", long="exclude", value=true, separator=" -e "},
{short="D", long="dist-name", value=true},
{short="t", long="top-level-only"},
})}\
%{expand:\\\
if [ ! -f "%{_pyproject_modules}" ]; then
echo 'ERROR: %%%%pyproject_check_import only works when %%%%pyproject_save_files is used' >&2
if [ ! -f "%{_pyproject_modules %{?__pyproject_opt_D:-D %{__pyproject_opt_D}}}" ]; then
_modules_glob=( %{_pyproject_modules_base}-* )
if [ -e "${_modules_glob[0]}" ]; then
_available=$(printf '%%s\n' "${_modules_glob[@]}" | sed 's|.*-pyproject-modules-||' | tr '\n' ' ')
echo "ERROR: %%%%pyproject_check_import requires -D <name> when %%%%pyproject_save_files was called with -D" >&2
echo "Available -D values: ${_available}" >&2
else
echo 'ERROR: %%%%pyproject_check_import only works when %%%%pyproject_save_files is used' >&2
fi
exit 1
fi
%py3_check_import -f "%{_pyproject_modules}" %{?__pyproject_opt_e:-e %{__pyproject_opt_e}} %{?__pyproject_opt_t:-t} %{?__pyproject_positional_args}
%py3_check_import -f "%{_pyproject_modules %{?__pyproject_opt_D:-D %{__pyproject_opt_D}}}" %{?__pyproject_opt_e:-e %{__pyproject_opt_e}} %{?__pyproject_opt_t:-t} %{?__pyproject_positional_args}
}
%_pyproject_check_import_allow_no_modules(-) \
%{lua:require("fedora.rpm.pyproject_getopt").getopt({
{short="e", long="exclude", value=true, separator=" -e "},
{short="D", long="dist-name", value=true},
{short="t", long="top-level-only"},
})}\
if [ -z "$(cat %{_pyproject_modules})" ]; then\
if [ -z "$(cat %{_pyproject_modules %{?__pyproject_opt_D:-D %{__pyproject_opt_D}}})" ]; then\
echo "No modules to check found, exiting check"\
else\
%pyproject_check_import %{?**}\

View File

@ -14,7 +14,7 @@ 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.22.2
Version: 1.23.0
Release: 1%{?dist}
# Macro files
@ -179,6 +179,9 @@ export HOSTNAME="rpmbuild" # to speedup tox in network-less mock, see rhbz#1856
%changelog
* Mon May 18 2026 Miro Hrončok <mhroncok@redhat.com> - 1.23.0-1
- %%pyproject_save_files: Implement support for multi-package specfiles
* Thu May 14 2026 Miro Hrončok <mhroncok@redhat.com> - 1.22.2-1
- %%pyproject_buildrequires: Fix "Requirement satisfied/not satisfied" messages to show overridden constraints from %%pyproject_patch_dependency
- %%pyproject_buildrequires: Stop using deprecated argparse.FileType

View File

@ -8,6 +8,7 @@ from collections import defaultdict
from keyword import iskeyword
from pathlib import PosixPath, PurePosixPath
from importlib.metadata import Distribution
from packaging.utils import canonicalize_name
# From RPM's build/files.c strtokWithQuotes delim argument
@ -45,6 +46,24 @@ MANDIRS = [
]
def canonical_name_from_distinfo(distinfo_dir_name):
"""Extract the canonicalized distribution name from a .dist-info directory name.
Examples:
>>> canonical_name_from_distinfo('MarkupSafe-2.0.1.dist-info')
'markupsafe'
>>> canonical_name_from_distinfo('tldr-0.5.dist-info')
'tldr'
>>> canonical_name_from_distinfo('My.Package_name-1.0.dist-info')
'my-package-name'
"""
raw = distinfo_dir_name.removesuffix('.dist-info').rsplit('-', 1)[0]
return canonicalize_name(raw)
class BuildrootPath(PurePosixPath):
"""
This path represents a path in a buildroot.
@ -760,15 +779,31 @@ def parse_varargs(varargs):
return globs, include_auto
def load_parsed_record(pyproject_record):
def load_parsed_record(pyproject_record, dist_name=None):
parsed_record = {}
with open(pyproject_record) as pyproject_record_file:
content = json.load(pyproject_record_file)
if len(content) > 1:
raise FileExistsError("%pyproject_install has found more than one *.dist-info/RECORD file. "
"Currently, %pyproject_save_files supports only one wheel → one file list mapping. "
"Feel free to open a bugzilla for pyproject-rpm-macros and describe your usecase.")
# Map each record path to its canonical dist name
dist_names = {rp: canonical_name_from_distinfo(BuildrootPath(rp).parent.name)
for rp in content}
available = sorted(dist_names.values()) # used in error messages
if dist_name:
normalized_target = canonicalize_name(dist_name)
content = {rp: files for rp, files in content.items()
if dist_names[rp] == normalized_target}
if not content:
raise ValueError(
f"No dist-info found matching dist name '{dist_name}'. "
f"Available: {', '.join(available)}"
)
elif len(content) > 1:
raise ValueError(
"%pyproject_install has found more than one *.dist-info/RECORD file. "
"Use %pyproject_save_files -D <name> to select a package. "
f"Available: {', '.join(available)}"
)
# Redefine strings stored in JSON to BuildRootPaths
for record_path, files in content.items():
@ -786,12 +821,12 @@ def dist_metadata(buildroot, record_path):
return dist.metadata
def pyproject_save_files_and_modules(buildroot, sitelib, sitearch, python_version, pyproject_record, prefix, assert_license, allow_no_modules, auto, varargs):
def pyproject_save_files_and_modules(buildroot, sitelib, sitearch, python_version, pyproject_record, prefix, assert_license, allow_no_modules, auto, varargs, dist_name=None):
"""
Takes arguments from the %{pyproject_save_files} macro
Returns tuple: list of paths for the %files section and list of module names
for the %check section
Returns tuple: list of paths for the %files section, list of module names
for the %check section, and list of record paths (BuildrootPaths)
Raises ValueError when assert_license is true and no License-File (PEP 639)
is found.
@ -811,7 +846,7 @@ def pyproject_save_files_and_modules(buildroot, sitelib, sitearch, python_versio
raise ValueError(
"%pyproject_save_files -M cannot be used together with module globs."
)
parsed_records = load_parsed_record(pyproject_record)
parsed_records = load_parsed_record(pyproject_record, dist_name)
final_file_list = []
final_module_list = []
@ -842,11 +877,11 @@ def pyproject_save_files_and_modules(buildroot, sitelib, sitearch, python_versio
"and include the %license file in %files manually."
)
return final_file_list, final_module_list
return final_file_list, final_module_list, list(parsed_records)
def main(cli_args):
file_section, module_names = pyproject_save_files_and_modules(
file_section, module_names, record_paths = pyproject_save_files_and_modules(
cli_args.buildroot,
cli_args.sitelib,
cli_args.sitearch,
@ -857,10 +892,24 @@ def main(cli_args):
cli_args.allow_no_modules,
cli_args.auto,
cli_args.varargs,
cli_args.dist_name,
)
cli_args.output_files.write_text("\n".join(file_section) + "\n", encoding="utf-8")
cli_args.output_modules.write_text("\n".join(module_names) + "\n", encoding="utf-8")
files_text = "\n".join(file_section) + "\n"
modules_text = "\n".join(module_names) + "\n"
cli_args.output_files.write_text(files_text, encoding="utf-8")
cli_args.output_modules.write_text(modules_text, encoding="utf-8")
# When no -D was given, also write to the named path (derived from dist-info)
# so that %{pyproject_files -D name} works even without -D in %pyproject_save_files
if not cli_args.dist_name:
record_path = record_paths[0]
normalized = canonical_name_from_distinfo(record_path.parent.name)
named_files = PosixPath(f"{cli_args.output_files}-{normalized}")
named_modules = PosixPath(f"{cli_args.output_modules}-{normalized}")
named_files.write_text(files_text, encoding="utf-8")
named_modules.write_text(modules_text, encoding="utf-8")
def argparser():
@ -897,6 +946,10 @@ def argparser():
"-M", "--allow-no-modules", action="store_true", default=False,
help="Don't fail when no globs are provided, only include non-modules data in the generated filelist.",
)
parser.add_argument(
"-D", "--dist-name", type=str, default=None,
help="Save files for a specific distribution package (for multi-wheel installs).",
)
parser.add_argument(
"-a", "--auto", action="store_true", default=False,
help="Include all non-module, non-sitelib files (same as +auto).",

View File

@ -6,7 +6,7 @@ from pprint import pprint
from pyproject_preprocess_record import parse_record, read_record, save_parsed_record
from pyproject_save_files import argparser, generate_file_list, BuildrootPath
from pyproject_save_files import argparser, canonical_name_from_distinfo, generate_file_list, BuildrootPath
from pyproject_save_files import main as save_files_main
from pyproject_save_files import module_names_from_path
@ -242,12 +242,12 @@ def test_cli_no_pyproject_record(tmp_path, pyproject_record):
def test_cli_too_many_RECORDS(tldr_root, output_files, output_modules, pyproject_record):
# Two calls to simulate how %pyproject_install process more than one RECORD file
prepare_pyproject_record(tldr_root,
content=("foo/bar/dist-info/RECORD", []))
content=("foo/bar-1.0.dist-info/RECORD", []))
prepare_pyproject_record(tldr_root,
content=("foo/baz/dist-info/RECORD", []))
content=("foo/baz-2.0.dist-info/RECORD", []))
cli_args = argparser().parse_args([*default_options(output_files, output_modules, tldr_root, pyproject_record), "tldr*"])
with pytest.raises(FileExistsError):
with pytest.raises(ValueError, match="Use %pyproject_save_files -D"):
save_files_main(cli_args)
@ -277,3 +277,66 @@ def test_cli_bad_namespace(tldr_root, output_files, output_modules, pyproject_re
with pytest.raises(ValueError):
save_files_main(cli_args)
def test_canonical_name_from_distinfo():
assert canonical_name_from_distinfo("MarkupSafe-2.0.1.dist-info") == "markupsafe"
assert canonical_name_from_distinfo("tldr-0.5.dist-info") == "tldr"
assert canonical_name_from_distinfo("my_package-1.0.0.dist-info") == "my-package"
assert canonical_name_from_distinfo("zope.event-1.0.dist-info") == "zope-event"
def test_cli_with_dist_name(tldr_root, pyproject_record):
output_files = tldr_root / "files"
output_modules = tldr_root / "modules"
cli_args = argparser().parse_args([
*default_options(output_files, output_modules, tldr_root, pyproject_record),
"-D", "tldr",
"tldr*",
])
save_files_main(cli_args)
assert output_files.exists()
assert "tldr" in output_files.read_text()
assert output_modules.exists()
def test_cli_with_dist_name_case_insensitive(tldr_root, pyproject_record):
output_files = tldr_root / "files"
output_modules = tldr_root / "modules"
cli_args = argparser().parse_args([
*default_options(output_files, output_modules, tldr_root, pyproject_record),
"-D", "TLDR",
"tldr*",
])
save_files_main(cli_args)
assert output_files.exists()
def test_cli_dist_name_not_found(tldr_root, pyproject_record):
output_files = tldr_root / "files"
output_modules = tldr_root / "modules"
cli_args = argparser().parse_args([
*default_options(output_files, output_modules, tldr_root, pyproject_record),
"-D", "nonexistent",
"tldr*",
])
with pytest.raises(ValueError, match="No dist-info found matching"):
save_files_main(cli_args)
def test_cli_single_record_named_output(tldr_root, pyproject_record):
output_files = tldr_root / "files"
output_modules = tldr_root / "modules"
cli_args = argparser().parse_args([
*default_options(output_files, output_modules, tldr_root, pyproject_record),
"tldr*",
])
save_files_main(cli_args)
# Without -D, base output should exist
assert output_files.exists()
# Named output (derived from dist-info) should also exist
named_files = tldr_root / "files-tldr"
named_modules = tldr_root / "modules-tldr"
assert named_files.exists()
assert named_modules.exists()
assert output_files.read_text() == named_files.read_text()

View File

@ -14,7 +14,23 @@ BuildRequires: python3-devel
%description
This package tests that we can build and install 2 wheels at once.
One of them is "noarch" and one has an extension module.
This also tests the -d option for %%pyproject_buildrequires/%%pyproject_wheel.
This also tests the -d option for %%pyproject_buildrequires/%%pyproject_wheel
and the -D option for %%pyproject_save_files/%%pyproject_check_import.
%package -n python3-double-markupsafe
Summary: MarkupSafe from double-install test
%description -n python3-double-markupsafe
MarkupSafe subpackage.
%package -n python3-double-tldr
Summary: tldr from double-install test
BuildArch: noarch
%description -n python3-double-tldr
tldr subpackage.
%prep
@ -25,7 +41,7 @@ tar xf %{SOURCE2}
%generate_buildrequires
%pyproject_buildrequires --no-runtime --directory markupsafe-%{markupsafe_version}
%pyproject_buildrequires --no-runtime --directory tldr-%{tldr_version}
%pyproject_buildrequires --directory tldr-%{tldr_version}
%build
@ -39,7 +55,8 @@ set -o pipefail
# This should install both the wheels:
%pyproject_install
) 2>&1 | tee install.log
#pyproject_save_files is not possible with 2 dist-infos
%pyproject_save_files --dist-name markupsafe markupsafe
%pyproject_save_files -D tldr tldr +auto
%check
@ -56,11 +73,11 @@ cd ..
# Internal regression check for %%pyproject_install with multiple wheels
grep 'binary operator expected' install.log && exit 1 || true
grep 'too many arguments' install.log && exit 1 || true
# Check that %%pyproject_check_import -D works
%pyproject_check_import -D markupsafe
%pyproject_check_import -D tldr
%files
%{_bindir}/tldr*
%pycached %{python3_sitelib}/tldr.py
%{python3_sitelib}/tldr-%{tldr_version}.dist-info/
%{python3_sitearch}/[Mm]arkup[Ss]afe-%{markupsafe_version}.dist-info/
%{python3_sitearch}/markupsafe/
%files -n python3-double-markupsafe -f %{pyproject_files -D markupsafe}
%files -n python3-double-tldr -f %{pyproject_files --dist-name tldr}