Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/installer/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def install(
# RECORD handling
record_file_path = posixpath.join(source.dist_info_dir, "RECORD")
written_records = []
generated_script_paths: set[str] = set()

# Write the entry_points based scripts.
if "entry_points.txt" in source.dist_info_filenames:
Expand All @@ -92,6 +93,7 @@ def install(
attr=attr,
section=section,
)
generated_script_paths.add(record.path)
written_records.append((Scheme("scripts"), record))

# Write all the files from the wheel.
Expand Down Expand Up @@ -121,6 +123,18 @@ def install(
source=source,
root_scheme=root_scheme,
)
if scheme == "scripts" and destination_path in generated_script_paths:
warnings.warn(
(
f"Skip installing script {path} from {source.distribution}"
f" because it conflicts with an entry point script named"
f" {destination_path!r}."
),
RuntimeWarning,
stacklevel=2,
)
continue

record = destination.write_file(
scheme=scheme,
path=destination_path,
Expand Down
65 changes: 58 additions & 7 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ def custom_write_file(scheme, path, stream, is_executable):
return (path, scheme, 0)

def custom_write_script(name, module, attr, section):
return (name, module, attr, section)
assert module
assert attr
assert section in {"console", "gui"}
return RecordEntry(name, None, None)

retval.write_file.side_effect = custom_write_file
retval.write_script.side_effect = custom_write_script
Expand Down Expand Up @@ -212,8 +215,8 @@ def main():
scheme="purelib",
record_file_path="fancy-1.0.0.dist-info/RECORD",
records=[
("scripts", ("fancy", "fancy", "main", "console")),
("scripts", ("fancy-gui", "fancy", "main", "gui")),
("scripts", RecordEntry("fancy", None, None)),
("scripts", RecordEntry("fancy-gui", None, None)),
("purelib", ("fancy/__init__.py", "purelib", 0)),
("purelib", ("fancy/__main__.py", "purelib", 0)),
("purelib", ("fancy-1.0.0.dist-info/METADATA", "purelib", 0)),
Expand Down Expand Up @@ -467,8 +470,8 @@ def main():
scheme="platlib",
record_file_path="fancy-1.0.0.dist-info/RECORD",
records=[
("scripts", ("fancy", "fancy", "main", "console")),
("scripts", ("fancy-gui", "fancy", "main", "gui")),
("scripts", RecordEntry("fancy", None, None)),
("scripts", RecordEntry("fancy-gui", None, None)),
("platlib", ("fancy/__init__.py", "platlib", 0)),
("platlib", ("fancy/__main__.py", "platlib", 0)),
("platlib", ("fancy-1.0.0.dist-info/METADATA", "platlib", 0)),
Expand Down Expand Up @@ -754,8 +757,8 @@ def test_handles_data_properly(self, mock_destination):
scheme="purelib",
record_file_path="fancy-1.0.0.dist-info/RECORD",
records=[
("scripts", ("fancy", "fancy", "main", "console")),
("scripts", ("fancy-gui", "fancy", "main", "gui")),
("scripts", RecordEntry("fancy", None, None)),
("scripts", RecordEntry("fancy-gui", None, None)),
("data", ("fancy/data.py", "data", 0)),
("headers", ("fancy/headers.py", "headers", 0)),
("platlib", ("fancy/platlib.py", "platlib", 0)),
Expand All @@ -781,6 +784,54 @@ def test_handles_data_properly(self, mock_destination):
]
)

def test_skips_data_script_that_matches_entrypoint(self, mock_destination):
source = FakeWheelSource(
distribution="fancy",
version="1.0.0",
regular_files={
"fancy/__init__.py": b"",
"fancy-1.0.0.data/scripts/fancy": b"""\
#!/usr/bin/env python
print("legacy script")
""",
},
dist_info_files={
"top_level.txt": b"""\
fancy
""",
"entry_points.txt": b"""\
[console_scripts]
fancy = fancy:main
""",
"WHEEL": b"""\
Wheel-Version: 1.0
Generator: magic (1.0.0)
Root-Is-Purelib: true
Tag: py3-none-any
""",
"METADATA": b"""\
Metadata-Version: 2.1
Name: fancy
Version: 1.0.0
""",
},
)

with pytest.warns(RuntimeWarning, match="conflicts with an entry point script"):
install(
source=source,
destination=mock_destination,
additional_metadata={},
)

assert not any(
call.kwargs["scheme"] == "scripts" and call.kwargs["path"] == "fancy"
for call in mock_destination.write_file.call_args_list
)
records = mock_destination.finalize_installation.call_args.kwargs["records"]
script_records = [record for scheme, record in records if scheme == "scripts"]
assert script_records == [RecordEntry("fancy", None, None)]

def test_errors_out_when_given_invalid_scheme_in_data(self, mock_destination):
# Create a fake wheel
source = FakeWheelSource(
Expand Down
Loading