-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Grayscale property passthrough #99
Conversation
WalkthroughThe recent changes to the Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Video
participant FrameReader
User->>Video: Set grayscale attribute
Video->>FrameReader: Update backend to reflect grayscale setting
Video->>Video: Update img_shape property based on grayscale
User->>Video: Fetch video properties
Video->>User: Return updated properties reflecting grayscale change
Possibly related issues
Poem
Tip Early access features: enabledWe are currently testing the following features in early access:
Note:
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (invoked as PR comments)
Additionally, you can add CodeRabbit Configration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Outside diff range and nitpick comments (1)
sleap_io/model/video.py (1)
Line range hint
123-145
: Refactor the grayscale property setter for better error handling and consistency.The setter for the
grayscale
property directly manipulates the backend and its cache without checking if the backend is initialized. This could lead toAttributeError
ifself.backend
isNone
. Additionally, ensure consistent handling of thegrayscale
state within the backend metadata.def grayscale(self, value: bool): """Set the grayscale value and adjust the backend.""" + if self.backend is None: + raise ValueError("Backend not initialized") self.backend.grayscale = value self.backend._cached_shape = None if "grayscale" in self.backend_metadata: self.backend_metadata["grayscale"] = valueTools
Ruff
139-139: Redefinition of unused
grayscale
from line 124 (F811)
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (3)
- sleap_io/io/video.py (3 hunks)
- sleap_io/model/video.py (2 hunks)
- tests/model/test_video.py (1 hunks)
Additional context used
Ruff
tests/model/test_video.py
129-129: Avoid equality comparisons to
True
; useif video.grayscale:
for truth checks (E712)Replace with
video.grayscale
sleap_io/model/video.py
9-9: Redefinition of unused
Optional
from line 9 (F811)Remove definition:
Optional
47-47: Use
X | Y
for type annotations (UP007)Convert to
X | Y
49-49: Use
X | Y
for type annotations (UP007)Convert to
X | Y
62-62: Use
X | Y
for type annotations (UP007)Convert to
X | Y
63-63: Use
X | Y
for type annotations (UP007)Convert to
X | Y
65-65: Use
X | Y
for type annotations (UP007)Convert to
X | Y
102-102: Use
tuple
instead ofTuple
for type annotation (UP006)Replace with
tuple
110-110: Use
tuple
instead ofTuple
for type annotation (UP006)Replace with
tuple
118-118: Do not use bare
except
(E722)
139-139: Redefinition of unused
grayscale
from line 124 (F811)
197-200: Use
return all(Path(f).exists() for f in self.filename)
instead offor
loop (SIM110)Replace with
return all(Path(f).exists() for f in self.filename)
212-212: Use
X | Y
for type annotations (UP007)Convert to
X | Y
213-213: Use
X | Y
for type annotations (UP007)Convert to
X | Y
sleap_io/io/video.py
16-19: Use
contextlib.suppress(ImportError)
instead oftry
-except
-pass
(SIM105)Replace with
contextlib.suppress(ImportError)
21-24: Use
contextlib.suppress(ImportError)
instead oftry
-except
-pass
(SIM105)Replace with
contextlib.suppress(ImportError)
22-22:
imageio_ffmpeg
imported but unused; consider usingimportlib.util.find_spec
to test for availability (F401)
26-29: Use
contextlib.suppress(ImportError)
instead oftry
-except
-pass
(SIM105)Replace with
contextlib.suppress(ImportError)
27-27:
av
imported but unused; consider usingimportlib.util.find_spec
to test for availability (F401)
55-55: Use
X | Y
for type annotations (UP007)Convert to
X | Y
57-57: Use
X | Y
for type annotations (UP007)Convert to
X | Y
57-57: Use
tuple
instead ofTuple
for type annotation (UP006)Replace with
tuple
58-58: Use
X | Y
for type annotations (UP007)Convert to
X | Y
64-64: Use
X | Y
for type annotations (UP007)Convert to
X | Y
65-65: Use
X | Y
for type annotations (UP007)Convert to
X | Y
87-87: Do not compare types, use
isinstance()
(E721)
90-90: Do not compare types, use
isinstance()
(E721)
161-161: Use
tuple
instead ofTuple
for type annotation (UP006)Replace with
tuple
171-171: Use
tuple
instead ofTuple
for type annotation (UP006)Replace with
tuple
490-490: Use
X | Y
for type annotations (UP007)Convert to
X | Y
496-496: Use
X | Y
for type annotations (UP007)Convert to
X | Y
497-497: Use
X | Y
for type annotations (UP007)Convert to
X | Y
532-534: Use a single
if
statement instead of nestedif
statements (SIM102)
563-563: Use
tuple
instead ofTuple
for type annotation (UP006)Replace with
tuple
581-584: Use ternary operator
frame_idx = list(self.frame_map.keys())[0] if self.frame_map else 0
instead ofif
-else
-block (SIM108)Replace
if
-else
-block withframe_idx = list(self.frame_map.keys())[0] if self.frame_map else 0
def test_grayscale(centered_pair_low_quality_path): | ||
video = Video.from_filename(centered_pair_low_quality_path) | ||
assert video.grayscale == True | ||
assert video.shape[-1] == 1 | ||
|
||
video.grayscale = False | ||
assert video.shape[-1] == 3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure proper boolean checks and optimize the test function.
The test for the grayscale
property should avoid direct equality checks against boolean values. Instead, use the truthiness of the value for a cleaner and more Pythonic approach.
- assert video.grayscale == True
+ assert video.grayscale
Additionally, consider adding assertions to ensure that the grayscale
property toggles correctly between True
and False
states, affecting the shape as expected.
Committable suggestion was skipped due to low confidence.
Tools
Ruff
129-129: Avoid equality comparisons to
True
; useif video.grayscale:
for truth checks (E712)Replace with
video.grayscale
sleap_io/io/video.py
Outdated
def img_shape(self) -> Tuple[int, int, int]: | ||
"""Shape of a single frame in the video.""" | ||
height, width, channels = self.get_frame(0).shape | ||
height, width, channels = self.read_test_frame().shape | ||
if self.grayscale is False: | ||
channels = 3 | ||
elif self.grayscale is True: | ||
channels = 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refine the implementation of img_shape
property.
The method to determine the img_shape
based on the grayscale
property is overly complex and can be simplified. Additionally, this method should not directly read a frame to determine the shape; it should utilize cached values to improve efficiency.
@property
def img_shape(self) -> tuple[int, int, int]:
- height, width, channels = self.read_test_frame().shape
+ if self._cached_shape is not None:
+ return self._cached_shape[1:] # Extract height, width, channels from cached shape
- if self.grayscale is False:
- channels = 3
- elif self.grayscale is True:
- channels = 1
- return int(height), int(width), int(channels)
+ raise ValueError("Shape not cached")
Committable suggestion was skipped due to low confidence.
Tools
Ruff
161-161: Use
tuple
instead ofTuple
for type annotation (UP006)Replace with
tuple
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Outside diff range and nitpick comments (5)
sleap_io/model/video.py (5)
Line range hint
9-9
: Remove redundant import.The
Optional
type is imported twice, which is unnecessary and could lead to confusion.- from typing import Tuple, Optional, Optional + from typing import Tuple, Optional
Line range hint
47-47
: Modernize type annotations.Several type annotations in the code use the older
typing
module syntax. Update these to use the more modern and concise|
syntax for unions, which is recommended in Python 3.10 and later.- from typing import Tuple, Optional + from typing import Tuple - backend: Optional[VideoBackend] = None + backend: VideoBackend | None = None - source_video: Optional[Video] = None + source_video: Video | None = None - dataset: Optional[str] = None, + dataset: str | None = None, - grayscale: Optional[bool] = None, + grayscale: bool | None = None, - source_video: Optional[Video] = None, + source_video: Video | None = None, - dataset: Optional[str] = None, + dataset: str | None = None, - grayscale: Optional[str] = None, + grayscale: str | None = None,Also applies to: 49-49, 62-62, 63-63, 65-65, 211-211, 212-212
Line range hint
102-102
: Update type annotations to use built-in types.The
tuple
type annotations should use the built-intuple
type instead ofTuple
from thetyping
module, as recommended in recent Python versions.- def shape(self) -> Tuple[int, int, int, int] | None: + def shape(self) -> tuple[int, int, int, int] | None: - def _get_shape(self) -> Tuple[int, int, int, int] | None: + def _get_shape(self) -> tuple[int, int, int, int] | None:Also applies to: 110-110
Line range hint
118-118
: Add specific exception handling.Using a bare
except
is not recommended as it can catch unexpected exceptions and make debugging difficult. Specify the type of exceptions you expect here.118 try: 119 return self.backend.shape 120 except Exception as e: # Specify the expected exceptions, e.g., AttributeError 121 if "shape" in self.backend_metadata: 122 return self.backend_metadata["shape"] 123 return None
Line range hint
196-199
: Optimize existence check with a generator expression.The loop for checking file existence can be simplified and optimized using a generator expression.
196 if isinstance(self.filename, list): 197 if check_all: - for f in self.filename: - if not Path(f).exists(): - return False - return True + return all(Path(f).exists() for f in self.filename) 198 else: 199 return Path(self.filename[0]).exists()
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (1)
- sleap_io/model/video.py (1 hunks)
Additional context used
Ruff
sleap_io/model/video.py
9-9: Redefinition of unused
Optional
from line 9 (F811)Remove definition:
Optional
47-47: Use
X | Y
for type annotations (UP007)Convert to
X | Y
49-49: Use
X | Y
for type annotations (UP007)Convert to
X | Y
62-62: Use
X | Y
for type annotations (UP007)Convert to
X | Y
63-63: Use
X | Y
for type annotations (UP007)Convert to
X | Y
65-65: Use
X | Y
for type annotations (UP007)Convert to
X | Y
102-102: Use
tuple
instead ofTuple
for type annotation (UP006)Replace with
tuple
110-110: Use
tuple
instead ofTuple
for type annotation (UP006)Replace with
tuple
118-118: Do not use bare
except
(E722)
196-199: Use
return all(Path(f).exists() for f in self.filename)
instead offor
loop (SIM110)Replace with
return all(Path(f).exists() for f in self.filename)
211-211: Use
X | Y
for type annotations (UP007)Convert to
X | Y
212-212: Use
X | Y
for type annotations (UP007)Convert to
X | Y
return self.backend_metadata.get("grayscale", None) | ||
|
||
@grayscale.setter | ||
def grayscale(self, value: bool): | ||
"""Set the grayscale value and adjust the backend.""" | ||
if self.backend is not None: | ||
self.backend.grayscale = value | ||
self.backend._cached_shape = None | ||
|
||
self.backend_metadata["grayscale"] = value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implement robust error handling in grayscale setter.
The setter for the grayscale
property lacks error handling, which could lead to issues if an incorrect type or a null value is passed. Consider adding type checks and handling potential exceptions to ensure robustness.
136 @grayscale.setter
137 def grayscale(self, value: bool):
138 """Set the grayscale value and adjust the backend."""
139 if not isinstance(value, bool):
140 raise ValueError("grayscale must be a boolean value.")
141 if self.backend is not None:
142 self.backend.grayscale = value
143 self.backend._cached_shape = None
144
145 self.backend_metadata["grayscale"] = value
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
return self.backend_metadata.get("grayscale", None) | |
@grayscale.setter | |
def grayscale(self, value: bool): | |
"""Set the grayscale value and adjust the backend.""" | |
if self.backend is not None: | |
self.backend.grayscale = value | |
self.backend._cached_shape = None | |
self.backend_metadata["grayscale"] = value | |
return self.backend_metadata.get("grayscale", None) | |
@grayscale.setter | |
def grayscale(self, value: bool): | |
"""Set the grayscale value and adjust the backend.""" | |
if not isinstance(value, bool): | |
raise ValueError("grayscale must be a boolean value.") | |
if self.backend is not None: | |
self.backend.grayscale = value | |
self.backend._cached_shape = None | |
self.backend_metadata["grayscale"] = value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (2)
- sleap_io/io/video.py (3 hunks)
- tests/model/test_video.py (1 hunks)
Additional context used
Ruff
tests/model/test_video.py
129-129: Avoid equality comparisons to
True
; useif video.grayscale:
for truth checksReplace with
video.grayscale
(E712)
137-137: Avoid equality comparisons to
False
; useif not video.grayscale:
for false checksReplace with
not video.grayscale
(E712)
143-143: Avoid equality comparisons to
True
; useif video.grayscale:
for truth checksReplace with
video.grayscale
(E712)
sleap_io/io/video.py
16-19: Use
contextlib.suppress(ImportError)
instead oftry
-except
-pass
Replace with
contextlib.suppress(ImportError)
(SIM105)
21-24: Use
contextlib.suppress(ImportError)
instead oftry
-except
-pass
Replace with
contextlib.suppress(ImportError)
(SIM105)
22-22:
imageio_ffmpeg
imported but unused; consider usingimportlib.util.find_spec
to test for availability(F401)
26-29: Use
contextlib.suppress(ImportError)
instead oftry
-except
-pass
Replace with
contextlib.suppress(ImportError)
(SIM105)
27-27:
av
imported but unused; consider usingimportlib.util.find_spec
to test for availability(F401)
55-55: Use
X | Y
for type annotationsConvert to
X | Y
(UP007)
57-57: Use
X | Y
for type annotationsConvert to
X | Y
(UP007)
57-57: Use
tuple
instead ofTuple
for type annotationReplace with
tuple
(UP006)
58-58: Use
X | Y
for type annotationsConvert to
X | Y
(UP007)
64-64: Use
X | Y
for type annotationsConvert to
X | Y
(UP007)
65-65: Use
X | Y
for type annotationsConvert to
X | Y
(UP007)
87-87: Do not compare types, use
isinstance()
(E721)
90-90: Do not compare types, use
isinstance()
(E721)
161-161: Use
tuple
instead ofTuple
for type annotationReplace with
tuple
(UP006)
173-173: Use
tuple
instead ofTuple
for type annotationReplace with
tuple
(UP006)
492-492: Use
X | Y
for type annotationsConvert to
X | Y
(UP007)
498-498: Use
X | Y
for type annotationsConvert to
X | Y
(UP007)
499-499: Use
X | Y
for type annotationsConvert to
X | Y
(UP007)
534-536: Use a single
if
statement instead of nestedif
statements(SIM102)
565-565: Use
tuple
instead ofTuple
for type annotationReplace with
tuple
(UP006)
583-586: Use ternary operator
frame_idx = list(self.frame_map.keys())[0] if self.frame_map else 0
instead ofif
-else
-blockReplace
if
-else
-block withframe_idx = list(self.frame_map.keys())[0] if self.frame_map else 0
(SIM108)
video.grayscale = True | ||
video.close() | ||
video.open() | ||
assert video.grayscale == True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optimize boolean check.
Avoid direct equality comparisons to True
; use truthiness checks for a cleaner and more Pythonic approach.
- assert video.grayscale == True
+ assert video.grayscale
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
assert video.grayscale == True | |
assert video.grayscale |
Tools
Ruff
143-143: Avoid equality comparisons to
True
; useif video.grayscale:
for truth checksReplace with
video.grayscale
(E712)
|
||
video.close() | ||
video.open() | ||
assert video.grayscale == False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optimize boolean check.
Avoid direct equality comparisons to False
; use falsiness checks for a cleaner and more Pythonic approach.
- assert video.grayscale == False
+ assert not video.grayscale
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
assert video.grayscale == False | |
assert not video.grayscale |
Tools
Ruff
137-137: Avoid equality comparisons to
False
; useif not video.grayscale:
for false checksReplace with
not video.grayscale
(E712)
|
||
def test_grayscale(centered_pair_low_quality_path): | ||
video = Video.from_filename(centered_pair_low_quality_path) | ||
assert video.grayscale == True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optimize boolean check.
Avoid direct equality comparisons to True
; use truthiness checks for a cleaner and more Pythonic approach.
- assert video.grayscale == True
+ assert video.grayscale
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
assert video.grayscale == True | |
assert video.grayscale |
Tools
Ruff
129-129: Avoid equality comparisons to
True
; useif video.grayscale:
for truth checksReplace with
video.grayscale
(E712)
height, width, channels = self.read_test_frame().shape | ||
if self.grayscale is None: | ||
self.detect_grayscale() | ||
if self.grayscale is False: | ||
channels = 3 | ||
elif self.grayscale is True: | ||
channels = 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optimize the implementation of img_shape
property.
The method to determine the img_shape
based on the grayscale
property is overly complex and can be simplified. Additionally, this method should not directly read a frame to determine the shape; it should utilize cached values to improve efficiency.
@property
def img_shape(self) -> tuple[int, int, int]:
- height, width, channels = self.read_test_frame().shape
+ if self._cached_shape is not None:
+ return self._cached_shape[1:] # Extract height, width, channels from cached shape
- if self.grayscale is None:
- self.detect_grayscale()
- if self.grayscale is False:
- channels = 3
- elif self.grayscale is True:
- channels = 1
- return int(height), int(width), int(channels)
+ raise ValueError("Shape not cached")
Committable suggestion was skipped due to low confidence.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #99 +/- ##
==========================================
+ Coverage 96.04% 96.11% +0.06%
==========================================
Files 17 17
Lines 2022 2032 +10
==========================================
+ Hits 1942 1953 +11
+ Misses 80 79 -1 ☔ View full report in Codecov by Sentry. |
This PR addresses #97 by implementing the behavior of setting the
video.grayscale
property so that it gets passed through to the backend appropriately and is used when computingvideo.shape
.Summary by CodeRabbit
New Features
Tests