Skip to content

Commit 8dce9ee

Browse files
committed
Raise an exception when comparing instance with differing framerates
Use NotImplemented for undefined comparisons to unknown types to allow other classes the chance to provide an implementation Closes #62
1 parent 224088b commit 8dce9ee

2 files changed

Lines changed: 49 additions & 37 deletions

File tree

tests/test_timecode.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -407,21 +407,31 @@ def test_add_with_two_different_frame_rates():
407407
"args,kwargs,func,tc2", [
408408
[["24", "00:00:01:00"], {}, lambda x, y: x + y, "not suitable"],
409409
[["24", "00:00:01:00"], {}, lambda x, y: x - y, "not suitable"],
410-
[["24", "00:00:01:00"], {}, lambda x, y: x * y, "not suitable"],
411410
[["24", "00:00:01:00"], {}, lambda x, y: x / y, "not suitable"],
412411
[["24", "00:00:01:00"], {}, lambda x, y: x / y, 32.4],
412+
[["24", "00:00:01:00"], {}, lambda x, y: x * y, 32.4],
413413
]
414414
)
415-
def test_add_with_non_suitable_class_instance(args, kwargs, func, tc2):
416-
"""TimecodeError is raised if the other class is not suitable for the operation."""
415+
def test_arithmetic_with_non_suitable_class_instance(args, kwargs, func, tc2):
416+
"""TypeError is raised if the other class is not suitable for the operation."""
417417
tc1 = Timecode(*args, **kwargs)
418-
with pytest.raises(TimecodeError) as cm:
418+
with pytest.raises(TypeError) as cm:
419419
_ = func(tc1, tc2)
420420

421-
assert str(cm.value) == "Type {} not supported for arithmetic.".format(
422-
tc2.__class__.__name__
423-
)
421+
assert str(cm.value).startswith(f"unsupported operand type(s) for")
422+
assert str(cm.value).endswith(f"'Timecode' and '{tc2.__class__.__name__}'")
424423

424+
@pytest.mark.parametrize(
425+
"args,kwargs,func,tc2", [
426+
[["24", "00:00:01:00"], {}, lambda x, y: x * y, "not suitable"],
427+
]
428+
)
429+
def test_multiply_with_sequence_type(args, kwargs, func, tc2):
430+
"""TypeError is raised if the other class is not suitable for the operation."""
431+
tc1 = Timecode(*args, **kwargs)
432+
with pytest.raises(TypeError) as cm:
433+
_ = func(tc1, tc2)
434+
assert str(cm.value) == (f"can't multiply sequence by non-int of type 'Timecode'")
425435

426436
def test_div_method_working_properly_1():
427437
"""__div__ method is working properly."""
@@ -973,6 +983,14 @@ def test_lt_method_with_integers():
973983
tc = Timecode("24", "00:00:10:00")
974984
assert tc < 250
975985

986+
def test_comparison_with_different_framerates_raises():
987+
"""Comparing Timecodes with different framerates."""
988+
tc1 = Timecode("24", "00:00:00:00")
989+
tc2 = Timecode("30", "00:00:00:00")
990+
with pytest.raises(ValueError) as cm:
991+
_ = tc1 < tc2
992+
_ = tc1 > tc2
993+
976994

977995
def test_fraction_lib_from_python3_raises_import_error_for_python2():
978996
"""ImportError is raised and the error is handled gracefully under Python 2 if

timecode/__init__.py

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -588,14 +588,16 @@ def __eq__(self, other: Union[int, str, "Timecode", object]) -> bool:
588588
bool: True if the other is equal to this Timecode instance.
589589
"""
590590
if isinstance(other, Timecode):
591-
return self.framerate == other.framerate and self.frames == other.frames
591+
if self.framerate != other.framerate:
592+
raise ValueError("'==' not supported between instances of 'Timecode' with different framerates")
593+
return self.frames == other.frames
592594
elif isinstance(other, str):
593595
new_tc = Timecode(self.framerate, other)
594596
return self.__eq__(new_tc)
595597
elif isinstance(other, int):
596598
return self.frames == other
597-
else:
598-
return False
599+
else:
600+
return NotImplemented
599601

600602
def __ge__(self, other: Union[int, str, "Timecode", object]) -> bool:
601603
"""Override greater than or equal to operator.
@@ -610,16 +612,16 @@ def __ge__(self, other: Union[int, str, "Timecode", object]) -> bool:
610612
bool: True if the other is greater than or equal to this Timecode instance.
611613
"""
612614
if isinstance(other, Timecode):
613-
return self.framerate == other.framerate and self.frames >= other.frames
615+
if self.framerate != other.framerate:
616+
raise ValueError("'>=' not supported between instances of 'Timecode' with different framerates")
617+
return self.frames >= other.frames
614618
elif isinstance(other, str):
615619
new_tc = Timecode(self.framerate, other)
616620
return self.frames >= new_tc.frames
617621
elif isinstance(other, int):
618622
return self.frames >= other
619623
else:
620-
raise TypeError(
621-
"'>=' not supported between instances of 'Timecode' and '{}'".format(other.__class__.__name__)
622-
)
624+
return NotImplemented
623625

