Skip to content

Conversation

@freakboy3742
Copy link

Adds a new pyroma.projectdata.installed_metadata() API that allows for the extraction and normalisation of project metadata from an installed wheel.

Existing APIs for build_metadata, wheel_metadata et al work great when pyroma is being run on a project directory, as an external check of project metadata compliance.

However, these mechanisms involve internally calling out to build to construct a project's wheel metadata from scratch. This can be problematic for 2 reasons:

  1. The test can't be invoked on platforms where subprocess isn't available (iOS, Android and WASM/Emscripten - all of which are Tier 3 Cpython platforms)
  2. From a reproducibility perspective of a CI/release pipeline, it would be preferable to evaluate the Pyroma compliance of the wheel that has been generated by the release process, rather than an artefact that was generated independently. In theory, this shouldn't make any difference - the same project configuration should generate the same wheel configuration; but in practice, there's no guarantee that this is the case.

This PR adds a new pyroma.projectdata.installed_metadata() API that takes the name of an installed package, and extracts the installed project metadata using importlib.metadata. In a CI release workflow, a test suite will (usually) be creating and installing a wheel of the project under test, so this new API allows for the extraction and normalization of that metadata, which can then be passed to a pyroma check.

This was previously possible with Pyroma 4.3.3, using the map_metadata_keys() method and importlib.metadata; however, this method was removed in #112, and the underlying behavior factored into build_metadata(). This PR effectively reverses that refactoring, and adds the importlib-based usage as a convenience.

The motivation for this PR comes from Pillow, which incorporates a Pyroma test into it's test suite; however, that test has required modification to support iOS (see python-pillow/Pillow#9030 and python-pillow/Pillow#9116).

import os
import pathlib
import re
from importlib.metadata import metadata
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be worth importing this under an alias, rather than having both metadata the import and metadata the variable in the same file?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure - or we can rename some of the variables to avoid the collision.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with calling the variable just "md", that's what we use for metadata at my work anyway. :-)

# metadata from PyPI, we just couldn't get the additional build data.
return {"_wheel_build_failed": True}

return normalize_metadata(metadata)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm right, this would be a bug that exists in master, but... isn't metadata not set here? Because the assignment earlier in the function raised an exception?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the call on L43 succeeds, then data metadata will be set on L51; if it fails, the return on L49 prevents L51 from being executed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right, sorry, I was thinking backwards.

@regebro
Copy link
Owner

regebro commented Jul 31, 2025

I mean, this basically just calls importlib.metadata.metadata(), I don't really know why Pyroma is involved. :-D

@freakboy3742
Copy link
Author

I mean, this basically just calls importlib.metadata.metadata(), I don't really know why Pyroma is involved. :-D

Sure - but build-metadata is basically just calls to build, so...

My argument would be that even though it's "just" a wrapper around importlib, that wrapper is (a) representative of a real world use case, and (b) acts as a protection against future "oh, we can refactor normalize_metadata() because it's only used in one place" efforts.

I've pushed an update that renames some variables to avoid the collisions, and corrects the test case that was failing (I swear that wasn't failing locally...).

@regebro
Copy link
Owner

regebro commented Aug 1, 2025

Well, the idea of using Pyroma as library is new to me, so... :-D

What is your usecase, more specifically?

@freakboy3742
Copy link
Author

What is your usecase, more specifically?

I flagged the use case in the original PR description - Pillow incorporates a Pyroma test into it's test suite; however, that test has required modification to support iOS (see python-pillow/Pillow#9030 and python-pillow/Pillow#9116).

I don't know the specific history of that test - my assumption would be that they want to assert that wheel metadata is correct for every wheel, not just the ones that are output by default on the platforms where CI is being executed.

I guess one of the outcomes here could be an official "don't do that" declaration from Pyroma, and modify Pillow's CI to perform the pyroma check externally, rather than as part of the test suite.

@hugovk
Copy link
Collaborator

hugovk commented Aug 4, 2025

I added the Pillow test back in 2014(!) (python-pillow/Pillow#743) to prevent a packaging regression (python-pillow/Pillow#740).

I guess one of the outcomes here could be an official "don't do that" declaration from Pyroma, and modify Pillow's CI to perform the pyroma check externally, rather than as part of the test suite.

Yes, we could do this.

@radarhere
Copy link
Contributor

@regebro did you have any thoughts, or further questions?

@regebro
Copy link
Owner

regebro commented Oct 1, 2025

No, but I also did not have any time. :-D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants