Skip to content

Commit 0a516cf

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 # Conflicts: # src/timecode/timecode.py # tests/test_timecode.py
1 parent b3445d6 commit 0a516cf

2 files changed

Lines changed: 49 additions & 41 deletions

File tree

src/timecode/timecode.py

Lines changed: 24 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -609,13 +609,15 @@ def __eq__(self, other: int | str | Timecode | object) -> bool:
609609
bool: True if the other is equal to this Timecode instance.
610610
"""
611611
if isinstance(other, Timecode):
612-
return self.framerate == other.framerate and self.frames == other.frames
612+
if self.framerate != other.framerate:
613+
raise ValueError("'==' not supported between instances of 'Timecode' with different framerates")
614+
return self.frames == other.frames
613615
if isinstance(other, str):
614616
new_tc = Timecode(self.framerate, other)
615617
return self.__eq__(new_tc)
616618
if isinstance(other, int):
617619
return self.frames == other
618-
return False
620+
return NotImplemented
619621

620622
def __ge__(self, other: int | str | Timecode | object) -> bool:
621623
"""Override greater than or equal to operator.
@@ -631,16 +633,15 @@ def __ge__(self, other: int | str | Timecode | object) -> bool:
631633
instance.
632634
"""
633635
if isinstance(other, Timecode):
634-
return self.framerate == other.framerate and self.frames >= other.frames
636+
if self.framerate != other.framerate:
637+
raise ValueError("'>=' not supported between instances of 'Timecode' with different framerates")
638+
return self.frames >= other.frames
635639
if isinstance(other, str):
636640
new_tc = Timecode(self.framerate, other)
637641
return self.frames >= new_tc.frames
638642
if isinstance(other, int):
639643
return self.frames >= other
640-
raise TypeError(
641-
"'>=' not supported between instances of 'Timecode' and "
642-
f"'{other.__class__.__name__}'"
643-
)
644+
return NotImplemented
644645

645646
def __gt__(self, other: int | str | Timecode) -> bool:
646647
"""Override greater than operator.
@@ -655,16 +656,15 @@ def __gt__(self, other: int | str | Timecode) -> bool:
655656
bool: True if the other is greater than this Timecode instance.
656657
"""
657658
if isinstance(other, Timecode):
658-
return self.framerate == other.framerate and self.frames > other.frames
659+
if self.framerate != other.framerate:
660+
raise ValueError("'>' not supported between instances of 'Timecode' with different framerates")
661+
return self.frames > other.frames
659662
if isinstance(other, str):
660663
new_tc = Timecode(self.framerate, other)
661664
return self.frames > new_tc.frames
662665
if isinstance(other, int):
663666
return self.frames > other
664-
raise TypeError(
665-
"'>' not supported between instances of 'Timecode' and "
666-
f"'{other.__class__.__name__}'"
667-
)
667+
return NotImplemented
668668

669669
def __le__(self, other: int | str | Timecode | object) -> bool:
670670
"""Override less or equal to operator.
@@ -679,16 +679,15 @@ def __le__(self, other: int | str | Timecode | object) -> bool:
679679
bool: True if the other is less than or equal to this Timecode instance.
680680
"""
681681
if isinstance(other, Timecode):
682-
return self.framerate == other.framerate and self.frames <= other.frames
683-
if isinstance(other, str):
682+
if self.framerate != other.framerate:
683+
raise ValueError("'<=' not supported between instances of 'Timecode' with different framerates")
684+
return self.frames <= other.frames
685+
elif isinstance(other, str):
684686
new_tc = Timecode(self.framerate, other)
685687
return self.frames <= new_tc.frames
686688
if isinstance(other, int):
687689
return self.frames <= other
688-
raise TypeError(
689-
"'<' not supported between instances of 'Timecode' and "
690-
f"'{other.__class__.__name__}'"
691-
)
690+
return NotImplemented
692691

693692
def __lt__(self, other: int | str | Timecode) -> bool:
694693
"""Override less than operator.
@@ -703,16 +702,15 @@ def __lt__(self, other: int | str | Timecode) -> bool:
703702
bool: True if the other is less than this Timecode instance.
704703
"""
705704
if isinstance(other, Timecode):
705+
if self.framerate != other.framerate:
706+
raise ValueError("'<' not supported between instances of 'Timecode' with different framerates".format())
706707
return self.framerate == other.framerate and self.frames < other.frames
707708
if isinstance(other, str):
708709
new_tc = Timecode(self.framerate, other)
709710
return self.frames < new_tc.frames
710711
if isinstance(other, int):
711712
return self.frames < other
712-
raise TypeError(
713-
"'<=' not supported between instances of 'Timecode' and "
714-
f"'{other.__class__.__name__}'"
715-
)
713+
return NotImplemented
716714

717715
def __add__(self, other: int | Timecode) -> Timecode:
718716
"""Return a new Timecode with the given timecode or frames added to this one.
@@ -736,9 +734,7 @@ def __add__(self, other: int | Timecode) -> Timecode:
736734
elif isinstance(other, int):
737735
tc.add_frames(other)
738736
else:
739-
raise TimecodeError(
740-
f"Type {other.__class__.__name__} not supported for arithmetic."
741-
)
737+
return NotImplemented
742738

