Skip to content

Commit 0edcaa7

Browse files
authored
Small refactor to installer.scripts (#239)
1 parent 6fa5993 commit 0edcaa7

File tree

1 file changed

+39
-50
lines changed

1 file changed

+39
-50
lines changed

src/installer/scripts.py

Lines changed: 39 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,10 @@
33
import io
44
import os
55
import shlex
6-
import sys
76
import zipfile
87
from dataclasses import dataclass, field
9-
from types import ModuleType
10-
from typing import TYPE_CHECKING, Mapping, Optional, Tuple, Union
11-
12-
if sys.version_info >= (3, 9): # pragma: no cover
13-
from importlib.resources import files
14-
15-
def read_binary(package: Union[str, ModuleType], file_path: str) -> bytes:
16-
return (files(package) / file_path).read_bytes()
17-
18-
else: # pragma: no cover
19-
from importlib.resources import read_binary
8+
from importlib.resources import read_binary
9+
from typing import TYPE_CHECKING, Mapping, Optional, Tuple
2010

2111
from installer import _scripts
2212

@@ -52,41 +42,6 @@ def read_binary(package: Union[str, ModuleType], file_path: str) -> bytes:
5242
"""
5343

5444

55-
def _is_executable_simple(executable: bytes) -> bool:
56-
if b" " in executable:
57-
return False
58-
shebang_length = len(executable) + 3 # Prefix #! and newline after.
59-
# According to distlib, Darwin can handle up to 512 characters. But I want
60-
# to avoid platform sniffing to make this as platform agnostic as possible.
61-
# The "complex" script isn't that bad anyway.
62-
return shebang_length <= 127
63-
64-
65-
def _build_shebang(executable: str, forlauncher: bool) -> bytes:
66-
"""Build a shebang line.
67-
68-
The non-launcher cases are taken directly from distlib's implementation,
69-
which tries its best to account for command length, spaces in path, etc.
70-
71-
https://bitbucket.org/pypa/distlib/src/58cd5c6/distlib/scripts.py#lines-124
72-
"""
73-
executable_bytes = executable.encode("utf-8")
74-
if forlauncher: # The launcher can just use the command as-is.
75-
return b"#!" + executable_bytes
76-
if _is_executable_simple(executable_bytes):
77-
return b"#!" + executable_bytes
78-
79-
# Shebang support for an executable with a space in it is under-specified
80-
# and platform-dependent, so we use a clever hack to generate a script to
81-
# run in ``/bin/sh`` that should work on all reasonably modern platforms.
82-
# Read the following message to understand how the hack works:
83-
# https://github.com/pypa/installer/pull/4#issuecomment-623668717
84-
85-
quoted = shlex.quote(executable).encode("utf-8")
86-
# I don't understand a lick what this is trying to do.
87-
return b"#!/bin/sh\n'''exec' " + quoted + b' "$0" "$@"\n' + b"' '''"
88-
89-
9045
class InvalidScript(ValueError):
9146
"""Raised if the user provides incorrect script section or kind."""
9247

@@ -146,19 +101,53 @@ def generate(self, executable: str, kind: "LauncherKind") -> Tuple[str, bytes]:
146101
"""
147102
launcher = self._get_launcher_data(kind)
148103
executable = self._get_alternate_executable(executable, kind)
149-
shebang = _build_shebang(executable, forlauncher=bool(launcher))
104+
shebang = self._build_shebang(executable, forlauncher=bool(launcher))
150105
code = _SCRIPT_TEMPLATE.format(
151106
module=self.module,
152107
import_name=self.attr.split(".")[0],
153108
func_path=self.attr,
154109
).encode("utf-8")
155110

156111
if launcher is None:
157-
return (self.name, shebang + b"\n" + code)
112+
return self.name, shebang + b"\n" + code
158113

159114
stream = io.BytesIO()
160115
with zipfile.ZipFile(stream, "w") as zf:
161116
zf.writestr("__main__.py", code)
162117
name = f"{self.name}.exe"
163118
data = launcher + shebang + b"\n" + stream.getvalue()
164-
return (name, data)
119+
return name, data
120+
121+
@staticmethod
122+
def _is_executable_simple(executable: bytes) -> bool:
123+
if b" " in executable:
124+
return False
125+
shebang_length = len(executable) + 3 # Prefix #! and newline after.
126+
# According to distlib, Darwin can handle up to 512 characters. But I want
127+
# to avoid platform sniffing to make this as platform-agnostic as possible.
128+
# The "complex" script isn't that bad anyway.
129+
return shebang_length <= 127
130+
131+
def _build_shebang(self, executable: str, forlauncher: bool) -> bytes:
132+
"""Build a shebang line.
133+
134+
The non-launcher cases are taken directly from distlib's implementation,
135+
which tries its best to account for command length, spaces in path, etc.
136+
137+
https://bitbucket.org/pypa/distlib/src/58cd5c6/distlib/scripts.py#lines-124
138+
"""
139+
executable_bytes = executable.encode("utf-8")
140+
if forlauncher: # The launcher can just use the command as-is.
141+
return b"#!" + executable_bytes
142+
if self._is_executable_simple(executable_bytes):
143+
return b"#!" + executable_bytes
144+
145+
# Shebang support for an executable with a space in it is under-specified
146+
# and platform-dependent, so we use a clever hack to generate a script to
147+
# run in ``/bin/sh`` that should work on all reasonably modern platforms.
148+
# Read the following message to understand how the hack works:
149+
# https://github.com/pypa/installer/pull/4#issuecomment-623668717
150+
151+
quoted = shlex.quote(executable).encode("utf-8")
152+
# I don't understand a lick what this is trying to do.
153+
return b"#!/bin/sh\n'''exec' " + quoted + b' "$0" "$@"\n' + b"' '''"

0 commit comments

Comments
 (0)