ext/Objects/object-test.cpp (1,371 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
#include "Python.h"
#include "gtest/gtest.h"
#include "capi-fixture.h"
#include "capi-testing.h"
namespace py {
namespace testing {
using ObjectExtensionApiTest = ExtensionApi;
TEST_F(ObjectExtensionApiTest, PyNoneIdentityIsEqual) {
// Test Identitiy
PyObject* none1 = Py_None;
PyObject* none2 = Py_None;
EXPECT_EQ(none1, none2);
}
TEST_F(ObjectExtensionApiTest, PyNotImplementedIdentityIsEqual) {
// Test Identitiy
PyObject* not_impl1 = Py_NotImplemented;
PyObject* not_impl2 = Py_NotImplemented;
EXPECT_EQ(not_impl1, not_impl2);
}
TEST_F(ObjectExtensionApiTest, BytesWithNullReturnsBytes) {
PyObjectPtr result(PyObject_Bytes(nullptr));
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_STREQ(PyBytes_AsString(result), "<NULL>");
}
TEST_F(ObjectExtensionApiTest, BytesWithBytesReturnsSameObject) {
PyObjectPtr bytes(PyBytes_FromString("hello"));
PyObjectPtr result(PyObject_Bytes(bytes));
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_EQ(result, bytes);
}
TEST_F(ObjectExtensionApiTest, BytesWithBadDunderBytesRaisesTypeError) {
PyRun_SimpleString(R"(
class Foo:
def __bytes__(self):
return 1
obj = Foo()
)");
PyObjectPtr obj(mainModuleGet("obj"));
ASSERT_EQ(PyObject_Bytes(obj), nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest, BytesWithDunderBytesReturnsBytes) {
PyRun_SimpleString(R"(
class Foo:
def __bytes__(self):
return b'123'
obj = Foo()
)");
PyObjectPtr obj(mainModuleGet("obj"));
PyObjectPtr result(PyObject_Bytes(obj));
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_STREQ(PyBytes_AsString(result), "123");
}
TEST_F(ObjectExtensionApiTest, BytesWithDunderBytesErrorRaisesValueError) {
PyRun_SimpleString(R"(
class Foo:
def __bytes__(self):
raise ValueError
obj = Foo()
)");
PyObjectPtr obj(mainModuleGet("obj"));
ASSERT_EQ(PyObject_Bytes(obj), nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_ValueError));
}
TEST_F(ObjectExtensionApiTest, BytesWithListOfByteReturnsBytes) {
PyObjectPtr list(PyList_New(2));
ASSERT_EQ(PyList_SetItem(list, 0, PyLong_FromLong('h')), 0);
ASSERT_EQ(PyList_SetItem(list, 1, PyLong_FromLong('i')), 0);
PyObjectPtr result(PyObject_Bytes(list));
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_STREQ(PyBytes_AsString(result), "hi");
}
TEST_F(ObjectExtensionApiTest, BytesWithTupleOfByteReturnsBytes) {
PyObjectPtr tuple(PyTuple_New(2));
ASSERT_EQ(PyTuple_SetItem(tuple, 0, PyLong_FromLong('h')), 0);
ASSERT_EQ(PyTuple_SetItem(tuple, 1, PyLong_FromLong('i')), 0);
PyObjectPtr result(PyObject_Bytes(tuple));
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_STREQ(PyBytes_AsString(result), "hi");
}
TEST_F(ObjectExtensionApiTest, BytesWithStringRaisesTypeError) {
PyObjectPtr str(PyUnicode_FromString("hello"));
EXPECT_EQ(PyObject_Bytes(str), nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest, CallableCheckWithNullReturnsZero) {
EXPECT_EQ(PyCallable_Check(nullptr), 0);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest, CallableCheckWithNoneDunderCallReturnsOne) {
PyRun_SimpleString(R"(
class C:
__call__ = None
c = C()
)");
PyObjectPtr c(mainModuleGet("c"));
EXPECT_EQ(PyCallable_Check(c), 1);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest,
CallableCheckWithNonCallableDunderCallReturnsOne) {
PyRun_SimpleString(R"(
class C:
__call__ = 5
c = C()
)");
PyObjectPtr c(mainModuleGet("c"));
EXPECT_EQ(PyCallable_Check(c), 1);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest, DelAttrStringRemovesAttribute) {
PyRun_SimpleString(R"(
class C:
pass
obj = C()
obj.a = 42
obj.b = 13
)");
PyObjectPtr obj(mainModuleGet("obj"));
ASSERT_TRUE(PyObject_HasAttrString(obj, "a"));
ASSERT_TRUE(PyObject_HasAttrString(obj, "b"));
EXPECT_EQ(PyObject_DelAttrString(obj, "a"), 0);
EXPECT_FALSE(PyObject_HasAttrString(obj, "a"));
EXPECT_TRUE(PyObject_HasAttrString(obj, "b"));
}
TEST_F(ObjectExtensionApiTest, DelAttrRemovesAttribute) {
PyRun_SimpleString(R"(
class C:
pass
obj = C()
obj.a = 42
obj.b = 13
)");
PyObjectPtr obj(mainModuleGet("obj"));
ASSERT_TRUE(PyObject_HasAttrString(obj, "a"));
ASSERT_TRUE(PyObject_HasAttrString(obj, "b"));
PyObjectPtr name(PyUnicode_FromString("a"));
EXPECT_EQ(PyObject_DelAttr(obj, name), 0);
EXPECT_FALSE(PyObject_HasAttrString(obj, "a"));
EXPECT_TRUE(PyObject_HasAttrString(obj, "b"));
}
TEST_F(ObjectExtensionApiTest, DelAttrRaisesAttributeError) {
PyObjectPtr obj(borrow(Py_None));
EXPECT_EQ(PyObject_DelAttrString(obj, "does_not_exist"), -1);
EXPECT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_AttributeError));
}
TEST_F(ObjectExtensionApiTest, LookupAttrWithNonStrNameRaisesTypeError) {
PyObjectPtr obj(borrow(Py_None));
PyObjectPtr name(borrow(Py_None));
PyObject* result = name.get(); // some non-NULL value
EXPECT_EQ(_PyObject_LookupAttr(obj, name, &result), -1);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_EQ(result, nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest, LookupAttrWithExtantAttrReturnsOne) {
PyRun_SimpleString(R"(
class C:
pass
obj = C()
obj.a = 42
)");
PyObjectPtr obj(mainModuleGet("obj"));
PyObjectPtr name(PyUnicode_FromString("a"));
ASSERT_TRUE(PyObject_HasAttr(obj, name));
PyObject* result = nullptr;
EXPECT_EQ(_PyObject_LookupAttr(obj, name, &result), 1);
EXPECT_EQ(PyErr_Occurred(), nullptr);
ASSERT_NE(result, nullptr);
EXPECT_EQ(PyLong_AsLong(result), 42);
Py_DECREF(result);
}
TEST_F(ObjectExtensionApiTest, LookupAttrWithNonexistentAttrReturnsZero) {
PyObjectPtr obj(borrow(Py_None));
PyObjectPtr name(PyUnicode_FromString("a"));
ASSERT_FALSE(PyObject_HasAttr(obj, name));
PyObject* result = name.get(); // some non-NULL value
EXPECT_EQ(_PyObject_LookupAttr(obj, name, &result), 0);
EXPECT_EQ(PyErr_Occurred(), nullptr);
EXPECT_EQ(result, nullptr);
}
TEST_F(ObjectExtensionApiTest,
LookupAttrWithSuccessfulDunderGetAttributeReturnsOne) {
PyRun_SimpleString(R"(
class C:
def __getattribute__(self, key):
return 42
obj = C()
)");
PyObjectPtr obj(mainModuleGet("obj"));
PyObjectPtr name(PyUnicode_FromString("a"));
PyObject* result = nullptr;
EXPECT_EQ(_PyObject_LookupAttr(obj, name, &result), 1);
EXPECT_EQ(PyErr_Occurred(), nullptr);
ASSERT_NE(result, nullptr);
EXPECT_EQ(PyLong_AsLong(result), 42);
Py_DECREF(result);
}
TEST_F(ObjectExtensionApiTest,
LookupAttrWithRaisingDunderGetAttributeReturnsNegativeOne) {
PyRun_SimpleString(R"(
class C:
def __getattribute__(self, key):
raise TypeError("foo")
obj = C()
)");
PyObjectPtr obj(mainModuleGet("obj"));
PyObjectPtr name(PyUnicode_FromString("a"));
PyObject* result = name.get(); // some non-NULL value
EXPECT_EQ(_PyObject_LookupAttr(obj, name, &result), -1);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_EQ(result, nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest,
LookupAttrWithAttributeErrorRaisingDunderGetAttributeReturnsZero) {
PyRun_SimpleString(R"(
class C:
def __getattribute__(self, key):
raise AttributeError("foo")
obj = C()
)");
PyObjectPtr obj(mainModuleGet("obj"));
PyObjectPtr name(PyUnicode_FromString("a"));
PyObject* result = name.get(); // some non-NULL value
EXPECT_EQ(_PyObject_LookupAttr(obj, name, &result), 0);
EXPECT_EQ(PyErr_Occurred(), nullptr);
EXPECT_EQ(result, nullptr);
}
TEST_F(ObjectExtensionApiTest,
LookupAttrWithSuccessfulDunderGetAttrReturnsOne) {
PyRun_SimpleString(R"(
class C:
def __getattr__(self, key):
return 42
obj = C()
)");
PyObjectPtr obj(mainModuleGet("obj"));
PyObjectPtr name(PyUnicode_FromString("a"));
PyObject* result = nullptr;
EXPECT_EQ(_PyObject_LookupAttr(obj, name, &result), 1);
EXPECT_EQ(PyErr_Occurred(), nullptr);
ASSERT_NE(result, nullptr);
EXPECT_EQ(PyLong_AsLong(result), 42);
Py_DECREF(result);
}
TEST_F(ObjectExtensionApiTest,
LookupAttrWithRaisingDunderGetAttrReturnsNegativeOne) {
PyRun_SimpleString(R"(
class C:
def __getattr__(self, key):
raise TypeError("foo")
obj = C()
)");
PyObjectPtr obj(mainModuleGet("obj"));
PyObjectPtr name(PyUnicode_FromString("a"));
PyObject* result = name.get(); // some non-NULL value
EXPECT_EQ(_PyObject_LookupAttr(obj, name, &result), -1);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_EQ(result, nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest,
LookupAttrWithAttributeErrorRaisingDunderGetAttrReturnsZero) {
PyRun_SimpleString(R"(
class C:
def __getattr__(self, key):
raise AttributeError("foo")
obj = C()
)");
PyObjectPtr obj(mainModuleGet("obj"));
PyObjectPtr name(PyUnicode_FromString("a"));
PyObject* result = name.get(); // some non-NULL value
EXPECT_EQ(_PyObject_LookupAttr(obj, name, &result), 0);
EXPECT_EQ(PyErr_Occurred(), nullptr);
EXPECT_EQ(result, nullptr);
}
TEST_F(
ObjectExtensionApiTest,
LookupAttrWithDunderGetAttributeAndDunderGetAttrCallsDunderGetAttribute) {
PyRun_SimpleString(R"(
class C:
def __getattr__(self, key):
return 5
def __getattribute__(self, key):
return 10
obj = C()
)");
PyObjectPtr obj(mainModuleGet("obj"));
PyObjectPtr name(PyUnicode_FromString("a"));
PyObject* result = nullptr;
EXPECT_EQ(_PyObject_LookupAttr(obj, name, &result), 1);
EXPECT_EQ(PyErr_Occurred(), nullptr);
ASSERT_NE(result, nullptr);
EXPECT_EQ(PyLong_AsLong(result), 10);
Py_DECREF(result);
}
TEST_F(
ObjectExtensionApiTest,
LookupAttrWithRaisingDunderGetAttributeAndDunderGetAttrCallsDunderGetAttr) {
PyRun_SimpleString(R"(
class C:
def __getattr__(self, key):
return 5
def __getattribute__(self, key):
raise AttributeError("foo")
obj = C()
)");
PyObjectPtr obj(mainModuleGet("obj"));
PyObjectPtr name(PyUnicode_FromString("a"));
PyObject* result = nullptr;
EXPECT_EQ(_PyObject_LookupAttr(obj, name, &result), 1);
EXPECT_EQ(PyErr_Occurred(), nullptr);
ASSERT_NE(result, nullptr);
EXPECT_EQ(PyLong_AsLong(result), 5);
Py_DECREF(result);
}
TEST_F(ObjectExtensionApiTest,
LookupAttrWithRaisingDescrAttrReturnsNegativeOne) {
PyRun_SimpleString(R"(
class Desc:
def __get__(self, instance, owner):
raise TypeError("foo")
class C:
a = Desc()
obj = C()
)");
PyObjectPtr obj(mainModuleGet("obj"));
PyObjectPtr name(PyUnicode_FromString("a"));
PyObject* result = name.get(); // some non-NULL value
EXPECT_EQ(_PyObject_LookupAttr(obj, name, &result), -1);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_EQ(result, nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest, PySizeReturnsLvalue) {
PyType_Slot slots[] = {
{0, nullptr},
};
static PyType_Spec spec;
spec = {
"foo.Bar", sizeof(PyObject) + 10, 5, Py_TPFLAGS_DEFAULT, slots,
};
PyObjectPtr type(PyType_FromSpec(&spec));
ASSERT_NE(type, nullptr);
ASSERT_TRUE(PyType_CheckExact(type));
PyObjectPtr result(
PyType_GenericAlloc(reinterpret_cast<PyTypeObject*>(type.get()), 5));
EXPECT_EQ(Py_SIZE(result.get()), 5);
Py_SIZE(result.get()) = 4;
EXPECT_EQ(Py_SIZE(result.get()), 4);
}
TEST_F(ObjectExtensionApiTest, SetAttrWithInvalidTypeReturnsNegative) {
PyObjectPtr key(PyUnicode_FromString("a_key"));
PyObjectPtr value(PyLong_FromLong(5));
EXPECT_EQ(PyObject_SetAttr(Py_None, key, value), -1);
}
TEST_F(ObjectExtensionApiTest, SetAttrWithInvalidKeyReturnsNegative) {
static PyModuleDef def;
def = {
PyModuleDef_HEAD_INIT,
"test",
};
PyObjectPtr module(PyModule_Create(&def));
PyObjectPtr value(PyLong_FromLong(5));
EXPECT_EQ(PyObject_SetAttr(module, Py_None, value), -1);
}
TEST_F(ObjectExtensionApiTest, SetAttrReturnsZero) {
static PyModuleDef def;
def = {
PyModuleDef_HEAD_INIT,
"test",
};
PyObjectPtr module(PyModule_Create(&def));
PyObjectPtr key(PyUnicode_FromString("a_key"));
PyObjectPtr value(PyLong_FromLong(5));
EXPECT_EQ(PyObject_SetAttr(module, key, value), 0);
}
TEST_F(ObjectExtensionApiTest, SetAttrStringWithNullRemovesAttribute) {
PyRun_SimpleString(R"(
class C:
pass
obj = C()
obj.a = 42
obj.b = 13
)");
PyObjectPtr obj(mainModuleGet("obj"));
ASSERT_TRUE(PyObject_HasAttrString(obj, "a"));
ASSERT_TRUE(PyObject_HasAttrString(obj, "b"));
EXPECT_EQ(PyObject_SetAttrString(obj, "a", nullptr), 0);
EXPECT_FALSE(PyObject_HasAttrString(obj, "a"));
EXPECT_TRUE(PyObject_HasAttrString(obj, "b"));
}
TEST_F(ObjectExtensionApiTest, SetAttrWithNullRemovesAttribute) {
PyRun_SimpleString(R"(
class C:
pass
obj = C()
obj.a = 42
obj.b = 13
)");
PyObjectPtr obj(mainModuleGet("obj"));
ASSERT_TRUE(PyObject_HasAttrString(obj, "a"));
ASSERT_TRUE(PyObject_HasAttrString(obj, "b"));
PyObjectPtr name(PyUnicode_FromString("a"));
EXPECT_EQ(PyObject_SetAttr(obj, name, nullptr), 0);
EXPECT_FALSE(PyObject_HasAttrString(obj, "a"));
EXPECT_TRUE(PyObject_HasAttrString(obj, "b"));
}
TEST_F(ObjectExtensionApiTest, GetAttrWithNoneExistingKeyReturnsNull) {
static PyModuleDef def;
def = {
PyModuleDef_HEAD_INIT,
"test",
};
PyObjectPtr module(PyModule_Create(&def));
PyObjectPtr key(PyUnicode_FromString("a_key"));
EXPECT_EQ(PyObject_GetAttr(module, key), nullptr);
}
TEST_F(ObjectExtensionApiTest, GetAttrWithInvalidTypeReturnsNull) {
static PyModuleDef def;
def = {
PyModuleDef_HEAD_INIT,
"test",
};
int expected_int = 5;
PyObjectPtr module(PyModule_Create(&def));
PyObjectPtr key(PyUnicode_FromString("a_key"));
PyObjectPtr value(PyLong_FromLong(expected_int));
ASSERT_EQ(PyObject_SetAttr(module, key, value), 0);
EXPECT_EQ(PyObject_GetAttr(Py_None, key), nullptr);
}
TEST_F(ObjectExtensionApiTest, GetAttrWithInvalidKeyReturnsNull) {
static PyModuleDef def;
def = {
PyModuleDef_HEAD_INIT,
"test",
};
int expected_int = 5;
PyObjectPtr module(PyModule_Create(&def));
PyObjectPtr key(PyUnicode_FromString("a_key"));
PyObjectPtr value(PyLong_FromLong(expected_int));
ASSERT_EQ(PyObject_SetAttr(module, key, value), 0);
EXPECT_EQ(PyObject_GetAttr(module, Py_None), nullptr);
}
TEST_F(ObjectExtensionApiTest, GetAttrReturnsCorrectValue) {
static PyModuleDef def;
def = {
PyModuleDef_HEAD_INIT,
"test",
};
int expected_int = 5;
PyObjectPtr module(PyModule_Create(&def));
PyObjectPtr key(PyUnicode_FromString("a_key"));
PyObjectPtr value(PyLong_FromLong(expected_int));
ASSERT_EQ(PyObject_SetAttr(module, key, value), 0);
PyObjectPtr dict_result(PyObject_GetAttr(module, key));
ASSERT_NE(dict_result, nullptr);
EXPECT_EQ(PyLong_AsLong(dict_result), expected_int);
}
TEST_F(ObjectExtensionApiTest, GetAttrStringReturnsCorrectValue) {
static PyModuleDef def;
def = {
PyModuleDef_HEAD_INIT,
"test",
};
const char* key = "a_key";
int expected_int = 5;
PyObjectPtr module(PyModule_Create(&def));
PyObjectPtr value(PyLong_FromLong(expected_int));
ASSERT_EQ(PyObject_SetAttrString(module, key, value), 0);
PyObjectPtr dict_result(PyObject_GetAttrString(module, key));
ASSERT_NE(dict_result, nullptr);
EXPECT_EQ(PyLong_AsLong(dict_result), expected_int);
}
TEST_F(ObjectExtensionApiTest, HasAttrWithImmediateWithAttributeReturnsTrue) {
PyObjectPtr num(PyLong_FromLong(6));
PyObjectPtr name(PyUnicode_FromString("__int__"));
EXPECT_TRUE(PyObject_HasAttr(num, name));
}
TEST_F(ObjectExtensionApiTest,
HasAttrStringWithImmediateWithoutAttributeReturnsFalse) {
PyObjectPtr str(PyUnicode_FromString(""));
EXPECT_FALSE(PyObject_HasAttrString(str, "foo"));
}
TEST_F(ObjectExtensionApiTest, HasAttrWithNonStringAttrReturnsFalse) {
PyObjectPtr set(PySet_New(nullptr));
PyObjectPtr num(PyLong_FromLong(1));
EXPECT_FALSE(PyObject_HasAttr(set, num));
ASSERT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest, HasAttrWithoutAttrReturnsFalse) {
static PyModuleDef def;
def = {
PyModuleDef_HEAD_INIT,
"test",
};
PyObjectPtr module(PyModule_Create(&def));
PyObjectPtr name(PyUnicode_FromString("foo"));
EXPECT_FALSE(PyObject_HasAttr(module, name));
}
TEST_F(ObjectExtensionApiTest, HasAttrStringWithoutAttrReturnsFalse) {
static PyModuleDef def;
def = {
PyModuleDef_HEAD_INIT,
"test",
};
PyObjectPtr module(PyModule_Create(&def));
EXPECT_FALSE(PyObject_HasAttrString(module, "foo"));
}
TEST_F(ObjectExtensionApiTest, HasAttrWithAttrReturnsTrue) {
static PyModuleDef def;
def = {
PyModuleDef_HEAD_INIT,
"test",
};
PyObjectPtr module(PyModule_Create(&def));
PyObjectPtr name(PyUnicode_FromString("foo"));
PyObjectPtr val(PyLong_FromLong(2));
ASSERT_EQ(PyObject_SetAttr(module, name, val), 0);
EXPECT_TRUE(PyObject_HasAttr(module, name));
}
TEST_F(ObjectExtensionApiTest, HasAttrStringWithAttrReturnsTrue) {
static PyModuleDef def;
def = {
PyModuleDef_HEAD_INIT,
"test",
};
PyObjectPtr module(PyModule_Create(&def));
PyObjectPtr name(PyUnicode_FromString("foo"));
PyObjectPtr val(PyLong_FromLong(2));
ASSERT_EQ(PyObject_SetAttr(module, name, val), 0);
EXPECT_TRUE(PyObject_HasAttrString(module, "foo"));
}
TEST_F(ObjectExtensionApiTest, PrintWithNullObjPrintsNil) {
CaptureStdStreams streams;
int result = PyObject_Print(nullptr, stdout, 0);
EXPECT_EQ(result, 0);
EXPECT_EQ(streams.out(), "<nil>");
}
TEST_F(ObjectExtensionApiTest, PrintWithZeroFlagsCallsDunderRepr) {
ASSERT_EQ(PyRun_SimpleString(R"(
class C:
def __repr__(self):
return "foo"
def __str__(self):
return "bar"
obj = C()
)"),
0);
CaptureStdStreams streams;
PyObjectPtr obj(mainModuleGet("obj"));
int result = PyObject_Print(obj, stdout, 0);
EXPECT_EQ(result, 0);
EXPECT_EQ(streams.out(), "foo");
}
TEST_F(ObjectExtensionApiTest, PrintWithRawFlagsCallsDunderStr) {
ASSERT_EQ(PyRun_SimpleString(R"(
class C:
def __repr__(self):
return "foo"
def __str__(self):
return "bar"
obj = C()
)"),
0);
CaptureStdStreams streams;
PyObjectPtr obj(mainModuleGet("obj"));
int result = PyObject_Print(obj, stdout, Py_PRINT_RAW);
EXPECT_EQ(result, 0);
EXPECT_EQ(streams.out(), "bar");
}
TEST_F(ObjectExtensionApiTest, PrintReplacesBackslashes) {
ASSERT_EQ(PyRun_SimpleString(R"(
class C:
def __repr__(self):
return r"foo\bar"
obj = C()
)"),
0);
CaptureStdStreams streams;
PyObjectPtr obj(mainModuleGet("obj"));
int result = PyObject_Print(obj, stdout, 0);
EXPECT_EQ(result, 0);
EXPECT_EQ(streams.out(), "foo\\bar");
}
TEST_F(ObjectExtensionApiTest, RefCountDecreaseDeallocsHandle) {
long value = 10;
PyObject* o = PyLong_FromLong(value);
Py_DECREF(o);
}
TEST_F(ObjectExtensionApiTest, IncrementDecrementRefCount) {
PyObject* o = PyTuple_New(1);
long refcnt = Py_REFCNT(o);
EXPECT_GE(Py_REFCNT(o), 1);
Py_INCREF(o);
EXPECT_EQ(Py_REFCNT(o), refcnt + 1);
Py_DECREF(o);
EXPECT_EQ(Py_REFCNT(o), refcnt);
Py_DECREF(o);
}
TEST_F(ObjectExtensionApiTest, IncrementDecrementRefCountWithPyObjectPtr) {
PyObject* o = PyTuple_New(1);
long refcnt = Py_REFCNT(o);
{
Py_INCREF(o);
EXPECT_EQ(Py_REFCNT(o), refcnt + 1);
testing::PyObjectPtr h(o);
static_cast<void>(h);
}
EXPECT_EQ(Py_REFCNT(o), refcnt);
{
Py_INCREF(o);
EXPECT_EQ(Py_REFCNT(o), refcnt + 1);
testing::PyObjectPtr h(o);
h = nullptr;
EXPECT_EQ(Py_REFCNT(o), refcnt);
}
Py_DECREF(o);
}
TEST_F(ObjectExtensionApiTest, CallFinalizerFromDeallocWithNonZeroRefcntDies) {
PyObject* obj = Py_None;
Py_INCREF(obj); // definitely has a non-zero refcount
EXPECT_DEATH(PyObject_CallFinalizerFromDealloc(obj),
"PyObject_CallFinalizerFromDealloc called on object with a "
"non-zero refcount");
}
TEST_F(ObjectExtensionApiTest,
CallFinalizerFromDeallocWithoutTpFinalizeFlagCallsTpFinalize) {
static bool dealloc_called;
static bool finalizer_called;
dealloc_called = false;
finalizer_called = false;
destructor dealloc_func = [](PyObject* self) {
dealloc_called = true;
if (PyObject_CallFinalizerFromDealloc(self) < 0) return;
PyTypeObject* type = Py_TYPE(self);
PyObject_Del(self);
Py_DECREF(type);
};
destructor finalizer_func = [](PyObject*) { finalizer_called = true; };
PyType_Slot slots[] = {
{Py_tp_dealloc, reinterpret_cast<void*>(dealloc_func)},
{Py_tp_finalize, reinterpret_cast<void*>(finalizer_func)},
{0, nullptr},
};
static PyType_Spec spec;
spec = {
"foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT, slots,
};
PyObjectPtr type(PyType_FromSpec(&spec));
ASSERT_NE(type, nullptr);
allocfunc func = reinterpret_cast<allocfunc>(
PyType_GetSlot(type.asTypeObject(), Py_tp_alloc));
ASSERT_NE(func, nullptr);
PyObject* obj = (*func)(type.asTypeObject(), 0);
ASSERT_NE(obj, nullptr);
EXPECT_GE(Py_REFCNT(obj), 1);
Py_DECREF(obj); // Drop the reference to it
// Trigger a GC. PyObject_CallFinalizerFromDealloc is called during GC in
// Pyro and immmediately in the decref in CPython.
collectGarbage();
EXPECT_TRUE(dealloc_called);
EXPECT_TRUE(finalizer_called);
}
TEST_F(ObjectExtensionApiTest,
CallFinalizerFromDeallocWithTpFinalizeFlagCallsTpFinalize) {
static bool dealloc_called;
static bool finalizer_called;
dealloc_called = false;
finalizer_called = false;
destructor dealloc_func = [](PyObject* self) {
dealloc_called = true;
if (PyObject_CallFinalizerFromDealloc(self) < 0) return;
PyTypeObject* type = Py_TYPE(self);
PyObject_Del(self);
Py_DECREF(type);
};
destructor finalizer_func = [](PyObject*) { finalizer_called = true; };
PyType_Slot slots[] = {
{Py_tp_dealloc, reinterpret_cast<void*>(dealloc_func)},
{Py_tp_finalize, reinterpret_cast<void*>(finalizer_func)},
{0, nullptr},
};
static PyType_Spec spec;
spec = {
"foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_FINALIZE, slots,
};
PyObjectPtr type(PyType_FromSpec(&spec));
ASSERT_NE(type, nullptr);
allocfunc func = reinterpret_cast<allocfunc>(
PyType_GetSlot(type.asTypeObject(), Py_tp_alloc));
ASSERT_NE(func, nullptr);
PyObject* obj = (*func)(type.asTypeObject(), 0);
ASSERT_NE(obj, nullptr);
EXPECT_GE(Py_REFCNT(obj), 1);
Py_DECREF(obj); // Drop the reference to it
// Trigger a GC. PyObject_CallFinalizerFromDealloc is called during GC in
// Pyro and immmediately in the decref in CPython.
collectGarbage();
EXPECT_TRUE(dealloc_called);
EXPECT_TRUE(finalizer_called);
}
TEST_F(
ObjectExtensionApiTest,
CallFinalizerFromDeallocWithTpFinalizeResurrectingObjectDoesNotGCObject) {
static bool dealloc_called;
static bool finalizer_called;
dealloc_called = false;
finalizer_called = false;
destructor dealloc_func = [](PyObject* self) {
dealloc_called = true;
if (PyObject_CallFinalizerFromDealloc(self) < 0) return;
PyTypeObject* type = Py_TYPE(self);
PyObject_Del(self);
Py_DECREF(type);
};
destructor finalizer_func = [](PyObject* self) {
finalizer_called = true;
Py_INCREF(self);
};
PyType_Slot slots[] = {
{Py_tp_dealloc, reinterpret_cast<void*>(dealloc_func)},
{Py_tp_finalize, reinterpret_cast<void*>(finalizer_func)},
{0, nullptr},
};
static PyType_Spec spec;
spec = {
"foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_FINALIZE, slots,
};
PyObjectPtr type(PyType_FromSpec(&spec));
ASSERT_NE(type, nullptr);
allocfunc func = reinterpret_cast<allocfunc>(
PyType_GetSlot(type.asTypeObject(), Py_tp_alloc));
ASSERT_NE(func, nullptr);
PyObject* obj = (*func)(type.asTypeObject(), 0);
ASSERT_NE(obj, nullptr);
EXPECT_GE(Py_REFCNT(obj), 1);
Py_DECREF(obj); // Drop the reference to it
// Trigger a GC. PyObject_CallFinalizerFromDealloc is called during GC in
// Pyro and immmediately in the decref in CPython.
collectGarbage();
EXPECT_TRUE(dealloc_called);
EXPECT_TRUE(finalizer_called);
EXPECT_GE(Py_REFCNT(obj), 1);
}
TEST_F(ObjectExtensionApiTest, PyDeallocCallsDeallocTypeSlot) {
static bool dealloc_called;
dealloc_called = false;
destructor dealloc_func = [](PyObject* self) {
dealloc_called = true;
PyTypeObject* type = Py_TYPE(self);
PyObject_Del(self);
Py_DECREF(type);
};
PyType_Slot slots[] = {
{Py_tp_dealloc, reinterpret_cast<void*>(dealloc_func)},
{0, nullptr},
};
static PyType_Spec spec;
spec = {
"foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_FINALIZE, slots,
};
PyObjectPtr type(PyType_FromSpec(&spec));
ASSERT_NE(type, nullptr);
allocfunc func = reinterpret_cast<allocfunc>(
PyType_GetSlot(type.asTypeObject(), Py_tp_alloc));
ASSERT_NE(func, nullptr);
PyObject* obj = (*func)(type.asTypeObject(), 0);
ASSERT_NE(obj, nullptr);
EXPECT_GE(Py_REFCNT(obj), 1);
_Py_Dealloc(obj);
EXPECT_TRUE(dealloc_called);
}
TEST_F(ObjectExtensionApiTest, GenericGetAttrFindsCorrectlySetValue) {
ASSERT_EQ(PyRun_SimpleString(R"(
class C: pass
i = C()
)"),
0);
PyObjectPtr i(mainModuleGet("i"));
ASSERT_NE(i, nullptr);
PyObjectPtr key(PyUnicode_FromString("key"));
PyObjectPtr value(PyUnicode_FromString("value"));
EXPECT_EQ(PyObject_GenericSetAttr(i, key, value), 0);
PyObjectPtr get_val(PyObject_GenericGetAttr(i, key));
EXPECT_TRUE(isUnicodeEqualsCStr(get_val, "value"));
}
TEST_F(ObjectExtensionApiTest, GenericSetAttrWithSealedTypeReturnsNegOne) {
ASSERT_EQ(PyRun_SimpleString(R"(
i = 3
)"),
0);
PyObjectPtr i(mainModuleGet("i"));
ASSERT_NE(i, nullptr);
PyObjectPtr key(PyUnicode_FromString("key"));
PyObjectPtr value(PyUnicode_FromString("value"));
EXPECT_EQ(PyObject_GenericSetAttr(i, key, value), -1);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_AttributeError));
}
TEST_F(ObjectExtensionApiTest, GetAttrIncrementsReferenceCount) {
static PyModuleDef def;
def = {
PyModuleDef_HEAD_INIT,
"test",
};
PyObjectPtr module(PyModule_Create(&def));
PyObjectPtr key(PyUnicode_FromString("test"));
PyObject* value = PyTuple_New(1);
ASSERT_EQ(PyObject_SetAttr(module, key, value), 0);
long refcnt = Py_REFCNT(value);
PyObject* result = PyObject_GetAttr(module, key);
EXPECT_EQ(Py_REFCNT(result), refcnt + 1);
Py_DECREF(result);
result = PyObject_GetAttr(module, key);
EXPECT_EQ(result, value);
EXPECT_EQ(Py_REFCNT(result), refcnt + 1);
Py_DECREF(result);
Py_DECREF(result);
}
TEST_F(ObjectExtensionApiTest, ReprOnNullReturnsSpecialNullString) {
PyObjectPtr repr(PyObject_Repr(nullptr));
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_TRUE(isUnicodeEqualsCStr(repr, "<NULL>"));
}
TEST_F(ObjectExtensionApiTest, ReprWithObjectWithBadDunderReprRaisesTypeError) {
PyRun_SimpleString(R"(
class C:
__repr__ = None
c = C()
)");
PyObjectPtr pyc(PyObject_GetAttrString(PyImport_AddModule("__main__"), "c"));
EXPECT_EQ(PyObject_Repr(pyc), nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest, ReprIsCorrectForObjectWithDunderRepr) {
PyRun_SimpleString(R"(
class C:
def __repr__(self):
return "bongo"
c = C()
)");
PyObjectPtr pyc(PyObject_GetAttrString(PyImport_AddModule("__main__"), "c"));
PyObjectPtr repr(PyObject_Repr(pyc));
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_TRUE(isUnicodeEqualsCStr(repr, "bongo"));
}
TEST_F(ObjectExtensionApiTest,
ReprWithRecursiveObjectDoesNotInfinitelyRecurse) {
PyRun_SimpleString(R"(
a = []
a.append(a)
)");
PyObjectPtr a(PyObject_GetAttrString(PyImport_AddModule("__main__"), "a"));
PyObjectPtr repr(PyObject_Repr(a));
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_TRUE(isUnicodeEqualsCStr(repr, "[[...]]"));
}
TEST_F(ObjectExtensionApiTest, StrOnNullReturnsSpecialNullString) {
PyObjectPtr str(PyObject_Str(nullptr));
ASSERT_EQ(PyErr_Occurred(), nullptr);
ASSERT_TRUE(isUnicodeEqualsCStr(str, "<NULL>"));
}
TEST_F(ObjectExtensionApiTest, StrCallsClassDunderReprWhenProvided) {
PyRun_SimpleString(R"(
class C:
def __repr__(self):
return "bongo"
c = C()
)");
PyObjectPtr pyc(PyObject_GetAttrString(PyImport_AddModule("__main__"), "c"));
PyObjectPtr str(PyObject_Str(pyc));
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_TRUE(isUnicodeEqualsCStr(str, "bongo"));
}
TEST_F(ObjectExtensionApiTest, StrWithObjectWithBadDunderStrRaisesTypeError) {
PyRun_SimpleString(R"(
class C:
__str__ = None
c = C()
)");
PyObjectPtr pyc(PyObject_GetAttrString(PyImport_AddModule("__main__"), "c"));
EXPECT_EQ(PyObject_Str(pyc), nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest, StrCallsClassDunderStrWhenProvided) {
PyRun_SimpleString(R"(
class C:
def __str__(self):
return "bongo"
c = C()
)");
PyObjectPtr pyc(PyObject_GetAttrString(PyImport_AddModule("__main__"), "c"));
PyObjectPtr str(PyObject_Str(pyc));
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_TRUE(isUnicodeEqualsCStr(str, "bongo"));
}
TEST_F(ObjectExtensionApiTest, RichCompareWithNullLhsRaisesSystemError) {
ASSERT_EQ(PyObject_RichCompare(nullptr, Py_None, 0), nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
}
TEST_F(ObjectExtensionApiTest, RichCompareWithNullRhsRaisesSystemError) {
ASSERT_EQ(PyObject_RichCompare(Py_None, nullptr, 0), nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
}
TEST_F(ObjectExtensionApiTest, RichCompareWithSameType) {
PyObjectPtr left(PyLong_FromLong(2));
PyObjectPtr right(PyLong_FromLong(3));
PyObjectPtr result(PyObject_RichCompare(left, right, Py_LT));
ASSERT_EQ(PyErr_Occurred(), nullptr);
ASSERT_EQ(result, Py_True);
}
TEST_F(ObjectExtensionApiTest, RichCompareNotComparableRaisesTypeError) {
PyObjectPtr left(PyLong_FromLong(2));
PyObjectPtr right(PyUnicode_FromString("hello"));
EXPECT_EQ(PyObject_RichCompare(left, right, Py_LT), nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest, IsTrueReturnsTrueOnTrue) {
EXPECT_EQ(PyObject_IsTrue(Py_True), 1);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest, IsTrueReturnsFalseOnFalse) {
EXPECT_EQ(PyObject_IsTrue(Py_False), 0);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest, IsTrueReturnsFalseOnNone) {
EXPECT_EQ(PyObject_IsTrue(Py_None), 0);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest,
IsTrueWithObjectWithNonCallableDunderBoolRaisesTypeError) {
PyRun_SimpleString(R"(
class C:
__bool__ = 4
c = C()
)");
PyObjectPtr pyc(PyObject_GetAttrString(PyImport_AddModule("__main__"), "c"));
ASSERT_EQ(PyObject_IsTrue(pyc), -1);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest,
IsTrueWithObjectWithNonCallableDunderLenRaisesTypeError) {
PyRun_SimpleString(R"(
class C:
__len__ = 4
c = C()
)");
PyObjectPtr pyc(PyObject_GetAttrString(PyImport_AddModule("__main__"), "c"));
ASSERT_EQ(PyObject_IsTrue(pyc), -1);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest,
IsTrueWithObjectWithDunderBoolThatReturnsNonIntRaisesTypeError) {
PyRun_SimpleString(R"(
class C:
def __bool__(self):
return "bongo"
c = C()
)");
PyObjectPtr pyc(PyObject_GetAttrString(PyImport_AddModule("__main__"), "c"));
ASSERT_EQ(PyObject_IsTrue(pyc), -1);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest,
IsTrueWithDunderLenThatReturnsNonIntRaisesTypeError) {
PyRun_SimpleString(R"(
class C:
def __len__(self):
return "bongo"
c = C()
)");
PyObjectPtr pyc(PyObject_GetAttrString(PyImport_AddModule("__main__"), "c"));
ASSERT_EQ(PyObject_IsTrue(pyc), -1);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest, IsTrueWithEmptyListReturnsFalse) {
PyObjectPtr empty_list(PyList_New(0));
ASSERT_EQ(PyObject_IsTrue(empty_list), 0);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest, IsTrueWithNonEmptyListReturnsTrue) {
PyObjectPtr list(PyList_New(0));
PyList_Append(list, Py_None);
ASSERT_EQ(PyObject_IsTrue(list), 1);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest,
IsTrueWithObjectWithDunderLenReturningNegativeOneRaisesValueError) {
PyRun_SimpleString(R"(
class C:
def __len__(self):
return -1
c = C()
)");
PyObjectPtr pyc(PyObject_GetAttrString(PyImport_AddModule("__main__"), "c"));
ASSERT_EQ(PyObject_IsTrue(pyc), -1);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_ValueError));
}
TEST_F(ObjectExtensionApiTest, ClearWithNullDoesNotRaise) {
PyObject* null = nullptr;
Py_CLEAR(null);
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_EQ(null, nullptr);
}
TEST_F(ObjectExtensionApiTest, ClearWithObjectSetsNull) {
PyObject* num = PyLong_FromLong(1);
Py_CLEAR(num);
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_EQ(num, nullptr);
}
TEST_F(ObjectExtensionApiTest, ClearWithObjectDecrefsObject) {
PyObject* original = PyTuple_New(1);
PyObject* num = original;
Py_ssize_t original_count = Py_REFCNT(original);
Py_CLEAR(num);
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_EQ(num, nullptr);
EXPECT_LT(Py_REFCNT(original), original_count);
}
TEST_F(ObjectExtensionApiTest, ASCIIOnNullReturnsSpecialNullString) {
PyObjectPtr ascii(PyObject_ASCII(nullptr));
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_TRUE(isUnicodeEqualsCStr(ascii, "<NULL>"));
}
TEST_F(ObjectExtensionApiTest,
ASCIIWithObjectWithBadDunderReprRaisesTypeError) {
PyRun_SimpleString(R"(
class C:
__repr__ = None
c = C()
)");
PyObjectPtr pyc(PyObject_GetAttrString(PyImport_AddModule("__main__"), "c"));
EXPECT_EQ(PyObject_ASCII(pyc), nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest, ASCIIcallsDunderRepr) {
PyRun_SimpleString(R"(
class C:
def __repr__(self):
return "bongo"
c = C()
)");
PyObjectPtr pyc(PyObject_GetAttrString(PyImport_AddModule("__main__"), "c"));
PyObjectPtr ascii(PyObject_ASCII(pyc));
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_TRUE(isUnicodeEqualsCStr(ascii, "bongo"));
}
TEST_F(ObjectExtensionApiTest, SelfIterIncrementsRefcount) {
PyObject* o = PyTuple_New(1);
long refcnt = Py_REFCNT(o);
EXPECT_GE(Py_REFCNT(o), 1);
PyObject* o2 = PyObject_SelfIter(o);
EXPECT_EQ(PyErr_Occurred(), nullptr);
EXPECT_EQ(Py_REFCNT(o2), refcnt + 1);
Py_DECREF(o);
Py_DECREF(o);
}
TEST_F(ObjectExtensionApiTest, NotWithTrueReturnsFalse) {
EXPECT_EQ(PyObject_Not(Py_True), 0);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest, NotWithFalseReturnsTrue) {
EXPECT_EQ(PyObject_Not(Py_False), 1);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest, NotWithNoneReturnsTrue) {
EXPECT_EQ(PyObject_Not(Py_None), 1);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest, NotCallsDunderBool) {
PyRun_SimpleString(R"(
sideeffect = 0
class C:
def __bool__(self):
global sideeffect
sideeffect = 10
return False
c = C()
)");
PyObjectPtr c(mainModuleGet("c"));
EXPECT_EQ(PyObject_Not(c), 1);
EXPECT_EQ(PyErr_Occurred(), nullptr);
PyObjectPtr sideeffect(mainModuleGet("sideeffect"));
EXPECT_EQ(PyLong_AsLong(sideeffect), 10);
}
TEST_F(ObjectExtensionApiTest,
NotWithDunderBoolRaisingExceptionRaisesTypeError) {
PyRun_SimpleString(R"(
class C:
def __bool__(self):
return -10
c = C()
)");
PyObjectPtr c(mainModuleGet("c"));
EXPECT_EQ(PyObject_Not(c), -1);
EXPECT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest, HashWithUncallableDunderHashRaisesTypeError) {
PyRun_SimpleString(R"(
class C:
__hash__ = None
c = C()
)");
PyObjectPtr c(mainModuleGet("c"));
EXPECT_EQ(PyObject_Hash(c), -1);
EXPECT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest, HashCallsDunderHash) {
PyRun_SimpleString(R"(
class C:
def __hash__(self):
return 7
c = C()
)");
PyObjectPtr c(mainModuleGet("c"));
EXPECT_EQ(PyObject_Hash(c), 7);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest, HashPropagatesRaisedException) {
PyRun_SimpleString(R"(
class C:
def __hash__(self):
raise IndexError
c = C()
)");
PyObjectPtr c(mainModuleGet("c"));
EXPECT_EQ(PyObject_Hash(c), -1);
EXPECT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_IndexError));
}
TEST_F(ObjectExtensionApiTest, HashNotImplementedRaisesTypeError) {
EXPECT_EQ(PyObject_HashNotImplemented(Py_None), -1);
EXPECT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest,
RichCompareBoolEqWithLeftEqualsRightReturnsTrue) {
EXPECT_EQ(PyObject_RichCompareBool(Py_None, Py_None, Py_EQ), 1);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest,
RichCompareBoolNeWithLeftEqualsRightReturnsFalse) {
EXPECT_EQ(PyObject_RichCompareBool(Py_None, Py_None, Py_NE), 0);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest, RichCompareBoolWithSameTypeReturnsTrue) {
PyObjectPtr left(PyLong_FromLong(2));
PyObjectPtr right(PyLong_FromLong(3));
EXPECT_EQ(PyObject_RichCompareBool(left, right, Py_LT), 1);
ASSERT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest, RichCompareBoolNotComparableRaisesTypeError) {
PyObjectPtr left(PyLong_FromLong(2));
PyObjectPtr right(PyUnicode_FromString("hello"));
EXPECT_EQ(PyObject_RichCompareBool(left, right, Py_LT), -1);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest, ReprEnterOnceReturnsZero) {
PyObjectPtr obj(PyLong_FromLong(7));
EXPECT_EQ(Py_ReprEnter(obj), 0);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest, ReprEnterSecondTimeReturnsOne) {
PyObjectPtr obj(PyLong_FromLong(7));
ASSERT_EQ(Py_ReprEnter(obj), 0);
ASSERT_EQ(PyErr_Occurred(), nullptr);
EXPECT_EQ(Py_ReprEnter(obj), 1);
EXPECT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest, ReprEnterThenLeaveRemovesFromSet) {
PyObjectPtr obj(PyLong_FromLong(7));
ASSERT_EQ(Py_ReprEnter(obj), 0);
ASSERT_EQ(PyErr_Occurred(), nullptr);
ASSERT_EQ(Py_ReprEnter(obj), 1);
ASSERT_EQ(PyErr_Occurred(), nullptr);
Py_ReprLeave(obj);
ASSERT_EQ(Py_ReprEnter(obj), 0);
ASSERT_EQ(PyErr_Occurred(), nullptr);
}
TEST_F(ObjectExtensionApiTest, InitWithNullRaisesNoMemoryError) {
PyType_Slot slots[] = {
{0, nullptr},
};
static PyType_Spec spec;
spec = {
"foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT, slots,
};
PyObjectPtr type(PyType_FromSpec(&spec));
ASSERT_NE(type, nullptr);
ASSERT_TRUE(PyType_CheckExact(type));
PyObject_Init(nullptr, reinterpret_cast<PyTypeObject*>(type.get()));
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_MemoryError));
}
TEST_F(ObjectExtensionApiTest, NewReturnsAllocatedObject) {
struct BarObject {
PyObject_HEAD
int value;
};
PyType_Slot slots[] = {
{0, nullptr},
};
static PyType_Spec spec;
spec = {
"foo.Bar", sizeof(BarObject), 0, Py_TPFLAGS_DEFAULT, slots,
};
PyObjectPtr type(PyType_FromSpec(&spec));
Py_ssize_t refcnt = Py_REFCNT(type.get());
PyObjectPtr instance(reinterpret_cast<PyObject*>(
PyObject_New(BarObject, type.asTypeObject())));
ASSERT_NE(instance, nullptr);
EXPECT_EQ(PyErr_Occurred(), nullptr);
// TODO(T53456038): Switch back to EXPECT_EQ, once initial refcount is fixed
EXPECT_GE(Py_REFCNT(instance), 1);
EXPECT_EQ(Py_REFCNT(type), refcnt + 1);
}
TEST_F(ObjectExtensionApiTest, NewVarReturnsAllocatedObject) {
struct BarObject {
PyObject_HEAD
int value;
};
struct BarContainer {
PyObject_VAR_HEAD
BarObject* items[1];
};
PyType_Slot slots[] = {
{0, nullptr},
};
static PyType_Spec spec;
spec = {
"foo.Bar", sizeof(BarContainer), sizeof(BarObject), Py_TPFLAGS_DEFAULT,
slots,
};
PyObjectPtr type(PyType_FromSpec(&spec));
PyObjectPtr instance(reinterpret_cast<PyObject*>(
PyObject_NewVar(BarContainer, type.asTypeObject(), 5)));
ASSERT_NE(instance, nullptr);
EXPECT_EQ(PyErr_Occurred(), nullptr);
// TODO(T53456038): Switch back to EXPECT_EQ, once initial refcount is fixed
EXPECT_GE(Py_REFCNT(instance), 1);
EXPECT_EQ(Py_SIZE(instance.get()), 5);
}
TEST_F(ObjectExtensionApiTest, PyEllipsisIdentityIsEqual) {
// Test Identitiy
PyObject* ellipsis1 = Py_Ellipsis;
PyObject* ellipsis2 = Py_Ellipsis;
EXPECT_EQ(ellipsis1, ellipsis2);
}
TEST_F(ObjectExtensionApiTest, DirWithoutExecutionFrameReturnsNull) {
EXPECT_EQ(PyObject_Dir(nullptr), nullptr);
}
TEST_F(ObjectExtensionApiTest, DirReturnsLocals) {
PyRun_SimpleString(R"(
class C: pass
)");
PyObjectPtr c_type(mainModuleGet("C"));
binaryfunc meth = [](PyObject*, PyObject*) { return PyObject_Dir(nullptr); };
static PyMethodDef foo_func = {"foo", meth, METH_NOARGS};
PyObjectPtr func(PyCFunction_NewEx(&foo_func, c_type, nullptr));
ASSERT_NE(func, nullptr);
PyObject_SetAttrString(c_type, "foo", func);
PyRun_SimpleString(R"(
foo = 123
c = C()
obj = c.foo()
)");
PyObjectPtr obj(mainModuleGet("obj"));
ASSERT_EQ(PyList_Check(obj), 1);
PyObjectPtr foo(PyUnicode_FromString("foo"));
EXPECT_EQ(PySequence_Contains(obj, foo), 1);
}
TEST_F(ObjectExtensionApiTest, DirOnInstanceReturnsListOfAttributes) {
PyRun_SimpleString(R"(
class C:
def __init__(self):
self.foo = 123
obj = C()
)");
PyObjectPtr obj(mainModuleGet("obj"));
ASSERT_EQ(PyErr_Occurred(), nullptr);
PyObjectPtr result(PyObject_Dir(obj));
ASSERT_EQ(PyList_Check(result), 1);
PyObjectPtr foo(PyUnicode_FromString("foo"));
EXPECT_EQ(PySequence_Contains(result, foo), 1);
}
TEST_F(ObjectExtensionApiTest, DirOnInstanceWithDunderDirRaisingReturnsNull) {
PyRun_SimpleString(R"(
class C:
def __init__(self):
self.foo = 123
def __dir__(self):
raise TypeError("no dir on this type")
obj = C()
)");
PyObjectPtr obj(mainModuleGet("obj"));
ASSERT_EQ(PyErr_Occurred(), nullptr);
PyObjectPtr result(PyObject_Dir(obj));
ASSERT_EQ(result, nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest,
DirOnInstanceWithNonIterableDunderDirReturnsNull) {
PyRun_SimpleString(R"(
class C:
def __init__(self):
self.foo = 123
def __dir__(self):
return 123
obj = C()
)");
PyObjectPtr obj(mainModuleGet("obj"));
ASSERT_EQ(PyErr_Occurred(), nullptr);
PyObjectPtr result(PyObject_Dir(obj));
ASSERT_EQ(result, nullptr);
ASSERT_NE(PyErr_Occurred(), nullptr);
EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
}
TEST_F(ObjectExtensionApiTest, DirOnInstanceIgnoresInstanceDictionary) {
PyRun_SimpleString(R"(
class C:
def __init__(self):
self.foo = 123
def new_dir(self):
return ("bar")
obj = C()
obj.__dir__ = new_dir.__get__(obj, C)
)");
PyObjectPtr obj(mainModuleGet("obj"));
ASSERT_EQ(PyErr_Occurred(), nullptr);
PyObjectPtr result(PyObject_Dir(obj));
ASSERT_EQ(PyList_Check(result), 1);
PyObjectPtr foo(PyUnicode_FromString("foo"));
EXPECT_EQ(PySequence_Contains(result, foo), 1);
}
TEST_F(ObjectExtensionApiTest, PyReturnNoneReturnsNone) {
PyObjectPtr module(PyModule_New("mod"));
binaryfunc meth = [](PyObject*, PyObject*) { Py_RETURN_NONE; };
static PyMethodDef foo_func = {"foo", meth, METH_NOARGS};
PyObjectPtr func(PyCFunction_NewEx(&foo_func, nullptr, module));
PyObjectPtr result(_PyObject_CallNoArg(func));
EXPECT_EQ(result, Py_None);
}
TEST_F(ObjectExtensionApiTest, PyReturnNotImplementedReturnsNotImplemented) {
PyObjectPtr module(PyModule_New("mod"));
binaryfunc meth = [](PyObject*, PyObject*) { Py_RETURN_NOTIMPLEMENTED; };
static PyMethodDef foo_func = {"foo", meth, METH_NOARGS};
PyObjectPtr func(PyCFunction_NewEx(&foo_func, nullptr, module));
PyObjectPtr result(_PyObject_CallNoArg(func));
EXPECT_EQ(result, Py_NotImplemented);
}
TEST_F(ObjectExtensionApiTest, PyEnumTypeIdentityIsEqual) {
// Test Identitiy
PyTypeObject* type1 = &PyEnum_Type;
PyTypeObject* type2 = &PyEnum_Type;
EXPECT_EQ(type1, type2);
}
TEST_F(ObjectExtensionApiTest, PyEnumTypeIsInBuiltins) {
PyObjectPtr builtins(borrow(PyEval_GetBuiltins()));
PyObjectPtr enumerate(PyMapping_GetItemString(builtins, "enumerate"));
EXPECT_EQ(enumerate.asTypeObject(), &PyEnum_Type);
}
} // namespace testing
} // namespace py