codecov[bot]: # [Codecov](https://codecov.io/gh/pypa/wheel/pull/250?src=pr&el=h1) Report
> Merging [#250](https://codecov.io/gh/pypa/wheel/pull/250?src=pr&el=desc) into [master](https://codecov.io/gh/pypa/wheel/commit/f3855494f20724f1ae844631d08e34367e977661?src=pr&el=desc) will **decrease** coverage by `4.17%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/pypa/wheel/pull/250/graphs/tree.svg?width=650&token=ey5B5hA7sW&height=150&src=pr)](https://codecov.io/gh/pypa/wheel/pull/250?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #250      +/-   ##
==========================================
- Coverage   59.31%   55.13%   -4.18%     
==========================================
  Files          12       12              
  Lines         816      818       +2     
==========================================
- Hits          484      451      -33     
- Misses        332      367      +35
```


| [Impacted Files](https://codecov.io/gh/pypa/wheel/pull/250?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [wheel/metadata.py](https://codecov.io/gh/pypa/wheel/pull/250/diff?src=pr&el=tree#diff-d2hlZWwvbWV0YWRhdGEucHk=) | `81.81% <100%> (-15.52%)` | :arrow_down: |
| [wheel/pkginfo.py](https://codecov.io/gh/pypa/wheel/pull/250/diff?src=pr&el=tree#diff-d2hlZWwvcGtnaW5mby5weQ==) | `48.27% <0%> (-37.94%)` | :arrow_down: |
| [wheel/util.py](https://codecov.io/gh/pypa/wheel/pull/250/diff?src=pr&el=tree#diff-d2hlZWwvdXRpbC5weQ==) | `76.92% <0%> (-23.08%)` | :arrow_down: |
| [wheel/pep425tags.py](https://codecov.io/gh/pypa/wheel/pull/250/diff?src=pr&el=tree#diff-d2hlZWwvcGVwNDI1dGFncy5weQ==) | `28.44% <0%> (-3.67%)` | :arrow_down: |
| [wheel/wheelfile.py](https://codecov.io/gh/pypa/wheel/pull/250/diff?src=pr&el=tree#diff-d2hlZWwvd2hlZWxmaWxlLnB5) | `98.09% <0%> (-1.91%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pypa/wheel/pull/250?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pypa/wheel/pull/250?src=pr&el=footer). Last update [f385549...5b576de](https://codecov.io/gh/pypa/wheel/pull/250?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

cam72cam: I can confirm that this works in relation to https://github.com/pypa/pip/issues/5768
pradyunsg: The PEP changes are accepted, so merging this should be acceptable now IMO.

/cc @pypa/wheel-committers 
diff --git a/wheel/metadata.py b/wheel/metadata.py
index 4fa17cd..c6a3736 100644
--- a/wheel/metadata.py
+++ b/wheel/metadata.py
@@ -16,7 +16,10 @@ EXTRA_RE = re.compile("""^(?P<package>.*?)(;\s*(?P<condition>.*?)(extra == '(?P<
 
 
 def requires_to_requires_dist(requirement):
-    """Compose the version predicates for requirement in PEP 345 fashion."""
+    """Return the version specifier for a requirement in PEP 345/566 fashion."""
+    if requirement.url:
+        return " @ " + requirement.url
+
     requires_dist = []
     for op, ver in requirement.specs:
         requires_dist.append(op + ver)

How should a PEP 508 requirement with an URL specifier be handled?
Starting with pip 10.0, requirements with [URL specifiers](https://www.python.org/dev/peps/pep-0508/#examples) are now supported (as replacement for dependency links).

With the following sample project' `setup.py`:
```python
from setuptools import setup

setup(name='projecta', version='42',
      install_requires='''
      lazyImport@git+https://gitlab.com/KOLANICH1/lazyImport.py.git#egg=lazyImport-dev
      ''')
```

Installing from source work, but looking at the generated wheel metadata:
```shell
> python setup.py -q bdist_wheel && unzip -p dist/projecta-42-py3-none-any.whl '*/METADATA'
Metadata-Version: 2.1
Name: projecta
Version: 42
Summary: Description!
Home-page: UNKNOWN
Author: UNKNOWN
Author-email: UNKNOWN
License: UNKNOWN
Platform: UNKNOWN
Requires-Dist: lazyImport

UNKNOWN
```

It works with the following patch:
```diff
 tests/test_metadata.py | 3 +++
 wheel/metadata.py      | 2 ++
 2 files changed, 5 insertions(+)

diff --git i/tests/test_metadata.py w/tests/test_metadata.py
index 3421430..2390e98 100644
--- i/tests/test_metadata.py
+++ w/tests/test_metadata.py
@@ -9,6 +9,7 @@ def test_pkginfo_to_metadata(tmpdir):
         ('Provides-Extra', 'test'),
         ('Provides-Extra', 'signatures'),
         ('Provides-Extra', 'faster-signatures'),
+        ('Requires-Dist', "pip @ https://github.com/pypa/pip/archive/1.3.1.zip"),
         ('Requires-Dist', "ed25519ll; extra == 'faster-signatures'"),
         ('Requires-Dist', "keyring; extra == 'signatures'"),
         ('Requires-Dist', "keyrings.alt; extra == 'signatures'"),
@@ -28,6 +29,8 @@ def test_pkginfo_to_metadata(tmpdir):
 
     egg_info_dir = tmpdir.ensure_dir('test.egg-info')
     egg_info_dir.join('requires.txt').write("""\
+pip@ https://github.com/pypa/pip/archive/1.3.1.zip
+
 [faster-signatures]
 ed25519ll
 
diff --git i/wheel/metadata.py w/wheel/metadata.py
index 4fa17cd..5b03e3e 100644
--- i/wheel/metadata.py
+++ w/wheel/metadata.py
@@ -17,6 +17,8 @@
 
 def requires_to_requires_dist(requirement):
     """Compose the version predicates for requirement in PEP 345 fashion."""
+    if requirement.url:
+        return " @ " + requirement.url
     requires_dist = []
     for op, ver in requirement.specs:
         requires_dist.append(op + ver)
```

```shell
> python setup.py -q bdist_wheel && unzip -p dist/projecta-42-py3-none-any.whl '*/METADATA'
Metadata-Version: 2.1
Name: projecta
Version: 42
Summary: Description!
Home-page: UNKNOWN
Author: UNKNOWN
Author-email: UNKNOWN
License: UNKNOWN
Platform: UNKNOWN
Requires-Dist: lazyImport @ git+https://gitlab.com/KOLANICH1/lazyImport.py.git

UNKNOWN
```

Unfortunately, this is technically not valid [PEP 345](https://www.python.org/dev/peps/pep-0345/#requires-dist-multiple-use) metadata.
diff --git a/tests/test_metadata.py b/tests/test_metadata.py
index 3421430..78fe40d 100644
--- a/tests/test_metadata.py
+++ b/tests/test_metadata.py
@@ -9,6 +9,7 @@ def test_pkginfo_to_metadata(tmpdir):
         ('Provides-Extra', 'test'),
         ('Provides-Extra', 'signatures'),
         ('Provides-Extra', 'faster-signatures'),
+        ('Requires-Dist', "pip @ https://github.com/pypa/pip/archive/1.3.1.zip"),
         ('Requires-Dist', "ed25519ll; extra == 'faster-signatures'"),
         ('Requires-Dist', "keyring; extra == 'signatures'"),
         ('Requires-Dist', "keyrings.alt; extra == 'signatures'"),
@@ -28,6 +29,8 @@ Provides-Extra: faster-signatures""")
 
     egg_info_dir = tmpdir.ensure_dir('test.egg-info')
     egg_info_dir.join('requires.txt').write("""\
+pip@https://github.com/pypa/pip/archive/1.3.1.zip
+
 [faster-signatures]
 ed25519ll
 

pip install -e .[test]
parse_log_pytest
pytest
pytest pytest-cov
apt-get update apt-get install -y gcc
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
coverage==7.8.0
exceptiongroup @ file:///croot/exceptiongroup_1706031385326/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
packaging @ file:///croot/packaging_1734472117206/work
pluggy @ file:///croot/pluggy_1733169602837/work
pytest @ file:///croot/pytest_1738938843180/work
pytest-cov==6.0.0
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work

name: wheel
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - exceptiongroup=1.2.0=py39h06a4308_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - packaging=24.2=py39h06a4308_0
  - pip=25.0=py39h06a4308_0
  - pluggy=1.5.0=py39h06a4308_0
  - pytest=8.3.4=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py39h06a4308_0
  - tzdata=2025a=h04d1e81_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - coverage==7.8.0
      - pytest-cov==6.0.0
prefix: /opt/conda/envs/wheel

tests/test_metadata.py::test_pkginfo_to_metadata
wheel/metadata.py
wheel/metadata.py