From: Arfrever Frehtes Taifersar Arahesis <Arfrever@gentoo.org>
To: Gentoo Development <gentoo-dev@lists.gentoo.org>
Subject: [gentoo-dev] Versioning of Python scripts
Date: Sat, 19 Dec 2009 16:24:49 +0100 [thread overview]
Message-ID: <200912191624.55244.Arfrever@gentoo.org> (raw)
[-- Attachment #1.1: Type: Text/Plain, Size: 2006 bytes --]
Distutils/Setuptools/Distribute modify shebangs of installed Python scripts, so that they
contain path of Python interpreter with version included (e.g. "#!/usr/bin/python3.2").
This behavior has both advantage and disadvantages:
- Scripts of packages supporting only e.g. Python 2 can be executed (without necessity
of using of e.g. "python2 /usr/bin/${script}") after activating of e.g. Python 3.
- Scripts of packages supporting multiple Python versions ignore active Python version.
- Scripts of packages supporting multiple Python versions cannot be easily (without
necessity of using of e.g. "python3.1 /usr/bin/${script}") executed with a Python
version different than active Python version.
The best solution, which removes these 2 disadvantages and preserves the advantage, seems
to be to rename Python scripts to include Python version [1] in filenames, and create wrapper
scripts, which call appropriate target scripts [2]. Some files sometimes try to execute
e.g. "/usr/bin/python /usr/bin/${script}", so wrapper scripts must be implemented in Python.
Wrapper scripts try to execute "${wrapper_script}-${PYTHON_ABI}" files (e.g. "py.test" will
execute "py.test-3.1", when Python 3.1 is set as active Python version).
distutils.eclass will automatically rename some scripts [3] in "${D}usr/bin" and call
the function, which generates wrapper scripts. In case somebody is interested in reading of
source code of python_generate_wrapper_scripts() function and potential suggesting of
improvements, I'm attaching this function and 2 example wrapper scripts. I'm planning to
commit addition of this function in next week.
[1] Actually Python ABI.
[2] Wrapper scripts can also be created for ELF target files (e.g. "mpipython").
[3] Only scripts of packages supporting installation for multiple Python versions will be
renamed. Scripts, whose filenames end in [[:digit:]]+\.[[:digit:]]+, won't be renamed.
--
Arfrever Frehtes Taifersar Arahesis
[-- Attachment #1.2: python_generate_wrapper_scripts.function --]
[-- Type: text/plain, Size: 4738 bytes --]
# @FUNCTION: python_generate_wrapper_scripts
# @USAGE: [-E|--respect-EPYTHON] [-f|--force] [-q|--quiet] [--] <file> [files]
# @DESCRIPTION:
# Generate wrapper scripts. Existing files are overwritten only with --force option.
# If --respect-EPYTHON option is specified, then generated wrapper scripts will
# respect EPYTHON variable at run time.
python_generate_wrapper_scripts() {
local eselect_python_option file force="0" quiet="0" PYTHON_ABI python2_enabled="0" python2_supported_versions python3_enabled="0" python3_supported_versions respect_EPYTHON="0"
python2_supported_versions="2.4 2.5 2.6 2.7"
python3_supported_versions="3.0 3.1 3.2"
while (($#)); do
case "$1" in
-E|--respect-EPYTHON)
respect_EPYTHON="1"
;;
-f|--force)
force="1"
;;
-q|--quiet)
quiet="1"
;;
--)
break
;;
-*)
die "${FUNCNAME}(): Unrecognized option '$1'"
;;
*)
break
;;
esac
shift
done
validate_PYTHON_ABIS
for PYTHON_ABI in ${python2_supported_versions}; do
if has "${PYTHON_ABI}" ${PYTHON_ABIS}; then
python2_enabled="1"
fi
done
for PYTHON_ABI in ${python3_supported_versions}; do
if has "${PYTHON_ABI}" ${PYTHON_ABIS}; then
python3_enabled="1"
fi
done
if [[ "${python2_enabled}" == "1" && "${python3_enabled}" == "1" ]]; then
eselect_python_option=
elif [[ "${python2_enabled}" == "1" && "${python3_enabled}" == "0" ]]; then
eselect_python_option="--python2"
elif [[ "${python2_enabled}" == "0" && "${python3_enabled}" == "1" ]]; then
eselect_python_option="--python3"
else
die "${FUNCNAME}(): Unsupported environment"
fi
for file in "$@"; do
if [[ -f "${file}" && "${force}" == "0" ]]; then
die "${FUNCNAME}(): '$1' already exists"
fi
if [[ "${quiet}" == "0" ]]; then
einfo "Generating '${file#${D%/}}' wrapper script"
fi
cat << EOF > "${file}"
#!/usr/bin/env python
# Gentoo '${file##*/}' wrapper script
import os
import re
import subprocess
import sys
EPYTHON_re = re.compile(r"^python(\d+\.\d+)$")
EOF
if [[ "$?" != "0" ]]; then
die "${FUNCNAME}(): Generation of '$1' failed"
fi
if [[ "${respect_EPYTHON}" == "1" ]]; then
cat << EOF >> "${file}"
EPYTHON = os.environ.get("EPYTHON")
if EPYTHON:
EPYTHON_matched = EPYTHON_re.match(EPYTHON)
if EPYTHON_matched:
PYTHON_ABI = EPYTHON_matched.group(1)
else:
sys.stderr.write("EPYTHON variable has unrecognized value '%s'\n" % EPYTHON)
sys.exit(1)
else:
try:
eselect_process = subprocess.Popen(["/usr/bin/eselect", "python", "show"${eselect_python_option:+, $(echo "\"")}${eselect_python_option}${eselect_python_option:+$(echo "\"")}], stdout=subprocess.PIPE)
if eselect_process.wait() != 0:
raise ValueError
except (OSError, ValueError):
sys.stderr.write("Execution of 'eselect python show${eselect_python_option:+ }${eselect_python_option}' failed\n")
sys.exit(1)
eselect_output = eselect_process.stdout.read()
if not isinstance(eselect_output, str):
# Python 3
eselect_output = eselect_output.decode()
EPYTHON_matched = EPYTHON_re.match(eselect_output)
if EPYTHON_matched:
PYTHON_ABI = EPYTHON_matched.group(1)
else:
sys.stderr.write("'eselect python show${eselect_python_option:+ }${eselect_python_option}' printed unrecognized value '%s" % eselect_output)
sys.exit(1)
EOF
if [[ "$?" != "0" ]]; then
die "${FUNCNAME}(): Generation of '$1' failed"
fi
else
cat << EOF >> "${file}"
try:
eselect_process = subprocess.Popen(["/usr/bin/eselect", "python", "show"${eselect_python_option:+, $(echo "\"")}${eselect_python_option}${eselect_python_option:+$(echo "\"")}], stdout=subprocess.PIPE)
if eselect_process.wait() != 0:
raise ValueError
except (OSError, ValueError):
sys.stderr.write("Execution of 'eselect python show${eselect_python_option:+ }${eselect_python_option}' failed\n")
sys.exit(1)
eselect_output = eselect_process.stdout.read()
if not isinstance(eselect_output, str):
# Python 3
eselect_output = eselect_output.decode()
EPYTHON_matched = EPYTHON_re.match(eselect_output)
if EPYTHON_matched:
PYTHON_ABI = EPYTHON_matched.group(1)
else:
sys.stderr.write("'eselect python show${eselect_python_option:+ }${eselect_python_option}' printed unrecognized value '%s" % eselect_output)
sys.exit(1)
EOF
if [[ "$?" != "0" ]]; then
die "${FUNCNAME}(): Generation of '$1' failed"
fi
fi
cat << EOF >> "${file}"
target_executable = "%s-%s" % (sys.argv[0], PYTHON_ABI)
if not os.path.exists(target_executable):
sys.stderr.write("'%s' does not exist\n" % target_executable)
sys.exit(1)
os.execv(target_executable, sys.argv)
EOF
if [[ "$?" != "0" ]]; then
die "${FUNCNAME}(): Generation of '$1' failed"
fi
fperms +x "${file#${D%/}}" || die "fperms '${file}' failed"
done
}
[-- Attachment #1.3: sphinx-build --]
[-- Type: text/x-python, Size: 1035 bytes --]
#!/usr/bin/env python
# Gentoo 'sphinx-build' wrapper script
import os
import re
import subprocess
import sys
EPYTHON_re = re.compile(r"^python(\d+\.\d+)$")
try:
eselect_process = subprocess.Popen(["/usr/bin/eselect", "python", "show", "--python2"], stdout=subprocess.PIPE)
if eselect_process.wait() != 0:
raise ValueError
except (OSError, ValueError):
sys.stderr.write("Execution of 'eselect python show --python2' failed\n")
sys.exit(1)
eselect_output = eselect_process.stdout.read()
if not isinstance(eselect_output, str):
# Python 3
eselect_output = eselect_output.decode()
EPYTHON_matched = EPYTHON_re.match(eselect_output)
if EPYTHON_matched:
PYTHON_ABI = EPYTHON_matched.group(1)
else:
sys.stderr.write("'eselect python show --python2' printed unrecognized value '%s" % eselect_output)
sys.exit(1)
target_executable = "%s-%s" % (sys.argv[0], PYTHON_ABI)
if not os.path.exists(target_executable):
sys.stderr.write("'%s' does not exist\n" % target_executable)
sys.exit(1)
os.execv(target_executable, sys.argv)
[-- Attachment #1.4: py.test --]
[-- Type: text/x-python, Size: 1274 bytes --]
#!/usr/bin/env python
# Gentoo 'py.test' wrapper script
import os
import re
import subprocess
import sys
EPYTHON_re = re.compile(r"^python(\d+\.\d+)$")
EPYTHON = os.environ.get("EPYTHON")
if EPYTHON:
EPYTHON_matched = EPYTHON_re.match(EPYTHON)
if EPYTHON_matched:
PYTHON_ABI = EPYTHON_matched.group(1)
else:
sys.stderr.write("EPYTHON variable has unrecognized value '%s'\n" % EPYTHON)
sys.exit(1)
else:
try:
eselect_process = subprocess.Popen(["/usr/bin/eselect", "python", "show"], stdout=subprocess.PIPE)
if eselect_process.wait() != 0:
raise ValueError
except (OSError, ValueError):
sys.stderr.write("Execution of 'eselect python show' failed\n")
sys.exit(1)
eselect_output = eselect_process.stdout.read()
if not isinstance(eselect_output, str):
# Python 3
eselect_output = eselect_output.decode()
EPYTHON_matched = EPYTHON_re.match(eselect_output)
if EPYTHON_matched:
PYTHON_ABI = EPYTHON_matched.group(1)
else:
sys.stderr.write("'eselect python show' printed unrecognized value '%s" % eselect_output)
sys.exit(1)
target_executable = "%s-%s" % (sys.argv[0], PYTHON_ABI)
if not os.path.exists(target_executable):
sys.stderr.write("'%s' does not exist\n" % target_executable)
sys.exit(1)
os.execv(target_executable, sys.argv)
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
next reply other threads:[~2009-12-19 15:23 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-12-19 15:24 Arfrever Frehtes Taifersar Arahesis [this message]
2009-12-21 10:50 ` [gentoo-dev] Versioning of Python scripts Brian Harring
2009-12-23 0:50 ` Arfrever Frehtes Taifersar Arahesis
2009-12-22 9:04 ` Peter Volkov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=200912191624.55244.Arfrever@gentoo.org \
--to=arfrever@gentoo.org \
--cc=gentoo-dev@lists.gentoo.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox