mirror of
https://github.com/gentoo-mirror/gentoo.git
synced 2026-09-24 04:59:14 -07:00
python-utils-r1.eclass: Make python_fix_shebang force full path
Change the behavior of python_fix_shebang to always output full path
to the Python interpreter (i.e. ${PYTHON}) instead of mangling
the original path. Most importantly, this ensures that:
1. EPREFIX is included in the final path
2. /usr/bin/env is replaced by the absolute path to avoid issues
when calling system executables from inside a venv
Note that this implies that a few unlikely corner cases may stop
working, notably:
a. "weird" shebangs such as "/usr/bin/foo python" will no longer work
b. the mangled scripts will escape temporary venv e.g. created
in distutils-r1 PEP517 mode (python_fix_shebang is not used in such
a way in ::gentoo)
Signed-off-by: Michał Górny <mgorny@gentoo.org>
This commit is contained in:
@@ -1001,25 +1001,30 @@ _python_wrapper_setup() {
|
||||
# @FUNCTION: python_fix_shebang
|
||||
# @USAGE: [-f|--force] [-q|--quiet] <path>...
|
||||
# @DESCRIPTION:
|
||||
# Replace the shebang in Python scripts with the current Python
|
||||
# implementation (EPYTHON). If a directory is passed, works recursively
|
||||
# on all Python scripts.
|
||||
# Replace the shebang in Python scripts with the full path
|
||||
# to the current Python implementation (PYTHON, including EPREFIX).
|
||||
# If a directory is passed, works recursively on all Python scripts
|
||||
# found inside the directory tree.
|
||||
#
|
||||
# Only files having a 'python*' shebang will be modified. Files with
|
||||
# other shebang will either be skipped when working recursively
|
||||
# on a directory or treated as error when specified explicitly.
|
||||
# Only files having a Python shebang (a path to any known Python
|
||||
# interpreter, optionally preceded by env(1) invocation) will
|
||||
# be processed. Files with any other shebang will either be skipped
|
||||
# silently when a directory was passed, or an error will be reported
|
||||
# for any files without Python shebangs specified explicitly.
|
||||
#
|
||||
# Shebangs matching explicitly current Python version will be left
|
||||
# unmodified. Shebangs requesting another Python version will be treated
|
||||
# as fatal error, unless --force is given.
|
||||
# Shebangs that are compatible with the current Python version will be
|
||||
# mangled unconditionally. Incompatible shebangs will cause a fatal
|
||||
# error, unless --force is specified.
|
||||
#
|
||||
# --force causes the function to replace even shebangs that require
|
||||
# incompatible Python version. --quiet causes the function not to list
|
||||
# modified files verbosely.
|
||||
# --force causes the function to replace shebangs with incompatible
|
||||
# Python version (but not non-Python shebangs). --quiet causes
|
||||
# the function not to list modified files verbosely.
|
||||
python_fix_shebang() {
|
||||
debug-print-function ${FUNCNAME} "${@}"
|
||||
|
||||
[[ ${EPYTHON} ]] || die "${FUNCNAME}: EPYTHON unset (pkg_setup not called?)"
|
||||
local PYTHON
|
||||
_python_export "${EPYTHON}" PYTHON
|
||||
|
||||
local force quiet
|
||||
while [[ ${@} ]]; do
|
||||
@@ -1035,13 +1040,13 @@ python_fix_shebang() {
|
||||
|
||||
local path f
|
||||
for path; do
|
||||
local any_correct any_fixed is_recursive
|
||||
local any_fixed is_recursive
|
||||
|
||||
[[ -d ${path} ]] && is_recursive=1
|
||||
|
||||
while IFS= read -r -d '' f; do
|
||||
local shebang i
|
||||
local error= from=
|
||||
local error= match=
|
||||
|
||||
# note: we can't ||die here since read will fail if file
|
||||
# has no newline characters
|
||||
@@ -1050,64 +1055,36 @@ python_fix_shebang() {
|
||||
# First, check if it's shebang at all...
|
||||
if [[ ${shebang} == '#!'* ]]; then
|
||||
local split_shebang=()
|
||||
read -r -a split_shebang <<<${shebang} || die
|
||||
read -r -a split_shebang <<<${shebang#"#!"} || die
|
||||
|
||||
# Match left-to-right in a loop, to avoid matching random
|
||||
# repetitions like 'python2.7 python2'.
|
||||
for i in "${split_shebang[@]}"; do
|
||||
case "${i}" in
|
||||
*"${EPYTHON}")
|
||||
debug-print "${FUNCNAME}: in file ${f#${D%/}}"
|
||||
debug-print "${FUNCNAME}: shebang matches EPYTHON: ${shebang}"
|
||||
local in_path=${split_shebang[0]}
|
||||
local from='^#! *[^ ]*'
|
||||
# if the first component is env(1), skip it
|
||||
if [[ ${in_path} == */env ]]; then
|
||||
in_path=${split_shebang[1]}
|
||||
from+=' *[^ ]*'
|
||||
fi
|
||||
|
||||
# Nothing to do, move along.
|
||||
any_correct=1
|
||||
from=${EPYTHON}
|
||||
break
|
||||
;;
|
||||
*python|*python[23])
|
||||
debug-print "${FUNCNAME}: in file ${f#${D%/}}"
|
||||
debug-print "${FUNCNAME}: rewriting shebang: ${shebang}"
|
||||
|
||||
if [[ ${i} == *python2 ]]; then
|
||||
from=python2
|
||||
if [[ ! ${force} ]]; then
|
||||
error=1
|
||||
fi
|
||||
elif [[ ${i} == *python3 ]]; then
|
||||
from=python3
|
||||
else
|
||||
from=python
|
||||
fi
|
||||
break
|
||||
;;
|
||||
*python[23].[0-9]|*python3.[1-9][0-9]|*pypy|*pypy3|*jython[23].[0-9])
|
||||
# Explicit mismatch.
|
||||
if [[ ! ${force} ]]; then
|
||||
error=1
|
||||
else
|
||||
case "${i}" in
|
||||
*python[23].[0-9])
|
||||
from="python[23].[0-9]";;
|
||||
*python3.[1-9][0-9])
|
||||
from="python3.[1-9][0-9]";;
|
||||
*pypy)
|
||||
from="pypy";;
|
||||
*pypy3)
|
||||
from="pypy3";;
|
||||
*jython[23].[0-9])
|
||||
from="jython[23].[0-9]";;
|
||||
*)
|
||||
die "${FUNCNAME}: internal error in 2nd pattern match";;
|
||||
esac
|
||||
fi
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
case ${in_path##*/} in
|
||||
"${EPYTHON}")
|
||||
match=1
|
||||
;;
|
||||
python|python[23])
|
||||
match=1
|
||||
[[ ${in_path##*/} == python2 ]] && error=1
|
||||
;;
|
||||
python[23].[0-9]|python3.[1-9][0-9]|pypy|pypy3|jython[23].[0-9])
|
||||
# Explicit mismatch.
|
||||
match=1
|
||||
error=1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if [[ ! ${error} && ! ${from} ]]; then
|
||||
# disregard mismatches in force mode
|
||||
[[ ${force} ]] && error=
|
||||
|
||||
if [[ ! ${match} ]]; then
|
||||
# Non-Python shebang. Allowed in recursive mode,
|
||||
# disallowed when specifying file explicitly.
|
||||
[[ ${is_recursive} ]] && continue
|
||||
@@ -1119,13 +1096,9 @@ python_fix_shebang() {
|
||||
fi
|
||||
|
||||
if [[ ! ${error} ]]; then
|
||||
# We either want to match ${from} followed by space
|
||||
# or at end-of-string.
|
||||
if [[ ${shebang} == *${from}" "* ]]; then
|
||||
sed -i -e "1s:${from} :${EPYTHON} :" "${f}" || die
|
||||
else
|
||||
sed -i -e "1s:${from}$:${EPYTHON}:" "${f}" || die
|
||||
fi
|
||||
debug-print "${FUNCNAME}: in file ${f#${D%/}}"
|
||||
debug-print "${FUNCNAME}: rewriting shebang: ${shebang}"
|
||||
sed -i -e "1s@${from}@#!${PYTHON}@" "${f}" || die
|
||||
any_fixed=1
|
||||
else
|
||||
eerror "The file has incompatible shebang:"
|
||||
@@ -1138,12 +1111,7 @@ python_fix_shebang() {
|
||||
|
||||
if [[ ! ${any_fixed} ]]; then
|
||||
eerror "QA error: ${FUNCNAME}, ${path#${D%/}} did not match any fixable files."
|
||||
if [[ ${any_correct} ]]; then
|
||||
eerror "All files have ${EPYTHON} shebang already."
|
||||
else
|
||||
eerror "There are no Python files in specified directory."
|
||||
fi
|
||||
|
||||
eerror "There are no Python files in specified directory."
|
||||
die "${FUNCNAME} did not match any fixable files"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -41,7 +41,7 @@ test_fix_shebang() {
|
||||
local expect=${3}
|
||||
local args=( "${@:4}" )
|
||||
|
||||
tbegin "python_fix_shebang${args[@]+ ${args[*]}} from ${from} to ${to} (exp: ${expect})"
|
||||
tbegin "python_fix_shebang${args[@]+ ${args[*]}} from ${from@Q} to ${to@Q} (exp: ${expect@Q})"
|
||||
|
||||
echo "${from}" > "${tmpfile}"
|
||||
output=$( EPYTHON=${to} python_fix_shebang "${args[@]}" -q "${tmpfile}" 2>&1 )
|
||||
@@ -156,36 +156,60 @@ fi
|
||||
test_var PYTHON_PKG_DEP pypy3 '*dev-python/pypy3*:0='
|
||||
test_var PYTHON_SCRIPTDIR pypy3 /usr/lib/python-exec/pypy3
|
||||
|
||||
# generic shebangs
|
||||
test_fix_shebang '#!/usr/bin/python' python3.6 '#!/usr/bin/python3.6'
|
||||
test_fix_shebang '#!/usr/bin/python' pypy3 '#!/usr/bin/pypy3'
|
||||
for EPREFIX in '' /foo; do
|
||||
einfo "with EPREFIX=${EPREFIX@Q}"
|
||||
eindent
|
||||
# generic shebangs
|
||||
test_fix_shebang '#!/usr/bin/python' python3.6 \
|
||||
"#!${EPREFIX}/usr/bin/python3.6"
|
||||
test_fix_shebang '#!/usr/bin/python' pypy3 \
|
||||
"#!${EPREFIX}/usr/bin/pypy3"
|
||||
|
||||
# python2/python3 matching
|
||||
test_fix_shebang '#!/usr/bin/python3' python3.6 '#!/usr/bin/python3.6'
|
||||
test_fix_shebang '#!/usr/bin/python2' python3.6 FAIL
|
||||
test_fix_shebang '#!/usr/bin/python2' python3.6 '#!/usr/bin/python3.6' --force
|
||||
# python2/python3 matching
|
||||
test_fix_shebang '#!/usr/bin/python3' python3.6 \
|
||||
"#!${EPREFIX}/usr/bin/python3.6"
|
||||
test_fix_shebang '#!/usr/bin/python2' python3.6 FAIL
|
||||
test_fix_shebang '#!/usr/bin/python2' python3.6 \
|
||||
"#!${EPREFIX}/usr/bin/python3.6" --force
|
||||
|
||||
# pythonX.Y matching (those mostly test the patterns)
|
||||
test_fix_shebang '#!/usr/bin/python2.7' python3.2 FAIL
|
||||
test_fix_shebang '#!/usr/bin/python2.7' python3.2 '#!/usr/bin/python3.2' --force
|
||||
test_fix_shebang '#!/usr/bin/python3.2' python3.2 '#!/usr/bin/python3.2'
|
||||
# pythonX.Y matching (those mostly test the patterns)
|
||||
test_fix_shebang '#!/usr/bin/python2.7' python3.2 FAIL
|
||||
test_fix_shebang '#!/usr/bin/python2.7' python3.2 \
|
||||
"#!${EPREFIX}/usr/bin/python3.2" --force
|
||||
test_fix_shebang '#!/usr/bin/python3.2' python3.2 \
|
||||
"#!${EPREFIX}/usr/bin/python3.2"
|
||||
|
||||
# fancy path handling
|
||||
test_fix_shebang '#!/mnt/python2/usr/bin/python' python3.6 \
|
||||
'#!/mnt/python2/usr/bin/python3.6'
|
||||
test_fix_shebang '#!/mnt/python2/usr/bin/python3' python3.8 \
|
||||
'#!/mnt/python2/usr/bin/python3.8'
|
||||
test_fix_shebang '#!/mnt/python2/usr/bin/env python' python3.8 \
|
||||
'#!/mnt/python2/usr/bin/env python3.8'
|
||||
test_fix_shebang '#!/mnt/python2/usr/bin/python3 python3' python3.8 \
|
||||
'#!/mnt/python2/usr/bin/python3.8 python3'
|
||||
test_fix_shebang '#!/mnt/python2/usr/bin/python2 python3' python3.8 FAIL
|
||||
test_fix_shebang '#!/mnt/python2/usr/bin/python2 python3' python3.8 \
|
||||
'#!/mnt/python2/usr/bin/python3.8 python3' --force
|
||||
test_fix_shebang '#!/usr/bin/foo' python3.8 FAIL
|
||||
# fancy path handling
|
||||
test_fix_shebang '#!/mnt/python2/usr/bin/python' python3.6 \
|
||||
"#!${EPREFIX}/usr/bin/python3.6"
|
||||
test_fix_shebang '#!/mnt/python2/usr/bin/python3' python3.8 \
|
||||
"#!${EPREFIX}/usr/bin/python3.8"
|
||||
test_fix_shebang '#!/mnt/python2/usr/bin/env python' python3.8 \
|
||||
"#!${EPREFIX}/usr/bin/python3.8"
|
||||
test_fix_shebang '#!/mnt/python2/usr/bin/python3 python3' python3.8 \
|
||||
"#!${EPREFIX}/usr/bin/python3.8 python3"
|
||||
test_fix_shebang '#!/mnt/python2/usr/bin/python2 python3' python3.8 FAIL
|
||||
test_fix_shebang '#!/mnt/python2/usr/bin/python2 python3' python3.8 \
|
||||
"#!${EPREFIX}/usr/bin/python3.8 python3" --force
|
||||
test_fix_shebang '#!/usr/bin/foo' python3.8 FAIL
|
||||
|
||||
# regression test for bug #522080
|
||||
test_fix_shebang '#!/usr/bin/python ' python3.8 '#!/usr/bin/python3.8 '
|
||||
# regression test for bug #522080
|
||||
test_fix_shebang '#!/usr/bin/python ' python3.8 \
|
||||
"#!${EPREFIX}/usr/bin/python3.8 "
|
||||
|
||||
# test random whitespace in shebang
|
||||
test_fix_shebang '#! /usr/bin/python' python3.8 \
|
||||
"#!${EPREFIX}/usr/bin/python3.8"
|
||||
test_fix_shebang '#! /usr/bin/python' python3.8 \
|
||||
"#!${EPREFIX}/usr/bin/python3.8"
|
||||
test_fix_shebang '#! /usr/bin/env python' python3.8 \
|
||||
"#!${EPREFIX}/usr/bin/python3.8"
|
||||
|
||||
# test preserving options
|
||||
test_fix_shebang '#! /usr/bin/python -b' python3.8 \
|
||||
"#!${EPREFIX}/usr/bin/python3.8 -b"
|
||||
eoutdent
|
||||
done
|
||||
|
||||
# check _python_impl_matches behavior
|
||||
test_is "_python_impl_matches python3_6 -3" 0
|
||||
|
||||
Reference in New Issue
Block a user