commit:     9b3a682c2cfe38d7749781f1c72abb2df87243dd
Author:     Michał Górny <mgorny <AT> gentoo <DOT> org>
AuthorDate: Sat May 27 10:41:38 2023 +0000
Commit:     Michał Górny <mgorny <AT> gentoo <DOT> org>
CommitDate: Sat May 27 13:20:42 2023 +0000
URL:        https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=9b3a682c

dev-python/cffi: Enable experimental py3.12 support

Add a patch that fixes the most important py3.12 issues, and enable it
in order to facilitate testing packages.

Signed-off-by: Michał Górny <mgorny <AT> gentoo.org>

 dev-python/cffi/cffi-1.15.1-r3.ebuild         |  81 +++++++++
 dev-python/cffi/files/cffi-1.15.1-py312.patch | 247 ++++++++++++++++++++++++++
 2 files changed, 328 insertions(+)

diff --git a/dev-python/cffi/cffi-1.15.1-r3.ebuild 
b/dev-python/cffi/cffi-1.15.1-r3.ebuild
new file mode 100644
index 000000000000..6c093945c628
--- /dev/null
+++ b/dev-python/cffi/cffi-1.15.1-r3.ebuild
@@ -0,0 +1,81 @@
+# Copyright 1999-2023 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+# please keep this ebuild at EAPI 8 -- sys-apps/portage dep
+EAPI=8
+
+# py3.12 support is experimental, expect problems in revdeps
+DISTUTILS_EXT=1
+DISTUTILS_USE_PEP517=setuptools
+# DO NOT ADD pypy to PYTHON_COMPAT
+# pypy bundles a modified version of cffi. Use python_gen_cond_dep instead.
+PYTHON_COMPAT=( python3_{10..12} )
+
+inherit distutils-r1 toolchain-funcs pypi
+
+DESCRIPTION="Foreign Function Interface for Python calling C code"
+HOMEPAGE="
+       https://cffi.readthedocs.io/
+       https://pypi.org/project/cffi/
+"
+SRC_URI+=" 
https://dev.gentoo.org/~sam/distfiles/${CATEGORY}/${PN}/${P}-drop-deprecated-py.patch.xz";
+
+LICENSE="MIT"
+SLOT="0/${PV}"
+KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~ia64 ~loong ~m68k ~mips ~ppc ~ppc64 
~riscv ~s390 ~sparc ~x86 ~amd64-linux ~x86-linux ~ppc-macos ~x64-macos 
~x64-solaris"
+
+# Needs recent libffi for HPPA fixes
+DEPEND="
+       >=dev-libs/libffi-3.4.4-r1:=
+"
+# setuptools as a modern distutils provider
+RDEPEND="
+       ${DEPEND}
+       dev-python/pycparser[${PYTHON_USEDEP}]
+       dev-python/setuptools[${PYTHON_USEDEP}]
+"
+BDEPEND="
+       ${RDEPEND}
+       virtual/pkgconfig
+"
+
+distutils_enable_sphinx doc/source
+distutils_enable_tests pytest
+
+PATCHES=(
+       "${FILESDIR}"/cffi-1.14.0-darwin-no-brew.patch
+       "${FILESDIR}"/${P}-hppa.patch
+       "${FILESDIR}"/${P}-python3.11-tests.patch
+       "${WORKDIR}"/${P}-drop-deprecated-py.patch
+       "${FILESDIR}"/${P}-py312.patch
+)
+
+src_prepare() {
+       if [[ ${CHOST} == *darwin* ]] ; then
+               # Don't obsessively try to find libffi
+               sed -i -e "s/.*\-iwithsysroot\/usr\/include\/ffi.*/\tpass/" 
setup.py || die
+       fi
+       distutils-r1_src_prepare
+}
+
+src_configure() {
+       tc-export PKG_CONFIG
+}
+
+python_test() {
+       local EPYTEST_IGNORE=(
+               # these tests call pip
+               testing/cffi0/test_zintegration.py
+       )
+       local EPYTEST_DESELECT=()
+       if [[ ${EPYTHON} == python3.12 ]]; then
+               EPYTEST_DESELECT+=(
+                       # TODO: these tests hang
+                       testing/embedding
+               )
+       fi
+
+       "${EPYTHON}" -c "import _cffi_backend as backend" || die
+       local -x PYTEST_DISABLE_PLUGIN_AUTOLOAD=1
+       epytest c testing
+}

