Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modernize DECREF (part 8). #3219

Merged
merged 19 commits into from
Nov 21, 2024
Merged
Changes from all commits
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
201 changes: 97 additions & 104 deletions src/nrnpython/nrnpy_hoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2782,8 +2782,8 @@
int size = hv->size();
double* x = vector_vec(hv);
// printf("%s.to_array\n", hoc_object_name(hv->obj_));
PyObject* po;
Object* ho = 0;
nb::object po;
Object* ho = nullptr;

// as_numpy_array=True is the case where this function is being called by the
// ivocvect __array__ member
Expand All @@ -2796,33 +2796,32 @@
if (ho->ctemplate->sym != nrnpy_pyobj_sym_) {
hoc_execerror(hoc_object_name(ho), " is not a PythonObject");
}
po = nrnpy_hoc2pyobject(ho);
if (!PySequence_Check(po)) {
po = nb::borrow(nrnpy_hoc2pyobject(ho));
if (!PySequence_Check(po.ptr())) {
hoc_execerror(hoc_object_name(ho), " is not a Python Sequence");
}
if (size != PySequence_Size(po)) {
if (size != PySequence_Size(po.ptr())) {
hoc_execerror(hoc_object_name(ho), "Python Sequence not same size as Vector");
}
} else {
if ((po = PyList_New(size)) == NULL) {
if (!(po = nb::steal(PyList_New(size)))) {
hoc_execerror("Could not create new Python List with correct size.", 0);
}

ho = nrnpy_po2ho(po);
Py_DECREF(po);
ho = nrnpy_po2ho(po.ptr());
--ho->refcount;
}
// printf("size = %d\n", size);
long stride;
char* y = double_array_interface(po, stride);
char* y = double_array_interface(po.ptr(), stride);
if (y) {
for (int i = 0, j = 0; i < size; ++i, j += stride) {
*(double*) (y + j) = x[i];
}
} else if (PyList_Check(po)) { // PySequence_SetItem does DECREF of old items
} else if (PyList_Check(po.ptr())) { // PySequence_SetItem does DECREF of old items
for (int i = 0; i < size; ++i) {
auto pn = nb::steal(PyFloat_FromDouble(x[i]));
if (!pn || PyList_SetItem(po, i, pn.release().ptr()) == -1) {
if (!pn || PyList_SetItem(po.ptr(), i, pn.release().ptr()) == -1) {
char buf[50];
Sprintf(buf, "%d of %d", i, size);
hoc_execerror("Could not set a Python Sequence item", buf);
Expand All @@ -2831,13 +2830,26 @@
} else { // assume PySequence_SetItem works
for (int i = 0; i < size; ++i) {
auto pn = nb::steal(PyFloat_FromDouble(x[i]));
if (!pn || PySequence_SetItem(po, i, pn.ptr()) == -1) {
if (!pn || PySequence_SetItem(po.ptr(), i, pn.ptr()) == -1) {

Check warning on line 2833 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L2833

Added line #L2833 was not covered by tests
char buf[50];
Sprintf(buf, "%d of %d", i, size);
hoc_execerror("Could not set a Python Sequence item", buf);
}
}
}

// The HOC reference throughout most of this function is 0 (preventing the need to decrement on
// error paths).
//
// Because the dtor of `po` will decrease the reference count of the `ho` (if it contains one),
// the order in which the decrements happen matter, or else `ho` can be deallocated.
//
// To avoid the situation described, we must briefly acquire a reference to the HOC object (by
// bumping its reference count) and then decrement the reference count again.
++ho->refcount;
po.dec_ref();
po.release();
--ho->refcount;
return hoc_temp_objptr(ho);
}

Expand Down Expand Up @@ -2893,53 +2905,48 @@
Vect* vec = (Vect*) pho->ho_->u.this_pointer;

// neuron module has a _pkl method that returns h.Vector(0)
PyObject* mod = PyImport_ImportModule("neuron");
if (mod == NULL) {
return NULL;
auto mod = nb::steal(PyImport_ImportModule("neuron"));
if (!mod) {
return nullptr;

Check warning on line 2910 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L2910

Added line #L2910 was not covered by tests
}
PyObject* obj = PyObject_GetAttrString(mod, "_pkl");
Py_DECREF(mod);
if (obj == NULL) {
auto obj = nb::steal(PyObject_GetAttrString(mod.ptr(), "_pkl"));
if (!obj) {
PyErr_SetString(PyExc_Exception, "neuron module has no _pkl method.");
return NULL;
return nullptr;

Check warning on line 2915 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L2915

Added line #L2915 was not covered by tests
}

PyObject* ret = PyTuple_New(3);
if (ret == NULL) {
return NULL;
auto ret = nb::steal(PyTuple_New(3));
if (!ret) {
return nullptr;

Check warning on line 2920 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L2920

Added line #L2920 was not covered by tests
}
PyTuple_SET_ITEM(ret, 0, obj);
PyTuple_SET_ITEM(ret, 1, Py_BuildValue("(N)", PyInt_FromLong(0)));
PyTuple_SET_ITEM(ret.ptr(), 0, obj.release().ptr());
PyTuple_SET_ITEM(ret.ptr(), 1, Py_BuildValue("(N)", PyInt_FromLong(0)));
// see numpy implementation if more ret[1] stuff needed in case we
// pickle anything but a hoc Vector. I don't think ret[1] can be None.

// Fill object's state. Tuple with 4 args:
// pickle version, 2.0 bytes to determine if swapbytes needed,
// vector size, string data
PyObject* state = PyTuple_New(4);
if (state == NULL) {
Py_DECREF(ret);
return NULL;
auto state = nb::steal(PyTuple_New(4));
if (!state) {
return nullptr;

Check warning on line 2932 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L2932

Added line #L2932 was not covered by tests
}
PyTuple_SET_ITEM(state, 0, PyInt_FromLong(1));
PyTuple_SET_ITEM(state.ptr(), 0, PyInt_FromLong(1));
double x = 2.0;
PyObject* str = PyBytes_FromStringAndSize((const char*) (&x), sizeof(double));
if (str == NULL) {
Py_DECREF(ret);
Py_DECREF(state);
return NULL;
auto two = nb::steal(PyBytes_FromStringAndSize((const char*) (&x), sizeof(double)));
if (!two) {
return nullptr;

Check warning on line 2938 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L2938

Added line #L2938 was not covered by tests
}
PyTuple_SET_ITEM(state, 1, str);
PyTuple_SET_ITEM(state, 2, PyInt_FromLong(vec->size()));
str = PyBytes_FromStringAndSize((const char*) vector_vec(vec), vec->size() * sizeof(double));
if (str == NULL) {
Py_DECREF(ret);
Py_DECREF(state);
return NULL;
PyTuple_SET_ITEM(state.ptr(), 1, two.release().ptr());
PyTuple_SET_ITEM(state.ptr(), 2, PyInt_FromLong(vec->size()));
auto str = nb::steal(
PyBytes_FromStringAndSize((const char*) vector_vec(vec), vec->size() * sizeof(double)));
if (!str) {
return nullptr;

Check warning on line 2945 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L2945

Added line #L2945 was not covered by tests
}
PyTuple_SET_ITEM(state, 3, str);
PyTuple_SET_ITEM(ret, 2, state);
return ret;
PyTuple_SET_ITEM(state.ptr(), 3, str.release().ptr());
PyTuple_SET_ITEM(ret.ptr(), 2, state.release().ptr());
return ret.release().ptr();
}

static PyObject* hocpickle_reduce_safe(PyObject* self, PyObject* args) {
Expand All @@ -2965,60 +2972,56 @@
BYTEHEADER
int version = -1;
int size = 0;
PyObject* rawdata = NULL;
PyObject* endian_data;
nb::object endian_data;
nb::object rawdata;
PyHocObject* pho = (PyHocObject*) self;
// printf("hocpickle_setstate %s\n", hoc_object_name(pho->ho_));
Vect* vec = (Vect*) pho->ho_->u.this_pointer;
if (!PyArg_ParseTuple(args, "(iOiO)", &version, &endian_data, &size, &rawdata)) {
return NULL;
{
PyObject* pendian_data;
PyObject* prawdata;
if (!PyArg_ParseTuple(args, "(iOiO)", &version, &pendian_data, &size, &prawdata)) {
return nullptr;

Check warning on line 2984 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L2984

Added line #L2984 was not covered by tests
}

rawdata = nb::borrow(prawdata);
endian_data = nb::borrow(pendian_data);
}
Py_INCREF(endian_data);
Py_INCREF(rawdata);
// printf("hocpickle version=%d size=%d\n", version, size);
vector_resize(vec, size);
if (!PyBytes_Check(rawdata) || !PyBytes_Check(endian_data)) {
if (!PyBytes_Check(rawdata.ptr()) || !PyBytes_Check(endian_data.ptr())) {
PyErr_SetString(PyExc_TypeError, "pickle not returning string");
Py_DECREF(endian_data);
Py_DECREF(rawdata);
return NULL;
return nullptr;

Check warning on line 2994 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L2994

Added line #L2994 was not covered by tests
}
char* datastr;
char* two;
Py_ssize_t len;
if (PyBytes_AsStringAndSize(endian_data, &datastr, &len) < 0) {
Py_DECREF(endian_data);
Py_DECREF(rawdata);
return NULL;
if (PyBytes_AsStringAndSize(endian_data.ptr(), &two, &len) < 0) {
return nullptr;

Check warning on line 2999 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L2999

Added line #L2999 was not covered by tests
}
if (len != sizeof(double)) {
PyErr_SetString(PyExc_ValueError, "endian_data size is not sizeof(double)");
Py_DECREF(endian_data);
Py_DECREF(rawdata);
return NULL;
return nullptr;

Check warning on line 3003 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L3003

Added line #L3003 was not covered by tests
}
BYTESWAP_FLAG = 0;
if (*((double*) datastr) != 2.0) {
if (*((double*) two) != 2.0) {
BYTESWAP_FLAG = 1;
}
Py_DECREF(endian_data);
// printf("byteswap = %d\n", BYTESWAP_FLAG);
if (PyBytes_AsStringAndSize(rawdata, &datastr, &len) < 0) {
Py_DECREF(rawdata);
char* str;
if (PyBytes_AsStringAndSize(rawdata.ptr(), &str, &len) < 0) {
return NULL;
}
if (len != Py_ssize_t(size * sizeof(double))) {
PyErr_SetString(PyExc_ValueError, "buffer size does not match array size");
Py_DECREF(rawdata);
return NULL;
return nullptr;

Check warning on line 3016 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L3016

Added line #L3016 was not covered by tests
}
if (BYTESWAP_FLAG) {
double* x = (double*) datastr;
double* x = (double*) str;

Check warning on line 3019 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L3019

Added line #L3019 was not covered by tests
for (int i = 0; i < size; ++i) {
BYTESWAP(x[i], double)
}
}
memcpy((char*) vector_vec(vec), datastr, len);
Py_DECREF(rawdata);
memcpy((char*) vector_vec(vec), str, len);

Py_RETURN_NONE;
}
Expand Down Expand Up @@ -3083,12 +3086,11 @@
static void add2topdict(PyObject* dict) {
for (PyMethodDef* meth = toplevel_methods; meth->ml_name != NULL; meth++) {
int err;
PyObject* nn = Py_BuildValue("s", meth->ml_doc);
auto nn = nb::steal(Py_BuildValue("s", meth->ml_doc));
if (!nn) {
return;
}
err = PyDict_SetItemString(dict, meth->ml_name, nn);
Py_DECREF(nn);
err = PyDict_SetItemString(dict, meth->ml_name, nn.ptr());
if (err) {
return;
}
Expand Down Expand Up @@ -3215,24 +3217,22 @@
}
PyObject* pargs = nrnpy_hoc2pyobject(args);

PyObject* iterator = PyObject_GetIter(pargs);
PyObject* item;
auto iterator = nb::steal(PyObject_GetIter(pargs));

if (iterator == NULL) {
if (!iterator) {
PyErr_Clear();
hoc_execerror("argument must be an iterable", "");
}

while ((item = PyIter_Next(iterator))) {
if (!PyObject_TypeCheck(item, psection_type)) {
nb::object item;
while ((item = nb::steal(PyIter_Next(iterator.ptr())))) {
if (!PyObject_TypeCheck(item.ptr(), psection_type)) {
hoc_execerror("iterable must contain only Section objects", 0);
}
NPySecObj* pysec = (NPySecObj*) item;
NPySecObj* pysec = (NPySecObj*) item.ptr();
lvappendsec_and_ref(sl, pysec->sec_);
Py_DECREF(item);
}

Py_DECREF(iterator);
if (PyErr_Occurred()) {
PyErr_Clear();
hoc_execerror("argument must be a Python iterable", "");
Expand All @@ -3257,10 +3257,9 @@
if (modules) {
PyObject* module = PyDict_GetItemString(modules, "neuron.coreneuron");
if (module) {
PyObject* val = PyObject_GetAttrString(module, option);
auto val = nb::steal(PyObject_GetAttrString(module, option));
if (val) {
long enable = PyLong_AsLong(val);
Py_DECREF(val);
long enable = PyLong_AsLong(val.ptr());
if (enable != -1) {
return enable;
}
Expand Down Expand Up @@ -3297,18 +3296,16 @@
if (module) {
auto callable = nb::steal(PyObject_GetAttrString(module, "nrncore_arg"));
if (callable) {
PyObject* ts = Py_BuildValue("(d)", tstop);
auto ts = nb::steal(Py_BuildValue("(d)", tstop));
if (ts) {
PyObject* arg = PyObject_CallObject(callable.ptr(), ts);
Py_DECREF(ts);
auto arg = nb::steal(PyObject_CallObject(callable.ptr(), ts.ptr()));
if (arg) {
Py2NRNString str(arg);
Py_DECREF(arg);
Py2NRNString str(arg.ptr());
if (str.err()) {
str.set_pyerr(
PyExc_TypeError,
"neuron.coreneuron.nrncore_arg() must return an ascii string");
return NULL;
return nullptr;

Check warning on line 3308 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L3308

Added line #L3308 was not covered by tests
}
if (strlen(str.c_str()) > 0) {
return strdup(str.c_str());
Expand All @@ -3321,7 +3318,7 @@
if (PyErr_Occurred()) {
PyErr_Print();
}
return NULL;
return nullptr;

Check warning on line 3321 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L3321

Added line #L3321 was not covered by tests
}


Expand Down Expand Up @@ -3409,7 +3406,6 @@
return NULL;
}


auto bases = nb::steal(PyTuple_Pack(1, hocobject_type));
for (auto name: py_exposed_classes) {
// TODO: obj_spec_from_name needs a hoc. prepended
Expand All @@ -3424,23 +3420,21 @@
sym_to_type_map[hclass->sym] = pto;
type_to_sym_map[pto] = hclass->sym;
if (PyType_Ready(pto) < 0) {
goto fail;
return nullptr;

Check warning on line 3423 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L3423

Added line #L3423 was not covered by tests
}
if (PyModule_AddObject(m, name, (PyObject*) pto) < 0) {
return NULL;
return nullptr;

Check warning on line 3426 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L3426

Added line #L3426 was not covered by tests
}
}

topmethdict = PyDict_New();
for (PyMethodDef* meth = toplevel_methods; meth->ml_name != NULL; meth++) {
PyObject* descr;
int err;
descr = PyDescr_NewMethod(hocobject_type, meth);
auto descr = nb::steal(PyDescr_NewMethod(hocobject_type, meth));
assert(descr);
err = PyDict_SetItemString(topmethdict, meth->ml_name, descr);
Py_DECREF(descr);
err = PyDict_SetItemString(topmethdict, meth->ml_name, descr.ptr());
if (err < 0) {
goto fail;
return nullptr;

Check warning on line 3437 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L3437

Added line #L3437 was not covered by tests
}
}

Expand All @@ -3466,8 +3460,9 @@

nrnpy_nrn();
endian_character = get_endian_character();
if (endian_character == 0)
goto fail;
if (endian_character == 0) {
return nullptr;

Check warning on line 3464 in src/nrnpython/nrnpy_hoc.cpp

View check run for this annotation

Codecov / codecov/patch

src/nrnpython/nrnpy_hoc.cpp#L3464

Added line #L3464 was not covered by tests
}
array_interface_typestr[0] = endian_character;

// Setup bytesize in typestr
Expand All @@ -3476,8 +3471,6 @@
assert(err == 0);
// Py_DECREF(m);
return m;
fail:
return NULL;
}

void nrnpython_reg_real_nrnpy_hoc_cpp(neuron::python::impl_ptrs* ptrs) {
Expand Down
Loading