Skip to content

Commit

Permalink
Use a weakref for _parent to prevent cycles. (#364)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssteinbach authored and jminor committed Nov 9, 2018
1 parent 8e8eec6 commit fec780c
Show file tree
Hide file tree
Showing 5 changed files with 16 additions and 14 deletions.
16 changes: 9 additions & 7 deletions opentimelineio/core/composable.py
Expand Up @@ -27,6 +27,8 @@
An object that can be composed by tracks.
"""

import weakref

from . import serializable_object
from . import type_registry

Expand Down Expand Up @@ -81,28 +83,28 @@ def _root_parent(self):
def _ancestors(self):
ancestors = []
seqi = self
while seqi._parent is not None:
seqi = seqi._parent
while seqi.parent() is not None:
seqi = seqi.parent()
ancestors.append(seqi)
return ancestors

def parent(self):
"""Return the parent Composable, or None if self has no parent."""

return self._parent
return self._parent() if self._parent is not None else None

def _set_parent(self, new_parent):
self._parent = new_parent
self._parent = weakref.ref(new_parent) if new_parent is not None else None

def is_parent_of(self, other):
"""Returns true if self is a parent or ancestor of other."""

visited = set([])
while other._parent is not None and other._parent not in visited:
if other._parent is self:
while other.parent() is not None and other.parent() not in visited:
if other.parent() is self:
return True
visited.add(other)
other = other._parent
other = other.parent()

return False

Expand Down
2 changes: 1 addition & 1 deletion opentimelineio/core/composition.py
Expand Up @@ -225,7 +225,7 @@ def _path_to_child(self, child):

while(current is not self):
try:
current = current._parent
current = current.parent()
except AttributeError:
raise exceptions.NotAChildError(
"Item '{}' is not a child of '{}'.".format(child, self)
Expand Down
4 changes: 2 additions & 2 deletions opentimelineio/core/item.py
Expand Up @@ -159,7 +159,7 @@ def transformed_time(self, t, to_item):
item = self
while item != root and item != to_item:

parent = item._parent
parent = item.parent()
result -= item.trimmed_range().start_time
result += parent.range_of_child(item).start_time

Expand All @@ -171,7 +171,7 @@ def transformed_time(self, t, to_item):
item = to_item
while item != root and item != ancestor:

parent = item._parent
parent = item.parent()
result += item.trimmed_range().start_time
result -= parent.range_of_child(item).start_time

Expand Down
4 changes: 2 additions & 2 deletions tests/test_composable.py
Expand Up @@ -85,9 +85,9 @@ def test_set_parent(self):

# set seqi from none
seqi_2._set_parent(seqi)
self.assertEqual(seqi, seqi_2._parent)
self.assertEqual(seqi, seqi_2.parent())

# change seqi
seqi_3 = otio.core.Composable()
seqi_2._set_parent(seqi_3)
self.assertEqual(seqi_3, seqi_2._parent)
self.assertEqual(seqi_3, seqi_2.parent())
4 changes: 2 additions & 2 deletions tests/test_composition.py
Expand Up @@ -120,7 +120,7 @@ def test_replacing_children(self):
def test_parent_manip(self):
it = otio.core.Item()
co = otio.core.Composition(children=[it])
self.assertIs(it._parent, co)
self.assertIs(it.parent(), co)

def test_each_child_recursion(self):
tl = otio.schema.Timeline(name="TL")
Expand Down Expand Up @@ -203,7 +203,7 @@ def test_serialize(self):
decoded = otio.adapters.otio_json.read_from_string(encoded)
self.assertIsOTIOEquivalentTo(st, decoded)

self.assertIsNotNone(decoded[0]._parent)
self.assertIsNotNone(decoded[0].parent())

def test_str(self):
st = otio.schema.Stack(name="foo", children=[])
Expand Down

0 comments on commit fec780c

Please sign in to comment.