Skip to content

Commit c2a7a8d

Browse files
authored
installer: do not fail on invalid wheels, print only a warning (#7694)
1 parent b933443 commit c2a7a8d

6 files changed

Lines changed: 81 additions & 5 deletions

File tree

src/poetry/installation/executor.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,14 @@ def execute(self, operations: list[Operation]) -> int:
207207

208208
for warning in self._yanked_warnings:
209209
self._io.write_error_line(f"<warning>Warning: {warning}</warning>")
210+
for path, issues in self._wheel_installer.invalid_wheels.items():
211+
formatted_issues = "\n".join(issues)
212+
warning = (
213+
f"Validation of the RECORD file of {path.name} failed."
214+
" Please report to the maintainers of that package so they can fix"
215+
f" their build process. Details:\n{formatted_issues}\n"
216+
)
217+
self._io.write_error_line(f"<warning>Warning: {warning}</warning>")
210218

211219
return 1 if self._shutdown else 0
212220

src/poetry/installation/wheel_installer.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from installer import install
1111
from installer.destinations import SchemeDictionaryDestination
1212
from installer.sources import WheelFile
13+
from installer.sources import _WheelFileValidationError
1314

1415
from poetry.__version__ import __version__
1516
from poetry.utils._compat import WINDOWS
@@ -93,12 +94,17 @@ def __init__(self, env: Env) -> None:
9394
schemes, interpreter=str(self._env.python), script_kind=script_kind
9495
)
9596

97+
self.invalid_wheels: dict[Path, list[str]] = {}
98+
9699
def enable_bytecode_compilation(self, enable: bool = True) -> None:
97100
self._destination.bytecode_optimization_levels = (-1,) if enable else ()
98101

99102
def install(self, wheel: Path) -> None:
100103
with WheelFile.open(wheel) as source:
101-
source.validate_record()
104+
try:
105+
source.validate_record()
106+
except _WheelFileValidationError as e:
107+
self.invalid_wheels[wheel] = e.issues
102108
install(
103109
source=source,
104110
destination=self._destination.for_source(source),
Binary file not shown.
Binary file not shown.

tests/installation/test_executor.py

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,9 @@ def callback(
148148
)
149149

150150
if not fixture.exists():
151-
if name == "demo-0.1.0.tar.gz":
152-
fixture = fixture_dir("distributions") / "demo-0.1.0.tar.gz"
153-
else:
151+
fixture = fixture_dir("distributions") / name
152+
153+
if not fixture.exists():
154154
fixture = (
155155
fixture_dir("distributions") / "demo-0.1.0-py2.py3-none-any.whl"
156156
)
@@ -337,6 +337,66 @@ def test_execute_prints_warning_for_yanked_package(
337337
assert error.count("yanked") == 0
338338

339339

340+
def test_execute_prints_warning_for_invalid_wheels(
341+
config: Config,
342+
pool: RepositoryPool,
343+
io: BufferedIO,
344+
tmp_dir: str,
345+
mock_file_downloads: None,
346+
env: MockEnv,
347+
):
348+
config.merge({"cache-dir": tmp_dir})
349+
350+
executor = Executor(env, pool, config, io)
351+
352+
base_url = "https://files.pythonhosted.org/"
353+
wheel1 = "demo_invalid_record-0.1.0-py2.py3-none-any.whl"
354+
wheel2 = "demo_invalid_record2-0.1.0-py2.py3-none-any.whl"
355+
return_code = executor.execute(
356+
[
357+
Install(
358+
Package(
359+
"demo-invalid-record",
360+
"0.1.0",
361+
source_type="url",
362+
source_url=f"{base_url}/{wheel1}",
363+
)
364+
),
365+
Install(
366+
Package(
367+
"demo-invalid-record2",
368+
"0.1.0",
369+
source_type="url",
370+
source_url=f"{base_url}/{wheel2}",
371+
)
372+
),
373+
]
374+
)
375+
376+
warning1 = f"""\
377+
<warning>Warning: Validation of the RECORD file of {wheel1} failed.\
378+
Please report to the maintainers of that package so they can fix their build process.\
379+
Details:
380+
In .*?{wheel1}, demo/__init__.py is not mentioned in RECORD
381+
In .*?{wheel1}, demo_invalid_record-0.1.0.dist-info/WHEEL is not mentioned in RECORD
382+
"""
383+
384+
warning2 = f"""\
385+
<warning>Warning: Validation of the RECORD file of {wheel2} failed.\
386+
Please report to the maintainers of that package so they can fix their build process.\
387+
Details:
388+
In .*?{wheel2}, hash / size of demo_invalid_record2-0.1.0.dist-info/METADATA didn't\
389+
match RECORD
390+
"""
391+
392+
output = io.fetch_output()
393+
error = io.fetch_error()
394+
assert return_code == 0, f"\noutput: {output}\nerror: {error}\n"
395+
assert re.match(f"{warning1}\n{warning2}", error) or re.match(
396+
f"{warning2}\n{warning1}", error
397+
), error
398+
399+
340400
def test_execute_shows_skipped_operations_if_verbose(
341401
config: Config,
342402
pool: RepositoryPool,

tests/utils/test_cache.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,9 @@ def test_get_cached_archives_for_link(
286286
)
287287

288288
assert archives
289-
assert set(archives) == set(distributions.glob("demo-0.1.*"))
289+
assert set(archives) == set(distributions.glob("*.whl")) | set(
290+
distributions.glob("*.tar.gz")
291+
)
290292

291293

292294
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)