aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2021-01-27 17:39:16 +0100
committerGitHub <noreply@github.com>2021-01-27 16:39:16 +0000
commitc9b8e9c421b57acdcaf24fab0c93bc29b3ef7c67 (patch)
treef174c17b926bd968bf19ddd330524bda46e7aa8e /Objects
parentbpo-42979: _zoneinfo exec function checks for PyDateTime_IMPORT failure (GH-2... (diff)
downloadcpython-c9b8e9c421b57acdcaf24fab0c93bc29b3ef7c67.tar.gz
cpython-c9b8e9c421b57acdcaf24fab0c93bc29b3ef7c67.tar.bz2
cpython-c9b8e9c421b57acdcaf24fab0c93bc29b3ef7c67.zip
bpo-42979: Enhance abstract.c assertions checking slot result (GH-24352)
* bpo-42979: Enhance abstract.c assertions checking slot result Add _Py_CheckSlotResult() function which fails with a fatal error if a slot function succeeded with an exception set or failed with no exception set: write the slot name, the type name and the current exception (if an exception is set).
Diffstat (limited to 'Objects')
-rw-r--r--Objects/abstract.c17
-rw-r--r--Objects/call.c36
2 files changed, 39 insertions, 14 deletions
diff --git a/Objects/abstract.c b/Objects/abstract.c
index 44ed5b3932..cc452eaaf3 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -1,11 +1,12 @@
/* Abstract Object Interface (many thanks to Jim Fulton) */
#include "Python.h"
-#include "pycore_unionobject.h" // _Py_UnionType && _Py_Union()
#include "pycore_abstract.h" // _PyIndex_Check()
#include "pycore_ceval.h" // _Py_EnterRecursiveCall()
+#include "pycore_object.h" // _Py_CheckSlotResult()
#include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "pycore_pystate.h" // _PyThreadState_GET()
+#include "pycore_unionobject.h" // _Py_UnionType && _Py_Union()
#include <ctype.h>
#include <stddef.h> // offsetof()
#include "longintrepr.h"
@@ -61,7 +62,7 @@ PyObject_Size(PyObject *o)
m = Py_TYPE(o)->tp_as_sequence;
if (m && m->sq_length) {
Py_ssize_t len = m->sq_length(o);
- assert(len >= 0 || PyErr_Occurred());
+ assert(_Py_CheckSlotResult(o, "__len__", len >= 0));
return len;
}
@@ -160,7 +161,7 @@ PyObject_GetItem(PyObject *o, PyObject *key)
m = Py_TYPE(o)->tp_as_mapping;
if (m && m->mp_subscript) {
PyObject *item = m->mp_subscript(o, key);
- assert((item != NULL) ^ (PyErr_Occurred() != NULL));
+ assert(_Py_CheckSlotResult(o, "__getitem__", item != NULL));
return item;
}
@@ -1564,7 +1565,7 @@ PySequence_Size(PyObject *s)
m = Py_TYPE(s)->tp_as_sequence;
if (m && m->sq_length) {
Py_ssize_t len = m->sq_length(s);
- assert(len >= 0 || PyErr_Occurred());
+ assert(_Py_CheckSlotResult(s, "__len__", len >= 0));
return len;
}
@@ -1708,8 +1709,8 @@ PySequence_GetItem(PyObject *s, Py_ssize_t i)
if (i < 0) {
if (m->sq_length) {
Py_ssize_t l = (*m->sq_length)(s);
+ assert(_Py_CheckSlotResult(s, "__len__", l >= 0));
if (l < 0) {
- assert(PyErr_Occurred());
return NULL;
}
i += l;
@@ -1762,8 +1763,8 @@ PySequence_SetItem(PyObject *s, Py_ssize_t i, PyObject *o)
if (i < 0) {
if (m->sq_length) {
Py_ssize_t l = (*m->sq_length)(s);
+ assert(_Py_CheckSlotResult(s, "__len__", l >= 0));
if (l < 0) {
- assert(PyErr_Occurred());
return -1;
}
i += l;
@@ -1795,8 +1796,8 @@ PySequence_DelItem(PyObject *s, Py_ssize_t i)
if (i < 0) {
if (m->sq_length) {
Py_ssize_t l = (*m->sq_length)(s);
+ assert(_Py_CheckSlotResult(s, "__len__", l >= 0));
if (l < 0) {
- assert(PyErr_Occurred());
return -1;
}
i += l;
@@ -2145,7 +2146,7 @@ PyMapping_Size(PyObject *o)
m = Py_TYPE(o)->tp_as_mapping;
if (m && m->mp_length) {
Py_ssize_t len = m->mp_length(o);
- assert(len >= 0 || PyErr_Occurred());
+ assert(_Py_CheckSlotResult(o, "__len__", len >= 0));
return len;
}
diff --git a/Objects/call.c b/Objects/call.c
index 35b06a9b9c..1fb85efab6 100644
--- a/Objects/call.c
+++ b/Objects/call.c
@@ -39,16 +39,16 @@ _Py_CheckFunctionResult(PyThreadState *tstate, PyObject *callable,
if (!_PyErr_Occurred(tstate)) {
if (callable)
_PyErr_Format(tstate, PyExc_SystemError,
- "%R returned NULL without setting an error",
+ "%R returned NULL without setting an exception",
callable);
else
_PyErr_Format(tstate, PyExc_SystemError,
- "%s returned NULL without setting an error",
+ "%s returned NULL without setting an exception",
where);
#ifdef Py_DEBUG
/* Ensure that the bug is caught in debug mode.
Py_FatalError() logs the SystemError exception raised above. */
- Py_FatalError("a function returned NULL without setting an error");
+ Py_FatalError("a function returned NULL without setting an exception");
#endif
return NULL;
}
@@ -60,17 +60,17 @@ _Py_CheckFunctionResult(PyThreadState *tstate, PyObject *callable,
if (callable) {
_PyErr_FormatFromCauseTstate(
tstate, PyExc_SystemError,
- "%R returned a result with an error set", callable);
+ "%R returned a result with an exception set", callable);
}
else {
_PyErr_FormatFromCauseTstate(
tstate, PyExc_SystemError,
- "%s returned a result with an error set", where);
+ "%s returned a result with an exception set", where);
}
#ifdef Py_DEBUG
/* Ensure that the bug is caught in debug mode.
Py_FatalError() logs the SystemError exception raised above. */
- Py_FatalError("a function returned a result with an error set");
+ Py_FatalError("a function returned a result with an exception set");
#endif
return NULL;
}
@@ -79,6 +79,30 @@ _Py_CheckFunctionResult(PyThreadState *tstate, PyObject *callable,
}
+int
+_Py_CheckSlotResult(PyObject *obj, const char *slot_name, int success)
+{
+ PyThreadState *tstate = _PyThreadState_GET();
+ if (!success) {
+ if (!_PyErr_Occurred(tstate)) {
+ _Py_FatalErrorFormat(__func__,
+ "Slot %s of type %s failed "
+ "without setting an exception",
+ slot_name, Py_TYPE(obj)->tp_name);
+ }
+ }
+ else {
+ if (_PyErr_Occurred(tstate)) {
+ _Py_FatalErrorFormat(__func__,
+ "Slot %s of type %s succeeded "
+ "with an exception set",
+ slot_name, Py_TYPE(obj)->tp_name);
+ }
+ }
+ return 1;
+}
+
+
/* --- Core PyObject call functions ------------------------------- */
/* Call a callable Python object without any arguments */