Skip to content

Commit 241cbd3

Browse files
authored
Bugfixes: Issue 320, 338, and 349 (biggest feature: constructors copy values) (#359)
* Ensure that upgrade functions are sorted. * Remove unneccesary check from deserialize_from_string * Make a copy of the environment in the rv adapter. * Enforce copying arguments to items. * Ensure that end_time_inclusive returns a copy. * Copy arguments to TimeRange and TimeTransform. * Use the "super" syntax for calling the parent constructor. * cleaned up extended_by. * Gate deepcopy with if rather than 'or' * Add unit tests for coverage.
1 parent 3fe5b33 commit 241cbd3

24 files changed

+143
-76
lines changed

opentimelineio/core/composable.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
from . import serializable_object
3131
from . import type_registry
3232

33+
import copy
34+
3335

3436
@type_registry.register_type
3537
class Composable(serializable_object.SerializableObject):
@@ -58,7 +60,7 @@ def __init__(self, name=None, metadata=None):
5860

5961
# initialize the serializable fields
6062
self.name = name
61-
self.metadata = metadata or {}
63+
self.metadata = copy.deepcopy(metadata) if metadata else {}
6264

6365
@staticmethod
6466
def visible():

opentimelineio/core/composition.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,16 @@ def __init__(
5757
name=None,
5858
children=None,
5959
source_range=None,
60+
markers=None,
61+
effects=None,
6062
metadata=None
6163
):
6264
item.Item.__init__(
6365
self,
6466
name=name,
6567
source_range=source_range,
68+
markers=markers,
69+
effects=effects,
6670
metadata=metadata
6771
)
6872
collections.MutableSequence.__init__(self)

opentimelineio/core/item.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,11 @@ def __init__(
6161
markers=None,
6262
metadata=None,
6363
):
64-
serializable_object.SerializableObject.__init__(self)
65-
66-
self.name = name
67-
self.source_range = source_range
68-
self.effects = effects or []
69-
self.markers = markers or []
70-
self.metadata = metadata or {}
71-
self._parent = None
64+
super(Item, self).__init__(name=name, metadata=metadata)
65+
66+
self.source_range = copy.deepcopy(source_range)
67+
self.effects = copy.deepcopy(effects) if effects else []
68+
self.markers = copy.deepcopy(markers) if markers else []
7269

7370
name = serializable_object.serializable_field("name", doc="Item name.")
7471
source_range = serializable_object.serializable_field(

opentimelineio/core/json_serializer.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,7 @@ def _as_otio(dct):
206206
def deserialize_json_from_string(otio_string):
207207
""" Deserialize a string containing JSON to OTIO objects. """
208208

209-
json_data = json.loads(otio_string, object_hook=_as_otio)
210-
211-
if json_data is {}:
212-
raise exceptions.CouldNotReadFileError
213-
214-
return json_data
209+
return json.loads(otio_string, object_hook=_as_otio)
215210

216211

217212
def deserialize_json_from_file(otio_filepath):

opentimelineio/core/media_reference.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
serializable_object,
3333
)
3434

35+
import copy
36+
3537

3638
@type_registry.register_type
3739
class MediaReference(serializable_object.SerializableObject):
@@ -52,11 +54,11 @@ def __init__(
5254
available_range=None,
5355
metadata=None
5456
):
55-
serializable_object.SerializableObject.__init__(self)
57+
super(MediaReference, self).__init__()
5658

5759
self.name = name
58-
self.available_range = available_range
59-
self.metadata = metadata or {}
60+
self.available_range = copy.deepcopy(available_range)
61+
self.metadata = copy.deepcopy(metadata) or {}
6062

6163
name = serializable_object.serializable_field(
6264
"name",

opentimelineio/core/type_registry.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,10 @@ def instance_from_schema(schema_name, schema_version, data_dict):
135135
)
136136

137137
if cls.schema_version() != schema_version:
138-
for version, upgrade_func in (
138+
# since the keys are the versions to upgrade to, sorting the keys
139+
# before iterating through them should ensure that upgrade functions
140+
# are called in order.
141+
for version, upgrade_func in sorted(
139142
_UPGRADE_FUNCTIONS[cls].items()
140143
):
141144
if version < schema_version:

opentimelineio/hooks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def __init__(
9797
):
9898
"""HookScript plugin constructor."""
9999

100-
plugins.PythonPlugin.__init__(self, name, execution_scope, filepath)
100+
super(HookScript, self).__init__(name, execution_scope, filepath)
101101

102102
def run(self, in_timeline, argument_map={}):
103103
"""Run the hook_function associated with this plugin."""

opentimelineio/media_linker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def __init__(
137137
execution_scope=None,
138138
filepath=None,
139139
):
140-
plugins.PythonPlugin.__init__(self, name, execution_scope, filepath)
140+
super(MediaLinker, self).__init__(name, execution_scope, filepath)
141141

142142
def link_media_reference(self, in_clip, media_linker_argument_map=None):
143143
media_linker_argument_map = media_linker_argument_map or {}

opentimelineio/opentime.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ def __iadd__(self, other):
136136
self.value = value
137137
self.rate = scale
138138

139+
# @TODO: make this construct and return a new object
139140
return self
140141

141142
def __add__(self, other):
@@ -233,7 +234,7 @@ class TimeTransform(object):
233234
"""1D Transform for RationalTime. Has offset and scale."""
234235

235236
def __init__(self, offset=RationalTime(), scale=1.0, rate=None):
236-
self.offset = offset
237+
self.offset = copy.copy(offset)
237238
self.scale = scale
238239
self.rate = rate
239240

@@ -315,8 +316,8 @@ class TimeRange(object):
315316
"""
316317

317318
def __init__(self, start_time=RationalTime(), duration=RationalTime()):
318-
self.start_time = start_time
319-
self.duration = duration
319+
self.start_time = copy.copy(start_time)
320+
self.duration = copy.copy(duration)
320321

321322
def __copy__(self, memodict=None):
322323
# Construct a new one directly to avoid the overhead of deepcopy
@@ -368,7 +369,7 @@ def end_time_inclusive(self):
368369

369370
return result
370371
else:
371-
return self.start_time
372+
return copy.deepcopy(self.start_time)
372373

373374
def end_time_exclusive(self):
374375
""""Time of the first sample outside the time range.
@@ -385,24 +386,20 @@ def end_time_exclusive(self):
385386
def extended_by(self, other):
386387
"""Construct a new TimeRange that is this one extended by another."""
387388

388-
result = TimeRange(self.start_time, self.duration)
389-
if isinstance(other, TimeRange):
390-
result.start_time = min(self.start_time, other.start_time)
391-
new_end_time = max(
392-
self.end_time_exclusive(),
393-
other.end_time_exclusive()
394-
)
395-
result.duration = duration_from_start_end_time(
396-
result.start_time,
397-
new_end_time
398-
)
399-
else:
389+
if not isinstance(other, TimeRange):
400390
raise TypeError(
401391
"extended_by requires rtime be a TimeRange, not a '{}'".format(
402392
type(other)
403393
)
404394
)
405-
return result
395+
396+
start_time = min(self.start_time, other.start_time)
397+
new_end_time = max(
398+
self.end_time_exclusive(),
399+
other.end_time_exclusive()
400+
)
401+
duration = duration_from_start_end_time(start_time, new_end_time)
402+
return TimeRange(start_time, duration)
406403

407404
# @TODO: remove?
408405
def clamped(
@@ -790,8 +787,12 @@ def duration_from_start_end_time(start_time, end_time_exclusive):
790787
)
791788
else:
792789
return RationalTime(
793-
end_time_exclusive.value_rescaled_to(start_time) - start_time.value,
794-
start_time.rate)
790+
(
791+
end_time_exclusive.value_rescaled_to(start_time)
792+
- start_time.value
793+
),
794+
start_time.rate
795+
)
795796

796797

797798
# @TODO: create range from start/end [in,ex]clusive

opentimelineio/plugins/manifest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class Manifest(core.SerializableObject):
8282
_serializable_label = "PluginManifest.1"
8383

8484
def __init__(self):
85-
core.SerializableObject.__init__(self)
85+
super(Manifest, self).__init__()
8686
self.adapters = []
8787
self.schemadefs = []
8888
self.media_linkers = []

0 commit comments

Comments
 (0)