Signed-off-by: Michał Górny <[email protected]>
---
 eclass/distutils-r2.eclass      | 1198 ++++++++++++++++++++++++
 eclass/python-any-r2.eclass     |  357 ++++++++
 eclass/python-r2.eclass         |  825 +++++++++++++++++
 eclass/python-single-r2.eclass  |  507 +++++++++++
 eclass/python-utils-r2.eclass   | 1518 +++++++++++++++++++++++++++++++
 eclass/tests/distutils-r2.sh    |   98 ++
 eclass/tests/python-utils-r2.sh |  237 +++++
 7 files changed, 4740 insertions(+)
 create mode 100644 eclass/distutils-r2.eclass
 create mode 100644 eclass/python-any-r2.eclass
 create mode 100644 eclass/python-r2.eclass
 create mode 100644 eclass/python-single-r2.eclass
 create mode 100644 eclass/python-utils-r2.eclass
 create mode 100755 eclass/tests/distutils-r2.sh
 create mode 100755 eclass/tests/python-utils-r2.sh

diff --git a/eclass/distutils-r2.eclass b/eclass/distutils-r2.eclass
new file mode 100644
index 000000000000..662bad3b9bcd
--- /dev/null
+++ b/eclass/distutils-r2.eclass
@@ -0,0 +1,1198 @@
+# Copyright 1999-2020 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+# @ECLASS: distutils-r2.eclass
+# @MAINTAINER:
+# Python team <[email protected]>
+# @AUTHOR:
+# Author: Michał Górny <[email protected]>
+# @SUPPORTED_EAPIS: 5 6 7
+# @BLURB: A simple eclass to build Python packages using distutils.
+# @DESCRIPTION:
+# A simple eclass providing functions to build Python packages using
+# the distutils build system. It exports phase functions for all
+# the src_* phases. Each of the phases runs two pseudo-phases:
+# python_..._all() (e.g. python_prepare_all()) once in ${S}, then
+# python_...() (e.g. python_prepare()) for each implementation
+# (see: python_foreach_impl() in python-r2).
+#
+# In distutils-r2_src_prepare(), the 'all' function is run before
+# per-implementation ones (because it creates the implementations),
+# per-implementation functions are run in a random order.
+#
+# In remaining phase functions, the per-implementation functions are run
+# before the 'all' one, and they are ordered from the least to the most
+# preferred implementation (so that 'better' files overwrite 'worse'
+# ones).
+#
+# If the ebuild doesn't specify a particular pseudo-phase function,
+# the default one will be used (distutils-r2_...). Defaults are provided
+# for all per-implementation pseudo-phases, python_prepare_all()
+# and python_install_all(); whenever writing your own pseudo-phase
+# functions, you should consider calling the defaults (and especially
+# distutils-r2_python_prepare_all).
+#
+# Please note that distutils-r2 sets RDEPEND and DEPEND unconditionally
+# for you.
+#
+# Also, please note that distutils-r2 will always inherit python-r2
+# as well. Thus, all the variables defined and documented there are
+# relevant to the packages using distutils-r2.
+
+case "${EAPI:-0}" in
+       0|1|2|3|4)
+               die "Unsupported EAPI=${EAPI:-0} (too old) for ${ECLASS}"
+               ;;
+       5|6|7)
+               ;;
+       *)
+               die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}"
+               ;;
+esac
+
+# @ECLASS-VARIABLE: DISTUTILS_OPTIONAL
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# If set to a non-null value, distutils part in the ebuild will
+# be considered optional. No dependencies will be added and no phase
+# functions will be exported.
+#
+# If you enable DISTUTILS_OPTIONAL, you have to set proper dependencies
+# for your package (using ${PYTHON_DEPS}) and to either call
+# distutils-r2 default phase functions or call the build system
+# manually.
+
+# @ECLASS-VARIABLE: DISTUTILS_SINGLE_IMPL
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# If set to a non-null value, the ebuild will support setting a single
+# Python implementation only. It will effectively replace the python-r2
+# eclass inherit with python-single-r2.
+#
+# Note that inheriting python-single-r2 will cause pkg_setup()
+# to be exported. It must be run in order for the eclass functions
+# to function properly.
+
+# @ECLASS-VARIABLE: DISTUTILS_USE_SETUPTOOLS
+# @PRE_INHERIT
+# @DESCRIPTION:
+# Controls adding dev-python/setuptools dependency.  The allowed values
+# are:
+#
+# - no -- do not add the dependency (pure distutils package)
+# - bdepend -- add it to BDEPEND (the default)
+# - rdepend -- add it to BDEPEND+RDEPEND (when using entry_points)
+# - pyproject.toml -- use pyproject2setuptools to install a project
+#                     using pyproject.toml (flit, poetry...)
+# - manual -- do not add the depedency and suppress the checks
+#             (assumes you will take care of doing it correctly)
+#
+# This variable is effective only if DISTUTILS_OPTIONAL is disabled.
+# It needs to be set before the inherit line.
+: ${DISTUTILS_USE_SETUPTOOLS:=bdepend}
+
+if [[ ! ${_DISTUTILS_R2} ]]; then
+
+[[ ${EAPI} == [456] ]] && inherit eutils
+[[ ${EAPI} == [56] ]] && inherit xdg-utils
+inherit multiprocessing toolchain-funcs
+
+if [[ ! ${DISTUTILS_SINGLE_IMPL} ]]; then
+       inherit python-r2
+else
+       inherit python-single-r2
+fi
+
+fi
+
+if [[ ! ${DISTUTILS_OPTIONAL} ]]; then
+       EXPORT_FUNCTIONS src_prepare src_configure src_compile src_test 
src_install
+fi
+
+if [[ ! ${_DISTUTILS_R2} ]]; then
+
+_distutils_set_globals() {
+       local rdep=${PYTHON_DEPS}
+       local bdep=${rdep}
+
+       if [[ ! ${DISTUTILS_SINGLE_IMPL} ]]; then
+               local sdep="dev-python/setuptools[${PYTHON_USEDEP}]"
+       else
+               local sdep="$(python_gen_cond_dep '
+                       dev-python/setuptools[${PYTHON_MULTI_USEDEP}]
+               ')"
+       fi
+
+       case ${DISTUTILS_USE_SETUPTOOLS} in
+               no|manual)
+                       ;;
+               bdepend)
+                       bdep+=" ${sdep}"
+                       ;;
+               rdepend)
+                       bdep+=" ${sdep}"
+                       rdep+=" ${sdep}"
+                       ;;
+               pyproject.toml)
+                       bdep+=" dev-python/pyproject2setuppy[${PYTHON_USEDEP}]"
+                       ;;
+               *)
+                       die "Invalid 
DISTUTILS_USE_SETUPTOOLS=${DISTUTILS_USE_SETUPTOOLS}"
+                       ;;
+       esac
+
+       RDEPEND=${rdep}
+       if [[ ${EAPI} != [56] ]]; then
+               BDEPEND=${bdep}
+       else
+               DEPEND=${bdep}
+       fi
+       REQUIRED_USE=${PYTHON_REQUIRED_USE}
+}
+[[ ! ${DISTUTILS_OPTIONAL} ]] && _distutils_set_globals
+unset -f _distutils_set_globals
+
+# @ECLASS-VARIABLE: PATCHES
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# An array containing patches to be applied to the sources before
+# copying them.
+#
+# If unset, no custom patches will be applied.
+#
+# Please note, however, that at some point the eclass may apply
+# additional distutils patches/quirks independently of this variable.
+#
+# Example:
+# @CODE
+# PATCHES=( "${FILESDIR}"/${P}-make-gentoo-happy.patch )
+# @CODE
+
+# @ECLASS-VARIABLE: DOCS
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# An array containing documents installed using dodoc. The files listed
+# there must exist in the directory from which
+# distutils-r2_python_install_all() is run (${S} by default).
+#
+# If unset, the function will instead look up files matching default
+# filename pattern list (from the Package Manager Specification),
+# and install those found.
+#
+# Example:
+# @CODE
+# DOCS=( NEWS README )
+# @CODE
+
+# @ECLASS-VARIABLE: HTML_DOCS
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# An array containing documents installed using dohtml. The files
+# and directories listed there must exist in the directory from which
+# distutils-r2_python_install_all() is run (${S} by default).
+#
+# If unset, no HTML docs will be installed.
+#
+# Example:
+# @CODE
+# HTML_DOCS=( doc/html/. )
+# @CODE
+
+# @ECLASS-VARIABLE: EXAMPLES
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# OBSOLETE: this variable is deprecated and banned in EAPI 6
+#
+# An array containing examples installed into 'examples' doc
+# subdirectory. The files and directories listed there must exist
+# in the directory from which distutils-r2_python_install_all() is run
+# (${S} by default).
+#
+# The 'examples' subdirectory will be marked not to be compressed
+# automatically.
+#
+# If unset, no examples will be installed.
+#
+# Example:
+# @CODE
+# EXAMPLES=( examples/. demos/. )
+# @CODE
+
+# @ECLASS-VARIABLE: DISTUTILS_IN_SOURCE_BUILD
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# If set to a non-null value, in-source builds will be enabled.
+# If unset, the default is to use in-source builds when python_prepare()
+# is declared, and out-of-source builds otherwise.
+#
+# If in-source builds are used, the eclass will create a copy of package
+# sources for each Python implementation in python_prepare_all(),
+# and work on that copy afterwards.
+#
+# If out-of-source builds are used, the eclass will instead work
+# on the sources directly, prepending setup.py arguments with
+# 'build --build-base ${BUILD_DIR}' to enforce keeping & using built
+# files in the specific root.
+
+# @ECLASS-VARIABLE: DISTUTILS_ALL_SUBPHASE_IMPLS
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# An array of patterns specifying which implementations can be used
+# for *_all() sub-phase functions. If undefined, defaults to '*'
+# (allowing any implementation). If multiple values are specified,
+# implementations matching any of the patterns will be accepted.
+#
+# The patterns can be either fnmatch-style patterns (matched via bash
+# == operator against PYTHON_COMPAT values) or '-2' / '-3' to indicate
+# appropriately all enabled Python 2/3 implementations (alike
+# python_is_python3). Remember to escape or quote the fnmatch patterns
+# to prevent accidental shell filename expansion.
+#
+# If the restriction needs to apply conditionally to a USE flag,
+# the variable should be set conditionally as well (e.g. in an early
+# phase function or other convenient location).
+#
+# Please remember to add a matching || block to REQUIRED_USE,
+# to ensure that at least one implementation matching the patterns will
+# be enabled.
+#
+# Example:
+# @CODE
+# REQUIRED_USE="doc? ( || ( $(python_gen_useflags 'python2*') ) )"
+#
+# pkg_setup() {
+#     use doc && DISTUTILS_ALL_SUBPHASE_IMPLS=( 'python2*' )
+# }
+# @CODE
+
+# @ECLASS-VARIABLE: mydistutilsargs
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# An array containing options to be passed to setup.py.
+#
+# Example:
+# @CODE
+# python_configure_all() {
+#      mydistutilsargs=( --enable-my-hidden-option )
+# }
+# @CODE
+
+# @FUNCTION: distutils_enable_sphinx
+# @USAGE: <subdir> [--no-autodoc | <plugin-pkgs>...]
+# @DESCRIPTION:
+# Set up IUSE, BDEPEND, python_check_deps() and python_compile_all() for
+# building HTML docs via dev-python/sphinx.  python_compile_all() will
+# append to HTML_DOCS if docs are enabled.
+#
+# This helper is meant for the most common case, that is a single Sphinx
+# subdirectory with standard layout, building and installing HTML docs
+# behind USE=doc.  It assumes it's the only consumer of the three
+# aforementioned functions.  If you need to use a custom implemention,
+# you can't use it.
+#
+# If your package uses additional Sphinx plugins, they should be passed
+# (without PYTHON_USEDEP) as <plugin-pkgs>.  The function will take care
+# of setting appropriate any-of dep and python_check_deps().
+#
+# If no plugin packages are specified, the eclass will still utilize
+# any-r2 API to support autodoc (documenting source code).
+# If the package uses neither autodoc nor additional plugins, you should
+# pass --no-autodoc to disable this API and simplify the resulting code.
+#
+# This function must be called in global scope.  Take care not to
+# overwrite the variables set by it.  If you need to extend
+# python_compile_all(), you can call the original implementation
+# as sphinx_compile_all.
+distutils_enable_sphinx() {
+       debug-print-function ${FUNCNAME} "${@}"
+       [[ ${#} -ge 1 ]] || die "${FUNCNAME} takes at least one arg: <subdir>"
+
+       _DISTUTILS_SPHINX_SUBDIR=${1}
+       shift
+       _DISTUTILS_SPHINX_PLUGINS=( "${@}" )
+
+       local deps autodoc=1 d
+       for d; do
+               if [[ ${d} == --no-autodoc ]]; then
+                       autodoc=
+               else
+                       deps+="
+                               ${d}[\${PYTHON_USEDEP}]"
+               fi
+       done
+
+       if [[ ! ${autodoc} && -n ${deps} ]]; then
+               die "${FUNCNAME}: do not pass --no-autodoc if external plugins 
are used"
+       fi
+       if [[ ${autodoc} ]]; then
+               deps="$(python_gen_any_dep "
+                       dev-python/sphinx[\${PYTHON_USEDEP}]
+                       ${deps}")"
+
+               python_check_deps() {
+                       use doc || return 0
+                       local p
+                       for p in dev-python/sphinx 
"${_DISTUTILS_SPHINX_PLUGINS[@]}"; do
+                               has_version "${p}[${PYTHON_USEDEP}]" || return 1
+                       done
+               }
+       else
+               deps="dev-python/sphinx"
+       fi
+
+       sphinx_compile_all() {
+               use doc || return
+
+               local confpy=${_DISTUTILS_SPHINX_SUBDIR}/conf.py
+               [[ -f ${confpy} ]] ||
+                       die "${confpy} not found, distutils_enable_sphinx call 
wrong"
+
+               if [[ ${_DISTUTILS_SPHINX_PLUGINS[0]} == --no-autodoc ]]; then
+                       if grep -F -q 'sphinx.ext.autodoc' "${confpy}"; then
+                               die "distutils_enable_sphinx: --no-autodoc 
passed but sphinx.ext.autodoc found in ${confpy}"
+                       fi
+               else
+                       if ! grep -F -q 'sphinx.ext.autodoc' "${confpy}"; then
+                               die "distutils_enable_sphinx: 
sphinx.ext.autodoc not found in ${confpy}, pass --no-autodoc"
+                       fi
+               fi
+
+               build_sphinx "${_DISTUTILS_SPHINX_SUBDIR}"
+       }
+       python_compile_all() { sphinx_compile_all; }
+
+       IUSE+=" doc"
+       if [[ ${EAPI} == [56] ]]; then
+               DEPEND+=" doc? ( ${deps} )"
+       else
+               BDEPEND+=" doc? ( ${deps} )"
+       fi
+
+       # we need to ensure successful return in case we're called last,
+       # otherwise Portage may wrongly assume sourcing failed
+       return 0
+}
+
+# @FUNCTION: distutils_enable_tests
+# @USAGE: <test-runner>
+# @DESCRIPTION:
+# Set up IUSE, RESTRICT, BDEPEND and python_test() for running tests
+# with the specified test runner.  Also copies the current value
+# of RDEPEND to test?-BDEPEND.  The test-runner argument must be one of:
+#
+# - nose: nosetests (dev-python/nose)
+# - pytest: dev-python/pytest
+# - setup.py: setup.py test (no deps included)
+# - unittest: for built-in Python unittest module
+#
+# This function is meant as a helper for common use cases, and it only
+# takes care of basic setup.  You still need to list additional test
+# dependencies manually.  If you have uncommon use case, you should
+# not use it and instead enable tests manually.
+#
+# This function must be called in global scope, after RDEPEND has been
+# declared.  Take care not to overwrite the variables set by it.
+distutils_enable_tests() {
+       debug-print-function ${FUNCNAME} "${@}"
+       [[ ${#} -eq 1 ]] || die "${FUNCNAME} takes exactly one argument: 
test-runner"
+
+       local test_pkg
+       case ${1} in
+               nose)
+                       test_pkg="dev-python/nose"
+                       python_test() {
+                               nosetests -v || die "Tests fail with ${EPYTHON}"
+                       }
+                       ;;
+               pytest)
+                       test_pkg="dev-python/pytest"
+                       python_test() {
+                               pytest -vv || die "Tests fail with ${EPYTHON}"
+                       }
+                       ;;
+               setup.py)
+                       python_test() {
+                               esetup.py test --verbose
+                       }
+                       ;;
+               unittest)
+                       python_test() {
+                               "${EPYTHON}" -m unittest discover -v ||
+                                       die "Tests fail with ${EPYTHON}"
+                       }
+                       ;;
+               *)
+                       die "${FUNCNAME}: unsupported argument: ${1}"
+       esac
+
+       local test_deps=${RDEPEND}
+       if [[ -n ${test_pkg} ]]; then
+               if [[ ! ${DISTUTILS_SINGLE_IMPL} ]]; then
+                       test_deps+=" ${test_pkg}[${PYTHON_USEDEP}]"
+               else
+                       test_deps+=" $(python_gen_cond_dep "
+                               ${test_pkg}[\${PYTHON_MULTI_USEDEP}]
+                       ")"
+               fi
+       fi
+       if [[ -n ${test_deps} ]]; then
+               IUSE+=" test"
+               RESTRICT+=" !test? ( test )"
+               if [[ ${EAPI} == [56] ]]; then
+                       DEPEND+=" test? ( ${test_deps} )"
+               else
+                       BDEPEND+=" test? ( ${test_deps} )"
+               fi
+       fi
+
+       # we need to ensure successful return in case we're called last,
+       # otherwise Portage may wrongly assume sourcing failed
+       return 0
+}
+
+# @FUNCTION: _distutils-r2_verify_use_setuptools
+# @INTERNAL
+# @DESCRIPTION:
+# Check setup.py for signs that DISTUTILS_USE_SETUPTOOLS have been set
+# incorrectly.
+_distutils_verify_use_setuptools() {
+       [[ ${DISTUTILS_OPTIONAL} ]] && return
+       [[ ${DISTUTILS_USE_SETUPTOOLS} == manual ]] && return
+       [[ ${DISTUTILS_USE_SETUPTOOLS} == pyproject.toml ]] && return
+
+       # ok, those are cheap greps.  we can try toimprove them if we hit
+       # false positives.
+       local expected=no
+       if [[ ${CATEGORY}/${PN} == dev-python/setuptools ]]; then
+               # as a special case, setuptools provides itself ;-)
+               :
+       elif grep -E -q -s '(from|import)\s+setuptools' setup.py; then
+               if grep -E -q -s 'entry_points\s*=' setup.py; then
+                       expected=rdepend
+               elif grep -F -q -s '[options.entry_points]' setup.cfg; then
+                       expected=rdepend
+               else
+                       expected=bdepend
+               fi
+       fi
+
+       if [[ ${DISTUTILS_USE_SETUPTOOLS} != ${expected} ]]; then
+               if [[ ! ${_DISTUTILS_SETUPTOOLS_WARNED} ]]; then
+                       _DISTUTILS_SETUPTOOLS_WARNED=1
+                       local def=
+                       [[ ${DISTUTILS_USE_SETUPTOOLS} == bdepend ]] && def=' 
(default?)'
+
+                       eqawarn "DISTUTILS_USE_SETUPTOOLS value is probably 
incorrect"
+                       eqawarn "  value:    
DISTUTILS_USE_SETUPTOOLS=${DISTUTILS_USE_SETUPTOOLS}${def}"
+                       eqawarn "  expected: 
DISTUTILS_USE_SETUPTOOLS=${expected}"
+               fi
+       fi
+}
+
+# @FUNCTION: esetup.py
+# @USAGE: [<args>...]
+# @DESCRIPTION:
+# Run setup.py using currently selected Python interpreter
+# (if ${EPYTHON} is set; fallback 'python' otherwise).
+#
+# setup.py will be passed the following, in order:
+# 1. ${mydistutilsargs[@]}
+# 2. additional arguments passed to the esetup.py function.
+#
+# Please note that setup.py will respect defaults (unless overridden
+# via command-line options) from setup.cfg that is created
+# in distutils-r2_python_compile and in distutils-r2_python_install.
+#
+# This command dies on failure.
+esetup.py() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local die_args=()
+       [[ ${EAPI} != [45] ]] && die_args+=( -n )
+
+       [[ ${BUILD_DIR} ]] && _distutils-r2_create_setup_cfg
+       _distutils_verify_use_setuptools
+
+       set -- "${EPYTHON:-python}" setup.py "${mydistutilsargs[@]}" "${@}"
+
+       echo "${@}" >&2
+       "${@}" || die "${die_args[@]}"
+       local ret=${?}
+
+       if [[ ${BUILD_DIR} ]]; then
+               rm "${HOME}"/.pydistutils.cfg || die "${die_args[@]}"
+       fi
+
+       return ${ret}
+}
+
+# @FUNCTION: distutils_install_for_testing
+# @USAGE: [<args>...]
+# @DESCRIPTION:
+# Install the package into a temporary location for running tests.
+# Update PYTHONPATH appropriately and set TEST_DIR to the test
+# installation root. The Python packages will be installed in 'lib'
+# subdir, and scripts in 'scripts' subdir (like in BUILD_DIR).
+#
+# Please note that this function should be only used if package uses
+# namespaces (and therefore proper install needs to be done to enforce
+# PYTHONPATH) or tests rely on the results of install command.
+# For most of the packages, tests built in BUILD_DIR are good enough.
+distutils_install_for_testing() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       # A few notes:
+       # 1) because of namespaces, we can't use 'install --root',
+       # 2) 'install --home' is terribly broken on pypy, so we need
+       #    to override --install-lib and --install-scripts,
+       # 3) non-root 'install' complains about PYTHONPATH and missing dirs,
+       #    so we need to set it properly and mkdir them,
+       # 4) it runs a bunch of commands which write random files to cwd,
+       #    in order to avoid that, we add the necessary path overrides
+       #    in _distutils-r2_create_setup_cfg.
+
+       TEST_DIR=${BUILD_DIR}/test
+       local bindir=${TEST_DIR}/scripts
+       local libdir=${TEST_DIR}/lib
+       PYTHONPATH=${libdir}:${PYTHONPATH}
+
+       local add_args=(
+               install
+                       --home="${TEST_DIR}"
+                       --install-lib="${libdir}"
+                       --install-scripts="${bindir}"
+       )
+
+       mkdir -p "${libdir}" || die
+       esetup.py "${add_args[@]}" "${@}"
+}
+
+# @FUNCTION: _distutils-r2_disable_ez_setup
+# @INTERNAL
+# @DESCRIPTION:
+# Stub out ez_setup.py and distribute_setup.py to prevent packages
+# from trying to download a local copy of setuptools.
+_distutils-r2_disable_ez_setup() {
+       local stub="def use_setuptools(*args, **kwargs): pass"
+       if [[ -f ez_setup.py ]]; then
+               echo "${stub}" > ez_setup.py || die
+       fi
+       if [[ -f distribute_setup.py ]]; then
+               echo "${stub}" > distribute_setup.py || die
+       fi
+}
+
+# @FUNCTION: _distutils-r2_handle_pyproject_toml
+# @INTERNAL
+# @DESCRIPTION:
+# Generate setup.py for pyproject.toml if requested.
+_distutils-r2_handle_pyproject_toml() {
+       if [[ ! -f setup.py && -f pyproject.toml ]]; then
+               if [[ ${DISTUTILS_USE_SETUPTOOLS} == pyproject.toml ]]; then
+                       cat > setup.py <<-EOF || die
+                               #!/usr/bin/env python
+                               from pyproject2setuppy.main import main
+                               main()
+                       EOF
+                       chmod +x setup.py || die
+               else
+                       eerror "No setup.py found but pyproject.toml is 
present.  In order to enable"
+                       eerror "pyproject.toml support in distutils-r2, set:"
+                       eerror "  DISTUTILS_USE_SETUPTOOLS=pyproject.toml"
+                       die "No setup.py found and 
DISTUTILS_USE_SETUPTOOLS!=pyproject.toml"
+               fi
+       fi
+}
+
+# @FUNCTION: distutils-r2_python_prepare_all
+# @DESCRIPTION:
+# The default python_prepare_all(). It applies the patches from PATCHES
+# array, then user patches and finally calls python_copy_sources to
+# create copies of resulting sources for each Python implementation.
+#
+# At some point in the future, it may also apply eclass-specific
+# distutils patches and/or quirks.
+distutils-r2_python_prepare_all() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       if [[ ! ${DISTUTILS_OPTIONAL} ]]; then
+               if [[ ${EAPI} != [45] ]]; then
+                       default
+               else
+                       [[ ${PATCHES} ]] && epatch "${PATCHES[@]}"
+                       epatch_user
+               fi
+       fi
+
+       # by default, use in-source build if python_prepare() is used
+       if [[ ! ${DISTUTILS_IN_SOURCE_BUILD+1} ]]; then
+               if declare -f python_prepare >/dev/null; then
+                       DISTUTILS_IN_SOURCE_BUILD=1
+               fi
+       fi
+
+       _distutils-r2_disable_ez_setup
+       _distutils-r2_handle_pyproject_toml
+
+       if [[ ${DISTUTILS_IN_SOURCE_BUILD} && ! ${DISTUTILS_SINGLE_IMPL} ]]
+       then
+               # create source copies for each implementation
+               python_copy_sources
+       fi
+
+       _DISTUTILS_DEFAULT_CALLED=1
+}
+
+# @FUNCTION: distutils-r2_python_prepare
+# @DESCRIPTION:
+# The default python_prepare(). A no-op.
+distutils-r2_python_prepare() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       [[ ${EAPI} == [45] ]] || die "${FUNCNAME} is banned in EAPI 6 (it was a 
no-op)"
+}
+
+# @FUNCTION: distutils-r2_python_configure
+# @DESCRIPTION:
+# The default python_configure(). A no-op.
+distutils-r2_python_configure() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       [[ ${EAPI} == [45] ]] || die "${FUNCNAME} is banned in EAPI 6 (it was a 
no-op)"
+}
+
+# @FUNCTION: _distutils-r2_create_setup_cfg
+# @INTERNAL
+# @DESCRIPTION:
+# Create implementation-specific configuration file for distutils,
+# setting proper build-dir (and install-dir) paths.
+_distutils-r2_create_setup_cfg() {
+       cat > "${HOME}"/.pydistutils.cfg <<-_EOF_ || die
+               [build]
+               build-base = ${BUILD_DIR}
+
+               # using a single directory for them helps us export
+               # ${PYTHONPATH} and ebuilds find the sources independently
+               # of whether the package installs extensions or not
+               #
+               # note: due to some packages (wxpython) relying on separate
+               # platlib & purelib dirs, we do not set --build-lib (which
+               # can not be overridden with --build-*lib)
+               build-platlib = %(build-base)s/lib
+               build-purelib = %(build-base)s/lib
+
+               # make the ebuild writer lives easier
+               build-scripts = %(build-base)s/scripts
+
+               # this is needed by distutils_install_for_testing since
+               # setuptools like to create .egg files for install --home.
+               [bdist_egg]
+               dist-dir = ${BUILD_DIR}/dist
+       _EOF_
+
+       # we can't refer to ${D} before src_install()
+       if [[ ${EBUILD_PHASE} == install ]]; then
+               cat >> "${HOME}"/.pydistutils.cfg <<-_EOF_ || die
+
+                       # installation paths -- allow calling extra install 
targets
+                       # without the default 'install'
+                       [install]
+                       compile = True
+                       optimize = 2
+                       root = ${D%/}
+               _EOF_
+
+               if [[ ! ${DISTUTILS_SINGLE_IMPL} ]]; then
+                       cat >> "${HOME}"/.pydistutils.cfg <<-_EOF_ || die
+                               install-scripts = $(python_get_scriptdir)
+                       _EOF_
+               fi
+       fi
+}
+
+# @FUNCTION: _distutils-r2_copy_egg_info
+# @INTERNAL
+# @DESCRIPTION:
+# Copy egg-info files to the ${BUILD_DIR} (that's going to become
+# egg-base in esetup.py). This way, we respect whatever's in upstream
+# egg-info.
+_distutils-r2_copy_egg_info() {
+       mkdir -p "${BUILD_DIR}" || die
+       # stupid freebsd can't do 'cp -t ${BUILD_DIR} {} +'
+       find -name '*.egg-info' -type d -exec cp -R -p {} "${BUILD_DIR}"/ ';' 
|| die
+}
+
+# @FUNCTION: distutils-r2_python_compile
+# @USAGE: [additional-args...]
+# @DESCRIPTION:
+# The default python_compile(). Runs 'esetup.py build'. Any parameters
+# passed to this function will be appended to setup.py invocation,
+# i.e. passed as options to the 'build' command.
+#
+# This phase also sets up initial setup.cfg with build directories
+# and copies upstream egg-info files if supplied.
+distutils-r2_python_compile() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       _distutils-r2_copy_egg_info
+
+       local build_args=()
+       # distutils is parallel-capable since py3.5
+       # to avoid breaking stable ebuilds, enable it only if either:
+       # a. we're dealing with EAPI 7
+       # b. we're dealing with Python 3.7 or PyPy3
+       if python_is_python3 && [[ ${EPYTHON} != python3.4 ]]; then
+               if [[ ${EAPI} != [56] || ${EPYTHON} != python3.[56] ]]; then
+                       local jobs=$(makeopts_jobs "${MAKEOPTS}" INF)
+                       if [[ ${jobs} == INF ]]; then
+                               local nproc=$(get_nproc)
+                               jobs=$(( nproc + 1 ))
+                       fi
+                       build_args+=( -j "${jobs}" )
+               fi
+       fi
+
+       esetup.py build "${build_args[@]}" "${@}"
+}
+
+# @FUNCTION: _distutils-r2_wrap_scripts
+# @USAGE: <path> <bindir>
+# @INTERNAL
+# @DESCRIPTION:
+# Moves and wraps all installed scripts/executables as necessary.
+_distutils-r2_wrap_scripts() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       [[ ${#} -eq 2 ]] || die "usage: ${FUNCNAME} <path> <bindir>"
+       local path=${1}
+       local bindir=${2}
+
+       local PYTHON_SCRIPTDIR
+       python_export PYTHON_SCRIPTDIR
+
+       local f python_files=() non_python_files=()
+
+       if [[ -d ${path}${PYTHON_SCRIPTDIR} ]]; then
+               for f in "${path}${PYTHON_SCRIPTDIR}"/*; do
+                       [[ -d ${f} ]] && die "Unexpected directory: ${f}"
+                       debug-print "${FUNCNAME}: found executable at 
${f#${path}/}"
+
+                       local shebang
+                       read -r shebang < "${f}"
+                       if [[ ${shebang} == '#!'*${EPYTHON}* ]]; then
+                               debug-print "${FUNCNAME}: matching shebang: 
${shebang}"
+                               python_files+=( "${f}" )
+                       else
+                               debug-print "${FUNCNAME}: non-matching shebang: 
${shebang}"
+                               non_python_files+=( "${f}" )
+                       fi
+
+                       mkdir -p "${path}${bindir}" || die
+               done
+
+               for f in "${python_files[@]}"; do
+                       local basename=${f##*/}
+
+                       debug-print "${FUNCNAME}: installing wrapper at 
${bindir}/${basename}"
+                       _python_ln_rel 
"${path}${EPREFIX}"/usr/lib/python-exec/python-exec2 \
+                               "${path}${bindir}/${basename}" || die
+               done
+
+               for f in "${non_python_files[@]}"; do
+                       local basename=${f##*/}
+
+                       debug-print "${FUNCNAME}: moving ${f#${path}/} to 
${bindir}/${basename}"
+                       mv "${f}" "${path}${bindir}/${basename}" || die
+               done
+       fi
+}
+
+# @FUNCTION: distutils-r2_python_install
+# @USAGE: [additional-args...]
+# @DESCRIPTION:
+# The default python_install(). Runs 'esetup.py install', doing
+# intermediate root install and handling script wrapping afterwards.
+# Any parameters passed to this function will be appended
+# to the setup.py invocation (i.e. as options to the 'install' command).
+#
+# This phase updates the setup.cfg file with install directories.
+distutils-r2_python_install() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local args=( "${@}" )
+
+       # enable compilation for the install phase.
+       local -x PYTHONDONTWRITEBYTECODE=
+
+       # python likes to compile any module it sees, which triggers sandbox
+       # failures if some packages haven't compiled their modules yet.
+       addpredict "${EPREFIX}/usr/lib/${EPYTHON}"
+       addpredict "${EPREFIX}/usr/$(get_libdir)/${EPYTHON}"
+       addpredict /usr/lib/pypy2.7
+       addpredict /usr/lib/pypy3.6
+       addpredict /usr/lib/portage/pym
+       addpredict /usr/local # bug 498232
+
+       if [[ ! ${DISTUTILS_SINGLE_IMPL} ]]; then
+               # user may override --install-scripts
+               # note: this is poor but distutils argv parsing is dumb
+               local mydistutilsargs=( "${mydistutilsargs[@]}" )
+               local scriptdir=${EPREFIX}/usr/bin
+
+               # construct a list of mydistutilsargs[0] args[0] args[1]...
+               local arg arg_vars
+               [[ ${mydistutilsargs[@]} ]] && eval arg_vars+=(
+                       'mydistutilsargs['{0..$(( ${#mydistutilsargs[@]} - 1 
))}']'
+               )
+               [[ ${args[@]} ]] && eval arg_vars+=(
+                       'args['{0..$(( ${#args[@]} - 1 ))}']'
+               )
+
+               set -- "${arg_vars[@]}"
+               while [[ ${@} ]]; do
+                       local arg_var=${1}
+                       shift
+                       local a=${!arg_var}
+
+                       case "${a}" in
+                               --install-scripts=*)
+                                       scriptdir=${a#--install-scripts=}
+                                       unset "${arg_var}"
+                                       ;;
+                               --install-scripts)
+                                       scriptdir=${!1}
+                                       unset "${arg_var}" "${1}"
+                                       shift
+                                       ;;
+                       esac
+               done
+       fi
+
+       local root=${D%/}/_${EPYTHON}
+       [[ ${DISTUTILS_SINGLE_IMPL} ]] && root=${D%/}
+
+       esetup.py install --root="${root}" "${args[@]}"
+
+       local forbidden_package_names=( examples test tests .pytest_cache )
+       local p
+       for p in "${forbidden_package_names[@]}"; do
+               if [[ -d ${root}$(python_get_sitedir)/${p} ]]; then
+                       die "Package installs '${p}' package which is forbidden 
and likely a bug in the build system."
+               fi
+       done
+
+       local shopt_save=$(shopt -p nullglob)
+       shopt -s nullglob
+       local pypy_dirs=(
+               "${root}/usr/$(get_libdir)"/pypy*/share
+               "${root}/usr/lib"/pypy*/share
+       )
+       ${shopt_save}
+
+       if [[ -n ${pypy_dirs} ]]; then
+               local cmd=die
+               [[ ${EAPI} == [45] ]] && cmd=eqawarn
+               "${cmd}" "Package installs 'share' in PyPy prefix, see bug 
#465546."
+       fi
+
+       if [[ ! ${DISTUTILS_SINGLE_IMPL} ]]; then
+               _distutils-r2_wrap_scripts "${root}" "${scriptdir}"
+               multibuild_merge_root "${root}" "${D%/}"
+       fi
+}
+
+# @FUNCTION: distutils-r2_python_install_all
+# @DESCRIPTION:
+# The default python_install_all(). It installs the documentation.
+distutils-r2_python_install_all() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       einstalldocs
+
+       if declare -p EXAMPLES &>/dev/null; then
+               [[ ${EAPI} != [45] ]] && die "EXAMPLES are banned in EAPI 
${EAPI}"
+
+               (
+                       docinto examples
+                       dodoc -r "${EXAMPLES[@]}"
+               )
+               docompress -x "/usr/share/doc/${PF}/examples"
+       fi
+
+       _DISTUTILS_DEFAULT_CALLED=1
+}
+
+# @FUNCTION: distutils-r2_run_phase
+# @USAGE: [<argv>...]
+# @INTERNAL
+# @DESCRIPTION:
+# Run the given command.
+#
+# If out-of-source builds are used, the phase function is run in source
+# directory, with BUILD_DIR pointing at the build directory
+# and PYTHONPATH having an entry for the module build directory.
+#
+# If in-source builds are used, the command is executed in the directory
+# holding the per-implementation copy of sources. BUILD_DIR points
+# to the 'build' subdirectory.
+distutils-r2_run_phase() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       if [[ ${DISTUTILS_IN_SOURCE_BUILD} ]]; then
+               # only force BUILD_DIR if implementation is explicitly enabled
+               # for building; any-r2 API may select one that is not
+               # https://bugs.gentoo.org/701506
+               if [[ ! ${DISTUTILS_SINGLE_IMPL} ]] &&
+                               has "${EPYTHON/./_}" ${PYTHON_TARGETS}; then
+                       cd "${BUILD_DIR}" || die
+               fi
+               local BUILD_DIR=${BUILD_DIR}/build
+       fi
+       local -x PYTHONPATH="${BUILD_DIR}/lib:${PYTHONPATH}"
+
+       # Bug 559644
+       # using PYTHONPATH when the ${BUILD_DIR}/lib is not created yet might 
lead to
+       # problems in setup.py scripts that try to import modules/packages from 
that path
+       # during the build process (Python at startup evaluates PYTHONPATH, if 
the dir is
+       # not valid then associates a NullImporter object to ${BUILD_DIR}/lib 
storing it
+       # in the sys.path_importer_cache)
+       mkdir -p "${BUILD_DIR}/lib" || die
+
+       # Set up build environment, bug #513664.
+       local -x AR=${AR} CC=${CC} CPP=${CPP} CXX=${CXX}
+       tc-export AR CC CPP CXX
+
+       # How to build Python modules in different worlds...
+       local ldopts
+       case "${CHOST}" in
+               # provided by haubi, 2014-07-08
+               *-aix*) ldopts='-shared -Wl,-berok';; # good enough
+               # provided by grobian, 2014-06-22, bug #513664 c7
+               *-darwin*) ldopts='-bundle -undefined dynamic_lookup';;
+               *) ldopts='-shared';;
+       esac
+
+       local -x LDSHARED="${CC} ${ldopts}" LDCXXSHARED="${CXX} ${ldopts}"
+
+       "${@}"
+
+       cd "${_DISTUTILS_INITIAL_CWD}" || die
+}
+
+# @FUNCTION: _distutils-r2_run_common_phase
+# @USAGE: [<argv>...]
+# @INTERNAL
+# @DESCRIPTION:
+# Run the given command, restoring the state for a most preferred Python
+# implementation matching DISTUTILS_ALL_SUBPHASE_IMPLS.
+#
+# If in-source build is used, the command will be run in the copy
+# of sources made for the selected Python interpreter.
+_distutils-r2_run_common_phase() {
+       local DISTUTILS_ORIG_BUILD_DIR=${BUILD_DIR}
+
+       if [[ ${DISTUTILS_SINGLE_IMPL} ]]; then
+               # reuse the dedicated code branch
+               _distutils-r2_run_foreach_impl "${@}"
+       else
+               local -x EPYTHON PYTHON
+               local -x PATH=${PATH} PKG_CONFIG_PATH=${PKG_CONFIG_PATH}
+               python_setup "${DISTUTILS_ALL_SUBPHASE_IMPLS[@]}"
+
+               local MULTIBUILD_VARIANTS=( "${EPYTHON/./_}" )
+               # store for restoring after distutils-r2_run_phase.
+               local _DISTUTILS_INITIAL_CWD=${PWD}
+               multibuild_foreach_variant \
+                       distutils-r2_run_phase "${@}"
+       fi
+}
+
+# @FUNCTION: _distutils-r2_run_foreach_impl
+# @INTERNAL
+# @DESCRIPTION:
+# Run the given phase for each implementation if multiple implementations
+# are enabled, once otherwise.
+_distutils-r2_run_foreach_impl() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       # store for restoring after distutils-r2_run_phase.
+       local _DISTUTILS_INITIAL_CWD=${PWD}
+       set -- distutils-r2_run_phase "${@}"
+
+       if [[ ! ${DISTUTILS_SINGLE_IMPL} ]]; then
+               python_foreach_impl "${@}"
+       else
+               if [[ ! ${EPYTHON} ]]; then
+                       die "EPYTHON unset, python-single-r2_pkg_setup not 
called?!"
+               fi
+               local BUILD_DIR=${BUILD_DIR:-${S}}
+               BUILD_DIR=${BUILD_DIR%%/}_${EPYTHON}
+
+               "${@}"
+       fi
+}
+
+distutils-r2_src_prepare() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local _DISTUTILS_DEFAULT_CALLED
+
+       # common preparations
+       if declare -f python_prepare_all >/dev/null; then
+               python_prepare_all
+       else
+               distutils-r2_python_prepare_all
+       fi
+
+       if [[ ! ${_DISTUTILS_DEFAULT_CALLED} ]]; then
+               local cmd=die
+               [[ ${EAPI} == [45] ]] && cmd=eqawarn
+
+               "${cmd}" "QA: python_prepare_all() didn't call 
distutils-r2_python_prepare_all"
+       fi
+
+       if declare -f python_prepare >/dev/null; then
+               _distutils-r2_run_foreach_impl python_prepare
+       fi
+}
+
+distutils-r2_src_configure() {
+       python_export_utf8_locale
+       [[ ${EAPI} == [56] ]] && xdg_environment_reset # Bug 577704
+
+       if declare -f python_configure >/dev/null; then
+               _distutils-r2_run_foreach_impl python_configure
+       fi
+
+       if declare -f python_configure_all >/dev/null; then
+               _distutils-r2_run_common_phase python_configure_all
+       fi
+}
+
+distutils-r2_src_compile() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       if declare -f python_compile >/dev/null; then
+               _distutils-r2_run_foreach_impl python_compile
+       else
+               _distutils-r2_run_foreach_impl distutils-r2_python_compile
+       fi
+
+       if declare -f python_compile_all >/dev/null; then
+               _distutils-r2_run_common_phase python_compile_all
+       fi
+}
+
+# @FUNCTION: _distutils-r2_clean_egg_info
+# @INTERNAL
+# @DESCRIPTION:
+# Clean up potential stray egg-info files left by setuptools test phase.
+# Those files ended up being unversioned, and caused issues:
+# https://bugs.gentoo.org/534058
+_distutils-r2_clean_egg_info() {
+       rm -rf "${BUILD_DIR}"/lib/*.egg-info || die
+}
+
+distutils-r2_src_test() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       if declare -f python_test >/dev/null; then
+               _distutils-r2_run_foreach_impl python_test
+               _distutils-r2_run_foreach_impl _distutils-r2_clean_egg_info
+       fi
+
+       if declare -f python_test_all >/dev/null; then
+               _distutils-r2_run_common_phase python_test_all
+       fi
+}
+
+# @FUNCTION: _distutils-r2_check_namespace_pth
+# @INTERNAL
+# @DESCRIPTION:
+# Check if any *-nspkg.pth files were installed (by setuptools)
+# and warn about the policy non-conformance if they were.
+_distutils-r2_check_namespace_pth() {
+       local f pth=()
+
+       while IFS= read -r -d '' f; do
+               pth+=( "${f}" )
+       done < <(find "${ED%/}" -name '*-nspkg.pth' -print0)
+
+       if [[ ${pth[@]} ]]; then
+               ewarn "The following *-nspkg.pth files were found installed:"
+               ewarn
+               for f in "${pth[@]}"; do
+                       ewarn "  ${f#${ED%/}}"
+               done
+               ewarn
+               ewarn "The presence of those files may break namespaces in 
Python 3.5+. Please"
+               ewarn "read our documentation on reliable handling of 
namespaces and update"
+               ewarn "the ebuild accordingly:"
+               ewarn
+               ewarn "  
https://wiki.gentoo.org/wiki/Project:Python/Namespace_packages";
+       fi
+}
+
+distutils-r2_src_install() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       if declare -f python_install >/dev/null; then
+               _distutils-r2_run_foreach_impl python_install
+       else
+               _distutils-r2_run_foreach_impl distutils-r2_python_install
+       fi
+
+       local _DISTUTILS_DEFAULT_CALLED
+
+       if declare -f python_install_all >/dev/null; then
+               _distutils-r2_run_common_phase python_install_all
+       else
+               _distutils-r2_run_common_phase distutils-r2_python_install_all
+       fi
+
+       if [[ ! ${_DISTUTILS_DEFAULT_CALLED} ]]; then
+               local cmd=die
+               [[ ${EAPI} == [45] ]] && cmd=eqawarn
+
+               "${cmd}" "QA: python_install_all() didn't call 
distutils-r2_python_install_all"
+       fi
+
+       _distutils-r2_check_namespace_pth
+}
+
+# -- distutils.eclass functions --
+
+distutils_get_intermediate_installation_image() {
+       die "${FUNCNAME}() is invalid for distutils-r2"
+}
+
+distutils_src_unpack() {
+       die "${FUNCNAME}() is invalid for distutils-r2, and you don't want it 
in EAPI ${EAPI} anyway"
+}
+
+distutils_src_prepare() {
+       die "${FUNCNAME}() is invalid for distutils-r2, you probably want: 
${FUNCNAME/_/-r2_}"
+}
+
+distutils_src_compile() {
+       die "${FUNCNAME}() is invalid for distutils-r2, you probably want: 
${FUNCNAME/_/-r2_}"
+}
+
+distutils_src_test() {
+       die "${FUNCNAME}() is invalid for distutils-r2, you probably want: 
${FUNCNAME/_/-r2_}"
+}
+
+distutils_src_install() {
+       die "${FUNCNAME}() is invalid for distutils-r2, you probably want: 
${FUNCNAME/_/-r2_}"
+}
+
+distutils_pkg_postinst() {
+       die "${FUNCNAME}() is invalid for distutils-r2, and pkg_postinst is 
unnecessary"
+}
+
+distutils_pkg_postrm() {
+       die "${FUNCNAME}() is invalid for distutils-r2, and pkg_postrm is 
unnecessary"
+}
+
+_DISTUTILS_R2=1
+fi
diff --git a/eclass/python-any-r2.eclass b/eclass/python-any-r2.eclass
new file mode 100644
index 000000000000..cf70f23f69d5
--- /dev/null
+++ b/eclass/python-any-r2.eclass
@@ -0,0 +1,357 @@
+# Copyright 1999-2020 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+# @ECLASS: python-any-r2.eclass
+# @MAINTAINER:
+# Python team <[email protected]>
+# @AUTHOR:
+# Author: Michał Górny <[email protected]>
+# @SUPPORTED_EAPIS: 5 6 7
+# @BLURB: An eclass for packages having build-time dependency on Python.
+# @DESCRIPTION:
+# A minimal eclass for packages which need any Python interpreter
+# installed without a need for explicit choice and invariability.
+# This usually involves packages requiring Python at build-time
+# but having no other relevance to it.
+#
+# This eclass provides a minimal PYTHON_DEPS variable with a dependency
+# string on any of the supported Python implementations. It also exports
+# pkg_setup() which finds the best supported implementation and sets it
+# as the active one.
+#
+# Optionally, you can define a python_check_deps() function. It will
+# be called by the eclass with EPYTHON set to each matching Python
+# implementation and it is expected to check whether the implementation
+# fulfills the package requirements. You can use the locally exported
+# PYTHON_USEDEP to check USE-dependencies of relevant packages. It
+# should return a true value (0) if the Python implementation fulfills
+# the requirements, a false value (non-zero) otherwise.
+#
+# Please note that python-any-r2 will always inherit python-utils-r2
+# as well. Thus, all the functions defined there can be used in the
+# packages using python-any-r2, and there is no need ever to inherit
+# both.
+
+case "${EAPI:-0}" in
+       [0-4]) die "Unsupported EAPI=${EAPI:-0} (too old) for ${ECLASS}" ;;
+       [5-7]) ;;
+       *)     die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}" ;;
+esac
+
+if [[ ! ${_PYTHON_ANY_R2} ]]; then
+
+if [[ ${_PYTHON_R2} ]]; then
+       die 'python-any-r2.eclass can not be used with python-r2.eclass.'
+elif [[ ${_PYTHON_SINGLE_R2} ]]; then
+       die 'python-any-r2.eclass can not be used with python-single-r2.eclass.'
+fi
+
+inherit python-utils-r2
+
+fi
+
+EXPORT_FUNCTIONS pkg_setup
+
+# @ECLASS-VARIABLE: PYTHON_COMPAT
+# @REQUIRED
+# @DESCRIPTION:
+# This variable contains a list of Python implementations the package
+# supports. It must be set before the `inherit' call. It has to be
+# an array.
+#
+# Example:
+# @CODE
+# PYTHON_COMPAT=( python{2_5,2_6,2_7} )
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_COMPAT_OVERRIDE
+# @INTERNAL
+# @DESCRIPTION:
+# This variable can be used when working with ebuilds to override
+# the in-ebuild PYTHON_COMPAT. It is a string naming the implementation
+# which will be used to build the package. It needs to be specified
+# in the calling environment, and not in ebuilds.
+#
+# It should be noted that in order to preserve metadata immutability,
+# PYTHON_COMPAT_OVERRIDE does not affect dependencies. The value of
+# EPYTHON and eselect-python preferences are ignored. Dependencies need
+# to be satisfied manually.
+#
+# Example:
+# @CODE
+# PYTHON_COMPAT_OVERRIDE='pypy' emerge -1v dev-python/bar
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_REQ_USE
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# The list of USEflags required to be enabled on the Python
+# implementations, formed as a USE-dependency string. It should be valid
+# for all implementations in PYTHON_COMPAT, so it may be necessary to
+# use USE defaults.
+#
+# Example:
+# @CODE
+# PYTHON_REQ_USE="gdbm,ncurses(-)?"
+# @CODE
+#
+# It will cause the Python dependencies to look like:
+# @CODE
+# || ( dev-lang/python:X.Y[gdbm,ncurses(-)?] ... )
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_DEPS
+# @DESCRIPTION:
+# This is an eclass-generated Python dependency string for all
+# implementations listed in PYTHON_COMPAT.
+#
+# Any of the supported interpreters will satisfy the dependency.
+#
+# Example use:
+# @CODE
+# DEPEND="${RDEPEND}
+#      ${PYTHON_DEPS}"
+# @CODE
+#
+# Example value:
+# @CODE
+# || ( dev-lang/python:2.7[gdbm]
+#      dev-lang/python:2.6[gdbm] )
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_USEDEP
+# @DESCRIPTION:
+# An eclass-generated USE-dependency string for the currently tested
+# implementation. It is set locally for python_check_deps() call.
+#
+# The generate USE-flag list is compatible with packages using python-r2,
+# python-single-r2 and python-distutils-ng eclasses. It must not be used
+# on packages using python.eclass.
+#
+# Example use:
+# @CODE
+# python_check_deps() {
+#      has_version "dev-python/foo[${PYTHON_USEDEP}]"
+# }
+# @CODE
+#
+# Example value:
+# @CODE
+# python_targets_python2_7(-)?,python_single_target_python2_7(+)?
+# @CODE
+
+_python_any_set_globals() {
+       local usestr deps i PYTHON_PKG_DEP
+       [[ ${PYTHON_REQ_USE} ]] && usestr="[${PYTHON_REQ_USE}]"
+
+       _python_set_impls
+
+       for i in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do
+               python_export "${i}" PYTHON_PKG_DEP
+
+               # note: need to strip '=' slot operator for || deps
+               deps="${PYTHON_PKG_DEP/:0=/:0} ${deps}"
+       done
+       deps="|| ( ${deps})"
+
+       if [[ ${PYTHON_DEPS+1} ]]; then
+               if [[ ${PYTHON_DEPS} != "${deps}" ]]; then
+                       eerror "PYTHON_DEPS have changed between inherits 
(PYTHON_REQ_USE?)!"
+                       eerror "Before: ${PYTHON_DEPS}"
+                       eerror "Now   : ${deps}"
+                       die "PYTHON_DEPS integrity check failed"
+               fi
+       else
+               PYTHON_DEPS=${deps}
+               readonly PYTHON_DEPS
+       fi
+
+       if [[ ! ${PYTHON_REQUIRED_USE+1} ]]; then
+               # fake var to catch mistaken usage
+               PYTHON_REQUIRED_USE='I-DO-NOT-EXIST-IN-PYTHON-ANY-R1'
+               readonly PYTHON_REQUIRED_USE
+       fi
+}
+_python_any_set_globals
+unset -f _python_any_set_globals
+
+if [[ ! ${_PYTHON_ANY_R2} ]]; then
+
+# @FUNCTION: python_gen_any_dep
+# @USAGE: <dependency-block>
+# @DESCRIPTION:
+# Generate an any-of dependency that enforces a version match between
+# the Python interpreter and Python packages. <dependency-block> needs
+# to list one or more dependencies with verbatim '${PYTHON_USEDEP}'
+# references (quoted!) that will get expanded inside the function.
+#
+# This should be used along with an appropriate python_check_deps()
+# that checks which of the any-of blocks were matched.
+#
+# Example use:
+# @CODE
+# DEPEND="$(python_gen_any_dep '
+#      dev-python/foo[${PYTHON_USEDEP}]
+#      || ( dev-python/bar[${PYTHON_USEDEP}]
+#              dev-python/baz[${PYTHON_USEDEP}] )')"
+#
+# python_check_deps() {
+#      has_version "dev-python/foo[${PYTHON_USEDEP}]" \
+#              && { has_version "dev-python/bar[${PYTHON_USEDEP}]" \
+#                      || has_version "dev-python/baz[${PYTHON_USEDEP}]"; }
+# }
+# @CODE
+#
+# Example value:
+# @CODE
+# || (
+#      (
+#              dev-lang/python:2.7
+#              
dev-python/foo[python_targets_python2_7(-)?,python_single_target_python2_7(+)?]
+#              || ( 
dev-python/bar[python_targets_python2_7(-)?,python_single_target_python2_7(+)?]
+#                      
dev-python/baz[python_targets_python2_7(-)?,python_single_target_python2_7(+)?] 
)
+#      )
+#      (
+#              dev-lang/python:3.3
+#              
dev-python/foo[python_targets_python3_3(-)?,python_single_target_python3_3(+)?]
+#              || ( 
dev-python/bar[python_targets_python3_3(-)?,python_single_target_python3_3(+)?]
+#                      
dev-python/baz[python_targets_python3_3(-)?,python_single_target_python3_3(+)?] 
)
+#      )
+# )
+# @CODE
+python_gen_any_dep() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local depstr=${1}
+       [[ ${depstr} ]] || die "No dependency string provided"
+
+       local i PYTHON_PKG_DEP out=
+       for i in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do
+               local 
PYTHON_USEDEP="python_targets_${i}(-),python_single_target_${i}(+)"
+               python_export "${i}" PYTHON_PKG_DEP
+
+               local i_depstr=${depstr//\$\{PYTHON_USEDEP\}/${PYTHON_USEDEP}}
+               # note: need to strip '=' slot operator for || deps
+               out="( ${PYTHON_PKG_DEP%=} ${i_depstr} ) ${out}"
+       done
+       echo "|| ( ${out})"
+}
+
+# @FUNCTION: _python_EPYTHON_supported
+# @USAGE: <epython>
+# @INTERNAL
+# @DESCRIPTION:
+# Check whether the specified implementation is supported by package
+# (specified in PYTHON_COMPAT). Calls python_check_deps() if declared.
+_python_EPYTHON_supported() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local EPYTHON=${1}
+       local i=${EPYTHON/./_}
+
+       case "${i}" in
+               python*|jython*|pypy*)
+                       ;;
+               *)
+                       ewarn "Invalid EPYTHON: ${EPYTHON}"
+                       return 1
+                       ;;
+       esac
+
+       if has "${i}" "${_PYTHON_SUPPORTED_IMPLS[@]}"; then
+               if python_is_installed "${i}"; then
+                       if declare -f python_check_deps >/dev/null; then
+                               local 
PYTHON_USEDEP="python_targets_${i}(-),python_single_target_${i}(+)"
+                               python_check_deps
+                               return ${?}
+                       fi
+
+                       return 0
+               fi
+       elif ! has "${i}" "${_PYTHON_ALL_IMPLS[@]}"; then
+               ewarn "Invalid EPYTHON: ${EPYTHON}"
+       fi
+       return 1
+}
+
+# @FUNCTION: python_setup
+# @DESCRIPTION:
+# Determine what the best installed (and supported) Python
+# implementation is, and set the Python build environment up for it.
+#
+# This function will call python_check_deps() if defined.
+python_setup() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       # support developer override
+       if [[ ${PYTHON_COMPAT_OVERRIDE} ]]; then
+               local impls=( ${PYTHON_COMPAT_OVERRIDE} )
+               [[ ${#impls[@]} -eq 1 ]] || die "PYTHON_COMPAT_OVERRIDE must 
name exactly one implementation for python-any-r2"
+
+               ewarn "WARNING: PYTHON_COMPAT_OVERRIDE in effect. The following 
Python"
+               ewarn "implementation will be used:"
+               ewarn
+               ewarn " ${PYTHON_COMPAT_OVERRIDE}"
+               ewarn
+               ewarn "Dependencies won't be satisfied, and 
EPYTHON/eselect-python will be ignored."
+
+               python_export "${impls[0]}" EPYTHON PYTHON
+               python_wrapper_setup
+               return
+       fi
+
+       # first, try ${EPYTHON}... maybe it's good enough for us.
+       if [[ ${EPYTHON} ]]; then
+               if _python_EPYTHON_supported "${EPYTHON}"; then
+                       python_export EPYTHON PYTHON
+                       python_wrapper_setup
+                       return
+               fi
+       fi
+
+       # then, try eselect-python
+       local variant i
+       for variant in '' '--python2' '--python3'; do
+               i=$(eselect python --show ${variant} 2>/dev/null)
+
+               if [[ ! ${i} ]]; then
+                       # no eselect-python?
+                       break
+               elif _python_EPYTHON_supported "${i}"; then
+                       python_export "${i}" EPYTHON PYTHON
+                       python_wrapper_setup
+                       return
+               fi
+       done
+
+       # fallback to best installed impl.
+       # (reverse iteration over _PYTHON_SUPPORTED_IMPLS)
+       for (( i = ${#_PYTHON_SUPPORTED_IMPLS[@]} - 1; i >= 0; i-- )); do
+               python_export "${_PYTHON_SUPPORTED_IMPLS[i]}" EPYTHON PYTHON
+               if _python_EPYTHON_supported "${EPYTHON}"; then
+                       python_wrapper_setup
+                       return
+               fi
+       done
+
+       eerror "No Python implementation found for the build. This is usually"
+       eerror "a bug in the ebuild. Please report it to bugs.gentoo.org"
+       eerror "along with the build log."
+       echo
+       die "No supported Python implementation installed."
+}
+
+# @FUNCTION: python-any-r2_pkg_setup
+# @DESCRIPTION:
+# Runs python_setup during from-source installs.
+#
+# In a binary package installs is a no-op. If you need Python in pkg_*
+# phases of a binary package, call python_setup directly.
+python-any-r2_pkg_setup() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       [[ ${MERGE_TYPE} != binary ]] && python_setup
+}
+
+_PYTHON_ANY_R2=1
+fi
diff --git a/eclass/python-r2.eclass b/eclass/python-r2.eclass
new file mode 100644
index 000000000000..8bf0b1a1ac29
--- /dev/null
+++ b/eclass/python-r2.eclass
@@ -0,0 +1,825 @@
+# Copyright 1999-2020 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+# @ECLASS: python-r2.eclass
+# @MAINTAINER:
+# Python team <[email protected]>
+# @AUTHOR:
+# Author: Michał Górny <[email protected]>
+# @SUPPORTED_EAPIS: 5 6 7
+# @BLURB: A common, simple eclass for Python packages.
+# @DESCRIPTION:
+# A common eclass providing helper functions to build and install
+# packages supporting being installed for multiple Python
+# implementations.
+#
+# This eclass sets correct IUSE. Modification of REQUIRED_USE has to
+# be done by the author of the ebuild (but PYTHON_REQUIRED_USE is
+# provided for convenience, see below). python-r2 exports PYTHON_DEPS
+# and PYTHON_USEDEP so you can create correct dependencies for your
+# package easily. It also provides methods to easily run a command for
+# each enabled Python implementation and duplicate the sources for them.
+#
+# Please note that python-r2 will always inherit python-utils-r2 as
+# well. Thus, all the functions defined there can be used
+# in the packages using python-r2, and there is no need ever to inherit
+# both.
+
+case "${EAPI:-0}" in
+       0|1|2|3|4)
+               die "Unsupported EAPI=${EAPI:-0} (too old) for ${ECLASS}"
+               ;;
+       5|6|7)
+               # EAPI=5 is required for sane USE_EXPAND dependencies
+               ;;
+       *)
+               die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}"
+               ;;
+esac
+
+if [[ ! ${_PYTHON_R2} ]]; then
+
+if [[ ${_PYTHON_SINGLE_R2} ]]; then
+       die 'python-r2.eclass can not be used with python-single-r2.eclass.'
+elif [[ ${_PYTHON_ANY_R2} ]]; then
+       die 'python-r2.eclass can not be used with python-any-r2.eclass.'
+fi
+
+[[ ${EAPI} == [45] ]] && inherit eutils
+inherit multibuild python-utils-r2
+
+fi
+
+# @ECLASS-VARIABLE: PYTHON_COMPAT
+# @REQUIRED
+# @DESCRIPTION:
+# This variable contains a list of Python implementations the package
+# supports. It must be set before the `inherit' call. It has to be
+# an array.
+#
+# Example:
+# @CODE
+# PYTHON_COMPAT=( python2_7 python3_3 python3_4 )
+# @CODE
+#
+# Please note that you can also use bash brace expansion if you like:
+# @CODE
+# PYTHON_COMPAT=( python2_7 python3_{3,4} )
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_COMPAT_OVERRIDE
+# @INTERNAL
+# @DESCRIPTION:
+# This variable can be used when working with ebuilds to override
+# the in-ebuild PYTHON_COMPAT. It is a string listing all
+# the implementations which package will be built for. It need be
+# specified in the calling environment, and not in ebuilds.
+#
+# It should be noted that in order to preserve metadata immutability,
+# PYTHON_COMPAT_OVERRIDE does not affect IUSE nor dependencies.
+# The state of PYTHON_TARGETS is ignored, and all the implementations
+# in PYTHON_COMPAT_OVERRIDE are built. Dependencies need to be satisfied
+# manually.
+#
+# Example:
+# @CODE
+# PYTHON_COMPAT_OVERRIDE='pypy python3_3' emerge -1v dev-python/foo
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_REQ_USE
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# The list of USEflags required to be enabled on the chosen Python
+# implementations, formed as a USE-dependency string. It should be valid
+# for all implementations in PYTHON_COMPAT, so it may be necessary to
+# use USE defaults.
+#
+# This should be set before calling `inherit'.
+#
+# Example:
+# @CODE
+# PYTHON_REQ_USE="gdbm,ncurses(-)?"
+# @CODE
+#
+# It will cause the Python dependencies to look like:
+# @CODE
+# python_targets_pythonX_Y? ( dev-lang/python:X.Y[gdbm,ncurses(-)?] )
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_DEPS
+# @DESCRIPTION:
+# This is an eclass-generated Python dependency string for all
+# implementations listed in PYTHON_COMPAT.
+#
+# Example use:
+# @CODE
+# RDEPEND="${PYTHON_DEPS}
+#      dev-foo/mydep"
+# DEPEND="${RDEPEND}"
+# @CODE
+#
+# Example value:
+# @CODE
+# dev-lang/python-exec:=
+# python_targets_python2_7? ( dev-lang/python:2.7[gdbm] )
+# python_targets_pypy? ( dev-python/pypy[gdbm] )
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_USEDEP
+# @DESCRIPTION:
+# This is an eclass-generated USE-dependency string which can be used to
+# depend on another Python package being built for the same Python
+# implementations.
+#
+# The generate USE-flag list is compatible with packages using python-r2
+# and python-distutils-ng eclasses. It must not be used on packages
+# using python.eclass.
+#
+# Example use:
+# @CODE
+# RDEPEND="dev-python/foo[${PYTHON_USEDEP}]"
+# @CODE
+#
+# Example value:
+# @CODE
+# python_targets_python2_7(-)?,python_targets_python3_4(-)?
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_REQUIRED_USE
+# @DESCRIPTION:
+# This is an eclass-generated required-use expression which ensures at
+# least one Python implementation has been enabled.
+#
+# This expression should be utilized in an ebuild by including it in
+# REQUIRED_USE, optionally behind a use flag.
+#
+# Example use:
+# @CODE
+# REQUIRED_USE="python? ( ${PYTHON_REQUIRED_USE} )"
+# @CODE
+#
+# Example value:
+# @CODE
+# || ( python_targets_python2_7 python_targets_python3_4 )
+# @CODE
+
+_python_set_globals() {
+       local deps i PYTHON_PKG_DEP
+
+       _python_set_impls
+
+       for i in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do
+               python_export "${i}" PYTHON_PKG_DEP
+               deps+="python_targets_${i}? ( ${PYTHON_PKG_DEP} ) "
+       done
+
+       local flags=( "${_PYTHON_SUPPORTED_IMPLS[@]/#/python_targets_}" )
+       local optflags=${flags[@]/%/(-)?}
+
+       # A nice QA trick here. Since a python-single-r2 package has to have
+       # at least one PYTHON_SINGLE_TARGET enabled (REQUIRED_USE),
+       # the following check will always fail on those packages. Therefore,
+       # it should prevent developers from mistakenly depending on packages
+       # not supporting multiple Python implementations.
+
+       local flags_st=( 
"${_PYTHON_SUPPORTED_IMPLS[@]/#/-python_single_target_}" )
+       optflags+=,${flags_st[@]/%/(-)}
+       local requse="|| ( ${flags[*]} )"
+       local usedep=${optflags// /,}
+
+       # 1) well, python-exec would suffice as an RDEP
+       # but no point in making this overcomplex, BDEP doesn't hurt anyone
+       # 2) python-exec should be built with all targets forced anyway
+       # but if new targets were added, we may need to force a rebuild
+       deps+=">=dev-lang/python-exec-2:=[${usedep}]"
+
+       if [[ ${PYTHON_DEPS+1} ]]; then
+               # IUSE is magical, so we can't really check it
+               # (but we verify PYTHON_COMPAT already)
+
+               if [[ ${PYTHON_DEPS} != "${deps}" ]]; then
+                       eerror "PYTHON_DEPS have changed between inherits 
(PYTHON_REQ_USE?)!"
+                       eerror "Before: ${PYTHON_DEPS}"
+                       eerror "Now   : ${deps}"
+                       die "PYTHON_DEPS integrity check failed"
+               fi
+
+               # these two are formality -- they depend on PYTHON_COMPAT only
+               if [[ ${PYTHON_REQUIRED_USE} != ${requse} ]]; then
+                       eerror "PYTHON_REQUIRED_USE have changed between 
inherits!"
+                       eerror "Before: ${PYTHON_REQUIRED_USE}"
+                       eerror "Now   : ${requse}"
+                       die "PYTHON_REQUIRED_USE integrity check failed"
+               fi
+
+               if [[ ${PYTHON_USEDEP} != "${usedep}" ]]; then
+                       eerror "PYTHON_USEDEP have changed between inherits!"
+                       eerror "Before: ${PYTHON_USEDEP}"
+                       eerror "Now   : ${usedep}"
+                       die "PYTHON_USEDEP integrity check failed"
+               fi
+       else
+               IUSE=${flags[*]}
+
+               PYTHON_DEPS=${deps}
+               PYTHON_REQUIRED_USE=${requse}
+               PYTHON_USEDEP=${usedep}
+               readonly PYTHON_DEPS PYTHON_REQUIRED_USE
+       fi
+}
+_python_set_globals
+unset -f _python_set_globals
+
+if [[ ! ${_PYTHON_R2} ]]; then
+
+# @FUNCTION: _python_validate_useflags
+# @INTERNAL
+# @DESCRIPTION:
+# Enforce the proper setting of PYTHON_TARGETS, if PYTHON_COMPAT_OVERRIDE
+# is not in effect. If it is, just warn that the flags will be ignored.
+_python_validate_useflags() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       if [[ ${PYTHON_COMPAT_OVERRIDE} ]]; then
+               if [[ ! ${_PYTHON_COMPAT_OVERRIDE_WARNED} ]]; then
+                       ewarn "WARNING: PYTHON_COMPAT_OVERRIDE in effect. The 
following Python"
+                       ewarn "implementations will be enabled:"
+                       ewarn
+                       ewarn " ${PYTHON_COMPAT_OVERRIDE}"
+                       ewarn
+                       ewarn "Dependencies won't be satisfied, and 
PYTHON_TARGETS will be ignored."
+                       _PYTHON_COMPAT_OVERRIDE_WARNED=1
+               fi
+               # we do not use flags with PCO
+               return
+       fi
+
+       local i
+
+       for i in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do
+               use "python_targets_${i}" && return 0
+       done
+
+       eerror "No Python implementation selected for the build. Please add one"
+       eerror "of the following values to your PYTHON_TARGETS (in make.conf):"
+       eerror
+       eerror "${PYTHON_COMPAT[@]}"
+       echo
+       die "No supported Python implementation in PYTHON_TARGETS."
+}
+
+# @FUNCTION: _python_gen_usedep
+# @INTERNAL
+# @USAGE: [<pattern>...]
+# @DESCRIPTION:
+# Output a USE dependency string for Python implementations which
+# are both in PYTHON_COMPAT and match any of the patterns passed
+# as parameters to the function.
+#
+# The patterns can be either fnmatch-style patterns (matched via bash
+# == operator against PYTHON_COMPAT values) or '-2' / '-3' to indicate
+# appropriately all enabled Python 2/3 implementations (alike
+# python_is_python3). Remember to escape or quote the fnmatch patterns
+# to prevent accidental shell filename expansion.
+#
+# This is an internal function used to implement python_gen_cond_dep
+# and deprecated python_gen_usedep.
+_python_gen_usedep() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local impl matches=()
+
+       for impl in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do
+               if _python_impl_matches "${impl}" "${@}"; then
+                       matches+=(
+                               "python_targets_${impl}(-)?"
+                               "-python_single_target_${impl}(-)"
+                       )
+               fi
+       done
+
+       [[ ${matches[@]} ]] || die "No supported implementations match 
python_gen_usedep patterns: ${@}"
+
+       local out=${matches[@]}
+       echo "${out// /,}"
+}
+
+# @FUNCTION: python_gen_usedep
+# @USAGE: <pattern> [...]
+# @DESCRIPTION:
+# DEPRECATED.  Please use python_gen_cond_dep instead.
+#
+# Output a USE dependency string for Python implementations which
+# are both in PYTHON_COMPAT and match any of the patterns passed
+# as parameters to the function.
+#
+# The patterns can be either fnmatch-style patterns (matched via bash
+# == operator against PYTHON_COMPAT values) or '-2' / '-3' to indicate
+# appropriately all enabled Python 2/3 implementations (alike
+# python_is_python3). Remember to escape or quote the fnmatch patterns
+# to prevent accidental shell filename expansion.
+#
+# When all implementations are requested, please use ${PYTHON_USEDEP}
+# instead. Please also remember to set an appropriate REQUIRED_USE
+# to avoid ineffective USE flags.
+#
+# Example:
+# @CODE
+# PYTHON_COMPAT=( python{2_7,3_4} )
+# DEPEND="doc? ( dev-python/epydoc[$(python_gen_usedep 'python2*')] )"
+# @CODE
+#
+# It will cause the dependency to look like:
+# @CODE
+# DEPEND="doc? ( dev-python/epydoc[python_targets_python2_7?] )"
+# @CODE
+python_gen_usedep() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       # output only once, during some reasonable phase
+       # (avoid spamming cache regen runs)
+       if [[ ${EBUILD_PHASE} == setup ]]; then
+               eqawarn "python_gen_usedep() is deprecated. Please use 
python_gen_cond_dep instead."
+       fi
+       _python_gen_usedep "${@}"
+}
+
+# @FUNCTION: python_gen_useflags
+# @USAGE: [<pattern>...]
+# @DESCRIPTION:
+# Output a list of USE flags for Python implementations which
+# are both in PYTHON_COMPAT and match any of the patterns passed
+# as parameters to the function.
+#
+# The patterns can be either fnmatch-style patterns (matched via bash
+# == operator against PYTHON_COMPAT values) or '-2' / '-3' to indicate
+# appropriately all enabled Python 2/3 implementations (alike
+# python_is_python3). Remember to escape or quote the fnmatch patterns
+# to prevent accidental shell filename expansion.
+#
+# Example:
+# @CODE
+# PYTHON_COMPAT=( python{2_7,3_4} )
+# REQUIRED_USE="doc? ( || ( $(python_gen_useflags python2*) ) )"
+# @CODE
+#
+# It will cause the variable to look like:
+# @CODE
+# REQUIRED_USE="doc? ( || ( python_targets_python2_7 ) )"
+# @CODE
+python_gen_useflags() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local impl matches=()
+
+       for impl in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do
+               if _python_impl_matches "${impl}" "${@}"; then
+                       matches+=( "python_targets_${impl}" )
+               fi
+       done
+
+       echo "${matches[@]}"
+}
+
+# @FUNCTION: python_gen_cond_dep
+# @USAGE: <dependency> [<pattern>...]
+# @DESCRIPTION:
+# Output a list of <dependency>-ies made conditional to USE flags
+# of Python implementations which are both in PYTHON_COMPAT and match
+# any of the patterns passed as the remaining parameters.
+#
+# The patterns can be either fnmatch-style patterns (matched via bash
+# == operator against PYTHON_COMPAT values) or '-2' / '-3' to indicate
+# appropriately all enabled Python 2/3 implementations (alike
+# python_is_python3). Remember to escape or quote the fnmatch patterns
+# to prevent accidental shell filename expansion.
+#
+# In order to enforce USE constraints on the packages, verbatim
+# '${PYTHON_USEDEP}' (quoted!) may be placed in the dependency
+# specification. It will get expanded within the function into a proper
+# USE dependency string.
+#
+# Example:
+# @CODE
+# PYTHON_COMPAT=( python{2_7,3_{3,4}} pypy )
+# RDEPEND="$(python_gen_cond_dep \
+#   'dev-python/unittest2[${PYTHON_USEDEP}]' python2_7 pypy )"
+# @CODE
+#
+# It will cause the variable to look like:
+# @CODE
+# RDEPEND="python_targets_python2_7? (
+#     dev-python/unittest2[python_targets_python2_7?] )
+#      python_targets_pypy? (
+#     dev-python/unittest2[python_targets_pypy?] )"
+# @CODE
+python_gen_cond_dep() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local impl matches=()
+       local dep=${1}
+       shift
+
+       for impl in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do
+               if _python_impl_matches "${impl}" "${@}"; then
+                       # substitute ${PYTHON_USEDEP} if used
+                       # (since python_gen_usedep() will not return 
${PYTHON_USEDEP}
+                       #  the code is run at most once)
+                       if [[ ${dep} == *'${PYTHON_USEDEP}'* ]]; then
+                               local usedep=$(_python_gen_usedep "${@}")
+                               dep=${dep//\$\{PYTHON_USEDEP\}/${usedep}}
+                       fi
+
+                       matches+=( "python_targets_${impl}? ( ${dep} )" )
+               fi
+       done
+
+       echo "${matches[@]}"
+}
+
+# @FUNCTION: python_gen_impl_dep
+# @USAGE: [<requested-use-flags> [<impl-pattern>...]]
+# @DESCRIPTION:
+# Output a dependency on Python implementations with the specified USE
+# dependency string appended, or no USE dependency string if called
+# without the argument (or with empty argument). If any implementation
+# patterns are passed, the output dependencies will be generated only
+# for the implementations matching them.
+#
+# The patterns can be either fnmatch-style patterns (matched via bash
+# == operator against PYTHON_COMPAT values) or '-2' / '-3' to indicate
+# appropriately all enabled Python 2/3 implementations (alike
+# python_is_python3). Remember to escape or quote the fnmatch patterns
+# to prevent accidental shell filename expansion.
+#
+# Use this function when you need to request different USE flags
+# on the Python interpreter depending on package's USE flags. If you
+# only need a single set of interpreter USE flags, just set
+# PYTHON_REQ_USE and use ${PYTHON_DEPS} globally.
+#
+# Example:
+# @CODE
+# PYTHON_COMPAT=( python{2_7,3_{3,4}} pypy )
+# RDEPEND="foo? ( $(python_gen_impl_dep 'xml(+)') )"
+# @CODE
+#
+# It will cause the variable to look like:
+# @CODE
+# RDEPEND="foo? (
+#   python_targets_python2_7? (
+#     dev-lang/python:2.7[xml(+)] )
+#      python_targets_pypy? (
+#     dev-python/pypy[xml(+)] ) )"
+# @CODE
+python_gen_impl_dep() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local impl matches=()
+       local PYTHON_REQ_USE=${1}
+       shift
+
+       for impl in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do
+               if _python_impl_matches "${impl}" "${@}"; then
+                       local PYTHON_PKG_DEP
+                       python_export "${impl}" PYTHON_PKG_DEP
+                       matches+=( "python_targets_${impl}? ( ${PYTHON_PKG_DEP} 
)" )
+               fi
+       done
+
+       echo "${matches[@]}"
+}
+
+# @FUNCTION: python_gen_any_dep
+# @USAGE: <dependency-block> [<impl-pattern>...]
+# @DESCRIPTION:
+# Generate an any-of dependency that enforces a version match between
+# the Python interpreter and Python packages. <dependency-block> needs
+# to list one or more dependencies with verbatim '${PYTHON_USEDEP}'
+# references (quoted!) that will get expanded inside the function.
+# Optionally, patterns may be specified to restrict the dependency
+# to a subset of Python implementations supported by the ebuild.
+#
+# The patterns can be either fnmatch-style patterns (matched via bash
+# == operator against PYTHON_COMPAT values) or '-2' / '-3' to indicate
+# appropriately all enabled Python 2/3 implementations (alike
+# python_is_python3). Remember to escape or quote the fnmatch patterns
+# to prevent accidental shell filename expansion.
+#
+# This should be used along with an appropriate python_check_deps()
+# that checks which of the any-of blocks were matched, and python_setup
+# call that enables use of the matched implementation.
+#
+# Example use:
+# @CODE
+# DEPEND="$(python_gen_any_dep '
+#      dev-python/foo[${PYTHON_USEDEP}]
+#      || ( dev-python/bar[${PYTHON_USEDEP}]
+#              dev-python/baz[${PYTHON_USEDEP}] )' -2)"
+#
+# python_check_deps() {
+#      has_version "dev-python/foo[${PYTHON_USEDEP}]" \
+#              && { has_version "dev-python/bar[${PYTHON_USEDEP}]" \
+#                      || has_version "dev-python/baz[${PYTHON_USEDEP}]"; }
+# }
+#
+# src_compile() {
+#      python_foreach_impl usual_code
+#
+#      # some common post-build task that requires Python 2
+#      python_setup -2
+#      emake frobnicate
+# }
+# @CODE
+#
+# Example value:
+# @CODE
+# || (
+#      (
+#              dev-lang/python:2.7
+#              
dev-python/foo[python_targets_python2_7(-)?,python_single_target_python2_7(+)?]
+#              || ( 
dev-python/bar[python_targets_python2_7(-)?,python_single_target_python2_7(+)?]
+#                      
dev-python/baz[python_targets_python2_7(-)?,python_single_target_python2_7(+)?] 
)
+#      )
+#      (
+#              dev-lang/python:3.3
+#              
dev-python/foo[python_targets_python3_3(-)?,python_single_target_python3_3(+)?]
+#              || ( 
dev-python/bar[python_targets_python3_3(-)?,python_single_target_python3_3(+)?]
+#                      
dev-python/baz[python_targets_python3_3(-)?,python_single_target_python3_3(+)?] 
)
+#      )
+# )
+# @CODE
+python_gen_any_dep() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local depstr=${1}
+       [[ ${depstr} ]] || die "No dependency string provided"
+       shift
+
+       local i PYTHON_PKG_DEP out=
+       for i in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do
+               if _python_impl_matches "${i}" "${@}"; then
+                       local 
PYTHON_USEDEP="python_targets_${i}(-),python_single_target_${i}(+)"
+                       python_export "${i}" PYTHON_PKG_DEP
+
+                       local 
i_depstr=${depstr//\$\{PYTHON_USEDEP\}/${PYTHON_USEDEP}}
+                       # note: need to strip '=' slot operator for || deps
+                       out="( ${PYTHON_PKG_DEP/:0=/:0} ${i_depstr} ) ${out}"
+               fi
+       done
+       echo "|| ( ${out})"
+}
+
+# @ECLASS-VARIABLE: BUILD_DIR
+# @DESCRIPTION:
+# The current build directory. In global scope, it is supposed to
+# contain an initial build directory; if unset, it defaults to ${S}.
+#
+# In functions run by python_foreach_impl(), the BUILD_DIR is locally
+# set to an implementation-specific build directory. That path is
+# created through appending a hyphen and the implementation name
+# to the final component of the initial BUILD_DIR.
+#
+# Example value:
+# @CODE
+# ${WORKDIR}/foo-1.3-python2_7
+# @CODE
+
+# @FUNCTION: python_copy_sources
+# @DESCRIPTION:
+# Create a single copy of the package sources for each enabled Python
+# implementation.
+#
+# The sources are always copied from initial BUILD_DIR (or S if unset)
+# to implementation-specific build directory matching BUILD_DIR used by
+# python_foreach_abi().
+python_copy_sources() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local MULTIBUILD_VARIANTS
+       _python_obtain_impls
+
+       multibuild_copy_sources
+}
+
+# @FUNCTION: _python_obtain_impls
+# @INTERNAL
+# @DESCRIPTION:
+# Set up the enabled implementation list.
+_python_obtain_impls() {
+       _python_validate_useflags
+
+       if [[ ${PYTHON_COMPAT_OVERRIDE} ]]; then
+               MULTIBUILD_VARIANTS=( ${PYTHON_COMPAT_OVERRIDE} )
+               return
+       fi
+
+       MULTIBUILD_VARIANTS=()
+
+       local impl
+       for impl in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do
+               has "${impl}" "${PYTHON_COMPAT[@]}" && \
+               use "python_targets_${impl}" && MULTIBUILD_VARIANTS+=( 
"${impl}" )
+       done
+}
+
+# @FUNCTION: _python_multibuild_wrapper
+# @USAGE: <command> [<args>...]
+# @INTERNAL
+# @DESCRIPTION:
+# Initialize the environment for Python implementation selected
+# for multibuild.
+_python_multibuild_wrapper() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local -x EPYTHON PYTHON
+       local -x PATH=${PATH} PKG_CONFIG_PATH=${PKG_CONFIG_PATH}
+       python_export "${MULTIBUILD_VARIANT}" EPYTHON PYTHON
+       python_wrapper_setup
+
+       "${@}"
+}
+
+# @FUNCTION: python_foreach_impl
+# @USAGE: <command> [<args>...]
+# @DESCRIPTION:
+# Run the given command for each of the enabled Python implementations.
+# If additional parameters are passed, they will be passed through
+# to the command.
+#
+# The function will return 0 status if all invocations succeed.
+# Otherwise, the return code from first failing invocation will
+# be returned.
+#
+# For each command being run, EPYTHON, PYTHON and BUILD_DIR are set
+# locally, and the former two are exported to the command environment.
+python_foreach_impl() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local MULTIBUILD_VARIANTS
+       _python_obtain_impls
+
+       multibuild_foreach_variant _python_multibuild_wrapper "${@}"
+}
+
+# @FUNCTION: python_setup
+# @USAGE: [<impl-pattern>...]
+# @DESCRIPTION:
+# Find the best (most preferred) Python implementation that is suitable
+# for running common Python code. Set the Python build environment up
+# for that implementation. This function has two modes of operation:
+# pure and any-of dep.
+#
+# The pure mode is used if python_check_deps() function is not declared.
+# In this case, an implementation is considered suitable if it is
+# supported (in PYTHON_COMPAT), enabled (via USE flags) and matches
+# at least one of the patterns passed (or '*' if no patterns passed).
+#
+# Implementation restrictions in the pure mode need to be accompanied
+# by appropriate REQUIRED_USE constraints. Otherwise, the eclass may
+# fail at build time due to unsatisfied dependencies.
+#
+# The any-of dep mode is used if python_check_deps() is declared.
+# In this mode, an implementation is considered suitable if it is
+# supported, matches at least one of the patterns and python_check_deps()
+# has successful return code. USE flags are not considered.
+#
+# The python_check_deps() function in the any-of mode needs to be
+# accompanied by appropriate any-of dependencies.
+#
+# The patterns can be either fnmatch-style patterns (matched via bash
+# == operator against PYTHON_COMPAT values) or '-2' / '-3' to indicate
+# appropriately all enabled Python 2/3 implementations (alike
+# python_is_python3). Remember to escape or quote the fnmatch patterns
+# to prevent accidental shell filename expansion.
+#
+# This function needs to be used when Python is being called outside
+# of python_foreach_impl calls (e.g. for shared processes like doc
+# building). python_foreach_impl sets up the build environment itself.
+#
+# Pure mode example:
+# @CODE
+# DEPEND="doc? ( dev-python/epydoc[$(python_gen_usedep 'python2*')] )"
+# REQUIRED_USE="doc? ( $(python_gen_useflags 'python2*') )"
+#
+# src_compile() {
+#   #...
+#   if use doc; then
+#     python_setup 'python2*'
+#     make doc
+#   fi
+# }
+# @CODE
+#
+# Any-of mode example:
+# @CODE
+# DEPEND="doc? (
+#      $(python_gen_any_dep 'dev-python/epydoc[${PYTHON_USEDEP}]' 'python2*') 
)"
+#
+# python_check_deps() {
+#      has_version "dev-python/epydoc[${PYTHON_USEDEP}]"
+# }
+#
+# src_compile() {
+#   #...
+#   if use doc; then
+#     python_setup 'python2*'
+#     make doc
+#   fi
+# }
+# @CODE
+python_setup() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       _python_validate_useflags
+       local pycompat=( "${PYTHON_COMPAT[@]}" )
+       if [[ ${PYTHON_COMPAT_OVERRIDE} ]]; then
+               pycompat=( ${PYTHON_COMPAT_OVERRIDE} )
+       fi
+
+       local has_check_deps
+       declare -f python_check_deps >/dev/null && has_check_deps=1
+
+       # (reverse iteration -- newest impl first)
+       local found
+       for (( i = ${#_PYTHON_SUPPORTED_IMPLS[@]} - 1; i >= 0; i-- )); do
+               local impl=${_PYTHON_SUPPORTED_IMPLS[i]}
+
+               # check PYTHON_COMPAT[_OVERRIDE]
+               has "${impl}" "${pycompat[@]}" || continue
+
+               # match USE flags only if override is not in effect
+               # and python_check_deps() is not defined
+               if [[ ! ${PYTHON_COMPAT_OVERRIDE} && ! ${has_check_deps} ]]; 
then
+                       use "python_targets_${impl}" || continue
+               fi
+
+               # check patterns
+               _python_impl_matches "${impl}" "${@}" || continue
+
+               python_export "${impl}" EPYTHON PYTHON
+
+               # if python_check_deps() is declared, switch into any-of mode
+               if [[ ${has_check_deps} ]]; then
+                       # first check if the interpreter is installed
+                       python_is_installed "${impl}" || continue
+                       # then run python_check_deps
+                       local 
PYTHON_USEDEP="python_targets_${impl}(-),python_single_target_${impl}(+)"
+                       python_check_deps || continue
+               fi
+
+               found=1
+               break
+       done
+
+       if [[ ! ${found} ]]; then
+               eerror "${FUNCNAME}: none of the enabled implementation matched 
the patterns."
+               eerror "  patterns: ${@-'(*)'}"
+               eerror "Likely a REQUIRED_USE constraint (possibly 
USE-conditional) is missing."
+               eerror "  suggested: || ( \$(python_gen_useflags ${@}) )"
+               eerror "(remember to quote all the patterns with '')"
+               die "${FUNCNAME}: no enabled implementation satisfy 
requirements"
+       fi
+
+       python_wrapper_setup
+}
+
+# @FUNCTION: python_replicate_script
+# @USAGE: <path>...
+# @DESCRIPTION:
+# Copy the given script to variants for all enabled Python
+# implementations, then replace it with a symlink to the wrapper.
+#
+# All specified files must start with a 'python' shebang. A file not
+# having a matching shebang will be refused.
+python_replicate_script() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       _python_replicate_script() {
+               local _PYTHON_FIX_SHEBANG_QUIET=1
+
+               local PYTHON_SCRIPTDIR
+               python_export PYTHON_SCRIPTDIR
+
+               (
+                       exeopts -m 0755
+                       exeinto "${PYTHON_SCRIPTDIR#${EPREFIX}}"
+                       doexe "${files[@]}"
+               )
+
+               python_fix_shebang -q \
+                       "${files[@]/*\//${D%/}/${PYTHON_SCRIPTDIR}/}"
+       }
+
+       local files=( "${@}" )
+       python_foreach_impl _python_replicate_script
+       unset -f _python_replicate_script
+
+       # install the wrappers
+       local f
+       for f; do
+               _python_ln_rel "${ED%/}/usr/lib/python-exec/python-exec2" 
"${f}" || die
+       done
+}
+
+_PYTHON_R2=1
+fi
diff --git a/eclass/python-single-r2.eclass b/eclass/python-single-r2.eclass
new file mode 100644
index 000000000000..5bf6ea7221bc
--- /dev/null
+++ b/eclass/python-single-r2.eclass
@@ -0,0 +1,507 @@
+# Copyright 1999-2020 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+# @ECLASS: python-single-r2.eclass
+# @MAINTAINER:
+# Python team <[email protected]>
+# @AUTHOR:
+# Author: Michał Górny <[email protected]>
+# @SUPPORTED_EAPIS: 5 6 7
+# @BLURB: An eclass for Python packages not installed for multiple 
implementations.
+# @DESCRIPTION:
+# An extension of the python-r2 eclass suite for packages which
+# don't support being installed for multiple Python implementations.
+# This mostly includes tools embedding Python and packages using foreign
+# build systems.
+#
+# This eclass sets correct IUSE.  It also provides PYTHON_DEPS
+# and PYTHON_REQUIRED_USE that need to be added to appropriate ebuild
+# metadata variables.
+#
+# The eclass exports PYTHON_SINGLE_USEDEP that is suitable for depending
+# on other packages using the eclass.  Dependencies on packages using
+# python-r2 should be created via python_gen_cond_dep() function,
+# using PYTHON_MULTI_USEDEP placeholder.
+#
+# Please note that packages support multiple Python implementations
+# (using python-r2 eclass) can not depend on packages not supporting
+# them (using this eclass).
+#
+# Please note that python-single-r2 will always inherit python-utils-r2
+# as well. Thus, all the functions defined there can be used
+# in the packages using python-single-r2, and there is no need ever
+# to inherit both.
+
+case "${EAPI:-0}" in
+       0|1|2|3|4)
+               die "Unsupported EAPI=${EAPI:-0} (too old) for ${ECLASS}"
+               ;;
+       5|6|7)
+               # EAPI=5 is required for sane USE_EXPAND dependencies
+               ;;
+       *)
+               die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}"
+               ;;
+esac
+
+if [[ ! ${_PYTHON_SINGLE_R2} ]]; then
+
+if [[ ${_PYTHON_R2} ]]; then
+       die 'python-single-r2.eclass can not be used with python-r2.eclass.'
+elif [[ ${_PYTHON_ANY_R2} ]]; then
+       die 'python-single-r2.eclass can not be used with python-any-r2.eclass.'
+fi
+
+inherit python-utils-r2
+
+fi
+
+EXPORT_FUNCTIONS pkg_setup
+
+# @ECLASS-VARIABLE: PYTHON_COMPAT
+# @REQUIRED
+# @DESCRIPTION:
+# This variable contains a list of Python implementations the package
+# supports. It must be set before the `inherit' call. It has to be
+# an array.
+#
+# Example:
+# @CODE
+# PYTHON_COMPAT=( python2_7 python3_3 python3_4 )
+# @CODE
+#
+# Please note that you can also use bash brace expansion if you like:
+# @CODE
+# PYTHON_COMPAT=( python2_7 python3_{3,4} )
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_COMPAT_OVERRIDE
+# @INTERNAL
+# @DESCRIPTION:
+# This variable can be used when working with ebuilds to override
+# the in-ebuild PYTHON_COMPAT. It is a string naming the implementation
+# which package will be built for. It needs to be specified
+# in the calling environment, and not in ebuilds.
+#
+# It should be noted that in order to preserve metadata immutability,
+# PYTHON_COMPAT_OVERRIDE does not affect IUSE nor dependencies.
+# The state of PYTHON_SINGLE_TARGET is ignored, and the implementation
+# in PYTHON_COMPAT_OVERRIDE is built instead.  Dependencies need to be
+# satisfied manually.
+#
+# Example:
+# @CODE
+# PYTHON_COMPAT_OVERRIDE='pypy' emerge -1v dev-python/bar
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_REQ_USE
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# The list of USEflags required to be enabled on the chosen Python
+# implementations, formed as a USE-dependency string. It should be valid
+# for all implementations in PYTHON_COMPAT, so it may be necessary to
+# use USE defaults.
+#
+# This should be set before calling `inherit'.
+#
+# Example:
+# @CODE
+# PYTHON_REQ_USE="gdbm,ncurses(-)?"
+# @CODE
+#
+# It will cause the Python dependencies to look like:
+# @CODE
+# python_single_target_pythonX_Y? ( dev-lang/python:X.Y[gdbm,ncurses(-)?] )
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_DEPS
+# @DESCRIPTION:
+# This is an eclass-generated Python dependency string for all
+# implementations listed in PYTHON_COMPAT.
+#
+# The dependency string is conditional on PYTHON_SINGLE_TARGET.
+#
+# Example use:
+# @CODE
+# RDEPEND="${PYTHON_DEPS}
+#      dev-foo/mydep"
+# DEPEND="${RDEPEND}"
+# @CODE
+#
+# Example value:
+# @CODE
+# dev-lang/python-exec:=
+# python_single_target_python2_7? ( dev-lang/python:2.7[gdbm] )
+# python_single_target_pypy? ( dev-python/pypy[gdbm] )
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_SINGLE_USEDEP
+# @DESCRIPTION:
+# This is an eclass-generated USE-dependency string which can be used to
+# depend on another python-single-r2 package being built for the same
+# Python implementations.
+#
+# If you need to depend on a multi-impl (python-r2) package, use
+# python_gen_cond_dep with PYTHON_MULTI_USEDEP placeholder instead.
+#
+# Example use:
+# @CODE
+# RDEPEND="dev-python/foo[${PYTHON_SINGLE_USEDEP}]"
+# @CODE
+#
+# Example value:
+# @CODE
+# python_single_target_python3_4(-)?
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_MULTI_USEDEP
+# @DESCRIPTION:
+# This is a placeholder variable supported by python_gen_cond_dep,
+# in order to depend on python-r2 packages built for the same Python
+# implementations.
+#
+# Example use:
+# @CODE
+# RDEPEND="$(python_gen_cond_dep '
+#     dev-python/foo[${PYTHON_MULTI_USEDEP}]
+#   ')"
+# @CODE
+#
+# Example value:
+# @CODE
+# python_targets_python3_4(-)
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_REQUIRED_USE
+# @DESCRIPTION:
+# This is an eclass-generated required-use expression which ensures
+# that exactly one PYTHON_SINGLE_TARGET value has been enabled.
+#
+# This expression should be utilized in an ebuild by including it in
+# REQUIRED_USE, optionally behind a use flag.
+#
+# Example use:
+# @CODE
+# REQUIRED_USE="python? ( ${PYTHON_REQUIRED_USE} )"
+# @CODE
+#
+# Example value:
+# @CODE
+# ^^ ( python_single_target_python2_7 python_single_target_python3_3 )
+# @CODE
+
+_python_single_set_globals() {
+       _python_set_impls
+
+       local flags=( "${_PYTHON_SUPPORTED_IMPLS[@]/#/python_single_target_}" )
+
+       if [[ ${#_PYTHON_SUPPORTED_IMPLS[@]} -eq 1 ]]; then
+               # if only one implementation is supported, use IUSE defaults
+               # to avoid requesting the user to enable it
+               IUSE="+${flags[0]}"
+       else
+               IUSE="${flags[*]}"
+       fi
+
+       local requse="^^ ( ${flags[*]} )"
+       local single_flags="${flags[@]/%/(-)?}"
+       local single_usedep=${single_flags// /,}
+
+       local deps= i PYTHON_PKG_DEP
+       for i in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do
+               python_export "${i}" PYTHON_PKG_DEP
+               # 1) well, python-exec would suffice as an RDEP
+               # but no point in making this overcomplex, BDEP doesn't hurt 
anyone
+               # 2) python-exec should be built with all targets forced anyway
+               # but if new targets were added, we may need to force a rebuild
+               deps+="python_single_target_${i}? (
+                       ${PYTHON_PKG_DEP}
+                       >=dev-lang/python-exec-2:=[python_targets_${i}]
+               ) "
+       done
+
+       if [[ ${PYTHON_DEPS+1} ]]; then
+               if [[ ${PYTHON_DEPS} != "${deps}" ]]; then
+                       eerror "PYTHON_DEPS have changed between inherits 
(PYTHON_REQ_USE?)!"
+                       eerror "Before: ${PYTHON_DEPS}"
+                       eerror "Now   : ${deps}"
+                       die "PYTHON_DEPS integrity check failed"
+               fi
+
+               # these two are formality -- they depend on PYTHON_COMPAT only
+               if [[ ${PYTHON_REQUIRED_USE} != ${requse} ]]; then
+                       eerror "PYTHON_REQUIRED_USE have changed between 
inherits!"
+                       eerror "Before: ${PYTHON_REQUIRED_USE}"
+                       eerror "Now   : ${requse}"
+                       die "PYTHON_REQUIRED_USE integrity check failed"
+               fi
+
+               if [[ ${PYTHON_SINGLE_USEDEP} != "${single_usedep}" ]]; then
+                       eerror "PYTHON_SINGLE_USEDEP have changed between 
inherits!"
+                       eerror "Before: ${PYTHON_SINGLE_USEDEP}"
+                       eerror "Now   : ${single_usedep}"
+                       die "PYTHON_SINGLE_USEDEP integrity check failed"
+               fi
+       else
+               PYTHON_DEPS=${deps}
+               PYTHON_REQUIRED_USE=${requse}
+               PYTHON_USEDEP='%PYTHON_USEDEP-HAS-BEEN-REMOVED%'
+               PYTHON_SINGLE_USEDEP=${single_usedep}
+               readonly PYTHON_DEPS PYTHON_REQUIRED_USE PYTHON_SINGLE_USEDEP \
+                       PYTHON_USEDEP
+       fi
+}
+_python_single_set_globals
+unset -f _python_single_set_globals
+
+if [[ ! ${_PYTHON_SINGLE_R2} ]]; then
+
+# @FUNCTION: _python_gen_usedep
+# @INTERNAL
+# @USAGE: [<pattern>...]
+# @DESCRIPTION:
+# Output a USE dependency string for Python implementations which
+# are both in PYTHON_COMPAT and match any of the patterns passed
+# as parameters to the function.
+#
+# The patterns can be either fnmatch-style patterns (matched via bash
+# == operator against PYTHON_COMPAT values) or '-2' / '-3' to indicate
+# appropriately all enabled Python 2/3 implementations (alike
+# python_is_python3). Remember to escape or quote the fnmatch patterns
+# to prevent accidental shell filename expansion.
+#
+# This is an internal function used to implement python_gen_cond_dep.
+_python_gen_usedep() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local impl matches=()
+
+       for impl in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do
+               if _python_impl_matches "${impl}" "${@}"; then
+                       matches+=(
+                               "python_single_target_${impl}(-)?"
+                       )
+               fi
+       done
+
+       [[ ${matches[@]} ]] || die "No supported implementations match 
python_gen_usedep patterns: ${@}"
+
+       local out=${matches[@]}
+       echo "${out// /,}"
+}
+
+# @FUNCTION: python_gen_useflags
+# @USAGE: [<pattern>...]
+# @DESCRIPTION:
+# Output a list of USE flags for Python implementations which
+# are both in PYTHON_COMPAT and match any of the patterns passed
+# as parameters to the function.
+#
+# The patterns can be either fnmatch-style patterns (matched via bash
+# == operator against PYTHON_COMPAT values) or '-2' / '-3' to indicate
+# appropriately all enabled Python 2/3 implementations (alike
+# python_is_python3). Remember to escape or quote the fnmatch patterns
+# to prevent accidental shell filename expansion.
+#
+# Example:
+# @CODE
+# PYTHON_COMPAT=( python{2_7,3_4} )
+# REQUIRED_USE="doc? ( ^^ ( $(python_gen_useflags 'python2*') ) )"
+# @CODE
+#
+# It will cause the variable to look like:
+# @CODE
+# REQUIRED_USE="doc? ( ^^ ( python_single_target_python2_7 ) )"
+# @CODE
+python_gen_useflags() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local impl matches=()
+
+       for impl in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do
+               if _python_impl_matches "${impl}" "${@}"; then
+                       matches+=( "python_single_target_${impl}" )
+               fi
+       done
+
+       echo "${matches[@]}"
+}
+
+# @FUNCTION: python_gen_cond_dep
+# @USAGE: <dependency> [<pattern>...]
+# @DESCRIPTION:
+# Output a list of <dependency>-ies made conditional to USE flags
+# of Python implementations which are both in PYTHON_COMPAT and match
+# any of the patterns passed as the remaining parameters.
+#
+# The patterns can be either fnmatch-style patterns (matched via bash
+# == operator against PYTHON_COMPAT values) or '-2' / '-3' to indicate
+# appropriately all enabled Python 2/3 implementations (alike
+# python_is_python3). Remember to escape or quote the fnmatch patterns
+# to prevent accidental shell filename expansion.
+#
+# In order to enforce USE constraints on the packages, verbatim
+# '${PYTHON_SINGLE_USEDEP}' and '${PYTHON_MULTI_USEDEP}' (quoted!) may
+# be placed in the dependency specification. It will get expanded within
+# the function into a proper USE dependency string.
+#
+# Example:
+# @CODE
+# PYTHON_COMPAT=( python{2_7,3_{3,4}} pypy )
+# RDEPEND="$(python_gen_cond_dep \
+#   'dev-python/unittest2[${PYTHON_MULTI_USEDEP}]' python2_7 pypy )"
+# @CODE
+#
+# It will cause the variable to look like:
+# @CODE
+# RDEPEND="python_single_target_python2_7? (
+#     dev-python/unittest2[python_targets_python2_7(-)?,...] )
+#      python_single_target_pypy? (
+#     dev-python/unittest2[python_targets_pypy(-)?,...] )"
+# @CODE
+python_gen_cond_dep() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local impl matches=()
+
+       local dep=${1}
+       shift
+
+       for impl in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do
+               if _python_impl_matches "${impl}" "${@}"; then
+                       # substitute ${PYTHON_SINGLE_USEDEP} if used
+                       # (since python_gen_usedep() will not return
+                       #  ${PYTHON_SINGLE_USEDEP}, the code is run at most 
once)
+                       if [[ ${dep} == *'${PYTHON_SINGLE_USEDEP}'* ]]; then
+                               local usedep=$(_python_gen_usedep "${@}")
+                               dep=${dep//\$\{PYTHON_SINGLE_USEDEP\}/${usedep}}
+                       fi
+                       local multi_usedep="python_targets_${impl}(-)"
+
+                       matches+=( "python_single_target_${impl}? (
+                               
${dep//\$\{PYTHON_MULTI_USEDEP\}/${multi_usedep}} )" )
+               fi
+       done
+
+       echo "${matches[@]}"
+}
+
+# @FUNCTION: python_gen_impl_dep
+# @USAGE: [<requested-use-flags> [<impl-pattern>...]]
+# @DESCRIPTION:
+# Output a dependency on Python implementations with the specified USE
+# dependency string appended, or no USE dependency string if called
+# without the argument (or with empty argument). If any implementation
+# patterns are passed, the output dependencies will be generated only
+# for the implementations matching them.
+#
+# The patterns can be either fnmatch-style patterns (matched via bash
+# == operator against PYTHON_COMPAT values) or '-2' / '-3' to indicate
+# appropriately all enabled Python 2/3 implementations (alike
+# python_is_python3). Remember to escape or quote the fnmatch patterns
+# to prevent accidental shell filename expansion.
+#
+# Use this function when you need to request different USE flags
+# on the Python interpreter depending on package's USE flags. If you
+# only need a single set of interpreter USE flags, just set
+# PYTHON_REQ_USE and use ${PYTHON_DEPS} globally.
+#
+# Example:
+# @CODE
+# PYTHON_COMPAT=( python{2_7,3_{3,4}} pypy )
+# RDEPEND="foo? ( $(python_gen_impl_dep 'xml(+)') )"
+# @CODE
+#
+# It will cause the variable to look like:
+# @CODE
+# RDEPEND="foo? (
+#   python_single_target_python2_7? (
+#     dev-lang/python:2.7[xml(+)] )
+#      python_single_target_pypy? (
+#     dev-python/pypy[xml(+)] ) )"
+# @CODE
+python_gen_impl_dep() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local impl
+       local matches=()
+
+       local PYTHON_REQ_USE=${1}
+       shift
+
+       for impl in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do
+               if _python_impl_matches "${impl}" "${@}"; then
+                       local PYTHON_PKG_DEP
+                       python_export "${impl}" PYTHON_PKG_DEP
+                       matches+=( "python_single_target_${impl}? ( 
${PYTHON_PKG_DEP} )" )
+               fi
+       done
+
+       echo "${matches[@]}"
+}
+
+# @FUNCTION: python_setup
+# @DESCRIPTION:
+# Determine what the selected Python implementation is and set
+# the Python build environment up for it.
+python_setup() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       unset EPYTHON
+
+       # support developer override
+       if [[ ${PYTHON_COMPAT_OVERRIDE} ]]; then
+               local impls=( ${PYTHON_COMPAT_OVERRIDE} )
+               [[ ${#impls[@]} -eq 1 ]] || die "PYTHON_COMPAT_OVERRIDE must 
name exactly one implementation for python-single-r2"
+
+               ewarn "WARNING: PYTHON_COMPAT_OVERRIDE in effect. The following 
Python"
+               ewarn "implementation will be used:"
+               ewarn
+               ewarn " ${PYTHON_COMPAT_OVERRIDE}"
+               ewarn
+               ewarn "Dependencies won't be satisfied, and 
PYTHON_SINGLE_TARGET flags will be ignored."
+
+               python_export "${impls[0]}" EPYTHON PYTHON
+               python_wrapper_setup
+               return
+       fi
+
+       local impl
+       for impl in "${_PYTHON_SUPPORTED_IMPLS[@]}"; do
+               if use "python_single_target_${impl}"; then
+                       if [[ ${EPYTHON} ]]; then
+                               eerror "Your PYTHON_SINGLE_TARGET setting lists 
more than a single Python"
+                               eerror "implementation. Please set it to just 
one value. If you need"
+                               eerror "to override the value for a single 
package, please use package.env"
+                               eerror "or an equivalent solution (man 5 
portage)."
+                               echo
+                               die "More than one implementation in 
PYTHON_SINGLE_TARGET."
+                       fi
+
+                       python_export "${impl}" EPYTHON PYTHON
+                       python_wrapper_setup
+               fi
+       done
+
+       if [[ ! ${EPYTHON} ]]; then
+               eerror "No Python implementation selected for the build. Please 
set"
+               eerror "the PYTHON_SINGLE_TARGET variable in your make.conf to 
one"
+               eerror "of the following values:"
+               eerror
+               eerror "${_PYTHON_SUPPORTED_IMPLS[@]}"
+               echo
+               die "No supported Python implementation in 
PYTHON_SINGLE_TARGET."
+       fi
+}
+
+# @FUNCTION: python-single-r2_pkg_setup
+# @DESCRIPTION:
+# Runs python_setup.
+python-single-r2_pkg_setup() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       [[ ${MERGE_TYPE} != binary ]] && python_setup
+}
+
+_PYTHON_SINGLE_R2=1
+fi
diff --git a/eclass/python-utils-r2.eclass b/eclass/python-utils-r2.eclass
new file mode 100644
index 000000000000..9821c043021a
--- /dev/null
+++ b/eclass/python-utils-r2.eclass
@@ -0,0 +1,1518 @@
+# Copyright 1999-2020 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+# @ECLASS: python-utils-r2.eclass
+# @MAINTAINER:
+# Python team <[email protected]>
+# @AUTHOR:
+# Author: Michał Górny <[email protected]>
+# @SUPPORTED_EAPIS: 5 6 7
+# @BLURB: Utility functions for packages with Python parts.
+# @DESCRIPTION:
+# A utility eclass providing functions to query Python implementations,
+# install Python modules and scripts.
+#
+# This eclass does not set any metadata variables nor export any phase
+# functions. It can be inherited safely.
+
+case "${EAPI:-0}" in
+       [0-4]) die "Unsupported EAPI=${EAPI:-0} (too old) for ${ECLASS}" ;;
+       [5-7]) ;;
+       *)     die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}" ;;
+esac
+
+if [[ ${_PYTHON_UTILS_R1} ]]; then
+       die 'python-r2 suite eclasses can not be combined with python-r1 suite.'
+fi
+
+if [[ ! ${_PYTHON_UTILS_R2} ]]; then
+
+[[ ${EAPI} == 5 ]] && inherit eutils multilib
+inherit toolchain-funcs
+
+# @ECLASS-VARIABLE: _PYTHON_ALL_IMPLS
+# @INTERNAL
+# @DESCRIPTION:
+# All supported Python implementations, most preferred last.
+_PYTHON_ALL_IMPLS=(
+       pypy3
+       python2_7
+       python3_6 python3_7 python3_8
+)
+readonly _PYTHON_ALL_IMPLS
+
+# @ECLASS-VARIABLE: PYTHON_COMPAT_NO_STRICT
+# @INTERNAL
+# @DESCRIPTION:
+# Set to a non-empty value in order to make eclass tolerate (ignore)
+# unknown implementations in PYTHON_COMPAT.
+#
+# This is intended to be set by the user when using ebuilds that may
+# have unknown (newer) implementations in PYTHON_COMPAT. The assumption
+# is that the ebuilds are intended to be used within multiple contexts
+# which can involve revisions of this eclass that support a different
+# set of Python implementations.
+
+# @FUNCTION: _python_impl_supported
+# @USAGE: <impl>
+# @INTERNAL
+# @DESCRIPTION:
+# Check whether the implementation <impl> (PYTHON_COMPAT-form)
+# is still supported.
+#
+# Returns 0 if the implementation is valid and supported. If it is
+# unsupported, returns 1 -- and the caller should ignore the entry.
+# If it is invalid, dies with an appopriate error messages.
+_python_impl_supported() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       [[ ${#} -eq 1 ]] || die "${FUNCNAME}: takes exactly 1 argument (impl)."
+
+       local impl=${1}
+
+       # keep in sync with _PYTHON_ALL_IMPLS!
+       # (not using that list because inline patterns shall be faster)
+       case "${impl}" in
+               python2_7|python3_[678]|pypy3)
+                       return 0
+                       ;;
+               jython2_7|pypy|pypy1_[89]|pypy2_0|python2_[56]|python3_[12345])
+                       return 1
+                       ;;
+               *)
+                       [[ ${PYTHON_COMPAT_NO_STRICT} ]] && return 1
+                       die "Invalid implementation in PYTHON_COMPAT: ${impl}"
+       esac
+}
+
+# @FUNCTION: _python_set_impls
+# @INTERNAL
+# @DESCRIPTION:
+# Check PYTHON_COMPAT for well-formedness and validity, then set
+# two global variables:
+#
+# - _PYTHON_SUPPORTED_IMPLS containing valid implementations supported
+#   by the ebuild (PYTHON_COMPAT - dead implementations),
+#
+# - and _PYTHON_UNSUPPORTED_IMPLS containing valid implementations that
+#   are not supported by the ebuild.
+#
+# Implementations in both variables are ordered using the pre-defined
+# eclass implementation ordering.
+#
+# This function must be called once in global scope by an eclass
+# utilizing PYTHON_COMPAT.
+_python_set_impls() {
+       local i
+
+       if ! declare -p PYTHON_COMPAT &>/dev/null; then
+               die 'PYTHON_COMPAT not declared.'
+       fi
+       if [[ $(declare -p PYTHON_COMPAT) != "declare -a"* ]]; then
+               die 'PYTHON_COMPAT must be an array.'
+       fi
+       for i in "${PYTHON_COMPAT[@]}"; do
+               # trigger validity checks
+               _python_impl_supported "${i}"
+       done
+
+       local supp=() unsupp=()
+
+       for i in "${_PYTHON_ALL_IMPLS[@]}"; do
+               if has "${i}" "${PYTHON_COMPAT[@]}"; then
+                       supp+=( "${i}" )
+               else
+                       unsupp+=( "${i}" )
+               fi
+       done
+
+       if [[ ! ${supp[@]} ]]; then
+               die "No supported implementation in PYTHON_COMPAT."
+       fi
+
+       if [[ ${_PYTHON_SUPPORTED_IMPLS[@]} ]]; then
+               # set once already, verify integrity
+               if [[ ${_PYTHON_SUPPORTED_IMPLS[@]} != ${supp[@]} ]]; then
+                       eerror "Supported impls (PYTHON_COMPAT) changed between 
inherits!"
+                       eerror "Before: ${_PYTHON_SUPPORTED_IMPLS[*]}"
+                       eerror "Now   : ${supp[*]}"
+                       die "_PYTHON_SUPPORTED_IMPLS integrity check failed"
+               fi
+               if [[ ${_PYTHON_UNSUPPORTED_IMPLS[@]} != ${unsupp[@]} ]]; then
+                       eerror "Unsupported impls changed between inherits!"
+                       eerror "Before: ${_PYTHON_UNSUPPORTED_IMPLS[*]}"
+                       eerror "Now   : ${unsupp[*]}"
+                       die "_PYTHON_UNSUPPORTED_IMPLS integrity check failed"
+               fi
+       else
+               _PYTHON_SUPPORTED_IMPLS=( "${supp[@]}" )
+               _PYTHON_UNSUPPORTED_IMPLS=( "${unsupp[@]}" )
+               readonly _PYTHON_SUPPORTED_IMPLS _PYTHON_UNSUPPORTED_IMPLS
+       fi
+}
+
+# @FUNCTION: _python_impl_matches
+# @USAGE: <impl> [<pattern>...]
+# @INTERNAL
+# @DESCRIPTION:
+# Check whether the specified <impl> matches at least one
+# of the patterns following it. Return 0 if it does, 1 otherwise.
+# Matches if no patterns are provided.
+#
+# <impl> can be in PYTHON_COMPAT or EPYTHON form. The patterns can be
+# either:
+# a) fnmatch-style patterns, e.g. 'python2*', 'pypy'...
+# b) '-2' to indicate all Python 2 variants (= !python_is_python3)
+# c) '-3' to indicate all Python 3 variants (= python_is_python3)
+_python_impl_matches() {
+       [[ ${#} -ge 1 ]] || die "${FUNCNAME}: takes at least 1 parameter"
+       [[ ${#} -eq 1 ]] && return 0
+
+       local impl=${1} pattern
+       shift
+
+       for pattern; do
+               if [[ ${pattern} == -2 ]]; then
+                       python_is_python3 "${impl}" || return 0
+               elif [[ ${pattern} == -3 ]]; then
+                       python_is_python3 "${impl}" && return 0
+                       return
+               # unify value style to allow lax matching
+               elif [[ ${impl/./_} == ${pattern/./_} ]]; then
+                       return 0
+               fi
+       done
+
+       return 1
+}
+
+# @ECLASS-VARIABLE: PYTHON
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# The absolute path to the current Python interpreter.
+#
+# This variable is set automatically in the following contexts:
+#
+# python-r2: Set in functions called by python_foreach_impl() or after
+# calling python_export_best().
+#
+# python-single-r2: Set after calling python-single-r2_pkg_setup().
+#
+# distutils-r2: Set within any of the python sub-phase functions.
+#
+# Example value:
+# @CODE
+# /usr/bin/python2.7
+# @CODE
+
+# @ECLASS-VARIABLE: EPYTHON
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# The executable name of the current Python interpreter.
+#
+# This variable is set automatically in the following contexts:
+#
+# python-r2: Set in functions called by python_foreach_impl() or after
+# calling python_export_best().
+#
+# python-single-r2: Set after calling python-single-r2_pkg_setup().
+#
+# distutils-r2: Set within any of the python sub-phase functions.
+#
+# Example value:
+# @CODE
+# python2.7
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_SITEDIR
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# The path to Python site-packages directory.
+#
+# Set and exported on request using python_export().
+# Requires a proper build-time dependency on the Python implementation.
+#
+# Example value:
+# @CODE
+# /usr/lib64/python2.7/site-packages
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_INCLUDEDIR
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# The path to Python include directory.
+#
+# Set and exported on request using python_export().
+# Requires a proper build-time dependency on the Python implementation.
+#
+# Example value:
+# @CODE
+# /usr/include/python2.7
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_LIBPATH
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# The path to Python library.
+#
+# Set and exported on request using python_export().
+# Valid only for CPython. Requires a proper build-time dependency
+# on the Python implementation.
+#
+# Example value:
+# @CODE
+# /usr/lib64/libpython2.7.so
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_CFLAGS
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# Proper C compiler flags for building against Python. Obtained from
+# pkg-config or python-config.
+#
+# Set and exported on request using python_export().
+# Valid only for CPython. Requires a proper build-time dependency
+# on the Python implementation and on pkg-config.
+#
+# Example value:
+# @CODE
+# -I/usr/include/python2.7
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_LIBS
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# Proper C compiler flags for linking against Python. Obtained from
+# pkg-config or python-config.
+#
+# Set and exported on request using python_export().
+# Valid only for CPython. Requires a proper build-time dependency
+# on the Python implementation and on pkg-config.
+#
+# Example value:
+# @CODE
+# -lpython2.7
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_CONFIG
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# Path to the python-config executable.
+#
+# Set and exported on request using python_export().
+# Valid only for CPython. Requires a proper build-time dependency
+# on the Python implementation and on pkg-config.
+#
+# Example value:
+# @CODE
+# /usr/bin/python2.7-config
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_PKG_DEP
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# The complete dependency on a particular Python package as a string.
+#
+# Set and exported on request using python_export().
+#
+# Example value:
+# @CODE
+# dev-lang/python:2.7[xml]
+# @CODE
+
+# @ECLASS-VARIABLE: PYTHON_SCRIPTDIR
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# The location where Python scripts must be installed for current impl.
+#
+# Set and exported on request using python_export().
+#
+# Example value:
+# @CODE
+# /usr/lib/python-exec/python2.7
+# @CODE
+
+# @FUNCTION: python_export
+# @USAGE: [<impl>] <variables>...
+# @DESCRIPTION:
+# Set and export the Python implementation-relevant variables passed
+# as parameters.
+#
+# The optional first parameter may specify the requested Python
+# implementation (either as PYTHON_TARGETS value, e.g. python2_7,
+# or an EPYTHON one, e.g. python2.7). If no implementation passed,
+# the current one will be obtained from ${EPYTHON}.
+#
+# The variables which can be exported are: PYTHON, EPYTHON,
+# PYTHON_SITEDIR. They are described more completely in the eclass
+# variable documentation.
+python_export() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local impl var
+
+       case "${1}" in
+               python*|jython*)
+                       impl=${1/_/.}
+                       shift
+                       ;;
+               pypy|pypy3)
+                       impl=${1}
+                       shift
+                       ;;
+               *)
+                       impl=${EPYTHON}
+                       if [[ -z ${impl} ]]; then
+                               die "python_export called without a python 
implementation and EPYTHON is unset"
+                       fi
+                       ;;
+       esac
+       debug-print "${FUNCNAME}: implementation: ${impl}"
+
+       for var; do
+               case "${var}" in
+                       EPYTHON)
+                               export EPYTHON=${impl}
+                               debug-print "${FUNCNAME}: EPYTHON = ${EPYTHON}"
+                               ;;
+                       PYTHON)
+                               export PYTHON=${EPREFIX}/usr/bin/${impl}
+                               debug-print "${FUNCNAME}: PYTHON = ${PYTHON}"
+                               ;;
+                       PYTHON_SITEDIR)
+                               [[ -n ${PYTHON} ]] || die "PYTHON needs to be 
set for ${var} to be exported, or requested before it"
+                               # sysconfig can't be used because:
+                               # 1) pypy doesn't give site-packages but stdlib
+                               # 2) jython gives paths with wrong case
+                               PYTHON_SITEDIR=$("${PYTHON}" -c 'import 
distutils.sysconfig; print(distutils.sysconfig.get_python_lib())') || die
+                               export PYTHON_SITEDIR
+                               debug-print "${FUNCNAME}: PYTHON_SITEDIR = 
${PYTHON_SITEDIR}"
+                               ;;
+                       PYTHON_INCLUDEDIR)
+                               [[ -n ${PYTHON} ]] || die "PYTHON needs to be 
set for ${var} to be exported, or requested before it"
+                               PYTHON_INCLUDEDIR=$("${PYTHON}" -c 'import 
distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') || die
+                               export PYTHON_INCLUDEDIR
+                               debug-print "${FUNCNAME}: PYTHON_INCLUDEDIR = 
${PYTHON_INCLUDEDIR}"
+
+                               # Jython gives a non-existing directory
+                               if [[ ! -d ${PYTHON_INCLUDEDIR} ]]; then
+                                       die "${impl} does not install any 
header files!"
+                               fi
+                               ;;
+                       PYTHON_LIBPATH)
+                               [[ -n ${PYTHON} ]] || die "PYTHON needs to be 
set for ${var} to be exported, or requested before it"
+                               PYTHON_LIBPATH=$("${PYTHON}" -c 'import 
os.path, sysconfig; print(os.path.join(sysconfig.get_config_var("LIBDIR"), 
sysconfig.get_config_var("LDLIBRARY")) if sysconfig.get_config_var("LDLIBRARY") 
else "")') || die
+                               export PYTHON_LIBPATH
+                               debug-print "${FUNCNAME}: PYTHON_LIBPATH = 
${PYTHON_LIBPATH}"
+
+                               if [[ ! ${PYTHON_LIBPATH} ]]; then
+                                       die "${impl} lacks a (usable) dynamic 
library"
+                               fi
+                               ;;
+                       PYTHON_CFLAGS)
+                               local val
+
+                               case "${impl}" in
+                                       python*)
+                                               # python-2.7, python-3.2, etc.
+                                               val=$($(tc-getPKG_CONFIG) 
--cflags ${impl/n/n-}) || die
+                                               ;;
+                                       *)
+                                               die "${impl}: obtaining ${var} 
not supported"
+                                               ;;
+                               esac
+
+                               export PYTHON_CFLAGS=${val}
+                               debug-print "${FUNCNAME}: PYTHON_CFLAGS = 
${PYTHON_CFLAGS}"
+                               ;;
+                       PYTHON_LIBS)
+                               local val
+
+                               case "${impl}" in
+                                       python*)
+                                               # python-2.7, python-3.2, etc.
+                                               val=$($(tc-getPKG_CONFIG) 
--libs ${impl/n/n-}) || die
+                                               ;;
+                                       *)
+                                               die "${impl}: obtaining ${var} 
not supported"
+                                               ;;
+                               esac
+
+                               export PYTHON_LIBS=${val}
+                               debug-print "${FUNCNAME}: PYTHON_LIBS = 
${PYTHON_LIBS}"
+                               ;;
+                       PYTHON_CONFIG)
+                               local flags val
+
+                               case "${impl}" in
+                                       python*)
+                                               [[ -n ${PYTHON} ]] || die 
"PYTHON needs to be set for ${var} to be exported, or requested before it"
+                                               flags=$("${PYTHON}" -c 'import 
sysconfig; print(sysconfig.get_config_var("ABIFLAGS") or "")') || die
+                                               val=${PYTHON}${flags}-config
+                                               ;;
+                                       *)
+                                               die "${impl}: obtaining ${var} 
not supported"
+                                               ;;
+                               esac
+
+                               export PYTHON_CONFIG=${val}
+                               debug-print "${FUNCNAME}: PYTHON_CONFIG = 
${PYTHON_CONFIG}"
+                               ;;
+                       PYTHON_PKG_DEP)
+                               local d
+                               case ${impl} in
+                                       python2.7)
+                                               
PYTHON_PKG_DEP='>=dev-lang/python-2.7.5-r2:2.7';;
+                                       python3.3)
+                                               
PYTHON_PKG_DEP='>=dev-lang/python-3.3.2-r2:3.3';;
+                                       python*)
+                                               
PYTHON_PKG_DEP="dev-lang/python:${impl#python}";;
+                                       pypy)
+                                               
PYTHON_PKG_DEP='>=dev-python/pypy-5:0=';;
+                                       pypy3)
+                                               
PYTHON_PKG_DEP='>=dev-python/pypy3-5:0=';;
+                                       jython2.7)
+                                               
PYTHON_PKG_DEP='dev-java/jython:2.7';;
+                                       *)
+                                               die "Invalid implementation: 
${impl}"
+                               esac
+
+                               # use-dep
+                               if [[ ${PYTHON_REQ_USE} ]]; then
+                                       PYTHON_PKG_DEP+=[${PYTHON_REQ_USE}]
+                               fi
+
+                               export PYTHON_PKG_DEP
+                               debug-print "${FUNCNAME}: PYTHON_PKG_DEP = 
${PYTHON_PKG_DEP}"
+                               ;;
+                       PYTHON_SCRIPTDIR)
+                               local dir
+                               export 
PYTHON_SCRIPTDIR=${EPREFIX}/usr/lib/python-exec/${impl}
+                               debug-print "${FUNCNAME}: PYTHON_SCRIPTDIR = 
${PYTHON_SCRIPTDIR}"
+                               ;;
+                       *)
+                               die "python_export: unknown variable ${var}"
+               esac
+       done
+}
+
+# @FUNCTION: python_get_sitedir
+# @USAGE: [<impl>]
+# @DESCRIPTION:
+# Obtain and print the 'site-packages' path for the given
+# implementation. If no implementation is provided, ${EPYTHON} will
+# be used.
+#
+# If you just need to have PYTHON_SITEDIR set (and exported), then it is
+# better to use python_export() directly instead.
+python_get_sitedir() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       python_export "${@}" PYTHON_SITEDIR
+       echo "${PYTHON_SITEDIR}"
+}
+
+# @FUNCTION: python_get_includedir
+# @USAGE: [<impl>]
+# @DESCRIPTION:
+# Obtain and print the include path for the given implementation. If no
+# implementation is provided, ${EPYTHON} will be used.
+#
+# If you just need to have PYTHON_INCLUDEDIR set (and exported), then it
+# is better to use python_export() directly instead.
+python_get_includedir() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       python_export "${@}" PYTHON_INCLUDEDIR
+       echo "${PYTHON_INCLUDEDIR}"
+}
+
+# @FUNCTION: python_get_library_path
+# @USAGE: [<impl>]
+# @DESCRIPTION:
+# Obtain and print the Python library path for the given implementation.
+# If no implementation is provided, ${EPYTHON} will be used.
+#
+# Please note that this function can be used with CPython only. Use
+# in another implementation will result in a fatal failure.
+python_get_library_path() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       python_export "${@}" PYTHON_LIBPATH
+       echo "${PYTHON_LIBPATH}"
+}
+
+# @FUNCTION: python_get_CFLAGS
+# @USAGE: [<impl>]
+# @DESCRIPTION:
+# Obtain and print the compiler flags for building against Python,
+# for the given implementation. If no implementation is provided,
+# ${EPYTHON} will be used.
+#
+# Please note that this function can be used with CPython only.
+# It requires Python and pkg-config installed, and therefore proper
+# build-time dependencies need be added to the ebuild.
+python_get_CFLAGS() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       python_export "${@}" PYTHON_CFLAGS
+       echo "${PYTHON_CFLAGS}"
+}
+
+# @FUNCTION: python_get_LIBS
+# @USAGE: [<impl>]
+# @DESCRIPTION:
+# Obtain and print the compiler flags for linking against Python,
+# for the given implementation. If no implementation is provided,
+# ${EPYTHON} will be used.
+#
+# Please note that this function can be used with CPython only.
+# It requires Python and pkg-config installed, and therefore proper
+# build-time dependencies need be added to the ebuild.
+python_get_LIBS() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       python_export "${@}" PYTHON_LIBS
+       echo "${PYTHON_LIBS}"
+}
+
+# @FUNCTION: python_get_PYTHON_CONFIG
+# @USAGE: [<impl>]
+# @DESCRIPTION:
+# Obtain and print the PYTHON_CONFIG location for the given
+# implementation. If no implementation is provided, ${EPYTHON} will be
+# used.
+#
+# Please note that this function can be used with CPython only.
+# It requires Python installed, and therefore proper build-time
+# dependencies need be added to the ebuild.
+python_get_PYTHON_CONFIG() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       python_export "${@}" PYTHON_CONFIG
+       echo "${PYTHON_CONFIG}"
+}
+
+# @FUNCTION: python_get_scriptdir
+# @USAGE: [<impl>]
+# @DESCRIPTION:
+# Obtain and print the script install path for the given
+# implementation. If no implementation is provided, ${EPYTHON} will
+# be used.
+python_get_scriptdir() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       python_export "${@}" PYTHON_SCRIPTDIR
+       echo "${PYTHON_SCRIPTDIR}"
+}
+
+# @FUNCTION: _python_ln_rel
+# @USAGE: <from> <to>
+# @INTERNAL
+# @DESCRIPTION:
+# Create a relative symlink.
+_python_ln_rel() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local target=${1}
+       local symname=${2}
+
+       local tgpath=${target%/*}/
+       local sympath=${symname%/*}/
+       local rel_target=
+
+       while [[ ${sympath} ]]; do
+               local tgseg= symseg=
+
+               while [[ ! ${tgseg} && ${tgpath} ]]; do
+                       tgseg=${tgpath%%/*}
+                       tgpath=${tgpath#${tgseg}/}
+               done
+
+               while [[ ! ${symseg} && ${sympath} ]]; do
+                       symseg=${sympath%%/*}
+                       sympath=${sympath#${symseg}/}
+               done
+
+               if [[ ${tgseg} != ${symseg} ]]; then
+                       rel_target=../${rel_target}${tgseg:+${tgseg}/}
+               fi
+       done
+       rel_target+=${tgpath}${target##*/}
+
+       debug-print "${FUNCNAME}: ${symname} -> ${target}"
+       debug-print "${FUNCNAME}: rel_target = ${rel_target}"
+
+       ln -fs "${rel_target}" "${symname}"
+}
+
+# @FUNCTION: python_optimize
+# @USAGE: [<directory>...]
+# @DESCRIPTION:
+# Compile and optimize Python modules in specified directories (absolute
+# paths). If no directories are provided, the default system paths
+# are used (prepended with ${D}).
+python_optimize() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       if [[ ${EBUILD_PHASE} == pre* || ${EBUILD_PHASE} == post* ]]; then
+               eerror "The new Python eclasses expect the compiled Python 
files to"
+               eerror "be controlled by the Package Manager. For this reason,"
+               eerror "the python_optimize function can be used only during 
src_* phases"
+               eerror "(src_install most commonly) and not during pkg_* 
phases."
+               echo
+               die "python_optimize is not to be used in pre/post* phases"
+       fi
+
+       [[ ${EPYTHON} ]] || die 'No Python implementation set (EPYTHON is 
null).'
+
+       local PYTHON=${PYTHON}
+       [[ ${PYTHON} ]] || python_export PYTHON
+
+       # default to sys.path
+       if [[ ${#} -eq 0 ]]; then
+               local f
+               while IFS= read -r -d '' f; do
+                       # 1) accept only absolute paths
+                       #    (i.e. skip '', '.' or anything like that)
+                       # 2) skip paths which do not exist
+                       #    (python2.6 complains about them verbosely)
+
+                       if [[ ${f} == /* && -d ${D%/}${f} ]]; then
+                               set -- "${D%/}${f}" "${@}"
+                       fi
+               done < <("${PYTHON}" -c 'import sys; print("".join(x + "\0" for 
x in sys.path))' || die)
+
+               debug-print "${FUNCNAME}: using sys.path: ${*/%/;}"
+       fi
+
+       local d
+       for d; do
+               # make sure to get a nice path without //
+               local instpath=${d#${D%/}}
+               instpath=/${instpath##/}
+
+               case "${EPYTHON}" in
+                       python2.7|python3.[34])
+                               "${PYTHON}" -m compileall -q -f -d 
"${instpath}" "${d}"
+                               "${PYTHON}" -OO -m compileall -q -f -d 
"${instpath}" "${d}"
+                               ;;
+                       python*|pypy3)
+                               # both levels of optimization are separate 
since 3.5
+                               "${PYTHON}" -m compileall -q -f -d 
"${instpath}" "${d}"
+                               "${PYTHON}" -O -m compileall -q -f -d 
"${instpath}" "${d}"
+                               "${PYTHON}" -OO -m compileall -q -f -d 
"${instpath}" "${d}"
+                               ;;
+                       *)
+                               "${PYTHON}" -m compileall -q -f -d 
"${instpath}" "${d}"
+                               ;;
+               esac
+       done
+}
+
+# @FUNCTION: python_scriptinto
+# @USAGE: <new-path>
+# @DESCRIPTION:
+# Set the directory to which files passed to python_doexe(),
+# python_doscript(), python_newexe() and python_newscript()
+# are going to be installed. The new value needs to be relative
+# to the installation root (${ED}).
+#
+# If not set explicitly, the directory defaults to /usr/bin.
+#
+# Example:
+# @CODE
+# src_install() {
+#   python_scriptinto /usr/sbin
+#   python_foreach_impl python_doscript foo
+# }
+# @CODE
+python_scriptinto() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       python_scriptroot=${1}
+}
+
+# @FUNCTION: python_doexe
+# @USAGE: <files>...
+# @DESCRIPTION:
+# Install the given executables into the executable install directory,
+# for the current Python implementation (${EPYTHON}).
+#
+# The executable will be wrapped properly for the Python implementation,
+# though no shebang mangling will be performed.
+python_doexe() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local f
+       for f; do
+               python_newexe "${f}" "${f##*/}"
+       done
+}
+
+# @FUNCTION: python_newexe
+# @USAGE: <path> <new-name>
+# @DESCRIPTION:
+# Install the given executable into the executable install directory,
+# for the current Python implementation (${EPYTHON}).
+#
+# The executable will be wrapped properly for the Python implementation,
+# though no shebang mangling will be performed. It will be renamed
+# to <new-name>.
+python_newexe() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       [[ ${EPYTHON} ]] || die 'No Python implementation set (EPYTHON is 
null).'
+       [[ ${#} -eq 2 ]] || die "Usage: ${FUNCNAME} <path> <new-name>"
+
+       local wrapd=${python_scriptroot:-/usr/bin}
+
+       local f=${1}
+       local newfn=${2}
+
+       local PYTHON_SCRIPTDIR d
+       python_export PYTHON_SCRIPTDIR
+       d=${PYTHON_SCRIPTDIR#${EPREFIX}}
+
+       (
+               dodir "${wrapd}"
+               exeopts -m 0755
+               exeinto "${d}"
+               newexe "${f}" "${newfn}" || return ${?}
+       )
+
+       # install the wrapper
+       _python_ln_rel "${ED%/}"/usr/lib/python-exec/python-exec2 \
+               "${ED%/}/${wrapd}/${newfn}" || die
+
+       # don't use this at home, just call python_doscript() instead
+       if [[ ${_PYTHON_REWRITE_SHEBANG} ]]; then
+               python_fix_shebang -q "${ED%/}/${d}/${newfn}"
+       fi
+}
+
+# @FUNCTION: python_doscript
+# @USAGE: <files>...
+# @DESCRIPTION:
+# Install the given scripts into the executable install directory,
+# for the current Python implementation (${EPYTHON}).
+#
+# All specified files must start with a 'python' shebang. The shebang
+# will be converted, and the files will be wrapped properly
+# for the Python implementation.
+#
+# Example:
+# @CODE
+# src_install() {
+#   python_foreach_impl python_doscript ${PN}
+# }
+# @CODE
+python_doscript() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local _PYTHON_REWRITE_SHEBANG=1
+       python_doexe "${@}"
+}
+
+# @FUNCTION: python_newscript
+# @USAGE: <path> <new-name>
+# @DESCRIPTION:
+# Install the given script into the executable install directory
+# for the current Python implementation (${EPYTHON}), and name it
+# <new-name>.
+#
+# The file must start with a 'python' shebang. The shebang will be
+# converted, and the file will be wrapped properly for the Python
+# implementation. It will be renamed to <new-name>.
+#
+# Example:
+# @CODE
+# src_install() {
+#   python_foreach_impl python_newscript foo.py foo
+# }
+# @CODE
+python_newscript() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local _PYTHON_REWRITE_SHEBANG=1
+       python_newexe "${@}"
+}
+
+# @FUNCTION: python_moduleinto
+# @USAGE: <new-path>
+# @DESCRIPTION:
+# Set the Python module install directory for python_domodule().
+# The <new-path> can either be an absolute target system path (in which
+# case it needs to start with a slash, and ${ED} will be prepended to
+# it) or relative to the implementation's site-packages directory
+# (then it must not start with a slash). The relative path can be
+# specified either using the Python package notation (separated by dots)
+# or the directory notation (using slashes).
+#
+# When not set explicitly, the modules are installed to the top
+# site-packages directory.
+#
+# In the relative case, the exact path is determined directly
+# by each python_doscript/python_newscript function. Therefore,
+# python_moduleinto can be safely called before establishing the Python
+# interpreter and/or a single call can be used to set the path correctly
+# for multiple implementations, as can be seen in the following example.
+#
+# Example:
+# @CODE
+# src_install() {
+#   python_moduleinto bar
+#   # installs ${PYTHON_SITEDIR}/bar/baz.py
+#   python_foreach_impl python_domodule baz.py
+# }
+# @CODE
+python_moduleinto() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       python_moduleroot=${1}
+}
+
+# @FUNCTION: python_domodule
+# @USAGE: <files>...
+# @DESCRIPTION:
+# Install the given modules (or packages) into the current Python module
+# installation directory. The list can mention both modules (files)
+# and packages (directories). All listed files will be installed
+# for all enabled implementations, and compiled afterwards.
+#
+# Example:
+# @CODE
+# src_install() {
+#   # (${PN} being a directory)
+#   python_foreach_impl python_domodule ${PN}
+# }
+# @CODE
+python_domodule() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       [[ ${EPYTHON} ]] || die 'No Python implementation set (EPYTHON is 
null).'
+
+       local d
+       if [[ ${python_moduleroot} == /* ]]; then
+               # absolute path
+               d=${python_moduleroot}
+       else
+               # relative to site-packages
+               local PYTHON_SITEDIR=${PYTHON_SITEDIR}
+               [[ ${PYTHON_SITEDIR} ]] || python_export PYTHON_SITEDIR
+
+               d=${PYTHON_SITEDIR#${EPREFIX}}/${python_moduleroot//.//}
+       fi
+
+       (
+               insopts -m 0644
+               insinto "${d}"
+               doins -r "${@}" || return ${?}
+       )
+
+       python_optimize "${ED%/}/${d}"
+}
+
+# @FUNCTION: python_doheader
+# @USAGE: <files>...
+# @DESCRIPTION:
+# Install the given headers into the implementation-specific include
+# directory. This function is unconditionally recursive, i.e. you can
+# pass directories instead of files.
+#
+# Example:
+# @CODE
+# src_install() {
+#   python_foreach_impl python_doheader foo.h bar.h
+# }
+# @CODE
+python_doheader() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       [[ ${EPYTHON} ]] || die 'No Python implementation set (EPYTHON is 
null).'
+
+       local d PYTHON_INCLUDEDIR=${PYTHON_INCLUDEDIR}
+       [[ ${PYTHON_INCLUDEDIR} ]] || python_export PYTHON_INCLUDEDIR
+
+       d=${PYTHON_INCLUDEDIR#${EPREFIX}}
+
+       (
+               insopts -m 0644
+               insinto "${d}"
+               doins -r "${@}" || return ${?}
+       )
+}
+
+# @FUNCTION: python_wrapper_setup
+# @USAGE: [<path> [<impl>]]
+# @DESCRIPTION:
+# Create proper 'python' executable and pkg-config wrappers
+# (if available) in the directory named by <path>. Set up PATH
+# and PKG_CONFIG_PATH appropriately. <path> defaults to ${T}/${EPYTHON}.
+#
+# The wrappers will be created for implementation named by <impl>,
+# or for one named by ${EPYTHON} if no <impl> passed.
+#
+# If the named directory contains a python symlink already, it will
+# be assumed to contain proper wrappers already and only environment
+# setup will be done. If wrapper update is requested, the directory
+# shall be removed first.
+python_wrapper_setup() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       local workdir=${1:-${T}/${EPYTHON}}
+       local impl=${2:-${EPYTHON}}
+
+       [[ ${workdir} ]] || die "${FUNCNAME}: no workdir specified."
+       [[ ${impl} ]] || die "${FUNCNAME}: no impl nor EPYTHON specified."
+
+       if [[ ! -x ${workdir}/bin/python ]]; then
+               _python_check_dead_variables
+
+               mkdir -p "${workdir}"/{bin,pkgconfig} || die
+
+               # Clean up, in case we were supposed to do a cheap update.
+               rm -f "${workdir}"/bin/python{,2,3}{,-config} || die
+               rm -f "${workdir}"/bin/2to3 || die
+               rm -f "${workdir}"/pkgconfig/python{,2,3}.pc || die
+
+               local EPYTHON PYTHON
+               python_export "${impl}" EPYTHON PYTHON
+
+               local pyver pyother
+               if python_is_python3; then
+                       pyver=3
+                       pyother=2
+               else
+                       pyver=2
+                       pyother=3
+               fi
+
+               # Python interpreter
+               # note: we don't use symlinks because python likes to do some
+               # symlink reading magic that breaks stuff
+               # https://bugs.gentoo.org/show_bug.cgi?id=555752
+               cat > "${workdir}/bin/python" <<-_EOF_ || die
+                       #!/bin/sh
+                       exec "${PYTHON}" "\${@}"
+               _EOF_
+               cp "${workdir}/bin/python" "${workdir}/bin/python${pyver}" || 
die
+               chmod +x "${workdir}/bin/python" 
"${workdir}/bin/python${pyver}" || die
+
+               local nonsupp=( "python${pyother}" "python${pyother}-config" )
+
+               # CPython-specific
+               if [[ ${EPYTHON} == python* ]]; then
+                       cat > "${workdir}/bin/python-config" <<-_EOF_ || die
+                               #!/bin/sh
+                               exec "${PYTHON}-config" "\${@}"
+                       _EOF_
+                       cp "${workdir}/bin/python-config" \
+                               "${workdir}/bin/python${pyver}-config" || die
+                       chmod +x "${workdir}/bin/python-config" \
+                               "${workdir}/bin/python${pyver}-config" || die
+
+                       # Python 2.6+.
+                       ln -s "${PYTHON/python/2to3-}" "${workdir}"/bin/2to3 || 
die
+
+                       # Python 2.7+.
+                       ln -s 
"${EPREFIX}"/usr/$(get_libdir)/pkgconfig/${EPYTHON/n/n-}.pc \
+                               "${workdir}"/pkgconfig/python.pc || die
+                       ln -s python.pc 
"${workdir}"/pkgconfig/python${pyver}.pc || die
+               else
+                       nonsupp+=( 2to3 python-config "python${pyver}-config" )
+               fi
+
+               local x
+               for x in "${nonsupp[@]}"; do
+                       cat >"${workdir}"/bin/${x} <<-_EOF_ || die
+                               #!/bin/sh
+                               echo "${ECLASS}: ${FUNCNAME}: ${x} is not 
supported by ${EPYTHON} (PYTHON_COMPAT)" >&2
+                               exit 127
+                       _EOF_
+                       chmod +x "${workdir}"/bin/${x} || die
+               done
+       fi
+
+       # Now, set the environment.
+       # But note that ${workdir} may be shared with something else,
+       # and thus already on top of PATH.
+       if [[ ${PATH##:*} != ${workdir}/bin ]]; then
+               PATH=${workdir}/bin${PATH:+:${PATH}}
+       fi
+       if [[ ${PKG_CONFIG_PATH##:*} != ${workdir}/pkgconfig ]]; then
+               
PKG_CONFIG_PATH=${workdir}/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}
+       fi
+       export PATH PKG_CONFIG_PATH
+}
+
+# @FUNCTION: python_is_python3
+# @USAGE: [<impl>]
+# @DESCRIPTION:
+# Check whether <impl> (or ${EPYTHON}) is a Python3k variant
+# (i.e. uses syntax and stdlib of Python 3.*).
+#
+# Returns 0 (true) if it is, 1 (false) otherwise.
+python_is_python3() {
+       local impl=${1:-${EPYTHON}}
+       [[ ${impl} ]] || die "python_is_python3: no impl nor EPYTHON"
+
+       [[ ${impl} == python3* || ${impl} == pypy3 ]]
+}
+
+# @FUNCTION: python_is_installed
+# @USAGE: [<impl>]
+# @DESCRIPTION:
+# Check whether the interpreter for <impl> (or ${EPYTHON}) is installed.
+# Uses has_version with a proper dependency string.
+#
+# Returns 0 (true) if it is, 1 (false) otherwise.
+python_is_installed() {
+       local impl=${1:-${EPYTHON}}
+       [[ ${impl} ]] || die "${FUNCNAME}: no impl nor EPYTHON"
+       local hasv_args=()
+
+       case ${EAPI} in
+               5|6)
+                       hasv_args+=( --host-root )
+                       ;;
+               *)
+                       hasv_args+=( -b )
+                       ;;
+       esac
+
+       case "${impl}" in
+               pypy|pypy3)
+                       local append=
+                       if [[ ${PYTHON_REQ_USE} ]]; then
+                               append=[${PYTHON_REQ_USE}]
+                       fi
+
+                       # be happy with just the interpeter, no need for the 
virtual
+                       has_version "${hasv_args[@]}" 
"dev-python/${impl}${append}" \
+                               || has_version "${hasv_args[@]}" 
"dev-python/${impl}-bin${append}"
+                       ;;
+               *)
+                       local PYTHON_PKG_DEP
+                       python_export "${impl}" PYTHON_PKG_DEP
+                       has_version "${hasv_args[@]}" "${PYTHON_PKG_DEP}"
+                       ;;
+       esac
+}
+
+# @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.
+#
+# 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.
+#
+# 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.
+#
+# --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} "${@}"
+
+       [[ ${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
+
+               [[ -d ${path} ]] && is_recursive=1
+
+               while IFS= read -r -d '' f; do
+                       local shebang i
+                       local error= from=
+
+                       # note: we can't ||die here since read will fail if file
+                       # has no newline characters
+                       IFS= read -r shebang <"${f}"
+
+                       # First, check if it's shebang at all...
+                       if [[ ${shebang} == '#!'* ]]; then
+                               local split_shebang=()
+                               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}"
+
+                                                       # 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|*pypy3|*jython[23].[0123456789])
+                                                       # Explicit mismatch.
+                                                       if [[ ! ${force} ]]; 
then
+                                                               error=1
+                                                       else
+                                                               case "${i}" in
+                                                                       
*python[23].[0123456789])
+                                                                               
from="python[23].[0123456789]";;
+                                                                       *pypy)
+                                                                               
from="pypy";;
+                                                                       *pypy3)
+                                                                               
from="pypy3";;
+                                                                       
*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}"
+                               eerror "  requested impl: ${EPYTHON}"
+                               die "${FUNCNAME}: conversion of incompatible 
shebang requested"
+                       fi
+               done < <(find -H "${path}" -type f -print0 || die)
+
+               if [[ ! ${any_fixed} ]]; then
+                       local cmd=eerror
+                       [[ ${EAPI} == 5 ]] && cmd=eqawarn
+
+                       "${cmd}" "QA warning: ${FUNCNAME}, ${path#${D%/}} did 
not match any fixable files."
+                       if [[ ${any_correct} ]]; then
+                               "${cmd}" "All files have ${EPYTHON} shebang 
already."
+                       else
+                               "${cmd}" "There are no Python files in 
specified directory."
+                       fi
+
+                       [[ ${cmd} == eerror ]] && die "${FUNCNAME} did not 
match any fixable files (QA warning fatal in EAPI ${EAPI})"
+               fi
+       done
+}
+
+# @FUNCTION: _python_check_locale_sanity
+# @USAGE: <locale>
+# @RETURN: 0 if sane, 1 otherwise
+# @DESCRIPTION:
+# Check whether the specified locale sanely maps between lowercase
+# and uppercase ASCII characters.
+_python_check_locale_sanity() {
+       local -x LC_ALL=${1}
+       local IFS=
+
+       local lc=( {a..z} )
+       local uc=( {A..Z} )
+       local input="${lc[*]}${uc[*]}"
+
+       local output=$(tr '[:lower:][:upper:]' '[:upper:][:lower:]' 
<<<"${input}")
+       [[ ${output} == "${uc[*]}${lc[*]}" ]]
+}
+
+# @FUNCTION: python_export_utf8_locale
+# @RETURN: 0 on success, 1 on failure.
+# @DESCRIPTION:
+# Attempts to export a usable UTF-8 locale in the LC_CTYPE variable. Does
+# nothing if LC_ALL is defined, or if the current locale uses a UTF-8 charmap.
+# This may be used to work around the quirky open() behavior of python3.
+python_export_utf8_locale() {
+       debug-print-function ${FUNCNAME} "${@}"
+
+       # If the locale program isn't available, just return.
+       type locale >/dev/null || return 0
+
+       if [[ $(locale charmap) != UTF-8 ]]; then
+               # Try English first, then everything else.
+               local lang locales="C.UTF-8 en_US.UTF-8 en_GB.UTF-8 $(locale 
-a)"
+
+               for lang in ${locales}; do
+                       if [[ $(LC_ALL=${lang} locale charmap 2>/dev/null) == 
UTF-8 ]]; then
+                               if _python_check_locale_sanity "${lang}"; then
+                                       export LC_CTYPE=${lang}
+                                       if [[ -n ${LC_ALL} ]]; then
+                                               export LC_NUMERIC=${LC_ALL}
+                                               export LC_TIME=${LC_ALL}
+                                               export LC_COLLATE=${LC_ALL}
+                                               export LC_MONETARY=${LC_ALL}
+                                               export LC_MESSAGES=${LC_ALL}
+                                               export LC_PAPER=${LC_ALL}
+                                               export LC_NAME=${LC_ALL}
+                                               export LC_ADDRESS=${LC_ALL}
+                                               export LC_TELEPHONE=${LC_ALL}
+                                               export LC_MEASUREMENT=${LC_ALL}
+                                               export 
LC_IDENTIFICATION=${LC_ALL}
+                                               export LC_ALL=
+                                       fi
+                                       return 0
+                               fi
+                       fi
+               done
+
+               ewarn "Could not find a UTF-8 locale. This may trigger build 
failures in"
+               ewarn "some python packages. Please ensure that a UTF-8 locale 
is listed in"
+               ewarn "/etc/locale.gen and run locale-gen."
+               return 1
+       fi
+
+       return 0
+}
+
+# @FUNCTION: build_sphinx
+# @USAGE: <directory>
+# @DESCRIPTION:
+# Build HTML documentation using dev-python/sphinx in the specified
+# <directory>.  Takes care of disabling Intersphinx and appending
+# to HTML_DOCS.
+#
+# If <directory> is relative to the current directory, care needs
+# to be taken to run einstalldocs from the same directory
+# (usually ${S}).
+build_sphinx() {
+       debug-print-function ${FUNCNAME} "${@}"
+       [[ ${#} -eq 1 ]] || die "${FUNCNAME} takes 1 arg: <directory>"
+
+       local dir=${1}
+
+       sed -i -e 's:^intersphinx_mapping:disabled_&:' \
+               "${dir}"/conf.py || die
+       # not all packages include the Makefile in pypi tarball
+       sphinx-build -b html -d "${dir}"/_build/doctrees "${dir}" \
+               "${dir}"/_build/html || die
+
+       HTML_DOCS+=( "${dir}/_build/html/." )
+}
+
+# -- python.eclass functions --
+
+_python_check_dead_variables() {
+       local v
+
+       for v in PYTHON_DEPEND PYTHON_USE_WITH{,_OR,_OPT} 
{RESTRICT,SUPPORT}_PYTHON_ABIS
+       do
+               if [[ ${!v} ]]; then
+                       die "${v} is invalid for python-r2 suite, please take a 
look @ 
https://wiki.gentoo.org/wiki/Project:Python/Python.eclass_conversion#Ebuild_head";
+               fi
+       done
+
+       for v in PYTHON_{CPPFLAGS,CFLAGS,CXXFLAGS,LDFLAGS}
+       do
+               if [[ ${!v} ]]; then
+                       die "${v} is invalid for python-r2 suite, please take a 
look @ 
https://wiki.gentoo.org/wiki/Project:Python/Python.eclass_conversion#PYTHON_CFLAGS";
+               fi
+       done
+
+       for v in PYTHON_TESTS_RESTRICTED_ABIS PYTHON_EXPORT_PHASE_FUNCTIONS \
+               PYTHON_VERSIONED_{SCRIPTS,EXECUTABLES} 
PYTHON_NONVERSIONED_EXECUTABLES
+       do
+               if [[ ${!v} ]]; then
+                       die "${v} is invalid for python-r2 suite"
+               fi
+       done
+
+       for v in DISTUTILS_USE_SEPARATE_SOURCE_DIRECTORIES 
DISTUTILS_SETUP_FILES \
+               DISTUTILS_GLOBAL_OPTIONS DISTUTILS_SRC_TEST PYTHON_MODNAME
+       do
+               if [[ ${!v} ]]; then
+                       die "${v} is invalid for distutils-r2, please take a 
look @ 
https://wiki.gentoo.org/wiki/Project:Python/Python.eclass_conversion#${v}";
+               fi
+       done
+
+       if [[ ${DISTUTILS_DISABLE_TEST_DEPENDENCY} ]]; then
+               die "${v} is invalid for distutils-r2, please take a look @ 
https://wiki.gentoo.org/wiki/Project:Python/Python.eclass_conversion#DISTUTILS_SRC_TEST";
+       fi
+
+       # python.eclass::progress
+       for v in PYTHON_BDEPEND PYTHON_MULTIPLE_ABIS PYTHON_ABI_TYPE \
+               PYTHON_RESTRICTED_ABIS PYTHON_TESTS_FAILURES_TOLERANT_ABIS \
+               PYTHON_CFFI_MODULES_GENERATION_COMMANDS
+       do
+               if [[ ${!v} ]]; then
+                       die "${v} is invalid for python-r2 suite"
+               fi
+       done
+}
+
+python_pkg_setup() {
+       die "${FUNCNAME}() is invalid for python-r2 suite, please take a look @ 
https://wiki.gentoo.org/wiki/Project:Python/Python.eclass_conversion#pkg_setup";
+}
+
+python_convert_shebangs() {
+       die "${FUNCNAME}() is invalid for python-r2 suite, please take a look @ 
https://wiki.gentoo.org/wiki/Project:Python/Python.eclass_conversion#python_convert_shebangs";
+}
+
+python_clean_py-compile_files() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_clean_installation_image() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_execute_function() {
+       die "${FUNCNAME}() is invalid for python-r2 suite, please take a look @ 
https://wiki.gentoo.org/wiki/Project:Python/Python.eclass_conversion#python_execute_function";
+}
+
+python_generate_wrapper_scripts() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_merge_intermediate_installation_images() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_set_active_version() {
+       die "${FUNCNAME}() is invalid for python-r2 suite, please take a look @ 
https://wiki.gentoo.org/wiki/Project:Python/Python.eclass_conversion#pkg_setup";
+}
+
+python_need_rebuild() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+PYTHON() {
+       die "${FUNCNAME}() is invalid for python-r2 suite, please take a look @ 
https://wiki.gentoo.org/wiki/Project:Python/Python.eclass_conversion#.24.28PYTHON.29.2C_.24.7BEPYTHON.7D";
+}
+
+python_get_implementation() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_get_implementational_package() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_get_libdir() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_get_library() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_get_version() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_get_implementation_and_version() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_execute_nosetests() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_execute_py.test() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_execute_trial() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_enable_pyc() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_disable_pyc() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_mod_optimize() {
+       die "${FUNCNAME}() is invalid for python-r2 suite, please take a look @ 
https://wiki.gentoo.org/wiki/Project:Python/Python.eclass_conversion#Python_byte-code_compilation";
+}
+
+python_mod_cleanup() {
+       die "${FUNCNAME}() is invalid for python-r2 suite, please take a look @ 
https://wiki.gentoo.org/wiki/Project:Python/Python.eclass_conversion#Python_byte-code_compilation";
+}
+
+# python.eclass::progress
+
+python_abi_depend() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_install_executables() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_get_extension_module_suffix() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_byte-compile_modules() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_clean_byte-compiled_modules() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+python_generate_cffi_modules() {
+       die "${FUNCNAME}() is invalid for python-r2 suite"
+}
+
+_PYTHON_UTILS_R2=1
+fi
diff --git a/eclass/tests/distutils-r2.sh b/eclass/tests/distutils-r2.sh
new file mode 100755
index 000000000000..15f59bcd7d48
--- /dev/null
+++ b/eclass/tests/distutils-r2.sh
@@ -0,0 +1,98 @@
+#!/bin/bash
+# Copyright 1999-2015 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+EAPI=5
+PYTHON_COMPAT=( python2_7 )
+source tests-common.sh
+
+test-phase_name_free() {
+       local ph=${1}
+
+       if declare -f "${ph}"; then
+               die "${ph} function declared while name reserved for phase!"
+       fi
+       if declare -f "${ph}_all"; then
+               die "${ph}_all function declared while name reserved for phase!"
+       fi
+}
+
+test-distutils_enable_tests() {
+       local runner=${1}
+       local exp_IUSE=${2}
+       local exp_RESTRICT=${3}
+       local exp_DEPEND=${4}
+
+       local IUSE=${IUSE}
+       local RESTRICT=${RESTRICT}
+       local DEPEND=${DEPEND}
+
+       tbegin "${runner}"
+
+       distutils_enable_tests "${runner}"
+
+       local ret var
+       for var in IUSE RESTRICT DEPEND; do
+               local exp_var=exp_${var}
+               if [[ ${!var} != "${!exp_var}" ]]; then
+                       eindent
+                       eerror "${var} expected: ${!exp_var}"
+                       eerror "${var}   actual: ${!var}"
+                       eoutdent
+                       ret=1
+                       tret=1
+               fi
+       done
+
+       tend ${ret}
+}
+
+DISTUTILS_USE_SETUPTOOLS=no
+inherit distutils-r2
+
+tbegin "sane function names"
+
+test-phase_name_free python_prepare
+test-phase_name_free python_configure
+test-phase_name_free python_compile
+test-phase_name_free python_test
+test-phase_name_free python_install
+
+tend
+
+einfo distutils_enable_tests
+eindent
+BASE_IUSE="python_targets_python2_7"
+BASE_DEPS="python_targets_python2_7? ( >=dev-lang/python-2.7.5-r2:2.7 ) 
>=dev-lang/python-exec-2:=[python_targets_python2_7(-)?,-python_single_target_python2_7(-)]"
+TEST_RESTRICT=" !test? ( test )"
+
+einfo "empty RDEPEND"
+eindent
+RDEPEND=""
+test-distutils_enable_tests pytest \
+       "${BASE_IUSE} test" "${TEST_RESTRICT}" "${BASE_DEPS} test? (  
dev-python/pytest[${PYTHON_USEDEP}] )"
+test-distutils_enable_tests nose \
+       "${BASE_IUSE} test" "${TEST_RESTRICT}" "${BASE_DEPS} test? (  
dev-python/nose[${PYTHON_USEDEP}] )"
+test-distutils_enable_tests unittest \
+       "${BASE_IUSE}" "" "${BASE_DEPS}"
+test-distutils_enable_tests setup.py \
+       "${BASE_IUSE}" "" "${BASE_DEPS}"
+eoutdent
+
+einfo "non-empty RDEPEND"
+eindent
+BASE_RDEPEND="dev-python/foo[${PYTHON_USEDEP}]"
+RDEPEND=${BASE_RDEPEND}
+test-distutils_enable_tests pytest \
+       "${BASE_IUSE} test" "${TEST_RESTRICT}" "${BASE_DEPS} test? ( 
${BASE_RDEPEND} dev-python/pytest[${PYTHON_USEDEP}] )"
+test-distutils_enable_tests nose \
+       "${BASE_IUSE} test" "${TEST_RESTRICT}" "${BASE_DEPS} test? ( 
${BASE_RDEPEND} dev-python/nose[${PYTHON_USEDEP}] )"
+test-distutils_enable_tests unittest \
+       "${BASE_IUSE} test" "${TEST_RESTRICT}" "${BASE_DEPS} test? ( 
${BASE_RDEPEND} )"
+test-distutils_enable_tests setup.py \
+       "${BASE_IUSE} test" "${TEST_RESTRICT}" "${BASE_DEPS} test? ( 
${BASE_RDEPEND} )"
+eoutdent
+
+eoutdent
+
+texit
diff --git a/eclass/tests/python-utils-r2.sh b/eclass/tests/python-utils-r2.sh
new file mode 100755
index 000000000000..64490cb0d24a
--- /dev/null
+++ b/eclass/tests/python-utils-r2.sh
@@ -0,0 +1,237 @@
+#!/bin/bash
+# Copyright 1999-2019 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+EAPI=7
+source tests-common.sh
+
+test_var() {
+       local var=${1}
+       local impl=${2}
+       local expect=${3}
+
+       tbegin "${var} for ${impl}"
+
+       local ${var}
+       python_export ${impl} PYTHON ${var}
+       [[ ${!var} == ${expect} ]] || eerror "(${impl}: ${var}: ${!var} != 
${expect}"
+
+       tend ${?}
+}
+
+test_is() {
+       local func=${1}
+       local expect=${2}
+
+       tbegin "${func} (expecting: ${expect})"
+
+       ${func}
+       [[ ${?} == ${expect} ]]
+
+       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-r2
+
+test_var EPYTHON python2_7 python2.7
+test_var PYTHON python2_7 /usr/bin/python2.7
+if [[ -x /usr/bin/python2.7 ]]; then
+       test_var PYTHON_SITEDIR python2_7 "/usr/lib*/python2.7/site-packages"
+       test_var PYTHON_INCLUDEDIR python2_7 /usr/include/python2.7
+       test_var PYTHON_LIBPATH python2_7 "/usr/lib*/libpython2.7$(get_libname)"
+       test_var PYTHON_CONFIG python2_7 /usr/bin/python2.7-config
+       test_var PYTHON_CFLAGS python2_7 "*-I/usr/include/python2.7*"
+       test_var PYTHON_LIBS python2_7 "*-lpython2.7*"
+fi
+test_var PYTHON_PKG_DEP python2_7 '*dev-lang/python*:2.7'
+test_var PYTHON_SCRIPTDIR python2_7 /usr/lib/python-exec/python2.7
+
+test_var EPYTHON python3_6 python3.6
+test_var PYTHON python3_6 /usr/bin/python3.6
+if [[ -x /usr/bin/python3.6 ]]; then
+       abiflags=$(/usr/bin/python3.6 -c 'import sysconfig; 
print(sysconfig.get_config_var("ABIFLAGS"))')
+       test_var PYTHON_SITEDIR python3_6 "/usr/lib*/python3.6/site-packages"
+       test_var PYTHON_INCLUDEDIR python3_6 "/usr/include/python3.6${abiflags}"
+       test_var PYTHON_LIBPATH python3_6 
"/usr/lib*/libpython3.6${abiflags}$(get_libname)"
+       test_var PYTHON_CONFIG python3_6 "/usr/bin/python3.6${abiflags}-config"
+       test_var PYTHON_CFLAGS python3_6 "*-I/usr/include/python3.6*"
+       test_var PYTHON_LIBS python3_6 "*-lpython3.6*"
+fi
+test_var PYTHON_PKG_DEP python3_6 '*dev-lang/python*:3.6'
+test_var PYTHON_SCRIPTDIR python3_6 /usr/lib/python-exec/python3.6
+
+test_var EPYTHON python3_7 python3.7
+test_var PYTHON python3_7 /usr/bin/python3.7
+if [[ -x /usr/bin/python3.7 ]]; then
+       abiflags=$(/usr/bin/python3.7 -c 'import sysconfig; 
print(sysconfig.get_config_var("ABIFLAGS"))')
+       test_var PYTHON_SITEDIR python3_7 "/usr/lib/python3.7/site-packages"
+       test_var PYTHON_INCLUDEDIR python3_7 "/usr/include/python3.7${abiflags}"
+       test_var PYTHON_LIBPATH python3_7 
"/usr/lib*/libpython3.7${abiflags}$(get_libname)"
+       test_var PYTHON_CONFIG python3_7 "/usr/bin/python3.7${abiflags}-config"
+       test_var PYTHON_CFLAGS python3_7 "*-I/usr/include/python3.7*"
+       test_var PYTHON_LIBS python3_7 "*-lpython3.7*"
+fi
+test_var PYTHON_PKG_DEP python3_7 '*dev-lang/python*:3.7'
+test_var PYTHON_SCRIPTDIR python3_7 /usr/lib/python-exec/python3.7
+
+test_var EPYTHON jython2_7 jython2.7
+test_var PYTHON jython2_7 /usr/bin/jython2.7
+if [[ -x /usr/bin/jython2.7 ]]; then
+       test_var PYTHON_SITEDIR jython2_7 
/usr/share/jython-2.7/Lib/site-packages
+fi
+test_var PYTHON_PKG_DEP jython2_7 '*dev-java/jython*:2.7'
+test_var PYTHON_SCRIPTDIR jython2_7 /usr/lib/python-exec/jython2.7
+
+test_var EPYTHON pypy pypy
+test_var PYTHON pypy /usr/bin/pypy
+if [[ -x /usr/bin/pypy ]]; then
+       test_var PYTHON_SITEDIR pypy "/usr/lib*/pypy2.7/site-packages"
+       test_var PYTHON_INCLUDEDIR pypy "/usr/lib*/pypy2.7/include"
+fi
+test_var PYTHON_PKG_DEP pypy '*dev-python/pypy*:0='
+test_var PYTHON_SCRIPTDIR pypy /usr/lib/python-exec/pypy
+
+test_var EPYTHON pypy3 pypy3
+test_var PYTHON pypy3 /usr/bin/pypy3
+if [[ -x /usr/bin/pypy3 ]]; then
+       test_var PYTHON_SITEDIR pypy3 "/usr/lib*/pypy3.?/site-packages"
+       test_var PYTHON_INCLUDEDIR pypy3 "/usr/lib*/pypy3.?/include"
+fi
+test_var PYTHON_PKG_DEP pypy3 '*dev-python/pypy3*:0='
+test_var PYTHON_SCRIPTDIR pypy3 /usr/lib/python-exec/pypy3
+
+test_is "python_is_python3 python2.7" 1
+test_is "python_is_python3 python3.2" 0
+test_is "python_is_python3 jython2.7" 1
+test_is "python_is_python3 pypy" 1
+test_is "python_is_python3 pypy3" 0
+
+# generic shebangs
+test_fix_shebang '#!/usr/bin/python' python2.7 '#!/usr/bin/python2.7'
+test_fix_shebang '#!/usr/bin/python' python3.6 '#!/usr/bin/python3.6'
+test_fix_shebang '#!/usr/bin/python' pypy '#!/usr/bin/pypy'
+test_fix_shebang '#!/usr/bin/python' pypy3 '#!/usr/bin/pypy3'
+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.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
+
+# 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.6 \
+       '#!/mnt/python2/usr/bin/python3.6'
+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
+test_fix_shebang '#!/usr/bin/foo' python2.7 FAIL
+
+# regression test for bug #522080
+test_fix_shebang '#!/usr/bin/python ' python2.7 '#!/usr/bin/python2.7 '
+
+# make sure we don't break pattern matching
+test_is "_python_impl_supported python2_5" 1
+test_is "_python_impl_supported python2_6" 1
+test_is "_python_impl_supported python2_7" 0
+test_is "_python_impl_supported python3_1" 1
+test_is "_python_impl_supported python3_2" 1
+test_is "_python_impl_supported python3_3" 1
+test_is "_python_impl_supported python3_4" 1
+test_is "_python_impl_supported python3_5" 1
+test_is "_python_impl_supported python3_6" 0
+test_is "_python_impl_supported python3_7" 0
+test_is "_python_impl_supported python3_8" 0
+test_is "_python_impl_supported pypy1_8" 1
+test_is "_python_impl_supported pypy1_9" 1
+test_is "_python_impl_supported pypy2_0" 1
+test_is "_python_impl_supported pypy" 1
+test_is "_python_impl_supported pypy3" 0
+test_is "_python_impl_supported jython2_7" 1
+
+# check _python_impl_matches behavior
+test_is "_python_impl_matches python2_7 -2" 0
+test_is "_python_impl_matches python3_6 -2" 1
+test_is "_python_impl_matches python3_7 -2" 1
+test_is "_python_impl_matches pypy -2" 0
+test_is "_python_impl_matches pypy3 -2" 1
+test_is "_python_impl_matches python2_7 -3" 1
+test_is "_python_impl_matches python3_6 -3" 0
+test_is "_python_impl_matches python3_7 -3" 0
+test_is "_python_impl_matches pypy -3" 1
+test_is "_python_impl_matches pypy3 -3" 0
+test_is "_python_impl_matches python2_7 -2 python3_6" 0
+test_is "_python_impl_matches python3_6 -2 python3_6" 0
+test_is "_python_impl_matches python3_7 -2 python3_6" 1
+test_is "_python_impl_matches pypy -2 python3_6" 0
+test_is "_python_impl_matches pypy3 -2 python3_6" 1
+test_is "_python_impl_matches python2_7 pypy3 -2 python3_6" 0
+test_is "_python_impl_matches python3_6 pypy3 -2 python3_6" 0
+test_is "_python_impl_matches python3_7 pypy3 -2 python3_6" 1
+test_is "_python_impl_matches pypy pypy3 -2 python3_6" 0
+test_is "_python_impl_matches pypy3 pypy3 -2 python3_6" 0
+set -f
+test_is "_python_impl_matches python2_7 pypy*" 1
+test_is "_python_impl_matches python3_6 pypy*" 1
+test_is "_python_impl_matches python3_7 pypy*" 1
+test_is "_python_impl_matches pypy pypy*" 0
+test_is "_python_impl_matches pypy3 pypy*" 0
+test_is "_python_impl_matches python2_7 python*" 0
+test_is "_python_impl_matches python3_6 python*" 0
+test_is "_python_impl_matches python3_7 python*" 0
+test_is "_python_impl_matches pypy python*" 1
+test_is "_python_impl_matches pypy3 python*" 1
+set +f
+
+rm "${tmpfile}"
+
+texit
-- 
2.25.1


Reply via email to