diff options
author | Armin Rigo <arigo@tunes.org> | 2021-06-20 21:14:08 +0200 |
---|---|---|
committer | Armin Rigo <arigo@tunes.org> | 2021-06-20 21:14:08 +0200 |
commit | 8f7155f8820223ed46fb4259fc688abad5ac9c25 (patch) | |
tree | 35f184cc2ebdb0b9cf0186e3a3748977669b75f1 | |
parent | 25ba410f60e9150e0f0f1fb4dbac239534d4e51e (diff) | |
parent | b9cbd7792003f9a262cfaf5ff6a1bc553e5ab6e4 (diff) | |
download | cffi-8f7155f8820223ed46fb4259fc688abad5ac9c25.tar.gz |
hg merge default
-rw-r--r-- | c/_cffi_backend.c | 66 | ||||
-rw-r--r-- | c/ffi_obj.c | 2 | ||||
-rw-r--r-- | c/test_c.py | 46 | ||||
-rw-r--r-- | cffi/verifier.py | 3 | ||||
-rw-r--r-- | doc/source/goals.rst | 4 | ||||
-rw-r--r-- | doc/source/whatsnew.rst | 11 | ||||
-rw-r--r-- | setup.py | 2 | ||||
-rw-r--r-- | testing/cffi0/test_function.py | 10 | ||||
-rw-r--r-- | testing/cffi1/test_recompiler.py | 7 | ||||
-rw-r--r-- | testing/support.py | 5 |
10 files changed, 135 insertions, 21 deletions
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index 23304d3..6652803 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -180,6 +180,10 @@ # define Py_SET_REFCNT(obj, val) (Py_REFCNT(obj) = (val)) #endif +#if PY_VERSION_HEX >= 0x03080000 +# define USE_WRITEUNRAISABLEMSG +#endif + /************************************************************/ /* base type flag: exactly one of the following: */ @@ -4516,14 +4520,18 @@ static void *b_do_dlopen(PyObject *args, const char **p_printable_filename, if (PyUnicode_Check(s)) { s = PyUnicode_AsUTF8String(s); - if (s == NULL) + if (s == NULL) { + PyMem_Free(filename_or_null); return NULL; + } *p_temp = s; } #endif *p_printable_filename = PyText_AsUTF8(s); - if (*p_printable_filename == NULL) + if (*p_printable_filename == NULL) { + PyMem_Free(filename_or_null); return NULL; + } } if ((flags & (RTLD_NOW | RTLD_LAZY)) == 0) flags |= RTLD_NOW; @@ -4536,6 +4544,7 @@ static void *b_do_dlopen(PyObject *args, const char **p_printable_filename, #endif handle = dlopen(filename_or_null, flags); + PyMem_Free(filename_or_null); #ifdef MS_WIN32 got_handle: @@ -5849,7 +5858,7 @@ static cif_description_t *fb_prepare_cif(PyObject *fargs, char *buffer; cif_description_t *cif_descr; struct funcbuilder_s funcbuffer; - ffi_status status = -1; + ffi_status status = (ffi_status)-1; funcbuffer.nb_bytes = 0; funcbuffer.bufferp = NULL; @@ -5884,7 +5893,7 @@ static cif_description_t *fb_prepare_cif(PyObject *fargs, } #endif - if (status == -1) { + if (status == (ffi_status)-1) { status = ffi_prep_cif(&cif_descr->cif, fabi, funcbuffer.nargs, funcbuffer.rtype, funcbuffer.atypes); } @@ -6061,6 +6070,43 @@ static void _my_PyErr_WriteUnraisable(PyObject *t, PyObject *v, PyObject *tb, char *extra_error_line) { /* like PyErr_WriteUnraisable(), but write a full traceback */ +#ifdef USE_WRITEUNRAISABLEMSG + + /* PyErr_WriteUnraisable actually writes the full traceback anyway + from Python 3.4, but we can't really get the formatting of the + custom text to be what we want. We can do better from Python + 3.8 by calling the new _PyErr_WriteUnraisableMsg(). + Luckily it's also Python 3.8 that adds new functionality that + people might want: the new sys.unraisablehook(). + */ + PyObject *s; + int first_char; + assert(objdescr != NULL && objdescr[0] != 0); /* non-empty */ + first_char = objdescr[0]; + if (first_char >= 'A' && first_char <= 'Z') + first_char += 'a' - 'A'; /* lower() the very first character */ + if (extra_error_line == NULL) + extra_error_line = ""; + + if (obj != NULL) + s = PyUnicode_FromFormat("%c%s%R%s", + first_char, objdescr + 1, obj, extra_error_line); + else + s = PyUnicode_FromFormat("%c%s%s", + first_char, objdescr + 1, extra_error_line); + + PyErr_Restore(t, v, tb); + if (s != NULL) { + _PyErr_WriteUnraisableMsg(PyText_AS_UTF8(s), NULL); + Py_DECREF(s); + } + else + PyErr_WriteUnraisable(obj); /* best effort */ + PyErr_Clear(); + +#else + + /* version for Python 2.7 and < 3.8 */ PyObject *f; #if PY_MAJOR_VERSION >= 3 /* jump through hoops to ensure the tb is attached to v, on Python 3 */ @@ -6085,6 +6131,8 @@ static void _my_PyErr_WriteUnraisable(PyObject *t, PyObject *v, PyObject *tb, Py_XDECREF(t); Py_XDECREF(v); Py_XDECREF(tb); + +#endif } static void general_invoke_callback(int decode_args_from_libffi, @@ -6134,7 +6182,11 @@ static void general_invoke_callback(int decode_args_from_libffi, goto error; if (convert_from_object_fficallback(result, SIGNATURE(1), py_res, decode_args_from_libffi) < 0) { +#ifdef USE_WRITEUNRAISABLEMSG + extra_error_line = ", trying to convert the result back to C"; +#else extra_error_line = "Trying to convert the result back to C:\n"; +#endif goto error; } done: @@ -6186,10 +6238,16 @@ static void general_invoke_callback(int decode_args_from_libffi, _my_PyErr_WriteUnraisable(exc1, val1, tb1, "From cffi callback ", py_ob, extra_error_line); +#ifdef USE_WRITEUNRAISABLEMSG + _my_PyErr_WriteUnraisable(exc2, val2, tb2, + "during handling of the above exception by 'onerror'", + NULL, NULL); +#else extra_error_line = ("\nDuring the call to 'onerror', " "another exception occurred:\n\n"); _my_PyErr_WriteUnraisable(exc2, val2, tb2, NULL, NULL, extra_error_line); +#endif _cffi_stop_error_capture(ecap); } } diff --git a/c/ffi_obj.c b/c/ffi_obj.c index 05c3a6d..f154146 100644 --- a/c/ffi_obj.c +++ b/c/ffi_obj.c @@ -1070,10 +1070,10 @@ static PyObject *ffi_init_once(FFIObject *self, PyObject *args, PyObject *kwds) if (res != NULL) { tup = PyTuple_Pack(2, Py_True, res); if (tup == NULL || PyDict_SetItem(cache, tag, tup) < 0) { - Py_XDECREF(tup); Py_DECREF(res); res = NULL; } + Py_XDECREF(tup); } } diff --git a/c/test_c.py b/c/test_c.py index 69ad15e..6c2e5a7 100644 --- a/c/test_c.py +++ b/c/test_c.py @@ -1331,7 +1331,9 @@ def test_callback_exception(): except ImportError: import io as cStringIO # Python 3 import linecache - def matches(istr, ipattern): + def matches(istr, ipattern, ipattern38): + if sys.version_info >= (3, 8): + ipattern = ipattern38 str, pattern = istr, ipattern while '$' in pattern: i = pattern.index('$') @@ -1364,6 +1366,8 @@ def test_callback_exception(): 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 assert f(100) == 300 assert sys.stderr.getvalue() == '' assert f(10000) == -42 @@ -1375,6 +1379,14 @@ Traceback (most recent call last): File "$", line $, in check_value $ ValueError: 42 +""", """\ +Exception ignored from cffi callback <function$Zcb1 at 0x$>: +Traceback (most recent call last): + File "$", line $, in Zcb1 + $ + File "$", line $, in check_value + $ +ValueError: 42 """) sys.stderr = cStringIO.StringIO() bigvalue = 20000 @@ -1383,6 +1395,12 @@ ValueError: 42 From cffi callback <function$Zcb1 at 0x$>: Trying to convert the result back to C: OverflowError: integer 60000 does not fit 'short' +""", """\ +Exception ignored from cffi callback <function$Zcb1 at 0x$>, 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 @@ -1420,11 +1438,24 @@ OverflowError: integer 60000 does not fit 'short' During the call to 'onerror', another exception occurred: TypeError: $integer$ +""", """\ +Exception ignored from cffi callback <function$Zcb1 at 0x$>, 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 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 <function$Zcb1 at 0x$>: Trying to convert the result back to C: @@ -1435,7 +1466,18 @@ During the call to 'onerror', another exception occurred: Traceback (most recent call last): File "$", line $, in oops $ -AttributeError: 'str' object has no attribute 'append' +AttributeError: 'str' object has no attribute 'append$ +""", """\ +Exception ignored from cffi callback <function$Zcb1 at 0x$>, 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 diff --git a/cffi/verifier.py b/cffi/verifier.py index 59b78c2..a500c78 100644 --- a/cffi/verifier.py +++ b/cffi/verifier.py @@ -50,7 +50,8 @@ class Verifier(object): if tag: raise TypeError("can't specify both 'modulename' and 'tag'") else: - key = '\x00'.join([sys.version[:3], __version_verifier_modules__, + key = '\x00'.join(['%d.%d' % sys.version_info[:2], + __version_verifier_modules__, preamble, flattened_kwds] + ffi._cdefsources) if sys.version_info >= (3,): diff --git a/doc/source/goals.rst b/doc/source/goals.rst index f66df21..df4877c 100644 --- a/doc/source/goals.rst +++ b/doc/source/goals.rst @@ -55,8 +55,8 @@ Get started by reading `the overview`__. Comments and bugs ----------------- -The best way to contact us is on the IRC ``#pypy`` channel of -``irc.freenode.net``. Feel free to discuss matters either there or in +The best way to contact us is on the IRC ``#cffi`` or ``#pypy`` channels of +``irc.libera.chat``. Feel free to discuss matters either there or in the `mailing list`_. Please report to the `issue tracker`_ any bugs. As a general rule, when there is a design issue to resolve, we pick the diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst index f6111d5..ff09168 100644 --- a/doc/source/whatsnew.rst +++ b/doc/source/whatsnew.rst @@ -2,6 +2,17 @@ What's New ====================== +v1.14.6 +======= + +* Test fixes for CPython 3.10.0b3 + +* Support for `sys.unraisablehook()` on Python >= 3.8 + +* Fix two minor memory leaks (thanks Sebastian!) + +* Like many projects that had an IRC channel on freenode, we moved it to + ``irc.libera.chat``. v1.14.5 ======= @@ -56,7 +56,7 @@ def no_working_compiler_found(): tries to compile C code. (Hints: on OS/X 10.8, for errors about -mno-fused-madd see http://stackoverflow.com/questions/22313407/ Otherwise, see https://wiki.python.org/moin/CompLangPython or - the IRC channel #python on irc.freenode.net.) + the IRC channel #python on irc.libera.chat.) Trying to continue anyway. If you are trying to install CFFI from a build done in a different context, you can ignore this warning. diff --git a/testing/cffi0/test_function.py b/testing/cffi0/test_function.py index 4f2c864..b4bb23d 100644 --- a/testing/cffi0/test_function.py +++ b/testing/cffi0/test_function.py @@ -5,7 +5,7 @@ import math, os, sys import ctypes.util from cffi.backend_ctypes import CTypesBackend from testing.udir import udir -from testing.support import FdWriteCapture +from testing.support import FdWriteCapture, StdErrCapture from .backend_tests import needs_dlopen_none try: @@ -227,13 +227,9 @@ class TestFunction(object): def cb(): return returnvalue fptr = ffi.callback("void(*)(void)", cb) - old_stderr = sys.stderr - try: - sys.stderr = StringIO() + with StdErrCapture() as f: returned = fptr() - printed = sys.stderr.getvalue() - finally: - sys.stderr = old_stderr + printed = f.getvalue() assert returned is None if returnvalue is None: assert printed == '' diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py index b35c3eb..fdb4d5a 100644 --- a/testing/cffi1/test_recompiler.py +++ b/testing/cffi1/test_recompiler.py @@ -1669,9 +1669,10 @@ def test_extern_python_bogus_result_type(): with StdErrCapture() as f: res = lib.bar(321) assert res is None - assert f.getvalue() == ( - "From cffi callback %r:\n" % (bar,) + - "Trying to convert the result back to C:\n" + msg = f.getvalue() + assert "rom cffi callback %r" % (bar,) in msg + assert "rying to convert the result back to C:\n" in msg + assert msg.endswith( "TypeError: callback with the return type 'void' must return None\n") def test_extern_python_redefine(): diff --git a/testing/support.py b/testing/support.py index de8131c..6339a94 100644 --- a/testing/support.py +++ b/testing/support.py @@ -33,9 +33,14 @@ class StdErrCapture(object): from io import StringIO self.old_stderr = sys.stderr sys.stderr = f = StringIO() + if hasattr(sys, '__unraisablehook__'): # work around pytest + self.old_unraisablebook = sys.unraisablehook # on recent CPythons + sys.unraisablehook = sys.__unraisablehook__ return f def __exit__(self, *args): sys.stderr = self.old_stderr + if hasattr(self, 'old_unraisablebook'): + sys.unraisablehook = self.old_unraisablebook class FdWriteCapture(object): |