Skip to content

Commit

Permalink
App: fix Python object leak in PropertyListT
Browse files Browse the repository at this point in the history
* Fix Python object leak in _setPyObject()
* Add support for Python iterables
* Minor performance improvement on setPyValues()
  • Loading branch information
realthunder authored and wwmayer committed Aug 17, 2019
1 parent 3fcb3e6 commit d3500ec
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 33 deletions.
55 changes: 35 additions & 20 deletions src/App/Property.cpp
Expand Up @@ -213,44 +213,59 @@ void Property::setStatus(Status pos, bool on) {
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void PropertyListsBase::_setPyObject(PyObject *value) {
std::vector<PyObject *> vals;
std::vector<int> indices;
std::vector<PyObject *> vals;
Py::Object pySeq;

if (PyDict_Check(value)) {
PyObject* keyList = PyDict_Keys(value);
PyObject* itemList = PyDict_Values(value);
Py_ssize_t nSize = PyList_Size(keyList);
vals.reserve(nSize);
indices.reserve(nSize);
Py::Dict dict(value);
auto size = dict.size();
vals.reserve(size);
indices.reserve(size);
int listSize = getSize();
for (Py_ssize_t i=0; i<nSize;++i) {
std::string keyStr;
PyObject* key = PyList_GetItem(keyList, i);
for(auto it=dict.begin();it!=dict.end();++it) {
const auto &item = *it;
PyObject *key = item.first.ptr();
#if PY_MAJOR_VERSION < 3
if(!PyInt_Check(key))
#else
if(!PyLong_Check(key))
#endif
throw Base::TypeError("expect key type to be integer");
auto idx = PyLong_AsLong(key);
throw Base::TypeError("expect key type to be interger");
long idx = PyLong_AsLong(key);
if(idx<-1 || idx>listSize)
throw Base::RuntimeError("index out of bound");
throw Base::ValueError("index out of bound");
if(idx==-1 || idx==listSize) {
idx = listSize;
++listSize;
}
indices.push_back(idx);
vals.push_back(PyList_GetItem(itemList,i));
vals.push_back(item.second.ptr());
}
}else if (PySequence_Check(value)) {
Py_ssize_t nSize = PySequence_Size(value);
vals.reserve(nSize);
for (Py_ssize_t i=0; i<nSize;++i)
vals.push_back(PySequence_GetItem(value, i));
}else
vals.push_back(value);
} else {
if (PySequence_Check(value))
pySeq = value;
else {
PyObject *iter = PyObject_GetIter(value);
if(iter) {
Py::Object pyIter(iter,true);
pySeq = Py::asObject(PySequence_Fast(iter,""));
} else {
PyErr_Clear();
vals.push_back(value);
}
}
if(!pySeq.isNone()) {
Py::Sequence seq(pySeq);
vals.reserve(seq.size());
for(auto it=seq.begin();it!=seq.end();++it)
vals.push_back((*it).ptr());
}
}
setPyValues(vals,indices);
}


//**************************************************************************
//**************************************************************************
// PropertyLists
Expand Down
25 changes: 12 additions & 13 deletions src/App/Property.h
Expand Up @@ -515,20 +515,19 @@ class PropertyListsT: public ParentT

void setPyValues(const std::vector<PyObject*> &vals, const std::vector<int> &indices) override
{
ListT values;
// old version boost::dynamic_bitset don't have reserve(). What a shame!
// values.reserve(vals.size());
for(auto item : vals)
values.push_back(getPyValue(item));
if(indices.empty())
setValues(values);
else {
atomic_change guard(*this);
assert(values.size()==indices.size());
for(int i=0,count=values.size();i<count;++i)
set1Value(indices[i],values[i]);
guard.tryInvoke();
if(indices.empty()) {
ListT values;
values.resize(vals.size());
for(std::size_t i=0,count=vals.size();i<count;++i)
values[i] = getPyValue(vals[i]);
setValues(std::move(values));
return;
}
assert(vals.size()==indices.size());
atomic_change guard(*this);
for(int i=0,count=indices.size();i<count;++i)
set1Value(indices[i],getPyValue(vals[i]));
guard.tryInvoke();
}

virtual T getPyValue(PyObject *item) const = 0;
Expand Down

0 comments on commit d3500ec

Please sign in to comment.