From fec30ea1925cf7ecc3c3dfeea17f0ae7fe7d1e565d69bad35542959a11107986 Mon Sep 17 00:00:00 2001 From: Dirk Mueller Date: Sun, 30 Jun 2024 21:27:50 +0000 Subject: [PATCH] - add py313-compat.patch py313-use-format-unraisable.patch py313-use-hashpointer.patch: add upstream patches for py3.13 support testsuite failures with Python 3.11 * Revert "grovel: detect :float and :double in the :auto type" * no upstream changelog provided * no upstream changelog provided * CPython 3 on Windows: we again try to compile with Py_LIMITED_API by default - Update pytest in spec to add c directory tests in addition to messages. OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-cffi?expand=0&rev=97 --- py313-compat.patch | 302 ++++++++++++++++++++++++++++++ py313-use-format-unraisable.patch | 30 +++ py313-use-hashpointer.patch | 32 ++++ python-cffi.changes | 22 ++- python-cffi.spec | 5 +- 5 files changed, 383 insertions(+), 8 deletions(-) create mode 100644 py313-compat.patch create mode 100644 py313-use-format-unraisable.patch create mode 100644 py313-use-hashpointer.patch diff --git a/py313-compat.patch b/py313-compat.patch new file mode 100644 index 0000000..ef756e4 --- /dev/null +++ b/py313-compat.patch @@ -0,0 +1,302 @@ +From 14723b0bbd127790c450945099db31018d80fa83 Mon Sep 17 00:00:00 2001 +From: Matt Davis <6775756+nitzmahone@users.noreply.github.com> +Date: Thu, 30 Nov 2023 18:27:52 -0800 +Subject: [PATCH] Python 3.13 compatibility (#24) + +* fix obsolete private API aliases for 3.13 compat + +* update unraisable tests to use sys.unraisablehook + +* assert shape of calls to unraisablehook and sanity check traceback contents instead of (varying) stderr output from default unraisablehook impl +--- + src/c/_cffi_backend.c | 4 +- + src/c/misc_thread_common.h | 4 +- + src/c/test_c.py | 217 ++++++++++++------------------------- + 3 files changed, 74 insertions(+), 151 deletions(-) + +diff --git a/src/c/_cffi_backend.c b/src/c/_cffi_backend.c +index 5e284e00..7c72ffe0 100644 +--- a/src/c/_cffi_backend.c ++++ b/src/c/_cffi_backend.c +@@ -134,8 +134,8 @@ + # define PyText_Check PyUnicode_Check + # define PyTextAny_Check PyUnicode_Check + # define PyText_FromFormat PyUnicode_FromFormat +-# define PyText_AsUTF8 _PyUnicode_AsString /* PyUnicode_AsUTF8 in Py3.3 */ +-# define PyText_AS_UTF8 _PyUnicode_AsString ++# define PyText_AsUTF8 PyUnicode_AsUTF8 ++# define PyText_AS_UTF8 PyUnicode_AsUTF8 + # if PY_VERSION_HEX >= 0x03030000 + # define PyText_GetSize PyUnicode_GetLength + # else +diff --git a/src/c/misc_thread_common.h b/src/c/misc_thread_common.h +index ead9c83c..7d29634b 100644 +--- a/src/c/misc_thread_common.h ++++ b/src/c/misc_thread_common.h +@@ -331,7 +331,9 @@ PyAPI_DATA(void *volatile) _PyThreadState_Current; + + static PyThreadState *get_current_ts(void) + { +-#if PY_VERSION_HEX >= 0x03060000 ++#if PY_VERSION_HEX >= 0x030D0000 ++ return PyThreadState_GetUnchecked(); ++#elif PY_VERSION_HEX >= 0x03060000 + return _PyThreadState_UncheckedGet(); + #elif defined(_Py_atomic_load_relaxed) + return (PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current); +diff --git a/src/c/test_c.py b/src/c/test_c.py +index 1cdab10f..10cc35cf 100644 +--- a/src/c/test_c.py ++++ b/src/c/test_c.py +@@ -1,5 +1,12 @@ ++from __future__ import annotations ++ ++import contextlib ++import traceback ++import unittest.mock ++ + import pytest + import sys ++import typing as t + + is_musl = False + if sys.platform == 'linux': +@@ -1337,27 +1344,37 @@ def cb(n): + e = pytest.raises(TypeError, f) + assert str(e.value) == "'int(*)(int)' expects 1 arguments, got 0" + ++@contextlib.contextmanager ++def _assert_unraisable(error_type: type[Exception] | None, message: str = '', traceback_tokens: list[str] | None = None): ++ """Assert that a given sys.unraisablehook interaction occurred (or did not occur, if error_type is None) while this context was active""" ++ raised_errors: list[Exception] = [] ++ raised_traceback: str = '' ++ ++ # sys.unraisablehook is called more than once for chained exceptions; accumulate the errors and tracebacks for inspection ++ def _capture_unraisable_hook(ur_args): ++ nonlocal raised_traceback ++ raised_errors.append(ur_args.exc_value) ++ ++ # NB: need to use the old etype/value/tb form until 3.10 is the minimum ++ raised_traceback += (ur_args.err_msg or '' + '\n') + ''.join(traceback.format_exception(None, ur_args.exc_value, ur_args.exc_traceback)) ++ ++ ++ with pytest.MonkeyPatch.context() as mp: ++ mp.setattr(sys, 'unraisablehook', _capture_unraisable_hook) ++ yield ++ ++ if error_type is None: ++ assert not raised_errors ++ assert not raised_traceback ++ return ++ ++ assert any(type(raised_error) is error_type for raised_error in raised_errors) ++ assert any(message in str(raised_error) for raised_error in raised_errors) ++ for t in traceback_tokens or []: ++ assert t in raised_traceback ++ ++ + def test_callback_exception(): +- try: +- import cStringIO +- except ImportError: +- import io as cStringIO # Python 3 +- import linecache +- def matches(istr, ipattern, ipattern38, ipattern311=None): +- if sys.version_info >= (3, 8): +- ipattern = ipattern38 +- if sys.version_info >= (3, 11): +- ipattern = ipattern311 or ipattern38 +- str, pattern = istr, ipattern +- while '$' in pattern: +- i = pattern.index('$') +- assert str[:i] == pattern[:i] +- j = str.find(pattern[i+1], i) +- assert i + 1 <= j <= str.find('\n', i) +- str = str[j:] +- pattern = pattern[i+1:] +- assert str == pattern +- return True + def check_value(x): + if x == 10000: + raise ValueError(42) +@@ -1366,148 +1383,52 @@ def Zcb1(x): + return x * 3 + BShort = new_primitive_type("short") + BFunc = new_function_type((BShort,), BShort, False) ++ + f = callback(BFunc, Zcb1, -42) +- # + seen = [] + oops_result = None + def oops(*args): + seen.append(args) + return oops_result + ff = callback(BFunc, Zcb1, -42, oops) +- # +- orig_stderr = sys.stderr +- orig_getline = linecache.getline +- try: +- linecache.getline = lambda *args: 'LINE' # hack: speed up PyPy tests +- sys.stderr = cStringIO.StringIO() +- if hasattr(sys, '__unraisablehook__'): # work around pytest +- sys.unraisablehook = sys.__unraisablehook__ # on recent CPythons ++ with _assert_unraisable(None): + assert f(100) == 300 +- assert sys.stderr.getvalue() == '' ++ with _assert_unraisable(ValueError, '42', ['in Zcb1', 'in check_value']): + assert f(10000) == -42 +- assert matches(sys.stderr.getvalue(), """\ +-From cffi callback : +-Traceback (most recent call last): +- File "$", line $, in Zcb1 +- $ +- File "$", line $, in check_value +- $ +-ValueError: 42 +-""", """\ +-Exception ignored from cffi callback : +-Traceback (most recent call last): +- File "$", line $, in Zcb1 +- $ +- File "$", line $, in check_value +- $ +-ValueError: 42 +-""") +- sys.stderr = cStringIO.StringIO() +- bigvalue = 20000 ++ ++ bigvalue = 20000 ++ with _assert_unraisable(OverflowError, "integer 60000 does not fit 'short'", ['callback', 'Zcb1']): + assert f(bigvalue) == -42 +- assert matches(sys.stderr.getvalue(), """\ +-From cffi callback : +-Trying to convert the result back to C: +-OverflowError: integer 60000 does not fit 'short' +-""", """\ +-Exception ignored from cffi callback , trying to convert the result back to C: +-Traceback (most recent call last): +- File "$", line $, in test_callback_exception +- $ +-OverflowError: integer 60000 does not fit 'short' +-""") +- sys.stderr = cStringIO.StringIO() +- bigvalue = 20000 +- assert len(seen) == 0 ++ assert len(seen) == 0 ++ ++ with _assert_unraisable(None): + assert ff(bigvalue) == -42 +- assert sys.stderr.getvalue() == "" +- assert len(seen) == 1 +- exc, val, tb = seen[0] +- assert exc is OverflowError +- assert str(val) == "integer 60000 does not fit 'short'" +- # +- sys.stderr = cStringIO.StringIO() +- bigvalue = 20000 +- del seen[:] +- oops_result = 81 ++ assert len(seen) == 1 ++ exc, val, tb = seen[0] ++ assert exc is OverflowError ++ assert str(val) == "integer 60000 does not fit 'short'" ++ ++ del seen[:] ++ oops_result = 81 ++ with _assert_unraisable(None): + assert ff(bigvalue) == 81 +- oops_result = None +- assert sys.stderr.getvalue() == "" +- assert len(seen) == 1 +- exc, val, tb = seen[0] +- assert exc is OverflowError +- assert str(val) == "integer 60000 does not fit 'short'" +- # +- sys.stderr = cStringIO.StringIO() +- bigvalue = 20000 +- del seen[:] +- oops_result = "xy" # not None and not an int! ++ ++ assert len(seen) == 1 ++ exc, val, tb = seen[0] ++ assert exc is OverflowError ++ assert str(val) == "integer 60000 does not fit 'short'" ++ ++ del seen[:] ++ oops_result = "xy" # not None and not an int! ++ ++ with _assert_unraisable(TypeError, "an integer is required", ["integer 60000 does not fit 'short'"]): + assert ff(bigvalue) == -42 +- oops_result = None +- assert matches(sys.stderr.getvalue(), """\ +-From cffi callback : +-Trying to convert the result back to C: +-OverflowError: integer 60000 does not fit 'short' +- +-During the call to 'onerror', another exception occurred: +- +-TypeError: $integer$ +-""", """\ +-Exception ignored from cffi callback , trying to convert the result back to C: +-Traceback (most recent call last): +- File "$", line $, in test_callback_exception +- $ +-OverflowError: integer 60000 does not fit 'short' +-Exception ignored during handling of the above exception by 'onerror': +-Traceback (most recent call last): +- File "$", line $, in test_callback_exception +- $ +-TypeError: $integer$ +-""") +- # +- sys.stderr = cStringIO.StringIO() +- seen = "not a list" # this makes the oops() function crash ++ ++ seen = "not a list" # this makes the oops() function crash ++ oops_result = None ++ with _assert_unraisable(AttributeError, "'str' object has no attribute 'append", ['Zcb1', 'ff', 'oops']): + assert ff(bigvalue) == -42 +- # the $ after the AttributeError message are for the suggestions that +- # will be added in Python 3.10 +- assert matches(sys.stderr.getvalue(), """\ +-From cffi callback : +-Trying to convert the result back to C: +-OverflowError: integer 60000 does not fit 'short' +- +-During the call to 'onerror', another exception occurred: +- +-Traceback (most recent call last): +- File "$", line $, in oops +- $ +-AttributeError: 'str' object has no attribute 'append$ +-""", """\ +-Exception ignored from cffi callback , trying to convert the result back to C: +-Traceback (most recent call last): +- File "$", line $, in test_callback_exception +- $ +-OverflowError: integer 60000 does not fit 'short' +-Exception ignored during handling of the above exception by 'onerror': +-Traceback (most recent call last): +- File "$", line $, in oops +- $ +-AttributeError: 'str' object has no attribute 'append$ +-""", """\ +-Exception ignored from cffi callback , trying to convert the result back to C: +-Traceback (most recent call last): +- File "$", line $, in test_callback_exception +- $ +-OverflowError: integer 60000 does not fit 'short' +-Exception ignored during handling of the above exception by 'onerror': +-Traceback (most recent call last): +- File "$", line $, in oops +- $ +- $ +-AttributeError: 'str' object has no attribute 'append$ +-""") +- finally: +- sys.stderr = orig_stderr +- linecache.getline = orig_getline ++ + + def test_callback_return_type(): + for rettype in ["signed char", "short", "int", "long", "long long", diff --git a/py313-use-format-unraisable.patch b/py313-use-format-unraisable.patch new file mode 100644 index 0000000..656dd04 --- /dev/null +++ b/py313-use-format-unraisable.patch @@ -0,0 +1,30 @@ +From 49127c6929bfc7186fbfd3819dd5e058ad888de4 Mon Sep 17 00:00:00 2001 +From: Victor Stinner +Date: Thu, 16 Nov 2023 17:26:12 +0100 +Subject: [PATCH] Use PyErr_FormatUnraisable() on Python 3.13 (#34) + +Use the new public PyErr_FormatUnraisable() on Python 3.13. + +The private _PyErr_WriteUnraisableMsg() function was removed in +Python 3.13: +https://github.com/python/cpython/pull/111643 +--- + src/c/_cffi_backend.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/c/_cffi_backend.c b/src/c/_cffi_backend.c +index 76ed8f09..5e284e00 100644 +--- a/src/c/_cffi_backend.c ++++ b/src/c/_cffi_backend.c +@@ -6118,7 +6118,11 @@ static void _my_PyErr_WriteUnraisable(PyObject *t, PyObject *v, PyObject *tb, + + PyErr_Restore(t, v, tb); + if (s != NULL) { ++#if PY_VERSION_HEX >= 0x030D0000 ++ PyErr_FormatUnraisable("Exception ignored %S", s); ++#else + _PyErr_WriteUnraisableMsg(PyText_AS_UTF8(s), NULL); ++#endif + Py_DECREF(s); + } + else diff --git a/py313-use-hashpointer.patch b/py313-use-hashpointer.patch new file mode 100644 index 0000000..0ee0159 --- /dev/null +++ b/py313-use-hashpointer.patch @@ -0,0 +1,32 @@ +From c27335f0f8e1e4436082236e92ddabe1b8324d22 Mon Sep 17 00:00:00 2001 +From: Matt Davis <6775756+nitzmahone@users.noreply.github.com> +Date: Wed, 7 Feb 2024 09:12:08 -0800 +Subject: [PATCH] use public Py_HashPointer API on 3.13+ (#52) + +* use public Py_HashPointer API on 3.13+ + +* Wrong direction in the conditional + +--------- + +Co-authored-by: Armin Rigo +--- + src/c/_cffi_backend.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/c/_cffi_backend.c b/src/c/_cffi_backend.c +index cb82e16d..14fe9106 100644 +--- a/src/c/_cffi_backend.c ++++ b/src/c/_cffi_backend.c +@@ -2466,7 +2466,11 @@ static Py_hash_t cdata_hash(PyObject *v) + } + Py_DECREF(vv); + } ++#if PY_VERSION_HEX < 0x030D0000 + return _Py_HashPointer(((CDataObject *)v)->c_data); ++#else ++ return Py_HashPointer(((CDataObject *)v)->c_data); ++#endif + } + + static Py_ssize_t diff --git a/python-cffi.changes b/python-cffi.changes index df82b90..f7b6608 100644 --- a/python-cffi.changes +++ b/python-cffi.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Sun Jun 30 21:26:53 UTC 2024 - Dirk Müller + +- add py313-compat.patch + py313-use-format-unraisable.patch + py313-use-hashpointer.patch: add upstream patches for py3.13 + support + ------------------------------------------------------------------- Thu Feb 29 09:46:12 UTC 2024 - Dan Čermák @@ -46,7 +54,7 @@ Thu Apr 13 22:40:28 UTC 2023 - Matej Cepl Fri Jan 6 08:59:54 UTC 2023 - Dirk Müller - add 8a3c2c816d789639b49d3ae867213393ed7abdff.patch to resolve - testsuite failures with Python 3.11 + testsuite failures with Python 3.11 ------------------------------------------------------------------- Mon Dec 5 12:22:40 UTC 2022 - Daniel Garcia @@ -78,7 +86,7 @@ Sat Nov 6 17:37:42 UTC 2021 - Dirk Müller Fri Jul 16 19:47:06 UTC 2021 - Dirk Müller - update to 1.14.6: - * Revert "grovel: detect :float and :double in the :auto type" + * Revert "grovel: detect :float and :double in the :auto type" ------------------------------------------------------------------- Fri Feb 12 20:15:07 UTC 2021 - Dirk Müller @@ -90,19 +98,19 @@ Fri Feb 12 20:15:07 UTC 2021 - Dirk Müller Sat Dec 19 10:46:36 UTC 2020 - Dirk Müller - update to 1.14.4: - * no upstream changelog provided + * no upstream changelog provided ------------------------------------------------------------------- Mon Sep 28 11:23:13 UTC 2020 - Dirk Mueller - update to 1.14.3: - * no upstream changelog provided + * no upstream changelog provided ------------------------------------------------------------------- Thu Aug 20 12:13:23 UTC 2020 - Ondřej Súkup - update to 1.14.2 - * CPython 3 on Windows: we again try to compile with Py_LIMITED_API by default + * CPython 3 on Windows: we again try to compile with Py_LIMITED_API by default ------------------------------------------------------------------- Mon Aug 10 08:10:36 UTC 2020 - Dirk Mueller @@ -274,7 +282,7 @@ Fri Nov 3 15:56:32 UTC 2017 - arun@gmx.de ------------------------------------------------------------------- Wed Oct 4 11:36:56 UTC 2017 - sean.marlow@suse.com -- Update pytest in spec to add c directory tests in addition to +- Update pytest in spec to add c directory tests in addition to testing directory. - Omit test_init_once_multithread tests as they rely on multiple threads finishing in a given time. Returns sporadic pass/fail @@ -335,7 +343,7 @@ Thu Sep 28 15:00:46 UTC 2017 - sean.marlow@suse.com I just noticed, but pycparser always supported the preprocessor directive # 42 "foo.h" to mean “from the next line, we’re in file foo.h starting from line 42”, which it puts in the error - messages. + messages. ------------------------------------------------------------------- Thu Jun 29 16:03:46 UTC 2017 - tbechtold@suse.com diff --git a/python-cffi.spec b/python-cffi.spec index c55d978..cccba51 100644 --- a/python-cffi.spec +++ b/python-cffi.spec @@ -1,7 +1,7 @@ # # spec file for package python-cffi # -# Copyright (c) 2023 SUSE LLC +# Copyright (c) 2024 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -25,6 +25,9 @@ License: MIT URL: https://cffi.readthedocs.org Source0: https://files.pythonhosted.org/packages/source/c/cffi/cffi-%{version}.tar.gz Source1: python-cffi-rpmlintrc +Patch1: https://github.com/python-cffi/cffi/commit/49127c6929bfc7186fbfd3819dd5e058ad888de4.patch#/py313-use-format-unraisable.patch +Patch2: https://github.com/python-cffi/cffi/commit/14723b0bbd127790c450945099db31018d80fa83.patch#/py313-compat.patch +Patch3: https://github.com/python-cffi/cffi/commit/c27335f0f8e1e4436082236e92ddabe1b8324d22.patch#/py313-use-hashpointer.patch BuildRequires: %{python_module devel} BuildRequires: %{python_module pip} BuildRequires: %{python_module pycparser}