Skip to content

Commit 340054a

Browse files
authored
Merge pull request #11052 from SpecLad/fix-script-record-hash
Update the RECORD entry when rewriting the shebang line in a script
2 parents 65680b4 + e4cd6da commit 340054a

File tree

3 files changed

+54
-15
lines changed

3 files changed

+54
-15
lines changed

news/10744.bugfix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
When pip rewrites the shebang line in a script during wheel installation,
2+
update the hash and size in the corresponding ``RECORD`` file entry.

src/pip/_internal/operations/install/wheel.py

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -224,19 +224,16 @@ def _normalized_outrows(
224224
)
225225

226226

227-
def _record_to_fs_path(record_path: RecordPath) -> str:
228-
return record_path
229-
230-
231-
def _fs_to_record_path(path: str, relative_to: Optional[str] = None) -> RecordPath:
232-
if relative_to is not None:
233-
# On Windows, do not handle relative paths if they belong to different
234-
# logical disks
235-
if (
236-
os.path.splitdrive(path)[0].lower()
237-
== os.path.splitdrive(relative_to)[0].lower()
238-
):
239-
path = os.path.relpath(path, relative_to)
227+
def _record_to_fs_path(record_path: RecordPath, lib_dir: str) -> str:
228+
return os.path.join(lib_dir, record_path)
229+
230+
231+
def _fs_to_record_path(path: str, lib_dir: str) -> RecordPath:
232+
# On Windows, do not handle relative paths if they belong to different
233+
# logical disks
234+
if os.path.splitdrive(path)[0].lower() == os.path.splitdrive(lib_dir)[0].lower():
235+
path = os.path.relpath(path, lib_dir)
236+
240237
path = path.replace(os.path.sep, "/")
241238
return cast("RecordPath", path)
242239

@@ -259,7 +256,7 @@ def get_csv_rows_for_installed(
259256
old_record_path = cast("RecordPath", row[0])
260257
new_record_path = installed.pop(old_record_path, old_record_path)
261258
if new_record_path in changed:
262-
digest, length = rehash(_record_to_fs_path(new_record_path))
259+
digest, length = rehash(_record_to_fs_path(new_record_path, lib_dir))
263260
else:
264261
digest = row[1] if len(row) > 1 else ""
265262
length = row[2] if len(row) > 2 else ""
@@ -475,7 +472,7 @@ def record_installed(
475472
newpath = _fs_to_record_path(destfile, lib_dir)
476473
installed[srcfile] = newpath
477474
if modified:
478-
changed.add(_fs_to_record_path(destfile))
475+
changed.add(newpath)
479476

480477
def is_dir_path(path: RecordPath) -> bool:
481478
return path.endswith("/")

tests/functional/test_install_wheel.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import base64
12
import csv
23
import distutils
4+
import hashlib
35
import os
46
import shutil
57
from pathlib import Path
@@ -364,6 +366,44 @@ def test_wheel_record_lines_have_hash_for_data_files(
364366
]
365367

366368

369+
def test_wheel_record_lines_have_updated_hash_for_scripts(
370+
script: PipTestEnvironment,
371+
) -> None:
372+
"""
373+
pip rewrites "#!python" shebang lines in scripts when it installs them;
374+
make sure it updates the RECORD file correspondingly.
375+
"""
376+
package = make_wheel(
377+
"simple",
378+
"0.1.0",
379+
extra_data_files={
380+
"scripts/dostuff": "#!python\n",
381+
},
382+
).save_to_dir(script.scratch_path)
383+
script.pip("install", package)
384+
record_file = script.site_packages_path / "simple-0.1.0.dist-info" / "RECORD"
385+
record_text = record_file.read_text()
386+
record_rows = list(csv.reader(record_text.splitlines()))
387+
records = {r[0]: r[1:] for r in record_rows}
388+
389+
script_path = script.bin_path / "dostuff"
390+
script_contents = script_path.read_bytes()
391+
assert not script_contents.startswith(b"#!python\n")
392+
393+
script_digest = hashlib.sha256(script_contents).digest()
394+
script_digest_b64 = (
395+
base64.urlsafe_b64encode(script_digest).decode("US-ASCII").rstrip("=")
396+
)
397+
398+
script_record_path = os.path.relpath(
399+
script_path, script.site_packages_path
400+
).replace(os.path.sep, "/")
401+
assert records[script_record_path] == [
402+
f"sha256={script_digest_b64}",
403+
str(len(script_contents)),
404+
]
405+
406+
367407
@pytest.mark.incompatible_with_test_venv
368408
@pytest.mark.usefixtures("with_wheel")
369409
def test_install_user_wheel(

0 commit comments

Comments
 (0)