diff --git a/dev-python/cffi/files/cffi-1.15.1-py312.patch 
b/dev-python/cffi/files/cffi-1.15.1-py312.patch
new file mode 100644
index 000000000000..a5477d686dd7
--- /dev/null
+++ b/dev-python/cffi/files/cffi-1.15.1-py312.patch
@@ -0,0 +1,247 @@
+diff -r 79b97f01064f cffi/vengine_cpy.py
+--- a/cffi/vengine_cpy.py      Thu Feb 23 05:42:01 2023 +0100
++++ b/cffi/vengine_cpy.py      Sat May 27 11:03:01 2023 +0200
+@@ -1,10 +1,16 @@
+ #
+ # DEPRECATED: implementation for ffi.verify()
+ #
+-import sys, imp
++import sys
+ from . import model
+ from .error import VerificationError
+ 
++if sys.version_info >= (3, 12):
++    import importlib.machinery
++    import importlib.util
++else:
++    import imp
++
+ 
+ class VCPythonEngine(object):
+     _class_key = 'x'
+@@ -20,16 +26,22 @@
+         pass
+ 
+     def find_module(self, module_name, path, so_suffixes):
+-        try:
+-            f, filename, descr = imp.find_module(module_name, path)
+-        except ImportError:
+-            return None
+-        if f is not None:
+-            f.close()
++        if sys.version_info >= (3, 12):
++            spec = importlib.machinery.PathFinder.find_spec(module_name, path)
++            if spec is None:
++                return None
++            filename = spec.origin
++        else:
++            try:
++                f, filename, descr = imp.find_module(module_name, path)
++            except ImportError:
++                return None
++            if f is not None:
++                f.close()
+         # Note that after a setuptools installation, there are both .py
+         # and .so files with the same basename.  The code here relies on
+         # imp.find_module() locating the .so in priority.
+-        if descr[0] not in so_suffixes:
++        if not filename.endswith(tuple(so_suffixes)):
+             return None
+         return filename
+ 
+@@ -145,15 +157,23 @@
+     def load_library(self, flags=None):
+         # XXX review all usages of 'self' here!
+         # import it as a new extension module
+-        imp.acquire_lock()
++        if sys.version_info < (3, 12):
++            imp.acquire_lock()
+         try:
+             if hasattr(sys, "getdlopenflags"):
+                 previous_flags = sys.getdlopenflags()
+             try:
+                 if hasattr(sys, "setdlopenflags") and flags is not None:
+                     sys.setdlopenflags(flags)
+-                module = imp.load_dynamic(self.verifier.get_module_name(),
+-                                          self.verifier.modulefilename)
++                if sys.version_info >= (3, 12):
++                    spec = importlib.util.spec_from_file_location(
++                        self.verifier.get_module_name(),
++                        self.verifier.modulefilename)
++                    module = importlib.util.module_from_spec(spec)
++                    spec.loader.exec_module(module)
++                else:
++                    module = imp.load_dynamic(self.verifier.get_module_name(),
++                                              self.verifier.modulefilename)
+             except ImportError as e:
+                 error = "importing %r: %s" % (self.verifier.modulefilename, e)
+                 raise VerificationError(error)
+@@ -161,7 +181,8 @@
+                 if hasattr(sys, "setdlopenflags"):
+                     sys.setdlopenflags(previous_flags)
+         finally:
+-            imp.release_lock()
++            if sys.version_info < (3, 12):
++                imp.release_lock()
+         #
+         # call loading_cpy_struct() to get the struct layout inferred by
+         # the C compiler
+diff -r 79b97f01064f testing/cffi0/test_verify.py
+--- a/testing/cffi0/test_verify.py     Thu Feb 23 05:42:01 2023 +0100
++++ b/testing/cffi0/test_verify.py     Sat May 27 11:03:01 2023 +0200
+@@ -1575,10 +1575,16 @@
+ def test_callback_in_thread():
+     if sys.platform == 'win32':
+         pytest.skip("pthread only")
+-    import os, subprocess, imp
++    import os, subprocess
+     arg = os.path.join(os.path.dirname(__file__), 'callback_in_thread.py')
+-    g = subprocess.Popen([sys.executable, arg,
+-                          os.path.dirname(imp.find_module('cffi')[1])])
++    if sys.version_info >= (3, 12):
++        import importlib.util
++        spec = importlib.util.find_spec('cffi')
++        cffi_path = os.path.dirname(spec.origin)
++    else:
++        import imp
++        cffi_path = imp.find_module('cffi')[1]
++    g = subprocess.Popen([sys.executable, arg, os.path.dirname(cffi_path)])
+     result = g.wait()
+     assert result == 0
+ 
+diff -r 79b97f01064f testing/cffi0/test_zdistutils.py
+--- a/testing/cffi0/test_zdistutils.py Thu Feb 23 05:42:01 2023 +0100
++++ b/testing/cffi0/test_zdistutils.py Sat May 27 11:03:01 2023 +0200
+@@ -1,8 +1,9 @@
+-import sys, os, imp, math, shutil
++import sys, os, math, shutil
+ import pytest
+ from cffi import FFI, FFIError
+ from cffi.verifier import Verifier, _locate_engine_class, _get_so_suffixes
+ from cffi.ffiplatform import maybe_relative_path
++from testing.support import load_dynamic
+ from testing.udir import udir
+ 
+ 
+@@ -80,7 +81,7 @@
+         v.compile_module()
+         assert v.get_module_name().startswith('_cffi_')
+         if v.generates_python_module():
+-            mod = imp.load_dynamic(v.get_module_name(), v.modulefilename)
++            mod = load_dynamic(v.get_module_name(), v.modulefilename)
+             assert hasattr(mod, '_cffi_setup')
+ 
+     def test_compile_module_explicit_filename(self):
+@@ -95,7 +96,7 @@
+         assert filename == v.modulefilename
+         assert v.get_module_name() == basename
+         if v.generates_python_module():
+-            mod = imp.load_dynamic(v.get_module_name(), v.modulefilename)
++            mod = load_dynamic(v.get_module_name(), v.modulefilename)
+             assert hasattr(mod, '_cffi_setup')
+ 
+     def test_name_from_checksum_of_cdef(self):
+diff -r 79b97f01064f testing/cffi1/test_new_ffi_1.py
+--- a/testing/cffi1/test_new_ffi_1.py  Thu Feb 23 05:42:01 2023 +0100
++++ b/testing/cffi1/test_new_ffi_1.py  Sat May 27 11:03:01 2023 +0200
+@@ -1,5 +1,5 @@
+ import pytest
+-import platform, imp
++import platform
+ import sys, os, ctypes
+ import cffi
+ from testing.udir import udir
+@@ -91,7 +91,7 @@
+ 
+     outputfilename = recompile(ffi1, "test_new_ffi_1", CCODE,
+                                tmpdir=str(udir))
+-    module = imp.load_dynamic("test_new_ffi_1", outputfilename)
++    module = load_dynamic("test_new_ffi_1", outputfilename)
+     ffi = module.ffi
+     construction_params = (ffi1, CCODE)
+ 
+@@ -1619,8 +1619,8 @@
+         ffi2 = cffi.FFI(); ffi2.cdef(CDEF2)
+         outputfilename = recompile(ffi2, "test_multiple_independent_structs",
+                                    CDEF2, tmpdir=str(udir))
+-        module = imp.load_dynamic("test_multiple_independent_structs",
+-                                  outputfilename)
++        module = load_dynamic("test_multiple_independent_structs",
++                              outputfilename)
+         ffi1 = module.ffi
+         foo1 = ffi1.new("struct ab *", [10])
+         foo2 = ffi .new("struct ab *", [20, 30])
+@@ -1635,8 +1635,8 @@
+         outputfilename = recompile(ffi2,
+                                    "test_include_struct_union_enum_typedef",
+                                    CCODE, tmpdir=str(udir))
+-        module = imp.load_dynamic("test_include_struct_union_enum_typedef",
+-                                  outputfilename)
++        module = load_dynamic("test_include_struct_union_enum_typedef",
++                              outputfilename)
+         ffi2 = module.ffi
+         #
+         p = ffi2.new("struct nonpacked *", [b'A', -43141])
+@@ -1783,7 +1783,7 @@
+                                    "int myfunc(int x) { return x + 1; }\n"
+                                    "int myvar = -5;\n"
+                                    "#define MYFOO 42", tmpdir=str(udir))
+-        imp.load_dynamic("_test_import_from_lib", outputfilename)
++        load_dynamic("_test_import_from_lib", outputfilename)
+         from _test_import_from_lib.lib import myfunc, myvar, MYFOO
+         assert MYFOO == 42
+         assert myfunc(43) == 44
+diff -r 79b97f01064f testing/support.py
+--- a/testing/support.py       Thu Feb 23 05:42:01 2023 +0100
++++ b/testing/support.py       Sat May 27 11:03:01 2023 +0200
+@@ -1,5 +1,11 @@
+ import sys, os
+ 
++if sys.version_info >= (3, 12):
++    import importlib.util
++else:
++    import imp
++
++
+ if sys.version_info < (3,):
+     __all__ = ['u', 'arraytostring']
+ 
+@@ -16,7 +22,7 @@
+         return a.tostring()
+ 
+ else:
+-    __all__ = ['u', 'unicode', 'long', 'arraytostring']
++    __all__ = ['u', 'unicode', 'long', 'arraytostring', 'load_dynamic']
+     u = ""
+     unicode = str
+     long = int
+@@ -71,15 +77,27 @@
+     def getvalue(self):
+         return self._value
+ 
++
++def load_dynamic(module_name, outputfilename):
++    if sys.version_info >= (3, 12):
++        import importlib.util
++        spec = importlib.util.spec_from_file_location(module_name,
++                                                      outputfilename)
++        module = importlib.util.module_from_spec(spec)
++        spec.loader.exec_module(module)
++        return module
++    else:
++        return imp.load_dynamic(module_name, outputfilename)
++
++
+ def _verify(ffi, module_name, preamble, *args, **kwds):
+-    import imp
+     from cffi.recompiler import recompile
+     from .udir import udir
+     assert module_name not in sys.modules, "module name conflict: %r" % (
+         module_name,)
+     kwds.setdefault('tmpdir', str(udir))
+     outputfilename = recompile(ffi, module_name, preamble, *args, **kwds)
+-    module = imp.load_dynamic(module_name, outputfilename)
++    module = load_dynamic(module_name, outputfilename)
+     #
+     # hack hack hack: copy all *bound methods* from module.ffi back to the
+     # ffi instance.  Then calls like ffi.new() will invoke module.ffi.new().

Reply via email to