Get codespell passing and add it as a CI job.

This commit is contained in:
Julian Stirling 2025-07-10 17:57:43 +01:00
parent e33fecaef0
commit 7e6017f648
25 changed files with 113 additions and 85 deletions

View file

@ -31,7 +31,7 @@ def configure_logging(log_folder):
"""
root_logger = logging.getLogger()
root_logger.setLevel(logging.INFO)
# Explictly make OFM_LOG_FILE a global so it can be updated based on log settings
# Explicitly make OFM_LOG_FILE a global so it can be updated based on log settings
global OFM_LOG_FILE
OFM_LOG_FILE = os.path.join(log_folder, "openflexure_microscope.log")
@ -64,11 +64,11 @@ def retrieve_log() -> PlainTextResponse:
This log is the one shown in the UI and on the logging page.
It does not include any of the ``uvicorn.access`` logs as these are emmitted
It does not include any of the ``uvicorn.access`` logs as these are emitted
every time any API route is called.
All logs, including ``uvicorn.access`` are logged to the OFM_LOG_FILE (see above)
ths is the best place to get logs about crashes.
this is the best place to get logs about crashes.
"""
return PlainTextResponse(OFM_HANDLER.log_history)
@ -92,7 +92,7 @@ class OFMHandler(logging.Handler):
"""A logging.Handler that stores the most recent logs for access by the server."""
def __init__(self, level=logging.INFO, max_logs=250):
"""Inialise the handler with a set logging level and message buffer size.
"""Initialise the handler with a set logging level and message buffer size.
:param level: The level of logs captured. As standard logs of INFO and above
are captured.

View file

