Skip to content

Commit c71ce61

Browse files
authored
Merge pull request #64 from nickswalker/deimplement-comparison
Raise an exception when comparing instance with differing framerates
2 parents 6629be4 + 4725cea commit c71ce61

2 files changed

Lines changed: 54 additions & 47 deletions

File tree

src/timecode/timecode.py

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -610,13 +610,16 @@ def __eq__(self, other: int | str | Timecode | object) -> bool:
610610
bool: True if the other is equal to this Timecode instance.
611611
"""
612612
if isinstance(other, Timecode):
613-
return self.framerate == other.framerate and self.frames == other.frames
613+
if self.framerate != other.framerate:
614+
raise ValueError("'==' not supported between instances of "
615+
"'Timecode' with different framerates")
616+
return self.frames == other.frames
614617
if isinstance(other, str):
615618
new_tc = Timecode(self.framerate, other)
616619
return self.__eq__(new_tc)
617620
if isinstance(other, int):
618621
return self.frames == other
619-
return False
622+
return NotImplemented
620623

621624
def __ge__(self, other: int | str | Timecode | object) -> bool:
622625
"""Override greater than or equal to operator.
@@ -632,16 +635,16 @@ def __ge__(self, other: int | str | Timecode | object) -> bool:
632635
instance.
633636
"""
634637
if isinstance(other, Timecode):
635-
return self.framerate == other.framerate and self.frames >= other.frames
638+
if self.framerate != other.framerate:
639+
raise ValueError("'>=' not supported between instances of "
640+
"'Timecode' with different framerates")
641+
return self.frames >= other.frames
636642
if isinstance(other, str):
637643
new_tc = Timecode(self.framerate, other)
638644
return self.frames >= new_tc.frames
639645
if isinstance(other, int):
640646
return self.frames >= other
641-
raise TypeError(
642-
"'>=' not supported between instances of 'Timecode' and "
643-
f"'{other.__class__.__name__}'"
644-
)
647+
return NotImplemented
645648

646649
def __gt__(self, other: int | str | Timecode) -> bool:
647650
"""Override greater than operator.
@@ -656,16 +659,16 @@ def __gt__(self, other: int | str | Timecode) -> bool:
656659
bool: True if the other is greater than this Timecode instance.
657660
"""
658661
if isinstance(other, Timecode):
659-
return self.framerate == other.framerate and self.frames > other.frames
662+
if self.framerate != other.framerate:
663+
raise ValueError("'>' not supported between instances of "
664+
"'Timecode' with different framerates")
665+
return self.frames > other.frames
660666
if isinstance(other, str):
661667
new_tc = Timecode(self.framerate, other)
662668
return self.frames > new_tc.frames
663669
if isinstance(other, int):
664670
return self.frames > other
665-
raise TypeError(
666-
"'>' not supported between instances of 'Timecode' and "
667-
f"'{other.__class__.__name__}'"
668-
)
671+
return NotImplemented
669672

670673
def __le__(self, other: int | str | Timecode | object) -> bool:
671674
"""Override less or equal to operator.
@@ -680,16 +683,16 @@ def __le__(self, other: int | str | Timecode | object) -> bool:
680683
bool: True if the other is less than or equal to this Timecode instance.
681684
"""
682685
if isinstance(other, Timecode):
683-
return self.framerate == other.framerate and self.frames <= other.frames
686+
if self.framerate != other.framerate:
687+
raise ValueError("'<=' not supported between instances of "
688+
"'Timecode' with different framerates")
689+
return self.frames <= other.frames
684690
if isinstance(other, str):
685691
new_tc = Timecode(self.framerate, other)
686692
return self.frames <= new_tc.frames
687693
if isinstance(other, int):
688694
return self.frames <= other
689-
raise TypeError(
690-
"'<' not supported between instances of 'Timecode' and "
691-
f"'{other.__class__.__name__}'"
692-
)
695+
return NotImplemented
693696

694697
def __lt__(self, other: int | str | Timecode) -> bool:
695698
"""Override less than operator.
@@ -704,16 +707,16 @@ def __lt__(self, other: int | str | Timecode) -> bool:
704707
bool: True if the other is less than this Timecode instance.
705708
"""
706709
if isinstance(other, Timecode):
710+
if self.framerate != other.framerate:
711+
raise ValueError("'<' not supported between instances of "
712+
"'Timecode' with different framerates")
707713
return self.framerate == other.framerate and self.frames < other.frames
708714
if isinstance(other, str):
709715
new_tc = Timecode(self.framerate, other)
710716
return self.frames < new_tc.frames
711717
if isinstance(other, int):
712718
return self.frames < other
713-
raise TypeError(
714-
"'<=' not supported between instances of 'Timecode' and "
715-
f"'{other.__class__.__name__}'"
716-
)
719+
return NotImplemented
717720

718721
def __add__(self, other: int | Timecode) -> Timecode:
719722
"""Return a new Timecode with the given timecode or frames added to this one.
@@ -737,9 +740,7 @@ def __add__(self, other: int | Timecode) -> Timecode:
737740
elif isinstance(other, int):
738741
tc.add_frames(other)
739742
else:
740-
raise TimecodeError(
741-
f"Type {other.__class__.__name__} not supported for arithmetic."
742-
)
743+
return NotImplemented
743744

