Gentoo Archives: gentoo-commits

From: "Michał Górny" <mgorny@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] repo/gentoo:master commit in: eclass/, eclass/tests/
Date: Sat, 02 Apr 2022 16:29:55
Message-Id: 1648916986.85820b0b54f298e1d96a4bc3ed9cd4952a46aea0.mgorny@gentoo
1 commit: 85820b0b54f298e1d96a4bc3ed9cd4952a46aea0
2 Author: Michał Górny <mgorny <AT> gentoo <DOT> org>
3 AuthorDate: Thu Mar 31 21:09:30 2022 +0000
4 Commit: Michał Górny <mgorny <AT> gentoo <DOT> org>
5 CommitDate: Sat Apr 2 16:29:46 2022 +0000
6 URL: https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=85820b0b
7
8 python-utils-r1.eclass: Make python_fix_shebang force full path
9
10 Change the behavior of python_fix_shebang to always output full path
11 to the Python interpreter (i.e. ${PYTHON}) instead of mangling
12 the original path. Most importantly, this ensures that:
13
14 1. EPREFIX is included in the final path
15
16 2. /usr/bin/env is replaced by the absolute path to avoid issues
17 when calling system executables from inside a venv
18
19 Note that this implies that a few unlikely corner cases may stop
20 working, notably:
21
22 a. "weird" shebangs such as "/usr/bin/foo python" will no longer work
23
24 b. the mangled scripts will escape temporary venv e.g. created
25 in distutils-r1 PEP517 mode (python_fix_shebang is not used in such
26 a way in ::gentoo)
27
28 Signed-off-by: Michał Górny <mgorny <AT> gentoo.org>
29
30 eclass/python-utils-r1.eclass | 134 +++++++++++++++-------------------------
31 eclass/tests/python-utils-r1.sh | 86 ++++++++++++++++----------
32 2 files changed, 106 insertions(+), 114 deletions(-)
33
34 diff --git a/eclass/python-utils-r1.eclass b/eclass/python-utils-r1.eclass
35 index 7b0c9aa280d8..98cb49c95fd7 100644
36 --- a/eclass/python-utils-r1.eclass
37 +++ b/eclass/python-utils-r1.eclass
38 @@ -1001,25 +1001,30 @@ _python_wrapper_setup() {
39 # @FUNCTION: python_fix_shebang
40 # @USAGE: [-f|--force] [-q|--quiet] <path>...
41 # @DESCRIPTION:
42 -# Replace the shebang in Python scripts with the current Python
43 -# implementation (EPYTHON). If a directory is passed, works recursively
44 -# on all Python scripts.
45 +# Replace the shebang in Python scripts with the full path
46 +# to the current Python implementation (PYTHON, including EPREFIX).
47 +# If a directory is passed, works recursively on all Python scripts
48 +# found inside the directory tree.
49 #
50 -# Only files having a 'python*' shebang will be modified. Files with
51 -# other shebang will either be skipped when working recursively
52 -# on a directory or treated as error when specified explicitly.
53 +# Only files having a Python shebang (a path to any known Python
54 +# interpreter, optionally preceded by env(1) invocation) will
55 +# be processed. Files with any other shebang will either be skipped
56 +# silently when a directory was passed, or an error will be reported
57 +# for any files without Python shebangs specified explicitly.
58 #
59 -# Shebangs matching explicitly current Python version will be left
60 -# unmodified. Shebangs requesting another Python version will be treated
61 -# as fatal error, unless --force is given.
62 +# Shebangs that are compatible with the current Python version will be
63 +# mangled unconditionally. Incompatible shebangs will cause a fatal
64 +# error, unless --force is specified.
65 #
66 -# --force causes the function to replace even shebangs that require
67 -# incompatible Python version. --quiet causes the function not to list
68 -# modified files verbosely.
69 +# --force causes the function to replace shebangs with incompatible
70 +# Python version (but not non-Python shebangs). --quiet causes
71 +# the function not to list modified files verbosely.
72 python_fix_shebang() {
73 debug-print-function ${FUNCNAME} "${@}"
74
75 [[ ${EPYTHON} ]] || die "${FUNCNAME}: EPYTHON unset (pkg_setup not called?)"
76 + local PYTHON
77 + _python_export "${EPYTHON}" PYTHON
78
79 local force quiet
80 while [[ ${@} ]]; do
81 @@ -1035,13 +1040,13 @@ python_fix_shebang() {
82
83 local path f
84 for path; do
85 - local any_correct any_fixed is_recursive
86 + local any_fixed is_recursive
87
88 [[ -d ${path} ]] && is_recursive=1
89
90 while IFS= read -r -d '' f; do
91 local shebang i
92 - local error= from=
93 + local error= match=
94
95 # note: we can't ||die here since read will fail if file
96 # has no newline characters
97 @@ -1050,64 +1055,36 @@ python_fix_shebang() {
98 # First, check if it's shebang at all...
99 if [[ ${shebang} == '#!'* ]]; then
100 local split_shebang=()
101 - read -r -a split_shebang <<<${shebang} || die
102 -
103 - # Match left-to-right in a loop, to avoid matching random
104 - # repetitions like 'python2.7 python2'.
105 - for i in "${split_shebang[@]}"; do
106 - case "${i}" in
107 - *"${EPYTHON}")
108 - debug-print "${FUNCNAME}: in file ${f#${D%/}}"
109 - debug-print "${FUNCNAME}: shebang matches EPYTHON: ${shebang}"
110 -
111 - # Nothing to do, move along.
112 - any_correct=1
113 - from=${EPYTHON}
114 - break
115 - ;;
116 - *python|*python[23])
117 - debug-print "${FUNCNAME}: in file ${f#${D%/}}"
118 - debug-print "${FUNCNAME}: rewriting shebang: ${shebang}"
119 -
120 - if [[ ${i} == *python2 ]]; then
121 - from=python2
122 - if [[ ! ${force} ]]; then
123 - error=1
124 - fi
125 - elif [[ ${i} == *python3 ]]; then
126 - from=python3
127 - else
128 - from=python
129 - fi
130 - break
131 - ;;
132 - *python[23].[0-9]|*python3.[1-9][0-9]|*pypy|*pypy3|*jython[23].[0-9])
133 - # Explicit mismatch.
134 - if [[ ! ${force} ]]; then
135 - error=1
136 - else
137 - case "${i}" in
138 - *python[23].[0-9])
139 - from="python[23].[0-9]";;
140 - *python3.[1-9][0-9])
141 - from="python3.[1-9][0-9]";;
142 - *pypy)
143 - from="pypy";;
144 - *pypy3)
145 - from="pypy3";;
146 - *jython[23].[0-9])
147 - from="jython[23].[0-9]";;
148 - *)
149 - die "${FUNCNAME}: internal error in 2nd pattern match";;
150 - esac
151 - fi
152 - break
153 - ;;
154 - esac
155 - done
156 + read -r -a split_shebang <<<${shebang#"#!"} || die
157 +
158 + local in_path=${split_shebang[0]}
159 + local from='^#! *[^ ]*'
160 + # if the first component is env(1), skip it
161 + if [[ ${in_path} == */env ]]; then
162 + in_path=${split_shebang[1]}
163 + from+=' *[^ ]*'
164 + fi
165 +
166 + case ${in_path##*/} in
167 + "${EPYTHON}")
168 + match=1
169 + ;;
170 + python|python[23])
171 + match=1
172 + [[ ${in_path##*/} == python2 ]] && error=1
173 + ;;
174 + python[23].[0-9]|python3.[1-9][0-9]|pypy|pypy3|jython[23].[0-9])
175 + # Explicit mismatch.
176 + match=1
177 + error=1
178 + ;;
179 + esac
180 fi
181
182 - if [[ ! ${error} && ! ${from} ]]; then
183 + # disregard mismatches in force mode
184 + [[ ${force} ]] && error=
185 +
186 + if [[ ! ${match} ]]; then
187 # Non-Python shebang. Allowed in recursive mode,
188 # disallowed when specifying file explicitly.
189 [[ ${is_recursive} ]] && continue
190 @@ -1119,13 +1096,9 @@ python_fix_shebang() {
191 fi
192
193 if [[ ! ${error} ]]; then
194 - # We either want to match ${from} followed by space
195 - # or at end-of-string.
196 - if [[ ${shebang} == *${from}" "* ]]; then
197 - sed -i -e "1s:${from} :${EPYTHON} :" "${f}" || die
198 - else
199 - sed -i -e "1s:${from}$:${EPYTHON}:" "${f}" || die
200 - fi
201 + debug-print "${FUNCNAME}: in file ${f#${D%/}}"
202 + debug-print "${FUNCNAME}: rewriting shebang: ${shebang}"
203 + sed -i -e "1s@${from}@#!${PYTHON}@" "${f}" || die
204 any_fixed=1
205 else
206 eerror "The file has incompatible shebang:"
207 @@ -1138,12 +1111,7 @@ python_fix_shebang() {
208
209 if [[ ! ${any_fixed} ]]; then
210 eerror "QA error: ${FUNCNAME}, ${path#${D%/}} did not match any fixable files."
211 - if [[ ${any_correct} ]]; then
212 - eerror "All files have ${EPYTHON} shebang already."
213 - else
214 - eerror "There are no Python files in specified directory."
215 - fi
216 -
217 + eerror "There are no Python files in specified directory."
218 die "${FUNCNAME} did not match any fixable files"
219 fi
220 done
221
222 diff --git a/eclass/tests/python-utils-r1.sh b/eclass/tests/python-utils-r1.sh
223 index 8c733b22294e..ef7687b8a9cf 100755
224 --- a/eclass/tests/python-utils-r1.sh
225 +++ b/eclass/tests/python-utils-r1.sh
226 @@ -41,7 +41,7 @@ test_fix_shebang() {
227 local expect=${3}
228 local args=( "${@:4}" )
229
230 - tbegin "python_fix_shebang${args[@]+ ${args[*]}} from ${from} to ${to} (exp: ${expect})"
231 + tbegin "python_fix_shebang${args[@]+ ${args[*]}} from ${from@Q} to ${to@Q} (exp: ${expect@Q})"
232
233 echo "${from}" > "${tmpfile}"
234 output=$( EPYTHON=${to} python_fix_shebang "${args[@]}" -q "${tmpfile}" 2>&1 )
235 @@ -156,36 +156,60 @@ fi
236 test_var PYTHON_PKG_DEP pypy3 '*dev-python/pypy3*:0='
237 test_var PYTHON_SCRIPTDIR pypy3 /usr/lib/python-exec/pypy3
238
239 -# generic shebangs
240 -test_fix_shebang '#!/usr/bin/python' python3.6 '#!/usr/bin/python3.6'
241 -test_fix_shebang '#!/usr/bin/python' pypy3 '#!/usr/bin/pypy3'
242 -
243 -# python2/python3 matching
244 -test_fix_shebang '#!/usr/bin/python3' python3.6 '#!/usr/bin/python3.6'
245 -test_fix_shebang '#!/usr/bin/python2' python3.6 FAIL
246 -test_fix_shebang '#!/usr/bin/python2' python3.6 '#!/usr/bin/python3.6' --force
247 -
248 -# pythonX.Y matching (those mostly test the patterns)
249 -test_fix_shebang '#!/usr/bin/python2.7' python3.2 FAIL
250 -test_fix_shebang '#!/usr/bin/python2.7' python3.2 '#!/usr/bin/python3.2' --force
251 -test_fix_shebang '#!/usr/bin/python3.2' python3.2 '#!/usr/bin/python3.2'
252 -
253 -# fancy path handling
254 -test_fix_shebang '#!/mnt/python2/usr/bin/python' python3.6 \
255 - '#!/mnt/python2/usr/bin/python3.6'
256 -test_fix_shebang '#!/mnt/python2/usr/bin/python3' python3.8 \
257 - '#!/mnt/python2/usr/bin/python3.8'
258 -test_fix_shebang '#!/mnt/python2/usr/bin/env python' python3.8 \
259 - '#!/mnt/python2/usr/bin/env python3.8'
260 -test_fix_shebang '#!/mnt/python2/usr/bin/python3 python3' python3.8 \
261 - '#!/mnt/python2/usr/bin/python3.8 python3'
262 -test_fix_shebang '#!/mnt/python2/usr/bin/python2 python3' python3.8 FAIL
263 -test_fix_shebang '#!/mnt/python2/usr/bin/python2 python3' python3.8 \
264 - '#!/mnt/python2/usr/bin/python3.8 python3' --force
265 -test_fix_shebang '#!/usr/bin/foo' python3.8 FAIL
266 -
267 -# regression test for bug #522080
268 -test_fix_shebang '#!/usr/bin/python ' python3.8 '#!/usr/bin/python3.8 '
269 +for EPREFIX in '' /foo; do
270 + einfo "with EPREFIX=${EPREFIX@Q}"
271 + eindent
272 + # generic shebangs
273 + test_fix_shebang '#!/usr/bin/python' python3.6 \
274 + "#!${EPREFIX}/usr/bin/python3.6"
275 + test_fix_shebang '#!/usr/bin/python' pypy3 \
276 + "#!${EPREFIX}/usr/bin/pypy3"
277 +
278 + # python2/python3 matching
279 + test_fix_shebang '#!/usr/bin/python3' python3.6 \
280 + "#!${EPREFIX}/usr/bin/python3.6"
281 + test_fix_shebang '#!/usr/bin/python2' python3.6 FAIL
282 + test_fix_shebang '#!/usr/bin/python2' python3.6 \
283 + "#!${EPREFIX}/usr/bin/python3.6" --force
284 +
285 + # pythonX.Y matching (those mostly test the patterns)
286 + test_fix_shebang '#!/usr/bin/python2.7' python3.2 FAIL
287 + test_fix_shebang '#!/usr/bin/python2.7' python3.2 \
288 + "#!${EPREFIX}/usr/bin/python3.2" --force
289 + test_fix_shebang '#!/usr/bin/python3.2' python3.2 \
290 + "#!${EPREFIX}/usr/bin/python3.2"
291 +
292 + # fancy path handling
293 + test_fix_shebang '#!/mnt/python2/usr/bin/python' python3.6 \
294 + "#!${EPREFIX}/usr/bin/python3.6"
295 + test_fix_shebang '#!/mnt/python2/usr/bin/python3' python3.8 \
296 + "#!${EPREFIX}/usr/bin/python3.8"
297 + test_fix_shebang '#!/mnt/python2/usr/bin/env python' python3.8 \
298 + "#!${EPREFIX}/usr/bin/python3.8"
299 + test_fix_shebang '#!/mnt/python2/usr/bin/python3 python3' python3.8 \
300 + "#!${EPREFIX}/usr/bin/python3.8 python3"
301 + test_fix_shebang '#!/mnt/python2/usr/bin/python2 python3' python3.8 FAIL
302 + test_fix_shebang '#!/mnt/python2/usr/bin/python2 python3' python3.8 \
303 + "#!${EPREFIX}/usr/bin/python3.8 python3" --force
304 + test_fix_shebang '#!/usr/bin/foo' python3.8 FAIL
305 +
306 + # regression test for bug #522080
307 + test_fix_shebang '#!/usr/bin/python ' python3.8 \
308 + "#!${EPREFIX}/usr/bin/python3.8 "
309 +
310 + # test random whitespace in shebang
311 + test_fix_shebang '#! /usr/bin/python' python3.8 \
312 + "#!${EPREFIX}/usr/bin/python3.8"
313 + test_fix_shebang '#! /usr/bin/python' python3.8 \
314 + "#!${EPREFIX}/usr/bin/python3.8"
315 + test_fix_shebang '#! /usr/bin/env python' python3.8 \
316 + "#!${EPREFIX}/usr/bin/python3.8"
317 +
318 + # test preserving options
319 + test_fix_shebang '#! /usr/bin/python -b' python3.8 \
320 + "#!${EPREFIX}/usr/bin/python3.8 -b"
321 + eoutdent
322 +done
323
324 # check _python_impl_matches behavior
325 test_is "_python_impl_matches python3_6 -3" 0