624626
def __gt__(self, other: Union[int, str, "Timecode"]) -> bool:
625627
"""Override greater than operator.
@@ -634,16 +636,16 @@ def __gt__(self, other: Union[int, str, "Timecode"]) -> bool:
634636
bool: True if the other is greater than this Timecode instance.
635637
"""
636638
if isinstance(other, Timecode):
637-
return self.framerate == other.framerate and self.frames > other.frames
639+
if self.framerate != other.framerate:
640+
raise ValueError("'>' not supported between instances of 'Timecode' with different framerates")
641+
return self.frames > other.frames
638642
elif isinstance(other, str):
639643
new_tc = Timecode(self.framerate, other)
640644
return self.frames > new_tc.frames
641645
elif isinstance(other, int):
642646
return self.frames > other
643647
else:
644-
raise TypeError(
645-
"'>' not supported between instances of 'Timecode' and '{}'".format(other.__class__.__name__)
646-
)
648+
return NotImplemented
647649

648650
def __le__(self, other: Union[int, str, "Timecode", object]) -> bool:
649651
"""Override less or equal to operator.
@@ -658,16 +660,16 @@ def __le__(self, other: Union[int, str, "Timecode", object]) -> bool:
658660
bool: True if the other is less than or equal to this Timecode instance.
659661
"""
660662
if isinstance(other, Timecode):
661-
return self.framerate == other.framerate and self.frames <= other.frames
663+
if self.framerate != other.framerate:
664+
raise ValueError("'<=' not supported between instances of 'Timecode' with different framerates")
665+
return self.frames <= other.frames
662666
elif isinstance(other, str):
663667
new_tc = Timecode(self.framerate, other)
664668
return self.frames <= new_tc.frames
665669
elif isinstance(other, int):
666670
return self.frames <= other
667671
else:
668-
raise TypeError(
669-
"'<' not supported between instances of 'Timecode' and '{}'".format(other.__class__.__name__)
670-
)
672+
return NotImplemented
671673

672674
def __lt__(self, other: Union[int, str, "Timecode"]) -> bool:
673675
"""Override less than operator.
@@ -682,16 +684,16 @@ def __lt__(self, other: Union[int, str, "Timecode"]) -> bool:
682684
bool: True if the other is less than this Timecode instance.
683685
"""
684686
if isinstance(other, Timecode):
687+
if self.framerate != other.framerate:
688+
raise ValueError("'<' not supported between instances of 'Timecode' with different framerates".format())
685689
return self.framerate == other.framerate and self.frames < other.frames
686690
elif isinstance(other, str):
687691
new_tc = Timecode(self.framerate, other)
688692
return self.frames < new_tc.frames
689693
elif isinstance(other, int):
690694
return self.frames < other
691695
else:
692-
raise TypeError(
693-
"'<=' not supported between instances of 'Timecode' and '{}'".format(other.__class__.__name__)
694-
)
696+
return NotImplemented
695697

696698
def __add__(self, other: Union["Timecode", int]) -> "Timecode":
697699
"""Return a new Timecode with the given timecode or frames added to this one.
@@ -715,9 +717,7 @@ def __add__(self, other: Union["Timecode", int]) -> "Timecode":
715717
elif isinstance(other, int):
716718
tc.add_frames(other)
717719
else:
718-
raise TimecodeError(
719-
"Type {} not supported for arithmetic.".format(other.__class__.__name__)
720-
)
720+
return NotImplemented
721721

722722
return tc
723723

@@ -739,9 +739,7 @@ def __sub__(self, other: Union["Timecode", int]) -> "Timecode":
739739
elif isinstance(other, int):
740740
subtracted_frames = self.frames - other
741741
else:
742-
raise TimecodeError(
743-
"Type {} not supported for arithmetic.".format(other.__class__.__name__)
744-
)
742+
return NotImplemented
745743
tc = Timecode(self.framerate, frames=abs(subtracted_frames))
746744
tc.drop_frame = self.drop_frame
747745
return tc
@@ -764,9 +762,7 @@ def __mul__(self, other: Union["Timecode", int]) -> "Timecode":
764762
elif isinstance(other, int):
765763
multiplied_frames = self.frames * other
766764
else:
767-
raise TimecodeError(
768-
"Type {} not supported for arithmetic.".format(other.__class__.__name__)
769-
)
765+
return NotImplemented
770766
tc = Timecode(self.framerate, frames=multiplied_frames)
771767
tc.drop_frame = self.drop_frame
772768
return tc
@@ -789,9 +785,7 @@ def __div__(self, other: Union["Timecode", int]) -> "Timecode":
789785
elif isinstance(other, int):
790786
div_frames = int(float(self.frames) / float(other))
791787
else:
792-
raise TimecodeError(
793-
"Type {} not supported for arithmetic.".format(other.__class__.__name__)
794-
)
788+
return NotImplemented
795789

796790
return Timecode(self.framerate, frames=div_frames)
797791

0 commit comments

Comments
 (0)