Skip to content

Conversation

@hongquanli
Copy link
Contributor

Summary

  • Bug fixes: Fixed move_z_to() not passing blocking parameter; acquire_image() now raises RuntimeError on failure instead of silently continuing
  • Code quality: Removed unused imports, replaced TypeVar placeholders with None, simplified boolean expressions, improved error handling in close()
  • API improvements: Added type hints and docstrings to all public methods, made internal method _wait_for_microcontroller private

Changes

Bug Fixes

  • move_z_to() now correctly passes blocking parameter to underlying stage method
  • acquire_image() raises RuntimeError when camera fails to return a frame (previously just printed and returned None)

Code Quality

  • Removed unused imports (serial, move_z_axis_to_safety_position)
  • Replaced TypeVar placeholders with None for conditional imports (cleaner pattern)
  • Removed unnecessary super().__init__() call
  • Simplified True if x else False to x is not None
  • Replaced print() with proper self._log.error() logging

API Design

  • Added type hints to all public method parameters
  • Added docstrings to all public methods
  • Renamed waitForMicrocontroller_wait_for_microcontroller (private, snake_case)
  • Made close() resilient to errors (each component close is wrapped in try-except)
  • Added None check for microcontroller in close()

Test plan

  • Run python -m pytest tests/control/test_microscope.py - all tests pass
  • Run black formatter
  • Verify module imports correctly

🤖 Generated with Claude Code

- Fix bug: move_z_to() now correctly passes blocking parameter to stage
- Fix bug: acquire_image() now raises RuntimeError instead of silently continuing on failure
- Replace print() with proper logging in acquire_image()
- Replace TypeVar placeholders with None for conditional imports
- Remove unused imports (serial, move_z_axis_to_safety_position)
- Remove unnecessary super().__init__() call
- Simplify boolean expression in send_hardware_trigger()
- Make wait_for_microcontroller private (_wait_for_microcontroller)
- Add type hints to all public method parameters
- Add docstrings to all public methods
- Make close() resilient to errors during shutdown
- Add None check for microcontroller in close()

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves code quality, fixes bugs, and enhances API design in microscope.py. The changes include fixing a missing blocking parameter in move_z_to(), improving error handling in acquire_image(), removing unused imports, replacing TypeVar placeholders with None, and adding comprehensive type hints and docstrings to all public methods.

  • Fixed bug where move_z_to() wasn't passing the blocking parameter to the underlying stage method
  • Enhanced acquire_image() to raise RuntimeError instead of silently returning None when camera fails
  • Added type hints and docstrings to all public methods, and made waitForMicrocontroller private as _wait_for_microcontroller

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 582 to 589
def get_x(self) -> float:
return self.stage.get_pos().x_mm

def get_y(self):
def get_y(self) -> float:
return self.stage.get_pos().y_mm

def get_z(self):
def get_z(self) -> float:
return self.stage.get_pos().z_mm
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getter methods get_x(), get_y(), and get_z() are missing docstrings. All other public methods in this PR have been documented, but these three simple getters lack documentation explaining what they return. Consider adding docstrings to maintain consistency, for example:

def get_x(self) -> float:
    """Get the current X position of the stage.
    
    Returns:
        Current X position in mm.
    """
    return self.stage.get_pos().x_mm

Copilot uses AI. Check for mistakes.
Address PR review feedback - add missing docstrings to position
getter methods for consistency with other public methods.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +39 to +49
ObjectiveChanger2PosController = None

if control._def.RUN_FLUIDICS:
from control.fluidics import Fluidics
else:
Fluidics = TypeVar("Fluidics")
Fluidics = None

if control._def.ENABLE_NL5:
import control.NL5 as NL5
else:
NL5 = TypeVar("NL5")
NL5 = None
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TypeVar to None replacement creates type checking issues. When these classes are set to None (feature disabled), type hints like Optional[ObjectiveChanger2PosController] become Optional[None], which is semantically incorrect. Consider using a string literal type or proper type stubs for conditional imports, or use TYPE_CHECKING to conditionally import these for type hints only.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Alpaca233 should it be TypeVar or None?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is what CC says
Analysis: This is a valid concern. When ObjectiveChanger2PosController = None, the type hint Optional[ObjectiveChanger2PosController] becomes Optional[None] which equals None - not useful for type checkers. However, the original TypeVar usage was also incorrect (TypeVar is for generics, not placeholders). A proper fix would use TYPE_CHECKING pattern.

Comment on lines 483 to 511
if image is None:
print("self.camera.read_frame() returned None")
self._log.error("camera.read_frame() returned None")
raise RuntimeError("Failed to acquire image: camera.read_frame() returned None")
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When an exception is raised because camera.read_frame() returns None, the illumination is not turned off if SOFTWARE trigger mode is being used. This could leave the illumination on indefinitely. Consider wrapping the frame reading logic in a try-finally block to ensure illumination is always turned off when using SOFTWARE trigger mode.

Copilot uses AI. Check for mistakes.
Wrap frame reading in try-finally to guarantee illumination is turned
off even when an exception is raised (e.g., camera returns None).
Previously, a failed acquisition could leave illumination on indefinitely.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants