From eec4a5446d96f6a717886ade0657b841b5328dab Mon Sep 17 00:00:00 2001 From: Duc Le Date: Tue, 16 Nov 2021 12:54:08 +0000 Subject: [PATCH 1/3] Change script shebang replacement to respect args --- news/10661.feature.rst | 1 + src/pip/_internal/operations/install/wheel.py | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 news/10661.feature.rst diff --git a/news/10661.feature.rst b/news/10661.feature.rst new file mode 100644 index 000000000..f168ff124 --- /dev/null +++ b/news/10661.feature.rst @@ -0,0 +1 @@ +Script shebang "#!python" replacement now preserves all arguments after the first space (e.g. "#!python -i") diff --git a/src/pip/_internal/operations/install/wheel.py b/src/pip/_internal/operations/install/wheel.py index e191b1343..41f2721a3 100644 --- a/src/pip/_internal/operations/install/wheel.py +++ b/src/pip/_internal/operations/install/wheel.py @@ -75,6 +75,8 @@ logger = logging.getLogger(__name__) RecordPath = NewType("RecordPath", str) InstalledCSVRow = Tuple[RecordPath, str, Union[int, str]] +SHEBANG_PYTHON = re.compile(b'^#!python[w]*([ \t].*)?$') + def rehash(path: str, blocksize: int = 1 << 20) -> Tuple[str, str]: """Return (encoded_digest, length) for path using hashlib.sha256()""" @@ -99,10 +101,11 @@ def fix_script(path: str) -> bool: with open(path, "rb") as script: firstline = script.readline() - if not firstline.startswith(b"#!python"): + match = SHEBANG_PYTHON.match(firstline) + if not match: return False exename = sys.executable.encode(sys.getfilesystemencoding()) - firstline = b"#!" + exename + os.linesep.encode("ascii") + firstline = b"#!" + exename + (match.group(1) or b'') + os.linesep.encode("ascii") rest = script.read() with open(path, "wb") as script: script.write(firstline) From 1ac04c6d95b18a3bbf487f1922ca9625d380533f Mon Sep 17 00:00:00 2001 From: Duc Le Date: Tue, 16 Nov 2021 13:39:14 +0000 Subject: [PATCH 2/3] Shebang replacement: precommit and review checks --- src/pip/_internal/operations/install/wheel.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pip/_internal/operations/install/wheel.py b/src/pip/_internal/operations/install/wheel.py index 41f2721a3..c7a83c94a 100644 --- a/src/pip/_internal/operations/install/wheel.py +++ b/src/pip/_internal/operations/install/wheel.py @@ -75,7 +75,7 @@ logger = logging.getLogger(__name__) RecordPath = NewType("RecordPath", str) InstalledCSVRow = Tuple[RecordPath, str, Union[int, str]] -SHEBANG_PYTHON = re.compile(b'^#!python[w]*([ \t].*)?$') +SHEBANG_PYTHON = re.compile(b"^#!python[w]*(\s.*)?$") def rehash(path: str, blocksize: int = 1 << 20) -> Tuple[str, str]: @@ -105,7 +105,10 @@ def fix_script(path: str) -> bool: if not match: return False exename = sys.executable.encode(sys.getfilesystemencoding()) - firstline = b"#!" + exename + (match.group(1) or b'') + os.linesep.encode("ascii") + postinterp = match.group(1).rstrip() or b"" + firstline = ( + b"#!" + exename + postinterp + os.linesep.encode("ascii") + ) rest = script.read() with open(path, "wb") as script: script.write(firstline) From c7feaf743b1b4a601754e2ce5b5ea6b12cd5ef7f Mon Sep 17 00:00:00 2001 From: Duc Le Date: Tue, 16 Nov 2021 14:10:09 +0000 Subject: [PATCH 3/3] Shebang replacement: Use split instead of re.match --- src/pip/_internal/operations/install/wheel.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/pip/_internal/operations/install/wheel.py b/src/pip/_internal/operations/install/wheel.py index c7a83c94a..b4e67baf6 100644 --- a/src/pip/_internal/operations/install/wheel.py +++ b/src/pip/_internal/operations/install/wheel.py @@ -75,8 +75,6 @@ logger = logging.getLogger(__name__) RecordPath = NewType("RecordPath", str) InstalledCSVRow = Tuple[RecordPath, str, Union[int, str]] -SHEBANG_PYTHON = re.compile(b"^#!python[w]*(\s.*)?$") - def rehash(path: str, blocksize: int = 1 << 20) -> Tuple[str, str]: """Return (encoded_digest, length) for path using hashlib.sha256()""" @@ -101,14 +99,12 @@ def fix_script(path: str) -> bool: with open(path, "rb") as script: firstline = script.readline() - match = SHEBANG_PYTHON.match(firstline) - if not match: + if not firstline.startswith(b"#!python"): return False exename = sys.executable.encode(sys.getfilesystemencoding()) - postinterp = match.group(1).rstrip() or b"" - firstline = ( - b"#!" + exename + postinterp + os.linesep.encode("ascii") - ) + parts = firstline.split(maxsplit=1) + postinterp = b" " + parts[1].rstrip() if len(parts) > 1 else b"" + firstline = b"#!" + exename + postinterp + os.linesep.encode("ascii") rest = script.read() with open(path, "wb") as script: script.write(firstline)