744745
return tc
745746

@@ -761,9 +762,7 @@ def __sub__(self, other: int | Timecode) -> Timecode:
761762
elif isinstance(other, int):
762763
subtracted_frames = self.frames - other
763764
else:
764-
raise TimecodeError(
765-
f"Type {other.__class__.__name__} not supported for arithmetic."
766-
)
765+
return NotImplemented
767766
tc = Timecode(self.framerate, frames=abs(subtracted_frames))
768767
tc.drop_frame = self.drop_frame
769768
return tc
@@ -786,9 +785,7 @@ def __mul__(self, other: int | Timecode) -> Timecode:
786785
elif isinstance(other, int):
787786
multiplied_frames = self.frames * other
788787
else:
789-
raise TimecodeError(
790-
f"Type {other.__class__.__name__} not supported for arithmetic."
791-
)
788+
return NotImplemented
792789
tc = Timecode(self.framerate, frames=multiplied_frames)
793790
tc.drop_frame = self.drop_frame
794791
return tc
@@ -811,9 +808,7 @@ def __div__(self, other: int | Timecode) -> Timecode:
811808
elif isinstance(other, int):
812809
div_frames = int(float(self.frames) / float(other))
813810
else:
814-
raise TimecodeError(
815-
f"Type {other.__class__.__name__} not supported for arithmetic."
816-
)
811+
return NotImplemented
817812

818813
return Timecode(self.framerate, frames=div_frames)
819814

tests/test_timecode.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,21 +1018,31 @@ def test_add_with_two_different_frame_rates():
10181018
[
10191019
[["24", "00:00:01:00"], {}, lambda x, y: x + y, "not suitable"],
10201020
[["24", "00:00:01:00"], {}, lambda x, y: x - y, "not suitable"],
1021-
[["24", "00:00:01:00"], {}, lambda x, y: x * y, "not suitable"],
10221021
[["24", "00:00:01:00"], {}, lambda x, y: x / y, "not suitable"],
10231022
[["24", "00:00:01:00"], {}, lambda x, y: x / y, 32.4],
1024-
],
1023+
[["24", "00:00:01:00"], {}, lambda x, y: x * y, 32.4],
1024+
]
10251025
)
1026-
def test_arithmetic_with_unsupported_type_raises_error(args, kwargs, func, tc2):
1027-
"""TimecodeError is raised if the other class is not suitable for the operation."""
1026+
def test_arithmetic_with_non_suitable_class_instance(args, kwargs, func, tc2):
1027+
"""TypeError is raised if the other class is not suitable for the operation."""
10281028
tc1 = Timecode(*args, **kwargs)
1029-
with pytest.raises(TimecodeError) as cm:
1029+
with pytest.raises(TypeError) as cm:
10301030
_ = func(tc1, tc2)
10311031

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

1035+
@pytest.mark.parametrize(
1036+
"args,kwargs,func,tc2", [
1037+
[["24", "00:00:01:00"], {}, lambda x, y: x * y, "not suitable"],
1038+
]
1039+
)
1040+
def test_multiply_with_sequence_type(args, kwargs, func, tc2):
1041+
"""TypeError is raised if the other class is not suitable for the operation."""
1042+
tc1 = Timecode(*args, **kwargs)
1043+
with pytest.raises(TypeError) as cm:
1044+
_ = func(tc1, tc2)
1045+
assert str(cm.value) == (f"can't multiply sequence by non-int of type 'Timecode'")
10361046

10371047
def test_div_method_working_properly_1():
10381048
"""__div__ method is working properly."""
@@ -1248,12 +1258,6 @@ def test_ms_vs_fraction_frames_2():
12481258

12491259

12501260
def test_ms_vs_fraction_frames_3():
1251-
tc1 = Timecode("ms", "00:00:00.040")
1252-
tc2 = Timecode(24, "00:00:00.042")
1253-
assert tc1 != tc2
1254-
1255-
1256-
def test_ms_vs_fraction_frames_4():
12571261
tc1 = Timecode("ms", "00:00:00.040")
12581262
tc2 = Timecode(24, "00:00:00.042")
12591263
assert tc1.frame_number == 40
@@ -1611,6 +1615,14 @@ def test_lt_method_with_integers():
16111615
tc = Timecode("24", "00:00:10:00")
16121616
assert tc < 250
16131617

1618+
def test_comparison_with_different_framerates_raises():
1619+
"""Comparing Timecodes with different framerates."""
1620+
tc1 = Timecode("24", "00:00:00:00")
1621+
tc2 = Timecode("30", "00:00:00:00")
1622+
with pytest.raises(ValueError) as cm:
1623+
_ = tc1 < tc2
1624+
_ = tc1 > tc2
1625+
16141626

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

0 commit comments

Comments
 (0)