Commit Graph

389 Commits

Author SHA1 Message Date
Miro Hrončok
44cabdfd57 %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>.
2026-07-09 12:18:58 +02:00
Miro Hrončok
b433764db6 Add long options to %_pyproject_check_import_allow_no_modules
For consistency. Also necessary for the following change.
2026-07-09 12:18:30 +02:00
Miro Hrončok
fd0552315a CI: Dump all package versions
This is useful for debugging some failures.
2026-07-09 12:17:00 +02:00
Miro Hrončok
7ef083b7f0 Remove F42-only code as Fedora 42 goes EOL 2026-06-30 15:52:25 +00:00
Miro Hrončok
6f855abc78 CI: Update specfiles that could no longer be built with Python 3.15
The updated ipykernel no longer appears to need pkg_resources.
2026-06-24 19:38:11 +02:00
Miro Hrončok
bcaf3ea9d6 Add missing METADATA to test data for Python 3.15 compatibility
Python 3.15 changed importlib.metadata.PathDistribution.metadata
to raise MetadataNotFound instead of returning None when no METADATA
file exists (https://github.com/python/cpython/issues/143387).

Add minimal METADATA entries for the 8 test packages that were
missing them, so the test fixtures create METADATA files on disk.

Assisted-By: Claude Opus 4.6
2026-06-24 14:42:55 +02:00
Miro Hrončok
ac52d4a3c3 pyproject_buildrequires.py: Stop using deprecated argparse.FileType for requirement files
argparse.FileType is deprecated since Python 3.14 and triggers
(Pending)DeprecationWarning. Use pathlib.Path as the argument type
instead, and read file contents via read_text() at the call site.

Note that this brings two little changes, as pointed out by Maxwell G during review:

> This code is theoretically less performant than the previous code since it
> reads the whole file to a str and then converts it into a list instead
> of just iterating through the file stream like it did before.

This is acceptable to me. I don't think the possibility of requirements.txt file
so large that it would not fit into memory is worth considering.
Such file would likely cause memory exhaustion even later when we generate the deps.

> ...the list created by splitlines does not have the \ns preserved ...,

That's why pyproject_requirements_txt.py removed the rstrip('\n') call.

Assisted-By: Claude Opus 4.6
2026-05-14 11:44:09 +02:00
Miro Hrončok
e2a7be92a2 Fix "Requirement satisfied/not satisfied" messages to show overridden constraints
Previously, the log messages used the original requirement string
(before dependency overrides were applied), which was confusing.

Add a regression test that checks stderr contains the overridden form.

Assisted-By: Claude Opus 4.6
2026-05-14 11:42:31 +02:00
Miro Hrončok
9b2f271530 Refactor _apply_dependency_overrides to accept and return a Requirement object
The override check in self.dependency_overrides was done twice: once at
the call site and once inside _apply_dependency_overrides. By moving the
full logic into the method and having it accept a Requirement directly,
the redundant check is eliminated.

Also remove _base_package_name, since Requirement.name already strips
extras and the only remaining need is canonicalize_name.

Assisted-By: Claude Opus 4.6
2026-05-13 16:34:51 +02:00
Miro Hrončok
86cc949ba5 CI: Account for fedora-eln-release changing ID from fedora to eln
https://src.fedoraproject.org/rpms/fedora-eln-release/c/5533e2399c
2026-05-13 16:34:16 +02:00
Miro Hrončok
52160235fe CI: BuildRequire python3-pkg-resources in spec files that use pkg_resources
https://fedoraproject.org/wiki/Changes/Setuptools_82+

Later, we might need to update the tested versions.
2026-05-13 16:34:11 +02:00
Miro Hrončok
2001bf804e CI: Add regression test for 2acda15
Assisted-By: Claude Opus 4.6
2026-05-09 14:27:53 +02:00
Miro Hrončok
b9d5b4061a Tests: Make it easier to debug failures to load Lua tests 2026-05-09 00:04:05 +02:00
Miro Hrončok
83cc2c14ea CI: Add a test that parses all rawhide pyproject specs with rpm --specfile
Downloads rpm-specs-latest.tar.xz, finds all specs using %pyproject
macros, and runs rpm --specfile on each to verify they parse without
errors. Only runs on rawhide as the spec files are rawhide only.

Assisted-By: Claude Opus 4.6
2026-05-09 00:04:05 +02:00
Miro Hrončok
2acda15168 Fix a regression wrt option parsing for macros with backslash-escaped newlines in argument list
When macro arguments span across multiple lines with backslash-escaped newlines, e.g.:

    %{pyproject_buildrequires \
        -x pathops \
        -x autohint \
        -x json \
        -x repacker}

rpm.define() for positional args would fail with:

    Macro %__pyproject_positional_args has empty body
    error: lua script failed: .../pyproject_getopt.lua:252: error defining macro

Because the "-x pathops" would be consumed by value parsing
and all the positional arguments would be just backslash-escaped newlines.

Defining the %__pyproject_positional_args macro to whitespace is not possible,
unless properly quoted.

Filter out line-continuation artifacts (tokens containing only whitespace,
backslashes, and newlines) before parsing, so they cannot be misinterpreted
as option values or positional args. Extract a shared quote_value() function
for safe embedding of values in rpm.define() calls.

Additionally, RPM may embed newlines within tokens when macros use
%{expand:} with literal newlines. Such tokens are split on newlines
before parsing.

The regression impacted parsing of 14 Fedora Rawhide spec files:

- dcm2niix
- fontmake
- fonttools
- libarrow
- python-django5
- python-django6
- python-pydantic-extra-types
- python-pydantic-settings
- python-pylero
- python-pynetdicom
- python-tablib
- python-trimesh
- python-urllib3
- scapy

And all are now fixed.

No other Fedora Rawhide spec files fail to parse due to the long option support
after this fix.

Assisted-By: Claude Opus 4.6
2026-05-09 00:04:05 +02:00
Miro Hrončok
ef1bdc962d %pyproject_patch_dependency: Validate arguments early, in %prep
When python3 and pyproject_dependency_overrides.py are available,
the macro now calls parse_override_string at %prep time,
catching invalid overrides immediately instead of in %pyproject_buildrequires.
When only pyproject-srpm-macros is installed, validation is skipped gracefully.

Assisted-By: Claude Opus 4.6
2026-05-07 11:46:32 +02:00
Miro Hrončok
965cf2fb3b Sort sources to please the OCD gods 2026-05-07 11:43:19 +02:00
Miro Hrončok
3ed9e9406d Begone, source enumeration! 2026-05-07 11:41:34 +02:00
Miro Hrončok
2c431733bf %pyproject_save_files: Allow to use --auto instead of +auto
The +auto flag was a hack around missing support for long options.
We keep it working with no intention to remove it.

Assisted-By: Claude Opus 4.6
2026-05-07 11:30:33 +02:00
Miro Hrončok
6d3ad45c08 Add long option support for all public parametric macros
All public parametric macros now accept long options in addition
to short options. For example, %pyproject_buildrequires --no-runtime
is equivalent to %pyproject_buildrequires -R.

This is implemented via a Lua option parser (pyproject_getopt.lua)
installed to %{_rpmluadir}/fedora/rpm/. All macros use (-) to opt out
of RPM native getopt and call the Lua parser for validation, mutual
exclusion checks, and value extraction.

Other changes:
- Toxenv construction inlined in Lua, replacing pyproject_construct_toxenv.py
- Added --config-settings and --directory long forms to Python argparse
- Renamed --read-pyproject-dependencies to --pyproject-dependencies
  (this was not part of API and now it is, so I picked a shorter name)
- Added test_getopt_consistency.py to assert option mappings match
  across macros.pyproject, Python scripts, and README.md
- Converted ~half of test specs to use long options
- Documented all short/long mappings in README.md
- All Python scripts expose their argparser() functions fer easier consistency asserts

Co-Authored-By: Maxwell G <maxwell@gtmx.me>
Assisted-By: Claude Opus 4.6
2026-05-07 11:30:14 +02:00
Miro Hrončok
1beab3185a Always use !? for "not defined"
See https://github.com/rpm-software-management/rpm/discussions/2340
2026-05-07 11:27:34 +02:00
Tomáš Hrnčiar
1a0fbd04e9 Implement extras validation
Validate whether extras passed into %pyproject_buildrequires -x exist
in upstream metadata, otherwise ValueError is raised on Fedora >=45
and RHEL >=11. It will emit a warning instead on older releases.

Both user-specified extras and metadata extras are normalized following PEP 685 conventions.

Fixes: rhbz#2360766
2026-04-29 15:01:18 +02:00
Miro Hrončok
ae7141a450 Document %pyproject_patch_dependency in "multiple co-dependent packages"
But leave the original remark about -R being the only supported way,
%pyproject_patch_dependency is provisional.
2026-04-28 10:32:36 +01:00
Benjamin A. Beasley
31a5782ec1 Consolidate comments in the srpm macros 2026-04-28 10:32:36 +01:00
Benjamin A. Beasley
162bf58103 In python-virtualenv.spec, remove obsolete claim from description 2026-04-28 10:20:30 +01:00
Benjamin A. Beasley
4b661a591c Test %pyproject_patch_dependency in srpm macros
Remove the explicit `BuildRequires` on `python3-devel` from
`python-virtualenv.spec`.

Suggested by Miro Hrončok.
2026-04-28 10:20:30 +01:00
Benjamin A. Beasley
0861d28d96 Move %pyproject_patch_dependency implementation to pyproject-srpm-macros
- Make `pyproject-rpm-macros` depend on `pyproject-srpm-macros`
- Resolves: rhbz#2463187

The idea here is that `%pyproject_patch_dependency` should normally be
used in `%prep`, before `%pyproject_buildrequires`. This does not work
unless there is an explicit, manual dependency on `python3-devel` (in
Fedora) or `pyproject-rpm-macros`.

Thanks to Miro Hrončok for noting that the implementation of
`%pyproject_patch_dependency` is compact enough to include in the srpm
macros.
2026-04-28 10:20:30 +01:00
Charalampos Stratakis
5d9798f289 Add %pyproject_patch_dependency macro for overriding dependency constraints
Overrides are declared in %prep and automatically apply to both
BuildRequires (via %pyproject_buildrequires) and runtime Requires
(by patching installed .dist-info/METADATA during %pyproject_install).

Supported actions: drop_upper, drop_lower, drop_constraints,
set_upper, set_lower, and ignore. For == and ~=, drop_upper/drop_lower
decompose into half-bounds following PEP 440 semantics.

The :br_only suffix restricts an override to BuildRequires only,
for multi-wheel specs where a sibling package must be kept as a
runtime dependency.

Resolves: rhbz#2386906

Assisted-by: Claude (Anthropic)
Made-with: Cursor
2026-04-24 23:33:52 +02:00
Miro Hrončok
19912910be CI: Ensure errors from commands piped to tee fail the build
The pipe in double-install made the entire %install section successful
regardless of exit code from %pyproject_install.
Such build failed anyway in %files, but this makes it much nicer.

The pipe in setuptools_scm would pass %check even when tox failed.
2026-04-08 12:07:48 +02:00
Miro Hrončok
981aae6f72 Add -d option for %pyproject_buildrequires and %pyproject_wheel to specify a working directory
- Fixes: rhbz#2371389

This adds a new option for the macros, so spec files that use it will not
be parseable with older version of the package. It will be parseable without them.

Also document the behavior for building multiple wheels at a time,
which turned out to be related to the newly added documentation.

The actual RPM implementation (sans the test and docs) is heavily based on
https://src.fedoraproject.org/rpms/pyproject-rpm-macros/pull-request/569
which is:

Co-Authored-By: Shrey Naithani <shrey1212@yahoo.com>
Assisted-By: Gemini
2026-04-08 12:07:48 +02:00
Miro Hrončok
1769a6ec0e CI: Use $ID instead of $NAME to identify the distribution
See https://src.fedoraproject.org/rpms/fedora-release/pull-request/402

NAME is defined as a presentation field, not an ID field,
see https://www.mankier.com/5/os-release
2026-03-24 21:52:31 +01:00
Lumir Balhar
51a3620fd2 Use deprecated LegacyVersion (_version) only if necessary
LegacyVersion has been removed from Packaging in 2022. If the call
to `parse` function returns LegacyVersion, access to
version._version cannot emit DeprecationWarning.
2026-03-04 12:07:58 +01:00
Miro Hrončok
7c1c80d9ac CI: When on Fedora ELN, use the fedora-eln-x86_64 mock chroot 2026-01-29 16:34:53 +01:00
Miro Hrončok
37e530a6cd CI: Allow running mock builds on different architectures
Apparently, this can run on c10s CI on aarch64 now.
2026-01-29 15:40:02 +01:00
Fedora Release Engineering
8285a1f7e9 Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild 2026-01-17 06:01:17 +00:00
Miro Hrončok
e19d2d0689 CI: Add Python 3.14 RHEL test 2025-12-10 10:49:51 +00:00
Miro Hrončok
b95c0052f0 CI: Run c9s+c10s mock builds, to verify %check passes there
For example, due to old versions of setuptools or tox,
the expected results of some of the tests might need adjustments.

Note that CentOS Stream mock builds run with EPEL (Next) enabled,
hence we also enable tox tests in %check for better coverage.
Hence, running this makes sense even for actual c10s/c9s backports.
2025-12-05 19:46:55 +01:00
Karolina Surma
8fa19f5f37 Properly expand a self-referencing dependency group
anyio contains this metadata in pyproject.toml:
    [project.optional-dependencies]
    trio = ["trio >= 0.31.0"]
    [dependency-groups]
    test = ["anyio[trio]", "pytest"]

Before, pyproject_buildrequires would generate a test dependency group
requiring "anyio[trio]", creating an incorrect cyclic dependency in the
resolved requirements.
With the change, the group is resolved to require "trio >= 0.31.0"; the
self-reference is no longer created.
2025-12-03 11:26:14 +01:00
Miro Hrončok
bbcef5f671 CI: Make python-ldap build on EPEL 10
Proper fix: https://src.fedoraproject.org/rpms/python-ldap/c/47844650aa
2025-11-28 15:14:33 +01:00
Gordon Messmer
10f0ca0ffa %pyproject_save_files: Also save top level typing stub files (.pyi) 2025-10-27 14:39:03 +01:00
Miro Hrončok
fbd3dcd22b %pyproject_extras_subpkg: Only %ghost the dist-info directory, not the content
That way, accidentally unpackaged files within are reported as errors.

Currently, when %pyproject_extras_subpkg is used, the dist-info directory
is packaged as %ghost. When the main package does not have it,
the RPM build would succeed. The extras packages would have the python3dist()
requires and provides, but the main package would not.

By adding %dir after %ghost, we only package the directory
(which is enough for python3-rpm-generators to process it),
but the files in the directory are not included.
When not packaged in the main package, the RPM build fails.

This is a safeguard against packaging mistakes.

The visible difference is that rpm -ql/repoquery -l would only return the metadata directory.

See also https://src.fedoraproject.org/rpms/python-rpm-macros/pull-request/195
2025-10-22 22:46:40 +02:00
Miro Hrončok
8a209c9005 CI: Add Python 3.13 on EPEL 9 and 10 2025-09-02 11:56:46 +02:00
4aaa4be87e Speed %pyproject_save_files up significantly by using sets instead of lists
For packages with a lot of files, it took a really long time to run.
Profiling the code revealed most of the time is spent in PurePath.__eq__.

The code was using lists but checked
if to-be-added paths were not already in them.

Considering the order is insignificant
(the generated %files list is sorted at the end anyway),
the lists were essentially working as (very slow) sets.

Using sets instead of lists makes %pyproject_save_files over 20 times faster
(51.66 -> 2.39 seconds) for the ansible package (~62k files).

Checking if an item is in a list is O(N),
checking every added item is O(N**2).

Checking if an item is in a set is O(1),
checking every added item is O(N).

Additionally, with set, it is unnecessary to check for presence before addition,
so the code is easier.

(Commit message and removal of the check by Miro.)

Co-Authored-By: Miro Hrončok <miro@hroncok.cz>
2025-09-02 09:09:53 +02:00
Miro Hrončok
3ae687d401 On RPM 4.20+ don't put pyproject-macros-specific files in %buildsubdir
- Works around https://github.com/rpm-software-management/rpm/issues/3890
2025-09-02 09:09:53 +02:00
Miro Hrončok
ea56d1c870 Don't exit from pyproject-srpm-macros implementation of %pyproject_buildrequires
- Fixes: rhbz#2391290

Consider code like this:

    %generate_buildrequires
    mv setup.py{,.ignore}
    %pyproject_buildrequires
    mv setup.py{.ignore,}

When %pyproject_buildrequires exits, the second mv is never executed.
The next round of %generate_buildrequires will then fail.

In fact, there is probably not a good reason to call exit from %pyproject_buildrequires.

From https://src.fedoraproject.org/rpms/libcpuid/pull-request/7
2025-09-01 13:18:45 +02:00
Lukáš Zachar
84e6d30255 CI: Drop STI and use tmt
Resolves: rhbz#2383022
2025-07-30 17:46:32 +02:00
Lukáš Zachar
8998e8e027 CI: Respect TEST_ARTIFACTS but detect tmt
STI used to allow /tmp/artifacts, in tmt we have
TMT_PLAN_DATA and TMT_TEST_DATA. Use the later, which is test specific.
2025-07-30 17:44:52 +02:00
Fedora Release Engineering
3feea218e7 Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild 2025-07-25 06:23:37 +00:00
Miro Hrončok
1a867b5ab7 Only BuildRequire wheel when setuptools < 71 2025-07-24 00:01:34 +02:00
Miro Hrončok
a4e0e04344 %pyproject_buildrequires: Do not generate BuildRequires from Requires core metadata fields
See https://packaging.python.org/en/latest/specifications/core-metadata/#requires

That field is deprecated and should include importable module names, not distribution packages.

We have no RPM Provides for importable names.
Treating this like python3dist() Requires is wrong and may result in stuff like:

    No match for argument: python3dist(pkg-resources)

For packages using python-distutils-extra.

See https://bugzilla.redhat.com/show_bug.cgi?id=2378463#c2

This bug existed from the very beginning of the %pyproject_buildrequires,
but the field is almost unused in real packages, so it was not noticed until
we asked all Python packages to be ported to the new macros.

---

I considered flattening the structure returned from requires_from_parsed_metadata_file,
but then we would need to hardcode "Requires-Dist" in various source= declarations,
so I kept the structure as is.
2025-07-11 12:34:57 +02:00