|
3 | 3 | import io |
4 | 4 | import os |
5 | 5 | import shlex |
6 | | -import sys |
7 | 6 | import zipfile |
8 | 7 | 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 |
20 | 10 |
|
21 | 11 | from installer import _scripts |
22 | 12 |
|
@@ -52,41 +42,6 @@ def read_binary(package: Union[str, ModuleType], file_path: str) -> bytes: |
52 | 42 | """ |
53 | 43 |
|
54 | 44 |
|
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 | | - |
90 | 45 | class InvalidScript(ValueError): |
91 | 46 | """Raised if the user provides incorrect script section or kind.""" |
92 | 47 |
|
@@ -146,19 +101,53 @@ def generate(self, executable: str, kind: "LauncherKind") -> Tuple[str, bytes]: |
146 | 101 | """ |
147 | 102 | launcher = self._get_launcher_data(kind) |
148 | 103 | executable = self._get_alternate_executable(executable, kind) |
149 | | - shebang = _build_shebang(executable, forlauncher=bool(launcher)) |
| 104 | + shebang = self._build_shebang(executable, forlauncher=bool(launcher)) |
150 | 105 | code = _SCRIPT_TEMPLATE.format( |
151 | 106 | module=self.module, |
152 | 107 | import_name=self.attr.split(".")[0], |
153 | 108 | func_path=self.attr, |
154 | 109 | ).encode("utf-8") |
155 | 110 |
|
156 | 111 | if launcher is None: |
157 | | - return (self.name, shebang + b"\n" + code) |
| 112 | + return self.name, shebang + b"\n" + code |
158 | 113 |
|
159 | 114 | stream = io.BytesIO() |
160 | 115 | with zipfile.ZipFile(stream, "w") as zf: |
161 | 116 | zf.writestr("__main__.py", code) |
162 | 117 | name = f"{self.name}.exe" |
163 | 118 | 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