public inbox for gentoo-dev@lists.gentoo.org
 help / color / mirror / Atom feed
From: "Michał Górny" <mgorny@gentoo.org>
To: gentoo-dev@lists.gentoo.org
Cc: python@gentoo.org, "Michał Górny" <mgorny@gentoo.org>
Subject: [gentoo-dev] [PATCH v2 01/20] eclass: Copy python-r1 suite to python-r2
Date: Thu,  5 Mar 2020 16:10:05 +0100	[thread overview]
Message-ID: <20200305151024.125834-2-mgorny@gentoo.org> (raw)
In-Reply-To: <20200305151024.125834-1-mgorny@gentoo.org>

Signed-off-by: Michał Górny <mgorny@gentoo.org>
---
 eclass/distutils-r2.eclass      | 1188 ++++++++++++++++++++++++
 eclass/python-any-r2.eclass     |  364 ++++++++
 eclass/python-r2.eclass         |  829 +++++++++++++++++
 eclass/python-single-r2.eclass  |  512 +++++++++++
 eclass/python-utils-r2.eclass   | 1508 +++++++++++++++++++++++++++++++
 eclass/tests/distutils-r2.sh    |   98 ++
 eclass/tests/python-utils-r2.sh |  237 +++++
 7 files changed, 4736 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..559ca2b72c1c
--- /dev/null
+++ b/eclass/distutils-r2.eclass
@@ -0,0 +1,1188 @@
+# Copyright 1999-2020 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+# @ECLASS: distutils-r2.eclass
+# @MAINTAINER:
+# Python team <python@gentoo.org>
+# @AUTHOR:
+# Author: Michał Górny <mgorny@gentoo.org>
+# @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.
+#
+# For more information, please see the Python Guide:
+# https://dev.gentoo.org/~mgorny/python-guide/
+
+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
+		die "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
+}
+
+# @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
+
+	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
+
+	_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..08e424254983
--- /dev/null
+++ b/eclass/python-any-r2.eclass
@@ -0,0 +1,364 @@
+# Copyright 1999-2020 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+# @ECLASS: python-any-r2.eclass
+# @MAINTAINER:
+# Python team <python@gentoo.org>
+# @AUTHOR:
+# Author: Michał Górny <mgorny@gentoo.org>
+# @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.
+#
+# For more information, please see the Python Guide:
+# https://dev.gentoo.org/~mgorny/python-guide/
+
+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
+		einfo "Using ${EPYTHON} to build"
+		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
+			einfo "Using ${EPYTHON} to build"
+			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
+			einfo "Using ${EPYTHON} to build"
+			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
+			einfo "Using ${EPYTHON} to build"
+			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..a71ebeba7c54
--- /dev/null
+++ b/eclass/python-r2.eclass
@@ -0,0 +1,829 @@
+# Copyright 1999-2020 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+# @ECLASS: python-r2.eclass
+# @MAINTAINER:
+# Python team <python@gentoo.org>
+# @AUTHOR:
+# Author: Michał Górny <mgorny@gentoo.org>
+# @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.
+#
+# For more information, please see the Python Guide:
+# https://dev.gentoo.org/~mgorny/python-guide/
+
+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
+	einfo "Using ${EPYTHON} in global scope"
+}
+
+# @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..d9d535711252
--- /dev/null
+++ b/eclass/python-single-r2.eclass
@@ -0,0 +1,512 @@
+# Copyright 1999-2020 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+# @ECLASS: python-single-r2.eclass
+# @MAINTAINER:
+# Python team <python@gentoo.org>
+# @AUTHOR:
+# Author: Michał Górny <mgorny@gentoo.org>
+# @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.
+#
+# For more information, please see the Python Guide:
+# https://dev.gentoo.org/~mgorny/python-guide/
+
+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
+		einfo "Using ${EPYTHON} to build"
+		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
+			einfo "Using ${EPYTHON} to build"
+		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..c28c42493173
--- /dev/null
+++ b/eclass/python-utils-r2.eclass
@@ -0,0 +1,1508 @@
+# Copyright 1999-2020 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+# @ECLASS: python-utils-r2.eclass
+# @MAINTAINER:
+# Python team <python@gentoo.org>
+# @AUTHOR:
+# Author: Michał Górny <mgorny@gentoo.org>
+# @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.
+#
+# For more information, please see the Python Guide:
+# https://dev.gentoo.org/~mgorny/python-guide/
+
+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
+
+	local PYTHON_PKG_DEP
+	python_export "${impl}" PYTHON_PKG_DEP
+	has_version "${hasv_args[@]}" "${PYTHON_PKG_DEP}"
+}
+
+# @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>
+# @INTERNAL
+# @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	other threads:[~2020-03-05 15:11 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-05 15:10 [gentoo-dev] [PATCH v2 00/20] python-r2 suite Michał Górny
2020-03-05 15:10 ` Michał Górny [this message]
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 02/20] distutils-r2.eclass: Remove EXAMPLES Michał Górny
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 03/20] distutils-r2.eclass: Remove no-op subphase defaults Michał Górny
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 04/20] distutils-r2.eclass: Remove distutils.eclass checks Michał Górny
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 05/20] python-r2.eclass: Remove python_gen_usedep Michał Górny
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 06/20] python-utils-r2.eclass: Remove python.eclass checks Michał Górny
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 07/20] python-utils-r2.eclass: Mark python_wrapper_setup private Michał Górny
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 08/20] python-utils-r2.eclass: Mark python_is_installed private Michał Górny
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 09/20] python-utils-r2.eclass: Fix obsolete docs on python_export_best Michał Górny
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 10/20] python-utils-r2.eclass: Mark python_export private Michał Górny
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 11/20] python-utils-r2.eclass: Remove PYTHON_SITEDIR export Michał Górny
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 12/20] python-utils-r2.eclass: Remove PYTHON_INCLUDEDIR export Michał Górny
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 13/20] python-utils-r2.eclass: Remove PYTHON_LIBPATH export Michał Górny
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 14/20] python-utils-r2.eclass: Remove PYTHON_CFLAGS export Michał Górny
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 15/20] python-utils-r2.eclass: Remove PYTHON_LIBS export Michał Górny
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 16/20] python-utils-r2.eclass: Remove PYTHON_CONFIG export Michał Górny
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 17/20] python-utils-r2.eclass: Remove PYTHON_SCRIPTDIR export Michał Górny
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 18/20] python-single-r2.eclass: PYTHON_MULTI_USEDEP → PYTHON_USEDEP Michał Górny
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 19/20] distutils-r2.eclass: Pass --skip-build to install Michał Górny
2020-03-05 15:10 ` [gentoo-dev] [PATCH v2 20/20] eclass: python-r2.eclass → python-multi-r2.eclass Michał Górny

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200305151024.125834-2-mgorny@gentoo.org \
    --to=mgorny@gentoo.org \
    --cc=gentoo-dev@lists.gentoo.org \
    --cc=python@gentoo.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox