If a wheel has a scripts entry and an entry_points entry that have the same name, installer cannot unpack the wheel.
(While I don't see the sense in having a script and an entry_point in the wheel that have the same name and I think it's probably buggy for a build system to create it, such wheels do exist in the wild and so installer should support this.)
We discovered this when looking at quodlibet 4.7.1:
$ python -m installer quodlibet-4.7.1-py3-none-any.whl
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "…/venv/lib/python3.13/site-packages/installer/__main__.py", line 98, in <module>
_main(sys.argv[1:], "python -m installer")
~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "…/venv/lib/python3.13/site-packages/installer/__main__.py", line 94, in _main
installer.install(source, destination, {})
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
File "…/venv/lib/python3.13/site-packages/installer/_core.py", line 109, in install
record = destination.write_file(
scheme=scheme,
...<2 lines>...
is_executable=is_executable,
)
File "…/venv/lib/python3.13/site-packages/installer/destinations.py", line 203, in write_file
return self.write_to_fs(
~~~~~~~~~~~~~~~~^
scheme, path_, stream_with_different_shebang, is_executable
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "…/venv/lib/python3.13/site-packages/installer/destinations.py", line 167, in write_to_fs
raise FileExistsError(message)
FileExistsError: File already exists: …/venv/bin/quodlibet
Inspecting the wheel:
$ zipinfo quodlibet-4.7.1-py3-none-any.whl
Archive: quodlibet-4.7.1-py3-none-any.whl
Zip file size: 2823277 bytes, number of entries: 549
[...]
-rwxr-xr-x 2.0 unx 391 b- defN 26-Mar-19 13:41 quodlibet-4.7.1.data/scripts/exfalso
-rwxr-xr-x 2.0 unx 395 b- defN 26-Mar-19 13:41 quodlibet-4.7.1.data/scripts/operon
-rwxr-xr-x 2.0 unx 387 b- defN 26-Mar-19 13:41 quodlibet-4.7.1.data/scripts/quodlibet
-rw-r--r-- 2.0 unx 18092 b- defN 26-Mar-19 13:41 quodlibet-4.7.1.dist-info/licenses/COPYING
-rw-r--r-- 2.0 unx 1176 b- defN 26-Mar-19 13:41 quodlibet-4.7.1.dist-info/METADATA
-rw-r--r-- 2.0 unx 91 b- defN 26-Mar-19 13:41 quodlibet-4.7.1.dist-info/WHEEL
-rw-r--r-- 2.0 unx 50 b- defN 26-Mar-19 13:41 quodlibet-4.7.1.dist-info/entry_points.txt
-rw-r--r-- 2.0 unx 10 b- defN 26-Mar-19 13:41 quodlibet-4.7.1.dist-info/top_level.txt
-rw-rw-r-- 2.0 unx 53016 b- defN 26-Mar-19 13:41 quodlibet-4.7.1.dist-info/RECORD
$ unzip -qca quodlibet-4.7.1-py3-none-any.whl quodlibet-4.7.1.dist-info/entry_points.txt
[console_scripts]
quodlibet = quodlibet.main:main
The copy of bin/quoadlibet that already exists is the entry_point one; the one that fails to be created is the one from the scripts in the wheel.
We actually discovered this via API usage not the command line; unfortunately the API doesn't give us a way of working around this as both the scripts and entry_points are unpacked into the same schema location.
In poking this, we also discovered that pip silently drops the script in favour of the entry_point. I doubt that's the right behaviour either.
Some potential options:
- in case of a namespace clash, drop the script in favour of the entry_point, following
pip's lead (but please at least log a warning about that!)
- permit scripts and entry_points to be written to different locations
- give a commandline option to just try harder and overwrite things if needed
If a wheel has a
scriptsentry and anentry_pointsentry that have the same name,installercannot unpack the wheel.(While I don't see the sense in having a script and an entry_point in the wheel that have the same name and I think it's probably buggy for a build system to create it, such wheels do exist in the wild and so
installershould support this.)We discovered this when looking at quodlibet 4.7.1:
Inspecting the wheel:
The copy of
bin/quoadlibetthat already exists is the entry_point one; the one that fails to be created is the one from thescriptsin the wheel.We actually discovered this via API usage not the command line; unfortunately the API doesn't give us a way of working around this as both the scripts and entry_points are unpacked into the same schema location.
In poking this, we also discovered that
pipsilently drops the script in favour of the entry_point. I doubt that's the right behaviour either.Some potential options:
pip's lead (but please at least log a warning about that!)