743739
return tc
744740

@@ -760,9 +756,7 @@ def __sub__(self, other: int | Timecode) -> Timecode:
760756
elif isinstance(other, int):
761757
subtracted_frames = self.frames - other
762758
else:
763-
raise TimecodeError(
764-
f"Type {other.__class__.__name__} not supported for arithmetic."
765-
)
759+
return NotImplemented
766760
tc = Timecode(self.framerate, frames=abs(subtracted_frames))
767761
tc.drop_frame = self.drop_frame
768762
return tc
@@ -785,9 +779,7 @@ def __mul__(self, other: int | Timecode) -> Timecode:
785779
elif isinstance(other, int):
786780
multiplied_frames = self.frames * other
787781
else:
788-
raise TimecodeError(
789-
f"Type {other.__class__.__name__} not supported for arithmetic."
790-
)
782+
return NotImplemented
791783
tc = Timecode(self.framerate, frames=multiplied_frames)
792784
tc.drop_frame = self.drop_frame
793785
return tc
@@ -810,9 +802,7 @@ def __div__(self, other: int | Timecode) -> Timecode:
810802
elif isinstance(other, int):
811803
div_frames = int(float(self.frames) / float(other))
812804
else:
813-
raise TimecodeError(
814-
f"Type {other.__class__.__name__} not supported for arithmetic."
815-
)
805+
return NotImplemented
816806

817807
return Timecode(self.framerate, frames=div_frames)
818808

tests/test_timecode.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -471,21 +471,31 @@ def test_add_with_two_different_frame_rates():
471471
"args,kwargs,func,tc2", [
472472
[["24", "00:00:01:00"], {}, lambda x, y: x + y, "not suitable"],
473473
[["24", "00:00:01:00"], {}, lambda x, y: x - y, "not suitable"],
474-
[["24", "00:00:01:00"], {}, lambda x, y: x * y, "not suitable"],
475474
[["24", "00:00:01:00"], {}, lambda x, y: x / y, "not suitable"],
476475
[["24", "00:00:01:00"], {}, lambda x, y: x / y, 32.4],
476+
[["24", "00:00:01:00"], {}, lambda x, y: x * y, 32.4],
477477
]
478478
)
479-
def test_arithmetic_with_unsupported_type_raises_error(args, kwargs, func, tc2):
480-
"""TimecodeError is raised if the other class is not suitable for the operation."""
479+
def test_arithmetic_with_non_suitable_class_instance(args, kwargs, func, tc2):
480+
"""TypeError is raised if the other class is not suitable for the operation."""
481481
tc1 = Timecode(*args, **kwargs)
482-
with pytest.raises(TimecodeError) as cm:
482+
with pytest.raises(TypeError) as cm:
483483
_ = func(tc1, tc2)
484484

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

488+
@pytest.mark.parametrize(
489+
"args,kwargs,func,tc2", [
490+
[["24", "00:00:01:00"], {}, lambda x, y: x * y, "not suitable"],
491+
]
492+
)
493+
def test_multiply_with_sequence_type(args, kwargs, func, tc2):
494+
"""TypeError is raised if the other class is not suitable for the operation."""
495+
tc1 = Timecode(*args, **kwargs)
496+
with pytest.raises(TypeError) as cm:
497+
_ = func(tc1, tc2)
498+
assert str(cm.value) == (f"can't multiply sequence by non-int of type 'Timecode'")
489499

490500
def test_div_method_working_properly_1():
491501
"""__div__ method is working properly."""
@@ -1053,6 +1063,14 @@ def test_lt_method_with_integers():
10531063
tc = Timecode("24", "00:00:10:00")
10541064
assert tc < 250
10551065

1066+
def test_comparison_with_different_framerates_raises():
1067+
"""Comparing Timecodes with different framerates."""
1068+
tc1 = Timecode("24", "00:00:00:00")
1069+
tc2 = Timecode("30", "00:00:00:00")
1070+
with pytest.raises(ValueError) as cm:
1071+
_ = tc1 < tc2
1072+
_ = tc1 > tc2
1073+
10561074

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

0 commit comments

Comments
 (0)