Reference counts and dictionary creadtion

Hello,
I am attempting to write a python extension in C to "glue" the ASIO SDK to python 2.5.4. However I am really confused about reference counts. Below is a code snippet that "seems" to work, however I suspect that some of the object reference counts have been incremented too many times.
static PyObject *
getAsioInfo(PyObject *self, PyObject *args) {
PyObject *dict = Py_None;
if (loadAsioDriver (ASIO_DRIVER_NAME)) {
if (ASIOInit (&asioDriverInfo.driverInfo) == ASE_OK){
dict = PyDict_New();
PyDict_SetItem(dict,
PyString_FromString("asioVersion"),
PyInt_FromLong(asioDriverInfo.driverInfo.asioVersion) );
PyDict_SetItem(dict,
PyString_FromString("driverName"),
PyString_FromString(asioDriverInfo.driverInfo.name) );
return dict;
}
}
return Null;
}
As nearly as I can tell from the Python source code, PyDict_SetItem will do a Py_INCREF on both the key and value parameters. However, it looks as if PyInt_FromLong will incref the object it is returning - at least if it is a "small value". Am I on the right track here? Or am I better off doing something like:
PyObject *str, *lng;
str = PyString_FromString("asioVersion");
lng = PyInt_FromLong(asioDriverInfo.driverInfo.asioVersion)
);
PyDict_SetItem(dict, str, lng);
Py_DECREF(str);
Py_DECREF(lng);
Thanks in advance for all advice!
Dan Colesworthy

Dan Colesworthy, 29.12.2009 21:38:
I am attempting to write a python extension in C to "glue" the ASIO SDK to python 2.5.4. However I am really confused about reference counts.
You might want to take a look into Cython, a Python-like language that is well suited for writing very fast C library wrappers. Besides many other features, it will do all the reference counting for you and generates code that works in Python 2 and Python 3.
Below is a code snippet that "seems" to work, however I suspect that some of the object reference counts have been incremented too many times.
static PyObject *
getAsioInfo(PyObject *self, PyObject *args) {
PyObject *dict = Py_None;
if (loadAsioDriver (ASIO_DRIVER_NAME)) {
if (ASIOInit (&asioDriverInfo.driverInfo) == ASE_OK){ dict = PyDict_New(); PyDict_SetItem(dict, PyString_FromString("asioVersion"),
Yes, this doesn't work. PyDict_SetItem() doesn't steal the reference, so you end up with a dangling reference to the just created string. Same for the other objects created below:
PyInt_FromLong(asioDriverInfo.driverInfo.asioVersion) );
PyDict_SetItem(dict, PyString_FromString("driverName"),
PyString_FromString(asioDriverInfo.driverInfo.name) );
You need to assign each object to a variable, call SetItem() on it, and then Py_DECREF() it afterwards.
Note that there is also a special function PyDict_SetItemString() that accepts a char* as key and creates the Python string object internally.
Also note that the PyString_*() functions are likely not what you want to use in Python 3.
Stefan

Dan Colesworthy wrote:
Hello,
I am attempting to write a python extension in C to "glue" the ASIO SDK to python 2.5.4. However I am really confused about reference counts. Below is a code snippet that "seems" to work, however I suspect that some of the object reference counts have been incremented too many times.
static PyObject *
getAsioInfo(PyObject *self, PyObject *args) {
PyObject *dict = Py_None;
if (loadAsioDriver (ASIO_DRIVER_NAME)) {
if (ASIOInit (&asioDriverInfo.driverInfo) == ASE_OK){ dict = PyDict_New(); PyDict_SetItem(dict, PyString_FromString("asioVersion"),
PyInt_FromLong(asioDriverInfo.driverInfo.asioVersion) );
PyDict_SetItem(dict, PyString_FromString("driverName"),
PyString_FromString(asioDriverInfo.driverInfo.name) );
return dict; }
}
return Null;
}
As nearly as I can tell from the Python source code, PyDict_SetItem will do a Py_INCREF on both the key and value parameters. However, it looks as if PyInt_FromLong will incref the object it is returning - at least if it is a "small value".
Object constructors will always return objects with a refcount of 1.
When adding a key, value pair to a dictionary using PyDict_SetItem() the function will take care of adjusting the refcounts.
After the call, you still own the objects key and value, so you have to decref them if you no longer need them.
Am I on the right track here? Or am I better off doing something like:
PyObject *str, *lng; str = PyString_FromString("asioVersion"); lng = PyInt_FromLong(asioDriverInfo.driverInfo.asioVersion)
);
PyDict_SetItem(dict, str, lng);
Py_DECREF(str);
Py_DECREF(lng);
See above. The second approach is likely what you need.
BTW: PyDict_SetItemString() is usually a better approach to filling dictionaries that use often used keys. It interns the key string for you, making subsequent lookups faster.
OTOH, if you plan to port to Python 3.x, you will likely want to use Unicode objects as keys.
-- Marc-Andre Lemburg eGenix.com
Professional Python Services directly from the Source (#1, Dec 30 2009)
Python/Zope Consulting and Support ... http://www.egenix.com/ mxODBC.Zope.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
::: Try our new mxODBC.Connect Python Database Interface for free ! ::::
eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
participants (3)
-
Dan Colesworthy
-
M.-A. Lemburg
-
Stefan Behnel