@ -67,9 +67,9 @@ class ScanPlanner:
* ``_parse()`` - to parse the planner_settings dictionary, saving values to class
variables
* ``_intial_location_list()`` - Sets the list of locations for the scan to follow
* ``_initial_location_list()`` - Sets the list of locations for the scan to follow
For a simple scan pattern this should be sufficent. For more complex ones that
For a simple scan pattern this should be sufficient. For more complex ones that
dynamically adjust the path it is suggested to override ``mark_location_visited()``
calling ``super().mark_location_visited()`` at the start of the method so that all
locations are adjusted.
@ -78,17 +78,19 @@ class ScanPlanner:
any user data before running.
"""
def __init__(self, intial_position: XYPos, planner_settings: Optional[dict] = None):
def __init__(
self, initial_position: XYPos, planner_settings: Optional[dict] = None
):
"""Set up lists for the path planning, and scan history."""
self._initial_position = enforce_xy_tuple(intial_position)
self._initial_position = enforce_xy_tuple(initial_position)
self._parse(planner_settings)
# The remaining (x,y) locations to scan
# (This was `path` before refactoring from the long `sample_scan` code)
self._remaining_locations: XYPosList = self._intial_location_list()
self._remaining_locations: XYPosList = self._initial_location_list()
# This holds a list of all (x,y,z) locations where images were taken
# this may not be equivalent to the x,y poistions ins self._path_history
# this may not be equivalent to the x,y positions ins self._path_history
# if background detect is used
# (This was not used in the `sample_scan` code)
self._imaged_locations: XYZPosList = []
@ -132,10 +134,10 @@ class ScanPlanner:
"""Parse any settings sent to this planner and store them if needed."""
raise NotImplementedError("Did you call the ScanPlanner base class?")
def _intial_location_list(self) -> XYPosList:
def _initial_location_list(self) -> XYPosList:
"""Set the initial list of locations for this scan planner.
This is called on initalisation.
This is called on initialisation.
For a simple grid scan/snake scan this would be all locations to move to.
@ -270,7 +272,7 @@ class SmartSpiral(ScanPlanner):
self._dy = int(planner_settings["dy"])
self._max_dist = int(planner_settings["max_dist"])
def _intial_location_list(self) -> XYPosList:
def _initial_location_list(self) -> XYPosList:
"""Set the initial list of locations for this scan planner.
This is salled on initialisation.
@ -327,7 +329,7 @@ class SmartSpiral(ScanPlanner):
self._remaining_locations.append(new_pos)
def _re_sort_remaining_locations(self, current_pos: XYPos) -> None:
"""Sort the remaining positions besed on the current location."""
"""Sort the remaining positions based on the current location."""
# Defined rather than use a lambda for readability
def sort_key(pos):

View file

@ -38,7 +38,7 @@ class StackParams:
images_dir: str,
save_resolution: tuple[int, int],
) -> None:
"""Initalise the parameters. All arguments are keyword only.
"""Initialise the parameters. All arguments are keyword only.
:param stack_dz: The number of motor steps between images
:param images_to_save: The number of images to save to disk
@ -205,7 +205,7 @@ class JPEGSharpnessMonitor:
"""
def __init__(self, stage: Stage, camera: Camera, portal: lt.deps.BlockingPortal):
"""Initalise a new JPEGSharpnessMonitor. The args are injected automatically.
"""Initialise a new JPEGSharpnessMonitor. The args are injected automatically.
:param stage: A direct_thing_client dependency for the the microscope stage.
:param camera: A raw_thing_client depeendency for the camera. This is a raw
@ -245,7 +245,7 @@ class JPEGSharpnessMonitor:
"""Move the stage by dz, monitoring the position over time.
This performs exactly one move. Multiple calls of this method
will append to the internal postition storage for more complex
will append to the internal position storage for more complex
autofocus procedures.
This should be run from within the JPEGSharpnessMonitor.run
@ -505,9 +505,9 @@ class AutofocusThing(lt.Thing):
save_resolution=save_resolution,
)
trys = 0
tries = 0
# Loop until a stack is successful
while trys < stack_parameters.max_attempts:
while tries < stack_parameters.max_attempts:
success, captures, sharpest_id = self.z_stack(
stack_parameters=stack_parameters,
cam=cam,

View file

@ -77,7 +77,7 @@ class BackgroundDetectThing(lt.Thing):
def background_mask(self, image: np.ndarray) -> np.ndarray:
"""Calculate a binary image, showing whether each pixel is background.
The image should be in LUV format, the ouput will be binary with the
The image should be in LUV format, the output will be binary with the
same shape in the first two dimensions.
"""
d = self.background_distributions

View file

@ -1,7 +1,7 @@
"""OpenFlexure Microscope Camera.
This module defines the interface for cameras. Any compatible lt.Thing
should enabe the server to work.
should enable the server to work.
See repository root for licensing information.
"""

View file

@ -86,7 +86,7 @@ class OpenCVCamera(BaseCamera):
It's likely to be highly inefficient - raw and/or uncompressed captures using
binary image formats will be added in due course.
"""
logging.warning(f"OpenCV camera doen't respect {resolution} setting")
logging.warning(f"OpenCV camera doesn't respect {resolution} setting")
ret, frame = self.cap.read()
if not ret:
raise RuntimeError(
@ -104,7 +104,7 @@ class OpenCVCamera(BaseCamera):
This function will produce a JPEG image.
"""
logging.warning(f"OpenCV camera doen't respect {resolution} setting")
logging.warning(f"OpenCV camera doesn't respect {resolution} setting")
frame = self.capture_array()
jpeg = cv2.imencode(".jpg", frame)[1].tobytes()
exif_dict = {

View file

@ -79,7 +79,7 @@ class SensorMode(BaseModel):
class SensorModeSelector(BaseModel):
"""A Pydantic model holding the two values needed to select a PiCamera Sensor mode.
Theses values are the output size and the bit depth.
These values are the output size and the bit depth.
This is a Pydantic model so that it can be saved to disk.
"""
@ -95,7 +95,7 @@ class LensShading(BaseModel):
(12, 16) in size. The arrays are luminance, red-difference chroma (Cr), and
blue-difference chroma (Cb).
This is a Pydantic modell so that it can be saved to the disk.
This is a Pydantic model so that it can be saved to the disk.
"""
luminance: list[list[float]]
@ -162,7 +162,7 @@ class StreamingPiCamera2(BaseCamera):
This method is run by any Thing when a ThingSetting is saved. However, the
method reads the thing_setting. As reading the thing setting talks to the
camera and calls save_settings if the value is not as expected, this could
cause recursion. Aslo this means that saving one setting causes all others
cause recursion. Also this means that saving one setting causes all others
to be read each time.
"""
try:
@ -197,7 +197,7 @@ class StreamingPiCamera2(BaseCamera):
@lt.thing_setting
def colour_gains(self) -> tuple[float, float]:
"""The red and blue colour gains, must be betwen 0.0 and 32.0."""
"""The red and blue colour gains, must be between 0.0 and 32.0."""
if not self._setting_save_in_progress and self.streaming:
with self._streaming_picamera() as cam:
cam_value = cam.capture_metadata()["ColourGains"]
@ -759,7 +759,7 @@ class StreamingPiCamera2(BaseCamera):
same as the default behaviour, which is to use an adaptive lens shading
table.
This flat table is used to take an image wth no lens shading so that the
This flat table is used to take an image with no lens shading so that the
correct lens shading table can be calibrated.
"""
with self._streaming_picamera(pause_stream=True):

View file

@ -84,7 +84,7 @@ def set_minimum_exposure(camera: Picamera2) -> None:
Note ISO is left at auto, because this is needed for the gains
to be set correctly.
"""
# Disable Automatic exposure and gain algoritm (AeEnable), and set analogue
# Disable Automatic exposure and gain algorithm (AeEnable), and set analogue
# gain and exposure time.
# Setting the shutter speed to 1us will result in it being set
# to the minimum possible, which is ~8us for PiCamera v2
@ -427,7 +427,7 @@ def set_static_lst(
cr: np.ndarray,
cb: np.ndarray,
) -> None:
"""Update the ``rpi.alsc`` section of a camera tuning dict to use a static correcton.
"""Update the ``rpi.alsc`` section of a camera tuning dict to use a static correction.
``tuning`` will be updated in-place to set its shading to static, and disable any
adaptive tweaking by the algorithm.
@ -452,7 +452,7 @@ def set_static_ccm(
float, float, float, float, float, float, float, float, float
],
) -> None:
"""Update the ``rpi.alsc`` section of a camera tuning dict to use a static correcton.
"""Update the ``rpi.alsc`` section of a camera tuning dict to use a static correction.
``tuning`` will be updated in-place to set its shading to static, and disable any
adaptive tweaking by the algorithm.
@ -480,7 +480,7 @@ def set_static_geq(
"""Update the ``rpi.geq`` section of a camera tuning dict.
:param tuning: the raspberry pi tuning file. This will be updated in-place to
set the geq offest to the given value.
set the geq offset to the given value.
:param offset: The desired green equalisation offset. Default 65535. The default is
the maximum allowed value. This means the brightness will always be below the
threshold where averaging is used. This is default as we always need the green

View file

@ -42,7 +42,7 @@ class SimulatedCamera(BaseCamera):
canvas_shape: tuple[int, int, int] = (3000, 4000, 3),
frame_interval: float = 0.1,
):
"""Initalise the simulated with settings for how images are generated.
"""Initialise the simulated with settings for how images are generated.
:param shape: The shape (size) of the generated image.
:param glyph_shape: The size randomly positioned glyphs.
@ -128,7 +128,7 @@ class SimulatedCamera(BaseCamera):
"""Wrap the attach_to_server method so the server instance can be stored.
Direct access to the server instance is needed to get the stage position while
maintianing the same public API as a real camera that doesn't need this access.
maintaining the same public API as a real camera that doesn't need this access.
"""
self._server = server
return super().attach_to_server(server, path, setting_storage_path)
@ -198,7 +198,7 @@ class SimulatedCamera(BaseCamera):
It's likely to be highly inefficient - raw and/or uncompressed captures using
binary image formats will be added in due course.
"""
logging.warning(f"Simulation camera doen't respect {resolution} setting")
logging.warning(f"Simulation camera doesn't respect {resolution} setting")
return self.generate_frame()
@lt.thing_action
@ -211,7 +211,7 @@ class SimulatedCamera(BaseCamera):
This function will produce a JPEG image.
"""
logging.warning(f"Simulation camera doen't respect {resolution} setting")
logging.warning(f"Simulation camera doesn't respect {resolution} setting")
frame = self.capture_array()
jpeg = cv2.imencode(".jpg", frame)[1].tobytes()
exif_dict = {

View file

@ -186,7 +186,7 @@ class CSMUncalibratedError(HTTPException):
"""An HTTP Exception raised if camera stage mapping data is needed but unavailable.
Camera Stage Mapping data is needed to convert from distances specified in fractions
of the feild of view to distances in motor steps. This is used when clicking on the
of the field of view to distances in motor steps. This is used when clicking on the
live preview to move, or when performing a scan.
"""

View file

@ -1,7 +1,7 @@
"""The core sample scanning functionality for the OpenFlexure Microscope.
SmartScan provides sample scanning functionality including automatic background
dectection (via the `BackgroundDetectThing`) and automatic path planning via
detection (via the `BackgroundDetectThing`) and automatic path planning via
`scan_planners`. It manages the directories of past scans via `scan_directories`.
It also controls external processes for live stitching composite images, and
the creation of the final stitched images.
@ -229,7 +229,7 @@ class SmartScanThing(lt.Thing):
def _move_to_next_point(
self, next_point: tuple[int, int], z_estimate: Optional[int] = None
) -> tuple[int, int, int]:
"""Move the stage to the next poistion.
"""Move the stage to the next position.
If no z_estimate is given then the current stage position is used. Must move
to the estimated focused position (although moving below would be marginally
@ -256,7 +256,7 @@ class SmartScanThing(lt.Thing):
:param overlap: The desired overlap as a fraction of the image. i.e. 0.5 means
that each image should overlap its nearest neighbour by 50%.
:returns: (dx, dy) - the x and y displacments in steps
:returns: (dx, dy) - the x and y displacements in steps
"""
test_jpg = self._cam.grab_jpeg()
test_image = np.array(Image.open(test_jpg.open()))
@ -488,7 +488,7 @@ class SmartScanThing(lt.Thing):
"max_dist": self._scan_data["max_dist"],
}
route_planner = scan_planners.SmartSpiral(
intial_position=(self._stage.position["x"], self._stage.position["y"]),
initial_position=(self._stage.position["x"], self._stage.position["y"]),
planner_settings=planner_settings,
)
@ -788,7 +788,7 @@ class SmartScanThing(lt.Thing):
This uses popen and returns immediately
- self._preview_stitch_popen holds the popen for polling
- self._preview_stitch_popen_lock is a lock aquired while interacting
- self._preview_stitch_popen_lock is a lock acquired while interacting
with Popen
"""
# Set minimum overlap to 90% of the scan overlap to catch only images directly adjacent,
@ -856,7 +856,7 @@ class SmartScanThing(lt.Thing):
os.set_blocking(process.stdout.fileno(), False)
logger.info(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())))
# Poll returns None while running, will return the error code when finnished
# Poll returns None while running, will return the error code when finished
while process.poll() is None:
log_buffer(process.stdout)
# Once buffer is clear sleep for 0.2s before trying again.