* [gentoo-dev] python-utils-r1.eclass: fixes & improvements for python_fix_shebang
@ 2014-06-13 22:36 99% Michał Górny
0 siblings, 0 replies; 1+ results
From: Michał Górny @ 2014-06-13 22:36 UTC (permalink / raw
To: gentoo-dev; +Cc: python
[-- Attachment #1.1: Type: text/plain, Size: 879 bytes --]
Hello, all.
Attaching two patches: one that practically rewrites
python_fix_shebang, and the other one that adds proper tests for it.
Major changes:
1. I replaced unclear space magic that attempted to handle corner cases
with plain 'for i in ${shebang}' -- trying to match various python*
patterns left-to-right to shebang. This specifically fixes corner cases
like:
/usr/bin/python2 python
(not that it's meaningful but we mangle it correctly now -- always
the leftmost matching thingie is replaced!)
2. I've added --quiet and --force options, the former to silence
the 'fixing shebang in ...' output, the latter to force replacing even
incompatible shebangs (e.g. python3 -> python2.7).
3. Added proper tests for a lot of cases, including corner cases like:
/mnt/python2/usr/bin/python3
Please review.
--
Best regards,
Michał Górny
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: python-utils-r1.eclass.diff --]
[-- Type: text/x-patch, Size: 5832 bytes --]
Index: python-utils-r1.eclass
===================================================================
RCS file: /var/cvsroot/gentoo-x86/eclass/python-utils-r1.eclass,v
retrieving revision 1.56
diff -u -B -r1.56 python-utils-r1.eclass
--- python-utils-r1.eclass 26 May 2014 16:13:35 -0000 1.56
+++ python-utils-r1.eclass 13 Jun 2014 22:28:39 -0000
@@ -670,8 +670,7 @@
# don't use this at home, just call python_doscript() instead
if [[ ${_PYTHON_REWRITE_SHEBANG} ]]; then
- local _PYTHON_FIX_SHEBANG_QUIET=1
- python_fix_shebang "${ED%/}/${d}/${newfn}"
+ python_fix_shebang -q "${ED%/}/${d}/${newfn}"
fi
}
@@ -935,7 +934,7 @@
}
# @FUNCTION: python_fix_shebang
-# @USAGE: <path>...
+# @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
@@ -947,13 +946,28 @@
#
# Shebangs matching explicitly current Python version will be left
# unmodified. Shebangs requesting another Python version will be treated
-# as fatal error.
+# as fatal error, unless --force is given.
+#
+# --force causes the function to replace even shebangs that require
+# incompatible Python version. --quiet causes the function not to list
+# modified files verbosely.
python_fix_shebang() {
debug-print-function ${FUNCNAME} "${@}"
- [[ ${1} ]] || die "${FUNCNAME}: no paths given"
[[ ${EPYTHON} ]] || die "${FUNCNAME}: EPYTHON unset (pkg_setup not called?)"
+ local force quiet
+ while [[ ${@} ]]; do
+ case "${1}" in
+ -f|--force) force=1; shift;;
+ -q|--quiet) quiet=1; shift;;
+ --) shift; break;;
+ *) break;;
+ esac
+ done
+
+ [[ ${1} ]] || die "${FUNCNAME}: no paths given"
+
local path f
for path; do
local any_correct any_fixed is_recursive
@@ -961,54 +975,88 @@
[[ -d ${path} ]] && is_recursive=1
while IFS= read -r -d '' f; do
- local shebang=$(head -n 1 "${f}")
- local error
+ local shebang i
+ local error from
- case "${shebang} " in
- '#!'*"${EPYTHON} "*)
- debug-print "${FUNCNAME}: in file ${f#${D}}"
- debug-print "${FUNCNAME}: shebang matches EPYTHON: ${shebang}"
-
- # Nothing to do, move along.
- any_correct=1
- ;;
- '#!'*python" "*|'#!'*python[23]" "*)
- debug-print "${FUNCNAME}: in file ${f#${D}}"
- debug-print "${FUNCNAME}: rewriting shebang: ${shebang}"
-
- # Note: for internal use.
- if [[ ! ${_PYTHON_FIX_SHEBANG_QUIET} ]]; then
- einfo "Fixing shebang in ${f#${D}}."
- fi
-
- local from
- if [[ "${shebang} " == *'python2 '* ]]; then
- from=python2
- python_is_python3 "${EPYTHON}" && error=1
- elif [[ "${shebang} " == *'python3 '* ]]; then
- from=python3
- python_is_python3 "${EPYTHON}" || error=1
- else
- from=python
- fi
-
- if [[ ! ${error} ]]; then
- sed -i -e "1s:${from}:${EPYTHON}:" "${f}" || die
- any_fixed=1
- fi
- ;;
- '#!'*python[23].[0123456789]" "*|'#!'*pypy" "*|'#!'*jython[23].[0123456789]" "*)
- # Explicit mismatch.
- error=1
- ;;
- *)
- # Non-Python shebang. Allowed in recursive mode,
- # disallowed when specifying file explicitly.
- [[ ${is_recursive} ]] || error=1
- ;;
- esac
+ read shebang <"${f}"
- if [[ ${error} ]]; then
+ # First, check if it's shebang at all...
+ if [[ ${shebang} == '#!'* ]]; then
+ # Match left-to-right in a loop, to avoid matching random
+ # repetitions like 'python2.7 python2'.
+ for i in ${shebang}; do
+ case "${i}" in
+ *"${EPYTHON}")
+ debug-print "${FUNCNAME}: in file ${f#${D}}"
+ debug-print "${FUNCNAME}: shebang matches EPYTHON: ${shebang}"
+
+ # 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
+ python_is_python3 "${EPYTHON}" && error=1
+ fi
+ elif [[ ${i} == *python3 ]]; then
+ from=python3
+ if [[ ! ${force} ]]; then
+ python_is_python3 "${EPYTHON}" || error=1
+ fi
+ else
+ from=python
+ fi
+ break
+ ;;
+ *python[23].[0123456789]|*pypy|*jython[23].[0123456789])
+ # Explicit mismatch.
+ if [[ ! ${force} ]]; then
+ error=1
+ else
+ case "${i}" in
+ *python[23].[0123456789])
+ from="python[23].[0123456789]";;
+ *pypy)
+ from="pypy";;
+ *jython[23].[0123456789])
+ from="jython[23].[0123456789]";;
+ *)
+ die "${FUNCNAME}: internal error in 2nd pattern match";;
+ esac
+ fi
+ break
+ ;;
+ esac
+ done
+ fi
+
+ if [[ ! ${error} && ! ${from} ]]; then
+ # Non-Python shebang. Allowed in recursive mode,
+ # disallowed when specifying file explicitly.
+ [[ ${is_recursive} ]] && continue
+ error=1
+ fi
+
+ if [[ ! ${quiet} ]]; then
+ einfo "Fixing shebang in ${f#${D}}."
+ 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
+ any_fixed=1
+ else
eerror "The file has incompatible shebang:"
eerror " file: ${f#${D}}"
eerror " current shebang: ${shebang}"
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.3: python-utils-r1.sh.diff --]
[-- Type: text/x-patch, Size: 3486 bytes --]
Index: python-utils-r1.sh
===================================================================
RCS file: /var/cvsroot/gentoo-x86/eclass/tests/python-utils-r1.sh,v
retrieving revision 1.6
diff -u -B -r1.6 python-utils-r1.sh
--- python-utils-r1.sh 8 Apr 2014 16:05:30 -0000 1.6
+++ python-utils-r1.sh 13 Jun 2014 22:28:50 -0000
@@ -30,6 +30,33 @@
tend ${?}
}
+test_fix_shebang() {
+ local from=${1}
+ local to=${2}
+ local expect=${3}
+ local args=( "${@:4}" )
+
+ tbegin "python_fix_shebang${args[@]+ ${args[*]}} from ${from} to ${to} (exp: ${expect})"
+
+ echo "${from}" > "${tmpfile}"
+ output=$( EPYTHON=${to} python_fix_shebang "${args[@]}" -q "${tmpfile}" 2>&1 )
+
+ if [[ ${?} != 0 ]]; then
+ if [[ ${expect} != FAIL ]]; then
+ echo "${output}"
+ tend 1
+ else
+ tend 0
+ fi
+ else
+ [[ $(<"${tmpfile}") == ${expect} ]] \
+ || eerror "${from} -> ${to}: $(<"${tmpfile}") != ${expect}"
+ tend ${?}
+ fi
+}
+
+tmpfile=$(mktemp)
+
inherit python-utils-r1
test_var EPYTHON python2_7 python2.7
@@ -66,4 +93,47 @@
test_is python_is_python3 jython2.7 1
test_is python_is_python3 pypy 1
+# generic shebangs
+test_fix_shebang '#!/usr/bin/python' python2.7 '#!/usr/bin/python2.7'
+test_fix_shebang '#!/usr/bin/python' python3.4 '#!/usr/bin/python3.4'
+test_fix_shebang '#!/usr/bin/python' pypy '#!/usr/bin/pypy'
+test_fix_shebang '#!/usr/bin/python' jython2.7 '#!/usr/bin/jython2.7'
+
+# python2/python3 matching
+test_fix_shebang '#!/usr/bin/python2' python2.7 '#!/usr/bin/python2.7'
+test_fix_shebang '#!/usr/bin/python3' python2.7 FAIL
+test_fix_shebang '#!/usr/bin/python3' python2.7 '#!/usr/bin/python2.7' --force
+test_fix_shebang '#!/usr/bin/python3' python3.4 '#!/usr/bin/python3.4'
+test_fix_shebang '#!/usr/bin/python2' python3.4 FAIL
+test_fix_shebang '#!/usr/bin/python2' python3.4 '#!/usr/bin/python3.4' --force
+
+# pythonX.Y matching (those mostly test the patterns)
+test_fix_shebang '#!/usr/bin/python2.7' python2.7 '#!/usr/bin/python2.7'
+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'
+test_fix_shebang '#!/usr/bin/python3.2' python2.7 FAIL
+test_fix_shebang '#!/usr/bin/python3.2' python2.7 '#!/usr/bin/python2.7' --force
+test_fix_shebang '#!/usr/bin/pypy' pypy '#!/usr/bin/pypy'
+test_fix_shebang '#!/usr/bin/pypy' python2.7 FAIL
+test_fix_shebang '#!/usr/bin/pypy' python2.7 '#!/usr/bin/python2.7' --force
+test_fix_shebang '#!/usr/bin/jython2.7' jython2.7 '#!/usr/bin/jython2.7'
+test_fix_shebang '#!/usr/bin/jython2.7' jython3.2 FAIL
+test_fix_shebang '#!/usr/bin/jython2.7' jython3.2 '#!/usr/bin/jython3.2' --force
+
+# fancy path handling
+test_fix_shebang '#!/mnt/python2/usr/bin/python' python3.4 \
+ '#!/mnt/python2/usr/bin/python3.4'
+test_fix_shebang '#!/mnt/python2/usr/bin/python2' python2.7 \
+ '#!/mnt/python2/usr/bin/python2.7'
+test_fix_shebang '#!/mnt/python2/usr/bin/env python' python2.7 \
+ '#!/mnt/python2/usr/bin/env python2.7'
+test_fix_shebang '#!/mnt/python2/usr/bin/python2 python2' python2.7 \
+ '#!/mnt/python2/usr/bin/python2.7 python2'
+test_fix_shebang '#!/mnt/python2/usr/bin/python3 python2' python2.7 FAIL
+test_fix_shebang '#!/mnt/python2/usr/bin/python3 python2' python2.7 \
+ '#!/mnt/python2/usr/bin/python2.7 python2' --force
+
+rm "${tmpfile}"
+
texit
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 949 bytes --]
^ permalink raw reply [relevance 99%]
Results 1-1 of 1 | reverse | options above
-- pct% links below jump to the message on this page, permalinks otherwise --
2014-06-13 22:36 99% [gentoo-dev] python-utils-r1.eclass: fixes & improvements for python_fix_shebang Michał Górny
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox