diff options
| author | root <root@alpha.trunkmasters.com> | 2026-05-31 21:38:10 -0500 |
|---|---|---|
| committer | root <root@alpha.trunkmasters.com> | 2026-05-31 21:38:10 -0500 |
| commit | 008c6bb5dee069ebd21087839198b81fae584fe0 (patch) | |
| tree | 709deeecc07b9d2aca5e272eb59bf8c2d61a89dc | |
| parent | af1749663d24ec0e6ef538ae11a18829edb8ac22 (diff) | |
| download | baldeagleos-repo-008c6bb5dee069ebd21087839198b81fae584fe0.tar.gz baldeagleos-repo-008c6bb5dee069ebd21087839198b81fae584fe0.tar.xz baldeagleos-repo-008c6bb5dee069ebd21087839198b81fae584fe0.zip | |
eclass: sync python-utils-r1 from upstream, add python3_15
| -rw-r--r-- | eclass/python-utils-r1.eclass | 205 |
1 files changed, 134 insertions, 71 deletions
diff --git a/eclass/python-utils-r1.eclass b/eclass/python-utils-r1.eclass index b50834480e19..6230cba30d92 100644 --- a/eclass/python-utils-r1.eclass +++ b/eclass/python-utils-r1.eclass @@ -1,4 +1,4 @@ -# Copyright 1999-2025 Gentoo Authors +# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 # @ECLASS: python-utils-r1.eclass @@ -27,7 +27,7 @@ if [[ -z ${_PYTHON_UTILS_R1_ECLASS} ]]; then _PYTHON_UTILS_R1_ECLASS=1 case ${EAPI} in - 7|8) ;; + 7|8) inherit eapi9-pipestatus ;; *) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;; esac @@ -40,8 +40,8 @@ inherit multiprocessing toolchain-funcs # All supported Python implementations, most preferred last. _PYTHON_ALL_IMPLS=( pypy3_11 - python3_{9..14}t - python3_{9..14} + python3_{13..15}t + python3_{11..15} ) readonly _PYTHON_ALL_IMPLS @@ -53,7 +53,7 @@ _PYTHON_HISTORICAL_IMPLS=( jython2_7 pypy pypy1_{8,9} pypy2_0 pypy3 python2_{5..7} - python3_{1..8} + python3_{1..10} ) readonly _PYTHON_HISTORICAL_IMPLS @@ -81,7 +81,7 @@ _python_verify_patterns() { local impl pattern for pattern; do case ${pattern} in - -[23]|3.[89]|3.1[0-4]) + -[23]|3.[89]|3.1[0-5]) continue ;; esac @@ -137,9 +137,9 @@ _python_set_impls() { # please keep them in sync with _PYTHON_ALL_IMPLS # and _PYTHON_HISTORICAL_IMPLS case ${i} in - pypy3_11|python3_9|python3_1[0-4]|python3_1[3-4]t) + pypy3_11|python3_9|python3_1[1-5]|python3_1[3-5]t) ;; - jython2_7|pypy|pypy1_[89]|pypy2_0|pypy3|python2_[5-7]|python3_[1-8]) + jython2_7|pypy|pypy1_[89]|pypy2_0|pypy3|python2_[5-7]|python3_[1-9]|python3_10) obsolete+=( "${i}" ) ;; *) @@ -230,7 +230,7 @@ _python_impl_matches() { fi return 0 ;; - 3.[89]|3.1[0-4]) + 3.[89]|3.1[0-5]) [[ ${impl%t} == python${pattern/./_} || ${impl} == pypy${pattern/./_} ]] && return 0 ;; @@ -602,7 +602,7 @@ python_optimize() { # default to sitedir [[ ${#} -eq 0 ]] && set -- "${D}$(python_get_sitedir)" - local jobs=$(makeopts_jobs) + local jobs=$(get_makeopts_jobs) local d for d; do einfo "Optimizing Python modules in ${d#${D}}" @@ -679,7 +679,7 @@ python_newexe() { local newfn=${2} local scriptdir=$(python_get_scriptdir) - local d=${scriptdir#${EPREFIX}} + local d=${scriptdir#"${EPREFIX}"} ( dodir "${wrapd}" @@ -817,7 +817,7 @@ python_domodule() { else # relative to site-packages local sitedir=$(python_get_sitedir) - d=${sitedir#${EPREFIX}}/${_PYTHON_MODULEROOT//.//} + d=${sitedir#"${EPREFIX}"}/${_PYTHON_MODULEROOT//.//} fi if [[ ${EBUILD_PHASE} == install ]]; then @@ -929,8 +929,12 @@ _python_wrapper_setup() { chmod +x "${workdir}/bin/python-config" \ "${workdir}/bin/python3-config" || die - # Python 2.6+. - ln -s "${PYTHON/python/2to3-}" "${workdir}"/bin/2to3 || die + # Python 2.6+. Deprecated in 3.11, removed in 3.13. + if ver_test ${EPYTHON#python} -lt 3.13; then + ln -s "${PYTHON/python/2to3-}" "${workdir}"/bin/2to3 || die + else + nonsupp+=( 2to3 ) + fi # Python 2.7+. ln -s "${EPREFIX}"/usr/$(get_libdir)/pkgconfig/${EPYTHON/n/n-}.pc \ @@ -1227,12 +1231,12 @@ _python_check_occluded_packages() { comm -1 -3 <( find "${fn}" -type f -not -path '*/__pycache__/*' | sort - assert + pipestatus || die ) <( cd "${sitedir}" && find "${fn}" -type f -not -path '*/__pycache__/*' | sort - assert + pipestatus || die ) ) @@ -1288,9 +1292,9 @@ _python_check_occluded_packages() { # Defaults to an empty list. # # The eclasses explicitly handle a number of pytest plugins, and assume -# the default of "dev-python/${package}" and "-p ${package}" for others. -# If this is incorrect for some plugin package, please report a bug -# to have it added. +# the default of "dev-python/${package}" and obtain "-p" via entry +# points. If this is incorrect for some plugin package, please report +# a bug. # # This is not a perfect solution, and may not be sufficient for some # packages. In these cases, either plugin autoloading should be used @@ -1312,6 +1316,15 @@ _python_check_occluded_packages() { # The recommended way to disable it in EAPI 8 or earlier is to set # EPYTEST_PLUGINS (possibly to an empty array). +# @ECLASS_VARIABLE: EPYTEST_PLUGIN_LOAD_VIA_ENV +# @DEFAULT_UNSET +# @DESCRIPTION: +# If set to a non-empty value, plugins will be loaded via PYTEST_PLUGINS +# environment variable rather than explicit "-p" options. This ensures +# that plugins are passed down to subprocess, which may be necessary +# when testing pytest plugins. However, this is also more likely +# to cause duplicate plugin errors. + # @FUNCTION: _set_epytest_plugins # @INTERNAL # @DESCRIPTION: @@ -1345,6 +1358,16 @@ _set_epytest_plugins() { fi } +# @ECLASS_VARIABLE: EPYTEST_RERUNS +# @DEFAULT_UNSET +# @DESCRIPTION: +# If set to a non-empty value, enables pytest-rerunfailures plugin +# and sets rerun count to the specified value. This variable can be +# either set in ebuilds with flaky tests, or by user to try if it helps. +# If this variable is set prior to calling distutils_enable_tests +# in distutils-r1, a test dependency on dev-python/pytest-rerunfailures +# is added automatically. + # @ECLASS_VARIABLE: EPYTEST_TIMEOUT # @DEFAULT_UNSET # @DESCRIPTION: @@ -1397,7 +1420,7 @@ epytest() { [[ ${NO_COLOR} ]] && color=no mkdir -p "${T}/pytest-xml" || die - local junit_xml=$(mktemp "${T}/pytest-xml/${EPYTHON}-XXX.xml" || die) + local junit_xml=$(mktemp "${T}/pytest-xml/${EPYTHON}-XXXXXX.xml" || die) local args=( # verbose progress reporting and tracebacks @@ -1441,57 +1464,43 @@ epytest() { fi if [[ ${PYTEST_DISABLE_PLUGIN_AUTOLOAD} ]]; then - local plugin - for plugin in "${EPYTEST_PLUGINS[@]}"; do - case ${plugin} in - # special cases - hypothesis) - plugin=hypothesispytest - ;; - noseofyeti) - plugin=nose_of_yeti - ;; - pytest-helpers-namespace) - plugin=helpers_namespace - ;; - pytest-lazy-fixtures) - plugin=pytest_lazyfixture - ;; - pytest-testinfra) - plugin=pytest11.testinfra - ;; - # "generic" cases - betamax) - plugin=pytest-${plugin} - ;; - pyfakefs) - plugin=pytest_${plugin} - ;; - # pytest-x-y-z -> x-y-z - pytest-aiohttp | pytest-asyncio | pytest-check | \ - pytest-console-scripts | pytest-django | pytest-env | \ - pytest-freezer | pytest-home | pytest-httpbin | \ - pytest-import-check | pytest-localftpserver | \ - pytest-localserver | pytest-plus | pytest-recording | \ - pytest-regressions | pytest-repeat | pytest-reraise | \ - pytest-rerunfailures | pytest-reserial | \ - pytest-shell-utilities | pytest-skip-markers | \ - pytest-subtests | pytest-timeout | pytest-tornasync | \ - pytest-trio | pytext-xdist | pytest-xprocess | \ - pytest-xvfb ) - plugin=${plugin#pytest-} - ;; - # foo-bar-baz unchanged - pytest-datadir | pytest-qt | pytest-subprocess) - ;; - # foo-bar-baz -> foo_bar_baz - *) - plugin=${plugin//-/_} - ;; - esac + if [[ ${EPYTEST_PLUGINS[@]} ]]; then + if [[ ${EPYTEST_PLUGIN_LOAD_VIA_ENV} ]]; then + local -x PYTEST_PLUGINS=$( + "${EPYTHON}" - "${EPYTEST_PLUGINS[@]}" <<-EOF || die + import sys + from importlib.metadata import distribution, entry_points - args+=( -p "${plugin}" ) - done + packages = {distribution(x).name for x in sys.argv[1:]} + # In packages defining multiple entry points, we must + # list them in the same order! + plugins = ( + x.value for x in entry_points(group="pytest11") + if x.dist.name in packages + ) + sys.stdout.write(",".join(plugins)) + EOF + ) + else + local plugin_args=() + readarray -t -d '' plugin_args < <( + "${EPYTHON}" - "${EPYTEST_PLUGINS[@]}" <<-EOF || die + import os + import sys + from importlib.metadata import distribution, entry_points + + env_plugins = os.environ.get("PYTEST_PLUGINS", "").split(",") + packages = {distribution(x).name for x in sys.argv[1:]} + eps = { + f"-p{x.name}" for x in entry_points(group="pytest11") + if x.dist.name in packages and x.value not in env_plugins + } + sys.stdout.write("\\0".join(sorted(eps))) + EOF + ) + args+=( "${plugin_args[@]}" ) + fi + fi else args+=( # disable the undesirable-dependency plugins by default to @@ -1522,6 +1531,26 @@ epytest() { ) fi + if [[ -n ${EPYTEST_RERUNS} ]]; then + if [[ ${PYTEST_PLUGINS} != *pytest_rerunfailures* ]]; then + args+=( + -p rerunfailures + ) + fi + + if has_version ">=dev-python/pytest-rerunfailures-16.1"; then + args+=( + # --reruns only adds N reruns for tests not marked for reruns + # --force-reruns overrides the rerun count for all tests + "--force-reruns=${EPYTEST_RERUNS}" + ) + else + args+=( + "--reruns=${EPYTEST_RERUNS}" + ) + fi + fi + if [[ -n ${EPYTEST_TIMEOUT} ]]; then if [[ ${PYTEST_PLUGINS} != *pytest_timeout* ]]; then args+=( @@ -1532,10 +1561,19 @@ epytest() { args+=( "--timeout=${EPYTEST_TIMEOUT}" ) + + if [[ -n ${EPYTEST_RERUNS} ]]; then + # This option helps with hangs in some cases when these plugins are used together. + # https://github.com/pytest-dev/pytest-timeout/issues/18 + # https://github.com/pytest-dev/pytest-rerunfailures/issues/99 + args+=( + -o timeout_func_only=true + ) + fi fi if [[ ${EPYTEST_XDIST} ]]; then - local jobs=${EPYTEST_JOBS:-$(makeopts_jobs)} + local jobs=${EPYTEST_JOBS:-$(get_makeopts_jobs)} if [[ ${jobs} -gt 1 ]]; then if [[ ${PYTEST_PLUGINS} != *xdist.plugin* ]]; then args+=( @@ -1550,9 +1588,31 @@ epytest() { # jobs are unevenly distributed --dist=worksteal ) + + if [[ ${MAKEFLAGS} == *--jobserver-auth=* ]]; then + if has_version "dev-python/pytest-jobserver[${PYTHON_USEDEP}]" + then + args+=( -p jobserver ) + elif [[ ! ${_EPYTEST_JOBSERVER_WARNED} ]]; then + ewarn "Jobserver found under pytest-xdist, but cannot be used without:" + ewarn " dev-python/pytest-jobserver" + _EPYTEST_JOBSERVER_WARNED=1 + fi + fi fi fi + # If we are using hypothesis (require use via EPYTEST_PLUGINS, since + # ebuilds may disable autoloading manually) *and* hypothesis-gentoo + # is available, use it to disable all health checks, to prevent the tests + # from failing randomly under load. + if has hypothesis "${EPYTEST_PLUGINS[@]}" && + "${EPYTHON}" -c 'import hypothesis_gentoo' 2>/dev/null && + [[ ! ${HYPOTHESIS_NO_PLUGINS} ]] + then + args+=( --hypothesis-profile=gentoo ) + fi + local x for x in "${EPYTEST_DESELECT[@]}"; do args+=( --deselect "${x}" ) @@ -1562,6 +1622,9 @@ epytest() { done set -- "${EPYTHON}" -m pytest "${args[@]}" "${@}" ${EPYTEST_FLAGS} + if [[ ${PYTEST_PLUGINS} ]]; then + einfo "PYTEST_PLUGINS=${PYTEST_PLUGINS}" + fi echo "${@}" >&2 "${@}" local ret=${?} |
