Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
gh-94731: Add a failing test
Co-Authored-By: da-woods <dw-git@d-woods.co.uk>
  • Loading branch information
encukou and da-woods committed Jul 12, 2022
commit f09b8c638bf07a3bfa605d62baa4fa18a47a848e
75 changes: 72 additions & 3 deletions Lib/test/_testcppext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
# define NAME _testcpp03ext
#endif

#define _STR(NAME) #NAME
#define STR(NAME) _STR(NAME)

PyDoc_STRVAR(_testcppext_add_doc,
"add(x, y)\n"
"\n"
Expand Down Expand Up @@ -123,11 +126,76 @@ test_unicode(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
Py_RETURN_NONE;
}

/* Test a `new`-allocated object with a virtual method.
* (https://github.com/python/cpython/issues/94731) */

class VirtualPyObject : public PyObject {
public:
VirtualPyObject();
virtual ~VirtualPyObject() {
delete [] internal_data;
--instance_count;
}
virtual void set_internal_data() {
internal_data[0] = 1;
}
static void dealloc(PyObject* o) {
delete static_cast<VirtualPyObject*>(o);
}

// Number of "living" instances
static int instance_count;
private:
// buffer that can get corrupted
int* internal_data;
};

int VirtualPyObject::instance_count = 0;

PyType_Slot VirtualPyObject_Slots[] = {
{Py_tp_free, (void*)VirtualPyObject::dealloc},
{0, _Py_NULL},
};

PyType_Spec VirtualPyObject_Spec = {
.name = STR(NAME) ".VirtualPyObject",
.basicsize = sizeof(VirtualPyObject),
.flags = Py_TPFLAGS_DEFAULT,
.slots = VirtualPyObject_Slots,
};

VirtualPyObject::VirtualPyObject() {
// Create a temporary type (just so we don't need to store it)
PyObject *ipotype = PyType_FromSpec(&VirtualPyObject_Spec);
// no good way to signal failure from a C++ constructor, so use assert
// for error handling
assert(ipotype);
assert(PyObject_Init(this, (PyTypeObject *)ipotype));
Py_DECREF(ipotype);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
PyObject *ipotype = PyType_FromSpec(&VirtualPyObject_Spec);
// no good way to signal failure from a C++ constructor, so use assert
// for error handling
assert(ipotype);
assert(PyObject_Init(this, (PyTypeObject *)ipotype));
Py_DECREF(ipotype);
PyObject *vpotype = PyType_FromSpec(&VirtualPyObject_Spec);
// no good way to signal failure from a C++ constructor, so use assert
// for error handling
assert(vpotype);
assert(PyObject_Init(this, (PyTypeObject *)vpotype));
Py_DECREF(vpotype);

Just suggesting changing the name slightly here to match the name you used from the class. I think the i from ipotype was a hold-over from the name I used.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! It's a local variable now so I'll just name it type :)

internal_data = new int[50];
++instance_count;
}

static PyObject *
test_virtual_object(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
{
VirtualPyObject* obj = new VirtualPyObject();
obj->set_internal_data();
Py_DECREF(obj);
if (VirtualPyObject::instance_count != 0) {
return PyErr_Format(
PyExc_AssertionError,
"instance_count should be 0, got %d",
VirtualPyObject::instance_count);
}
Py_RETURN_NONE;
}

static PyMethodDef _testcppext_methods[] = {
{"add", _testcppext_add, METH_VARARGS, _testcppext_add_doc},
{"test_api_casts", test_api_casts, METH_NOARGS, _Py_NULL},
{"test_unicode", test_unicode, METH_NOARGS, _Py_NULL},
{"test_virtual_object", test_virtual_object, METH_NOARGS, _Py_NULL},
// Note: _testcppext_exec currently runs all test functions directly.
// When adding a new one, add a call there.

Expand All @@ -152,6 +220,10 @@ _testcppext_exec(PyObject *module)
if (!result) return -1;
Py_DECREF(result);

result = PyObject_CallMethod(module, "test_virtual_object", "");
if (!result) return -1;
Py_DECREF(result);

return 0;
}

Expand All @@ -163,9 +235,6 @@ static PyModuleDef_Slot _testcppext_slots[] = {

PyDoc_STRVAR(_testcppext_doc, "C++ test extension.");

#define _STR(NAME) #NAME
#define STR(NAME) _STR(NAME)

static struct PyModuleDef _testcppext_module = {
PyModuleDef_HEAD_INIT, // m_base
STR(NAME), // m_name
Expand Down