Browse Source

pgraph: fix crash accessing python_tags via dict property

Fixes #326
rdb 7 years ago
parent
commit
5c9705c21c
3 changed files with 42 additions and 10 deletions
  1. 20 10
      panda/src/pgraph/pandaNode_ext.cxx
  2. 2 0
      panda/src/pgraph/pandaNode_ext.h
  3. 20 0
      tests/pgraph/test_nodepath.py

+ 20 - 10
panda/src/pgraph/pandaNode_ext.cxx

@@ -94,14 +94,9 @@ get_tag_keys() const {
  */
 PyObject *Extension<PandaNode>::
 get_python_tags() {
-  if (_this->_python_tag_data == NULL) {
-    _this->_python_tag_data = new PythonTagDataImpl;
-
-  } else if (_this->_python_tag_data->get_ref_count() > 1) {
-    // Copy-on-write.
-    _this->_python_tag_data = new PythonTagDataImpl(*(PythonTagDataImpl *)_this->_python_tag_data.p());
-  }
-  return ((PythonTagDataImpl *)_this->_python_tag_data.p())->_dict;
+  PyObject *dict = do_get_python_tags();
+  Py_INCREF(dict);
+  return dict;
 }
 
 /**
@@ -116,7 +111,7 @@ get_python_tags() {
  */
 int Extension<PandaNode>::
 set_python_tag(PyObject *key, PyObject *value) {
-  return PyDict_SetItem(get_python_tags(), key, value);
+  return PyDict_SetItem(do_get_python_tags(), key, value);
 }
 
 /**
@@ -166,7 +161,7 @@ clear_python_tag(PyObject *key) {
     return;
   }
 
-  PyObject *dict = get_python_tags();
+  PyObject *dict = do_get_python_tags();
   if (PyDict_GetItem(dict, key) != NULL) {
     PyDict_DelItem(dict, key);
   }
@@ -201,6 +196,21 @@ __traverse__(visitproc visit, void *arg) {
   return 0;
 }
 
+/**
+ * Same as get_python_tags, without incrementing the reference count.
+ */
+PyObject *Extension<PandaNode>::
+do_get_python_tags() {
+  if (_this->_python_tag_data == NULL) {
+    _this->_python_tag_data = new PythonTagDataImpl;
+
+  } else if (_this->_python_tag_data->get_ref_count() > 1) {
+    // Copy-on-write.
+    _this->_python_tag_data = new PythonTagDataImpl(*(PythonTagDataImpl *)_this->_python_tag_data.p());
+  }
+  return ((PythonTagDataImpl *)_this->_python_tag_data.p())->_dict;
+}
+
 /**
  * Destroys the tags associated with the node.
  */

+ 2 - 0
panda/src/pgraph/pandaNode_ext.h

@@ -45,6 +45,8 @@ public:
   int __traverse__(visitproc visit, void *arg);
 
 private:
+  PyObject *do_get_python_tags();
+
   // This is what actually stores the Python tags.
   class PythonTagDataImpl : public PandaNode::PythonTagData {
   public:

+ 20 - 0
tests/pgraph/test_nodepath.py

@@ -1,3 +1,5 @@
+import pytest, sys
+
 def test_nodepath_empty():
     """Tests NodePath behavior for empty NodePaths."""
     from panda3d.core import NodePath
@@ -103,3 +105,21 @@ def test_weak_nodepath_comparison():
     assert hash(path) == hash(weak)
     assert weak.get_node_path() == path
     assert weak.node() == path.node()
+
+
+def test_nodepath_python_tags():
+    from panda3d.core import NodePath
+
+    path = NodePath("node")
+
+    with pytest.raises(KeyError):
+        path.python_tags["foo"]
+
+    path.python_tags["foo"] = "bar"
+
+    assert path.python_tags["foo"] == "bar"
+
+    # Make sure reference count stays the same
+    rc1 = sys.getrefcount(path.python_tags)
+    rc2 = sys.getrefcount(path.python_tags)
+    assert rc1 == rc2