ext/Python/errors-test.cpp (751 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
#include <cerrno>
#include "Python.h"
#include "gmock/gmock-matchers.h"
#include "gtest/gtest.h"
#include "capi-fixture.h"
#include "capi-testing.h"
namespace py {
namespace testing {
using ErrorsExtensionApiTest = ExtensionApi;
TEST_F(ErrorsExtensionApiTest, CompareErrorMessageOnThread) {
ASSERT_EQ(nullptr, PyErr_Occurred());
PyErr_SetString(PyExc_Exception, "An exception occured");
ASSERT_EQ(PyExc_Exception, PyErr_Occurred());
}
TEST_F(ErrorsExtensionApiTest, SetObjectSetsTypeAndValue) {
PyObject* type = nullptr;
PyObject* value = nullptr;
PyObject* traceback = nullptr;
PyErr_Fetch(&type, &value, &traceback);
EXPECT_EQ(type, nullptr);
EXPECT_EQ(value, nullptr);
EXPECT_EQ(traceback, nullptr);
PyErr_SetObject(PyExc_Exception, Py_True);
PyErr_Fetch(&type, &value, &traceback);
EXPECT_EQ(type, PyExc_Exception);
EXPECT_EQ(value, Py_True);
EXPECT_EQ(traceback, nullptr);
Py_DECREF(type);
Py_DECREF(value);
}
TEST_F(ErrorsExtensionApiTest, SetObjectWithNonExceptionTypeRaisesSystemError) {
PyObjectPtr bool_type(PyObject_Type(Py_True));
PyErr_SetObject(bool_type, Py_None);
EXPECT_EQ(PyErr_Occurred(), PyExc_SystemError);
}
TEST_F(ErrorsExtensionApiTest, SetObjectWithNonTypeRaisesSystemError) {
PyErr_SetObject(Py_True, Py_None);
EXPECT_EQ(PyErr_Occurred(), PyExc_SystemError);
}
TEST_F(ErrorsExtensionApiTest, SetObjectWithPendingExceptionDoesNotAbort) {
ASSERT_EQ(PyErr_Occurred(), nullptr);
PyErr_SetObject(PyExc_TypeError, Py_True);
PyErr_SetObject(PyExc_UserWarning, Py_False);
PyObject* type = nullptr;
PyObject* value = nullptr;
PyObject* traceback = nullptr;
PyErr_Fetch(&type, &value, &traceback);
EXPECT_EQ(type, PyExc_UserWarning);
EXPECT_EQ(value, Py_False);
EXPECT_EQ(traceback, nullptr);
Py_DECREF(type);
Py_DECREF(value);
}
TEST_F(ErrorsExtensionApiTest, ClearClearsExceptionState) {
// Set the exception state
Py_INCREF(PyExc_Exception);
Py_INCREF(Py_True);
PyErr_Restore(PyExc_Exception, Py_True, nullptr);
// Check that an exception is pending
EXPECT_EQ(PyErr_Occurred(), PyExc_Exception);
// Clear the exception
PyErr_Clear();
// Read the exception state again and check for null
PyObject* type = nullptr;
PyObject* value = nullptr;
PyObject* traceback = nullptr;
PyErr_Fetch(&type, &value, &traceback);
EXPECT_EQ(type, nullptr);
EXPECT_EQ(value, nullptr);
EXPECT_EQ(traceback, nullptr);
}
TEST_F(ErrorsExtensionApiTest, BadArgumentRaisesTypeError) {
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_EQ(PyErr_BadArgument(), 0);
PyObject* type = nullptr;
PyObject* value = nullptr;
PyObject* traceback = nullptr;
PyErr_Fetch(&type, &value, &traceback);
EXPECT_EQ(type, PyExc_TypeError);
PyObjectPtr message(
PyUnicode_FromString("bad argument type for built-in operation"));
ASSERT_TRUE(PyUnicode_Check(message));
EXPECT_EQ(_PyUnicode_EQ(value, message), 1);
// TODO(T42241510): Traceback support isn't implemented yet. Once it's ready,
// inspect the traceback here.
Py_DECREF(type);
Py_DECREF(value);
}
TEST_F(ErrorsExtensionApiTest, NewExceptionWithBadNameRaisesSystemError) {
EXPECT_EQ(PyErr_NewException("NameWithoutADot", nullptr, nullptr), nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
}
TEST_F(ErrorsExtensionApiTest, NewExceptionWithoutDictOrBaseReturnsType) {
PyObjectPtr type(PyErr_NewException("Module.Name", nullptr, nullptr));
ASSERT_EQ(PyErr_Occurred(), nullptr);
ASSERT_TRUE(PyType_CheckExact(type));
EXPECT_TRUE(
PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(type.get()),
reinterpret_cast<PyTypeObject*>(PyExc_Exception)));
PyObjectPtr name(PyObject_GetAttrString(type, "__name__"));
ASSERT_TRUE(PyUnicode_CheckExact(name));
EXPECT_TRUE(isUnicodeEqualsCStr(name, "Name"));
PyObjectPtr module_name(PyObject_GetAttrString(type, "__module__"));
ASSERT_TRUE(PyUnicode_CheckExact(module_name));
EXPECT_TRUE(isUnicodeEqualsCStr(module_name, "Module"));
}
TEST_F(ErrorsExtensionApiTest, NewExceptionWithSingleBaseCreatesBasesTuple) {
PyObjectPtr type(
PyErr_NewException("Module.Name", PyExc_ValueError, nullptr));
ASSERT_EQ(PyErr_Occurred(), nullptr);
ASSERT_TRUE(PyType_CheckExact(type));
EXPECT_TRUE(
PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(type.get()),
reinterpret_cast<PyTypeObject*>(PyExc_ValueError)));
PyObjectPtr bases(PyObject_GetAttrString(type, "__bases__"));
ASSERT_TRUE(PyTuple_CheckExact(bases));
EXPECT_EQ(PyTuple_GetItem(bases, 0), PyExc_ValueError);
}
TEST_F(ErrorsExtensionApiTest, NewExceptionWithBaseTupleStoresTuple) {
PyObjectPtr bases(PyTuple_New(2));
Py_INCREF(PyExc_SystemError);
ASSERT_EQ(PyTuple_SetItem(bases, 0, PyExc_SystemError), 0);
Py_INCREF(PyExc_ValueError);
ASSERT_EQ(PyTuple_SetItem(bases, 1, PyExc_ValueError), 0);
PyObjectPtr type(PyErr_NewException("Module.Name", bases, nullptr));
ASSERT_EQ(PyErr_Occurred(), nullptr);
ASSERT_TRUE(PyType_CheckExact(type));
EXPECT_TRUE(
PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(type.get()),
reinterpret_cast<PyTypeObject*>(PyExc_ValueError)));
EXPECT_TRUE(
PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(type.get()),
reinterpret_cast<PyTypeObject*>(PyExc_SystemError)));
PyObjectPtr type_bases(PyObject_GetAttrString(type, "__bases__"));
EXPECT_EQ(type_bases, bases);
}
TEST_F(ErrorsExtensionApiTest, NewExceptionWithEmptyDictAddsModuleName) {
PyObjectPtr dict(PyDict_New());
PyObjectPtr type(PyErr_NewException("Module.Name", nullptr, dict));
ASSERT_EQ(PyErr_Occurred(), nullptr);
ASSERT_TRUE(PyType_CheckExact(type));
PyObjectPtr module_name(PyObject_GetAttrString(type, "__module__"));
ASSERT_TRUE(PyUnicode_CheckExact(module_name));
EXPECT_TRUE(isUnicodeEqualsCStr(module_name, "Module"));
}
TEST_F(ErrorsExtensionApiTest,
NewExceptionWithDocWithNullDocReturnsTypeWithNoneDoc) {
PyObjectPtr type(
PyErr_NewExceptionWithDoc("Module.Name", nullptr, nullptr, nullptr));
ASSERT_EQ(PyErr_Occurred(), nullptr);
ASSERT_TRUE(PyType_CheckExact(type));
EXPECT_TRUE(
PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(type.get()),
reinterpret_cast<PyTypeObject*>(PyExc_Exception)));
PyObjectPtr name(PyObject_GetAttrString(type, "__name__"));
PyObjectPtr module_name(PyObject_GetAttrString(type, "__module__"));
PyObjectPtr doc_string(PyObject_GetAttrString(type, "__doc__"));
EXPECT_TRUE(isUnicodeEqualsCStr(name, "Name"));
EXPECT_TRUE(isUnicodeEqualsCStr(module_name, "Module"));
EXPECT_EQ(doc_string, Py_None);
}
TEST_F(ErrorsExtensionApiTest,
NewExceptionWithDocWithNonDictRaisesSystemError) {
PyObjectPtr not_dict(PyList_New(0));
EXPECT_EQ(PyErr_NewExceptionWithDoc("Module.Name", "DOC", nullptr, not_dict),
nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
}
TEST_F(ErrorsExtensionApiTest, NewExceptionWithDocWithStrReturnsType) {
PyObjectPtr type(
PyErr_NewExceptionWithDoc("Module.Name", "DOC", nullptr, nullptr));
ASSERT_EQ(PyErr_Occurred(), nullptr);
ASSERT_TRUE(PyType_CheckExact(type));
EXPECT_TRUE(
PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(type.get()),
reinterpret_cast<PyTypeObject*>(PyExc_Exception)));
PyObjectPtr name(PyObject_GetAttrString(type, "__name__"));
PyObjectPtr module_name(PyObject_GetAttrString(type, "__module__"));
PyObjectPtr doc_string(PyObject_GetAttrString(type, "__doc__"));
EXPECT_TRUE(isUnicodeEqualsCStr(name, "Name"));
EXPECT_TRUE(isUnicodeEqualsCStr(module_name, "Module"));
EXPECT_TRUE(isUnicodeEqualsCStr(doc_string, "DOC"));
}
TEST_F(ErrorsExtensionApiTest, NoMemoryRaisesMemoryError) {
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_EQ(PyErr_NoMemory(), nullptr);
PyObject* type = nullptr;
PyObject* value = nullptr;
PyObject* traceback = nullptr;
PyErr_Fetch(&type, &value, &traceback);
EXPECT_EQ(type, PyExc_MemoryError);
EXPECT_EQ(value, nullptr);
// TODO(T42241510): Traceback support isn't implemented yet. Once it's ready,
// inspect the traceback here.
Py_DECREF(type);
}
#pragma push_macro("PyErr_BadInternalCall")
#undef PyErr_BadInternalCall
// PyErr_BadInternalCall() has an assert(0) in CPython.
TEST_F(ErrorsExtensionApiTest, BadInternalCallRaisesSystemErrorPyro) {
ASSERT_EQ(PyErr_Occurred(), nullptr);
PyErr_BadInternalCall();
PyObject* type = nullptr;
PyObject* value = nullptr;
PyObject* traceback = nullptr;
PyErr_Fetch(&type, &value, &traceback);
EXPECT_EQ(type, PyExc_SystemError);
PyObjectPtr message(
PyUnicode_FromString("bad argument to internal function"));
ASSERT_TRUE(PyUnicode_Check(message));
EXPECT_EQ(_PyUnicode_EQ(value, message), 1);
// TODO(T42241510): Traceback support isn't implemented yet. Once it's ready,
// inspect the traceback here.
Py_DECREF(type);
Py_DECREF(value);
}
#pragma pop_macro("PyErr_BadInternalCall")
TEST_F(ErrorsExtensionApiTest, UnderBadInternalCallRaisesSystemError) {
ASSERT_EQ(PyErr_Occurred(), nullptr);
_PyErr_BadInternalCall("abc", 123);
PyObject* type = nullptr;
PyObject* value = nullptr;
PyObject* traceback = nullptr;
PyErr_Fetch(&type, &value, &traceback);
EXPECT_EQ(type, PyExc_SystemError);
PyObjectPtr message(
PyUnicode_FromString("abc:123: bad argument to internal function"));
ASSERT_TRUE(PyUnicode_Check(message));
EXPECT_EQ(_PyUnicode_EQ(value, message), 1);
// TODO(T42241510): Traceback support isn't implemented yet. Once it's ready,
// inspect the traceback here.
Py_DECREF(type);
Py_DECREF(value);
}
TEST_F(ErrorsExtensionApiTest, ExceptionMatches) {
ASSERT_EQ(PyErr_Occurred(), nullptr);
PyErr_NoMemory();
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_MemoryError));
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_Exception));
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_BaseException));
}
TEST_F(ErrorsExtensionApiTest, Fetch) {
PyErr_SetObject(PyExc_Exception, Py_True);
PyObject* type = nullptr;
PyObject* value = nullptr;
PyObject* traceback = nullptr;
PyErr_Fetch(&type, &value, &traceback);
EXPECT_EQ(type, PyExc_Exception);
EXPECT_EQ(value, Py_True);
EXPECT_EQ(traceback, nullptr);
Py_DECREF(type);
Py_DECREF(value);
}
TEST_F(ErrorsExtensionApiTest, GetExcInfoWhenNoCaughtException) {
PyObject* p_type;
PyObject* p_value;
PyObject* p_traceback;
PyErr_SetExcInfo(nullptr, nullptr, nullptr);
PyErr_GetExcInfo(&p_type, &p_value, &p_traceback);
EXPECT_EQ(p_type, nullptr);
EXPECT_EQ(p_value, nullptr);
EXPECT_EQ(p_traceback, nullptr);
}
TEST_F(ErrorsExtensionApiTest, GetExcInfoWhenCaughtException) {
binaryfunc func = [](PyObject*, PyObject*) {
PyObject* p_type;
PyObject* p_value;
PyObject* p_traceback;
PyErr_GetExcInfo(&p_type, &p_value, &p_traceback);
EXPECT_EQ(p_type, PyExc_Exception);
PyObjectPtr args(PyObject_GetAttrString(p_value, "args"));
PyObject* first_arg = PyTuple_GetItem(args, 0);
EXPECT_TRUE(isUnicodeEqualsCStr(first_arg, "some str"));
EXPECT_TRUE(PyTraceBack_Check(p_traceback));
Py_INCREF(Py_None);
Py_XDECREF(p_type);
Py_XDECREF(p_value);
Py_XDECREF(p_traceback);
return Py_None;
};
PyMethodDef foo_methods[] = {{"noargs", func, METH_NOARGS}, {nullptr}};
static PyModuleDef def;
def = {
PyModuleDef_HEAD_INIT, "foo", nullptr, 0, foo_methods,
};
PyObjectPtr module(PyModule_Create(&def));
moduleSet("__main__", "foo", module);
ASSERT_TRUE(PyModule_CheckExact(module));
EXPECT_EQ(PyErr_Occurred(), nullptr);
PyRun_SimpleString(R"(
try:
raise Exception('some str')
except:
foo.noargs()
)");
}
TEST_F(ErrorsExtensionApiTest, GivenExceptionMatches) {
// An exception matches itself and all of its super types up to and including
// BaseException.
EXPECT_EQ(PyErr_GivenExceptionMatches(PyExc_MemoryError, PyExc_MemoryError),
1);
EXPECT_EQ(PyErr_GivenExceptionMatches(PyExc_MemoryError, PyExc_Exception), 1);
EXPECT_EQ(PyErr_GivenExceptionMatches(PyExc_MemoryError, PyExc_BaseException),
1);
// An exception should not match a disjoint exception type.
EXPECT_EQ(PyErr_GivenExceptionMatches(PyExc_MemoryError, PyExc_IOError), 0);
// If the objects are not exceptions or exception classes, the matching falls
// back to an identity comparison.
EXPECT_TRUE(PyErr_GivenExceptionMatches(Py_True, Py_True));
}
TEST_F(ErrorsExtensionApiTest, GivenExceptionMatchesWithNullptr) {
// If any argument is a null pointer zero is returned.
EXPECT_EQ(PyErr_GivenExceptionMatches(nullptr, nullptr), 0);
EXPECT_EQ(PyErr_GivenExceptionMatches(PyExc_SystemError, nullptr), 0);
EXPECT_EQ(PyErr_GivenExceptionMatches(nullptr, PyExc_SystemError), 0);
}
TEST_F(ErrorsExtensionApiTest, GivenExceptionMatchesWithTuple) {
PyObject* exc1 = PyTuple_Pack(1, PyExc_Exception);
ASSERT_NE(exc1, nullptr);
EXPECT_EQ(PyErr_GivenExceptionMatches(PyExc_MemoryError, exc1), 1);
EXPECT_EQ(PyErr_GivenExceptionMatches(PyExc_SystemExit, exc1), 0);
Py_DECREF(exc1);
// Linear search
PyObject* exc2 = PyTuple_Pack(2, PyExc_Warning, PyExc_Exception);
ASSERT_NE(exc2, nullptr);
EXPECT_EQ(PyErr_GivenExceptionMatches(PyExc_MemoryError, exc2), 1);
EXPECT_EQ(PyErr_GivenExceptionMatches(PyExc_SystemExit, exc2), 0);
Py_DECREF(exc2);
// Recursion
PyObjectPtr inner(PyTuple_Pack(1, PyExc_Exception));
PyObject* exc3 = PyTuple_Pack(2, inner.get(), PyExc_Warning);
ASSERT_NE(exc3, nullptr);
EXPECT_EQ(PyErr_GivenExceptionMatches(PyExc_MemoryError, exc3), 1);
EXPECT_EQ(PyErr_GivenExceptionMatches(PyExc_SystemExit, exc3), 0);
Py_DECREF(exc3);
}
TEST_F(ErrorsExtensionApiTest, Restore) {
ASSERT_EQ(PyErr_Occurred(), nullptr);
Py_INCREF(PyExc_Exception);
Py_INCREF(Py_True);
PyErr_Restore(PyExc_Exception, Py_True, nullptr);
PyObject* type = nullptr;
PyObject* value = nullptr;
PyObject* traceback = nullptr;
PyErr_Fetch(&type, &value, &traceback);
EXPECT_EQ(type, PyExc_Exception);
EXPECT_EQ(value, Py_True);
EXPECT_EQ(traceback, nullptr);
Py_DECREF(type);
Py_DECREF(value);
}
TEST_F(ErrorsExtensionApiTest, ChainExceptionsSetsContext) {
// First, set an exception.
PyErr_SetString(PyExc_RuntimeError, "whoops");
// Next, attempt to restore a different exception. It should be chained to the
// existing RuntimeError.
PyObject* exc = PyExc_TypeError;
Py_INCREF(exc);
PyObject* val = Py_None;
Py_INCREF(val);
PyObject* tb = Py_None;
Py_INCREF(tb);
_PyErr_ChainExceptions(exc, val, tb);
ASSERT_NE(PyErr_Occurred(), nullptr);
// Make sure the RuntimeError has the new TypeError as its context attribute.
PyErr_Fetch(&exc, &val, &tb);
ASSERT_EQ(PyErr_GivenExceptionMatches(exc, PyExc_RuntimeError), 1);
ASSERT_EQ(PyErr_GivenExceptionMatches(val, PyExc_RuntimeError), 1);
PyObjectPtr ctx(PyException_GetContext(val));
EXPECT_EQ(PyErr_GivenExceptionMatches(ctx, PyExc_TypeError), 1);
EXPECT_EQ(tb, nullptr);
Py_DECREF(exc);
Py_DECREF(val);
}
TEST_F(ErrorsExtensionApiTest, NormalizeCreatesException) {
PyObject* exc = PyExc_RuntimeError;
PyObject* val = PyUnicode_FromString("something went wrong!");
PyObjectPtr val_orig(val);
Py_INCREF(val_orig);
PyObject* tb = nullptr;
PyErr_NormalizeException(&exc, &val, &tb);
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_GivenExceptionMatches(exc, PyExc_RuntimeError));
ASSERT_TRUE(PyErr_GivenExceptionMatches(val, PyExc_RuntimeError));
PyObjectPtr args(PyObject_GetAttrString(val, "args"));
ASSERT_TRUE(PyTuple_CheckExact(args));
ASSERT_EQ(PyTuple_Size(args), 1);
EXPECT_EQ(PyTuple_GetItem(args, 0), val_orig);
Py_DECREF(val);
}
TEST_F(ErrorsExtensionApiTest, NormalizeWithNullTypeDoesNothing) {
PyObject* exc = nullptr;
PyObject* val = nullptr;
PyObject* tb = nullptr;
PyErr_NormalizeException(&exc, &val, &tb);
EXPECT_EQ(PyErr_Occurred(), nullptr);
EXPECT_EQ(exc, nullptr);
EXPECT_EQ(val, nullptr);
EXPECT_EQ(tb, nullptr);
}
TEST_F(ErrorsExtensionApiTest, NormalizeWithNullValueUsesNone) {
PyObject* exc = PyExc_TypeError;
PyObject* val = Py_None;
Py_INCREF(val);
PyObject* tb = nullptr;
PyErr_NormalizeException(&exc, &val, &tb);
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_GivenExceptionMatches(exc, PyExc_TypeError));
ASSERT_TRUE(PyErr_GivenExceptionMatches(val, PyExc_TypeError));
PyObjectPtr args(PyObject_GetAttrString(val, "args"));
ASSERT_TRUE(PyTuple_CheckExact(args));
EXPECT_EQ(PyTuple_Size(args), 0);
Py_DECREF(val);
}
TEST_F(ErrorsExtensionApiTest, NormalizeWithTupleUsesArgs) {
PyObject* exc = PyExc_Exception;
PyObject* val = PyTuple_New(2);
PyObjectPtr t0(PyLong_FromLong(111));
PyObjectPtr t1(PyUnicode_FromString("hello"));
Py_INCREF(t0);
PyTuple_SET_ITEM(val, 0, t0);
Py_INCREF(t1);
PyTuple_SET_ITEM(val, 1, t1);
PyObject* tb = nullptr;
PyErr_NormalizeException(&exc, &val, &tb);
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_GivenExceptionMatches(exc, PyExc_Exception));
ASSERT_TRUE(PyErr_GivenExceptionMatches(val, PyExc_Exception));
PyObjectPtr args(PyObject_GetAttrString(val, "args"));
ASSERT_TRUE(PyTuple_CheckExact(args));
ASSERT_EQ(PyTuple_Size(args), 2);
EXPECT_EQ(PyTuple_GetItem(args, 0), t0);
EXPECT_EQ(PyTuple_GetItem(args, 1), t1);
Py_DECREF(val);
}
TEST_F(ErrorsExtensionApiTest, NormalizeWithNonExceptionDoesNothing) {
PyObject *exc = PyLong_FromLong(123), *exc_orig = exc;
PyObject *val = PyLong_FromLong(456), *val_orig = val;
PyObject* tb = nullptr;
PyErr_NormalizeException(&exc, &val, &tb);
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_EQ(exc, exc_orig);
EXPECT_EQ(val, val_orig);
EXPECT_EQ(tb, nullptr);
Py_DECREF(val);
Py_DECREF(exc);
}
TEST_F(ErrorsExtensionApiTest, NormalizeWithFailingConstructorReturnsNewError) {
// TODO(bsimmers): Once we have PyType_FromSpec() (or PyType_Ready() can
// handle base classes), add a similar test to ensure that
// PyErr_NormalizeException() doesn't loop infinintely when normalization
// keeps failing.
ASSERT_EQ(PyRun_SimpleString(R"(
class BadException(Exception):
def __init__(self, arg):
raise RuntimeError(arg)
)"),
0);
PyObject* exc = mainModuleGet("BadException");
ASSERT_TRUE(PyType_Check(exc));
const char* msg = "couldn't construct BadException";
PyObject* val = PyUnicode_FromString(msg);
PyObject* tb = nullptr;
PyErr_NormalizeException(&exc, &val, &tb);
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_GivenExceptionMatches(exc, PyExc_RuntimeError));
ASSERT_TRUE(PyErr_GivenExceptionMatches(val, PyExc_RuntimeError));
PyObjectPtr args(PyObject_GetAttrString(val, "args"));
ASSERT_TRUE(PyTuple_CheckExact(args));
ASSERT_EQ(PyTuple_Size(args), 1);
PyObject* str = PyTuple_GetItem(args, 0);
EXPECT_TRUE(isUnicodeEqualsCStr(str, msg));
Py_XDECREF(val);
Py_XDECREF(exc);
Py_XDECREF(tb);
}
TEST_F(ErrorsExtensionApiTest, ProgramTextObjectWithNullFilenameReturnsNull) {
EXPECT_EQ(PyErr_ProgramTextObject(nullptr, 5), nullptr);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ErrorsExtensionApiTest,
ProgramTextObjectWithNonPositiveLinenoReturnsNull) {
PyObjectPtr filename(PyUnicode_FromString("filename"));
EXPECT_EQ(PyErr_ProgramTextObject(filename, -5), nullptr);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ErrorsExtensionApiTest,
ProgramTextObjectWithNonExistentFileReturnsNull) {
PyObjectPtr filename(PyUnicode_FromString("foobarbazquux"));
EXPECT_EQ(PyErr_ProgramTextObject(filename, 5), nullptr);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ErrorsExtensionApiTest, SetExcInfoValuesRetrievedByGetExcInfo) {
PyObjectPtr type(PyExc_TypeError);
Py_INCREF(type);
PyObjectPtr val(PyUnicode_FromString("some str"));
PyObject* traceback = nullptr;
PyErr_SetExcInfo(type, val, traceback);
PyObject* p_type;
PyObject* p_value;
PyObject* p_traceback;
PyErr_GetExcInfo(&p_type, &p_value, &p_traceback);
EXPECT_EQ(p_type, type);
EXPECT_EQ(p_value, val);
// TODO(T77866913): EXPECT_EQ(p_traceback, traceback);
}
TEST_F(ErrorsExtensionApiTest, SetFromErrnoWithZeroSetsError) {
errno = 0;
ASSERT_EQ(PyErr_SetFromErrno(PyExc_TypeError), nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ErrorsExtensionApiTest, SetFromErrnoWithNonZeroSetsError) {
errno = 1;
ASSERT_EQ(PyErr_SetFromErrno(PyExc_SystemError), nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
}
TEST_F(ErrorsExtensionApiTest,
SetFromErrnoWithInterruptRaisesKeyboardInterrupt) {
PyErr_SetInterrupt();
errno = EINTR;
ASSERT_EQ(PyErr_SetFromErrno(PyExc_SystemError), nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_KeyboardInterrupt));
}
TEST_F(ErrorsExtensionApiTest, SetFromErrnoWithFilenameSetsError) {
errno = 1;
ASSERT_EQ(PyErr_SetFromErrnoWithFilename(PyExc_NameError, "foo"), nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_NameError));
}
TEST_F(ErrorsExtensionApiTest, SetFromErrnoWithFilenameObjectSetsError) {
errno = 1;
PyObjectPtr foo(PyUnicode_FromString("foo"));
ASSERT_EQ(PyErr_SetFromErrnoWithFilenameObject(PyExc_KeyError, foo), nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_KeyError));
}
TEST_F(ErrorsExtensionApiTest, SetFromErrnoWithFilenameObjectsSetsError) {
errno = 1;
PyObjectPtr foo(PyUnicode_FromString("foo"));
PyObjectPtr bar(PyUnicode_FromString("bar"));
ASSERT_EQ(
PyErr_SetFromErrnoWithFilenameObjects(PyExc_ChildProcessError, foo, bar),
nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_ChildProcessError));
}
TEST_F(ErrorsExtensionApiTest, SetNoneCreatesExceptionWithNoArgs) {
PyErr_SetNone(PyExc_Exception);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_Exception));
PyObject* type = nullptr;
PyObject* value = nullptr;
PyObject* traceback = nullptr;
// Fetch the non-normalized error
PyErr_Fetch(&type, &value, &traceback);
EXPECT_EQ(type, PyExc_Exception);
ASSERT_EQ(value, nullptr);
// Normalize the exception
PyErr_NormalizeException(&type, &value, &traceback);
EXPECT_EQ(type, PyExc_Exception);
PyObjectPtr args(PyObject_GetAttrString(value, "args"));
ASSERT_TRUE(PyTuple_CheckExact(args));
ASSERT_EQ(PyTuple_Size(args), 0);
Py_DECREF(type);
Py_DECREF(value);
}
TEST_F(ErrorsExtensionApiTest, SetStringSetsValue) {
PyErr_SetString(PyExc_Exception, "An exception occured");
PyObject* type = nullptr;
PyObject* value = nullptr;
PyObject* traceback = nullptr;
PyErr_Fetch(&type, &value, &traceback);
EXPECT_EQ(traceback, nullptr);
EXPECT_EQ(type, PyExc_Exception);
EXPECT_TRUE(isUnicodeEqualsCStr(value, "An exception occured"));
Py_DECREF(type);
Py_DECREF(value);
}
TEST_F(ErrorsExtensionApiTest, FormatWithNoArgsSetsAppropriateFields) {
ASSERT_EQ(PyErr_Format(PyExc_TypeError, "hello error"), nullptr);
PyObject* type = nullptr;
PyObject* value = nullptr;
PyObject* traceback = nullptr;
PyErr_Fetch(&type, &value, &traceback);
EXPECT_EQ(type, PyExc_TypeError);
EXPECT_TRUE(isUnicodeEqualsCStr(value, "hello error"));
EXPECT_EQ(traceback, nullptr);
Py_DECREF(type);
Py_DECREF(value);
}
TEST_F(ErrorsExtensionApiTest, FormatWithManyArgsSetsAppropriateFields) {
ASSERT_EQ(PyErr_Format(PyExc_MemoryError, "h%c%s", 'e', "llo world"),
nullptr);
PyObject* type = nullptr;
PyObject* value = nullptr;
PyObject* traceback = nullptr;
PyErr_Fetch(&type, &value, &traceback);
EXPECT_EQ(type, PyExc_MemoryError);
EXPECT_TRUE(isUnicodeEqualsCStr(value, "hello world"));
EXPECT_EQ(traceback, nullptr);
Py_DECREF(type);
Py_DECREF(value);
}
TEST_F(ErrorsExtensionApiTest, FormatFromCauseWithoutExceptionFailsDeathTest) {
EXPECT_DEATH(_PyErr_FormatFromCause(PyExc_TypeError, ""), "");
}
TEST_F(ErrorsExtensionApiTest, FormatFromCauseSetsCauseAndContext) {
ASSERT_EQ(PyErr_Format(PyExc_MemoryError, "%s", "original cause"), nullptr);
ASSERT_EQ(_PyErr_FormatFromCause(PyExc_TypeError, "%s", "new error"),
nullptr);
PyObject* type = nullptr;
PyObject* value = nullptr;
PyObject* traceback = nullptr;
PyErr_Fetch(&type, &value, &traceback);
EXPECT_EQ(type, PyExc_TypeError);
Py_XDECREF(type);
EXPECT_EQ(traceback, nullptr);
Py_XDECREF(traceback);
PyObjectPtr cause(PyException_GetCause(value));
PyObjectPtr context(PyException_GetContext(value));
EXPECT_TRUE(PyErr_GivenExceptionMatches(cause, PyExc_MemoryError));
EXPECT_TRUE(PyErr_GivenExceptionMatches(context, PyExc_MemoryError));
Py_XDECREF(value);
}
TEST_F(ErrorsExtensionApiTest, WriteUnraisableClearsException) {
PyErr_SetString(PyExc_MemoryError, "original cause");
PyErr_WriteUnraisable(Py_None);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ErrorsExtensionApiTest, WriteUnraisableCallsDunderRepr) {
PyRun_SimpleString(R"(
class C:
def __repr__(self):
return "foo"
c = C()
)");
PyObjectPtr c(mainModuleGet("c"));
PyErr_SetString(PyExc_MemoryError, "original cause");
CaptureStdStreams streams;
PyErr_WriteUnraisable(c);
EXPECT_EQ(PyErr_Occurred(), nullptr);
EXPECT_THAT(streams.err(),
::testing::StartsWith("Exception ignored in: foo"));
EXPECT_EQ(streams.out(), "");
}
TEST_F(ErrorsExtensionApiTest,
WriteUnraisableDoesNotFailWithNonCallableDunderRepr) {
PyRun_SimpleString(R"(
class C:
__repr__ = 5
c = C()
)");
PyObjectPtr c(mainModuleGet("c"));
PyErr_SetString(PyExc_MemoryError, "original cause");
CaptureStdStreams streams;
PyErr_WriteUnraisable(c);
EXPECT_EQ(PyErr_Occurred(), nullptr);
EXPECT_THAT(
streams.err(),
::testing::StartsWith("Exception ignored in: <object repr() failed>"));
EXPECT_EQ(streams.out(), "");
}
TEST_F(ErrorsExtensionApiTest,
WriteUnraisableWithNonStrDunderModuleWritesUnknown) {
PyRun_SimpleString(R"(
class C(BaseException):
pass
C.__module__ = 5
c = C()
)");
PyObjectPtr c(mainModuleGet("c"));
PyObjectPtr ctype(mainModuleGet("C"));
PyErr_SetString(ctype, "original cause");
CaptureStdStreams streams;
PyErr_WriteUnraisable(c);
EXPECT_EQ(PyErr_Occurred(), nullptr);
EXPECT_THAT(streams.err(),
::testing::EndsWith("<unknown>C: original cause\n"));
EXPECT_EQ(streams.out(), "");
}
TEST_F(ErrorsExtensionApiTest, WriteUnraisableWritesModuleName) {
PyRun_SimpleString(R"(
class C(BaseException):
pass
C.__module__ = "foo"
c = C()
)");
PyObjectPtr c(mainModuleGet("c"));
PyObjectPtr ctype(mainModuleGet("C"));
PyErr_SetString(ctype, "original cause");
CaptureStdStreams streams;
PyErr_WriteUnraisable(c);
EXPECT_EQ(PyErr_Occurred(), nullptr);
EXPECT_THAT(streams.err(), ::testing::EndsWith("foo.C: original cause\n"));
EXPECT_EQ(streams.out(), "");
}
TEST_F(ErrorsExtensionApiTest, WriteUnraisableCallsDunderStrOnVal) {
PyRun_SimpleString(R"(
class C:
def __str__(self):
return "bar"
C.__module__ = "foo"
c = C()
)");
PyObjectPtr c(mainModuleGet("c"));
PyErr_SetObject(PyExc_MemoryError, c);
CaptureStdStreams streams;
PyErr_WriteUnraisable(Py_None);
EXPECT_EQ(PyErr_Occurred(), nullptr);
EXPECT_THAT(streams.err(), ::testing::EndsWith("MemoryError: bar\n"));
EXPECT_EQ(streams.out(), "");
}
TEST_F(ErrorsExtensionApiTest,
WriteUnraisableDoesNotFailWithNonCallableDunderStr) {
PyRun_SimpleString(R"(
class C:
__str__ = 5
C.__module__ = "foo"
c = C()
)");
PyObjectPtr c(mainModuleGet("c"));
PyErr_SetObject(PyExc_MemoryError, c);
CaptureStdStreams streams;
PyErr_WriteUnraisable(Py_None);
EXPECT_EQ(PyErr_Occurred(), nullptr);
EXPECT_THAT(streams.err(),
::testing::EndsWith("MemoryError: <exception str() failed>\n"));
EXPECT_EQ(streams.out(), "");
}
TEST_F(ErrorsExtensionApiTest, SetObjectWithCaughtExceptionSetsContext) {
PyCFunction test_set_object = [](PyObject*, PyObject*) -> PyObject* {
PyErr_SetString(PyExc_ValueError, "something went wrong");
return nullptr;
};
static PyMethodDef methods[2];
methods[0] = {
"test_set_object",
test_set_object,
METH_NOARGS,
"doc",
};
methods[1] = {nullptr};
static PyModuleDef mod_def;
mod_def = {
PyModuleDef_HEAD_INIT,
"errors_test", // m_name
"doc", // m_doc
0, // m_size
methods, // m_methods
nullptr, // m_slots
nullptr, // m_traverse
nullptr, // m_clear
nullptr, // m_free
};
PyObjectPtr module(PyModule_Create(&mod_def));
ASSERT_NE(module, nullptr);
ASSERT_EQ(moduleSet("__main__", "errors_test", module), 0);
ASSERT_EQ(PyRun_SimpleString(R"(
try:
try:
raise RuntimeError("blorp")
except RuntimeError as exc:
inner_exc = exc
errors_test.test_set_object()
except ValueError as exc:
outer_exc = exc
)"),
0);
PyObjectPtr inner_exc(mainModuleGet("inner_exc"));
ASSERT_NE(inner_exc, nullptr);
PyObjectPtr outer_exc(mainModuleGet("outer_exc"));
ASSERT_NE(outer_exc, nullptr);
PyObjectPtr outer_ctx(PyException_GetContext(outer_exc));
EXPECT_EQ(outer_ctx, inner_exc);
EXPECT_EQ(PyException_GetContext(inner_exc), nullptr);
}
} // namespace testing
} // namespace py