Single line summaries of docstrings

This commit is contained in:
Julian Stirling 2025-07-10 01:58:14 +01:00
parent 35d47fe3ed
commit 4dc41bb008
20 changed files with 153 additions and 115 deletions

View file

@ -68,9 +68,7 @@ def retrieve_log_from_file() -> PlainTextResponse:
class OFMHandler(logging.Handler):
"""A child class of logging.Handler. This class handles storing the most recent
logs for access by the server.
"""
"""A logging.Handler that stores the most recent logs for access by the server."""
def __init__(self, level=logging.INFO, max_logs=250):
super().__init__(level=level)
@ -78,8 +76,11 @@ class OFMHandler(logging.Handler):
self._max_logs = max_logs
def append_record(self, record):
"""Use the built in formatter to format the record, then save
it to an array. Pop any in excess of the maximum number of logs
"""Format message and append it to a list of records.
The built in formatter is used to format the record.
Any records in excess of the maximum number of logs are removed.
"""
self._log.append(self.format(record))
while len(self._log) > self._max_logs:

View file

@ -224,8 +224,9 @@ class ScanDirectory:
@property
def images_dir(self) -> Optional[str]:
"""The path to the images directory. None is returned if no images directory
was created
"""The path to the images directory.
None is returned if no images directory was created.
"""
im_path = os.path.join(self.dir_path, IMG_DIR_NAME)
if os.path.isdir(im_path):

View file

@ -155,8 +155,7 @@ class ScanPlanner:
return tuple(position) in self._remaining_locations
def get_next_location_and_z_estimate(self) -> tuple[XYPos, Optional[int]]:
"""Return the next location to scan, and the estimated z-position
for this location.
"""Return the next location to scan and its estimated z-position.
Note z-position may be None! This indicates that the current z, position
should be used.
@ -176,9 +175,12 @@ class ScanPlanner:
return next_location, z
def closest_focus_site(self, xy_pos: XYPos) -> Optional[XYZPos]:
"""Return the xyz position of the closest site where focus was achieved
to the input xy_position, with the most recently taken image returned in
the case of a tie
"""Return the xyz position of the closest site where focus was achieved.
The most recently taken image is returned in the case of a tie.
:param xy_pos: The xy_position which the returned position should be closest
to.
Returns None if there if no focussed locations are present
"""
@ -338,9 +340,10 @@ class SmartSpiral(ScanPlanner):
self._remaining_locations.sort(key=sort_key)
def get_next_location_and_z_estimate(self) -> tuple[XYPos, Optional[int]]:
"""Return the next location to scan, and the estimated z-position
for this location. This overrides the default behaviour of ScanPlanner
to take the lowest value of nearest neighbours as this works best for smart stack
"""Return the next location to scan and its estimated z-position.
This overrides the default behaviour of ScanPlanner to take the lowest value of
nearest neighbours as this works best for smart stack.
Note z-position may be None! This indicates that the current z position
should be used.
@ -361,9 +364,11 @@ class SmartSpiral(ScanPlanner):
def select_nearby_focus_site(self, xy_pos: XYPos) -> Optional[XYZPos]:
"""Return the xyz position of the nearby site with the lowest z position.
Lowest position is best, as starting too high causes smart stacking to
autofocus and restart. Starting too low just requires extra movements in +z.
Nearby is defined as within NEIGHBOUR_CUTOFF times the distance to the closest neighbour.
Nearby is defined as within NEIGHBOUR_CUTOFF times the distance to the closest
neighbour.
Returns None if there if no focused locations are present
"""
@ -397,12 +402,10 @@ class SmartSpiral(ScanPlanner):
starting_pos: XYPos | np.ndarray,
ending_pos: XYPos | np.ndarray,
) -> float:
"""Return the number of moves between two xy positions in the x or y direction
whichever is largest
"""Return the larger of x moves or y moves between two xy positions.
Args:
starting_pos: the position to measure from
ending_pos: the position to measure to
:param starting_pos: the position to measure from
:param ending_pos: the position to measure to
"""
move_size = np.array([self._dx, self._dy])

View file

@ -9,7 +9,8 @@ THIS_DIR = os.path.dirname(os.path.abspath(__file__))
def add_static_file(app: FastAPI, fname: str, folder: str) -> None:
"""Add a single file to the root of the FastAPI app
"""Add a single file to the root of the FastAPI app.
The file with name ``fname`` will be mounted at ``/fname`` - the
``folder`` does not affect where it is mounted in the app.

View file

@ -159,8 +159,7 @@ def _get_capture_by_id(captures: list[CaptureInfo], buffer_id: int) -> CaptureIn
def _get_capture_index_by_id(captures: list[CaptureInfo], buffer_id: int) -> int:
"""Return the index of the capture with the matching id from a list of CaptureInfo
objects
"""Return the index of the capture with the matching id.
:param captures: A list of capture objects
:param buffer_id: The buffer id of the image to return
@ -432,10 +431,13 @@ class AutofocusThing(lt.Thing):
autofocus_dz: int,
save_resolution: tuple[int, int],
) -> tuple[bool, int]:
"""Run a smart stack, which captures images offset in z, testing
whether the sharpest image is towards the centre of the stack.
The sharpest image, and optionally images around the sharpest,
will be saved using their coordinates to images_dir
"""Run a smart stack.
A smart stack captures images offset in z, testing whether the sharpest image
is towards the centre of the stack.
The sharpest image, and optionally images around the sharpest, will be saved
to the images_dir with their coordinates in the filename.
:param cam: Camera Dependency supplied by LabThings dependency injection
@ -506,15 +508,13 @@ class AutofocusThing(lt.Thing):
stage: Stage,
sharpness_monitor: SharpnessMonitorDep,
) -> None:
"""Return to the initial height of the current stack, and run
a looping autofocus.
"""Return to the initial z position and run a looping autofocus.
:param initial_z_pos: The initial z positions of previous captures
:param autofocus_dz: the range in steps to autofocus
``stage`` and ``sharpness_monitor`` are Thing dependencies passed through
from the calling action
from the calling action.
"""
stage.move_absolute(z=initial_z_pos)
self.looping_autofocus(
@ -530,8 +530,10 @@ class AutofocusThing(lt.Thing):
stack_parameters: StackParams,
cam: WrappedCamera,
) -> int:
"""Save the required captures to disk. Will save the sharpest image,
and any images either side of focus.
"""Save the required captures to disk.
This will save the sharpest image, and optionally extra images either
side of focus (see ``stack_parameters.images_to_save``).
:param sharpest_id: the buffer id index of the sharpest image
:param captures: a list of captures, including file name, image data and
@ -559,8 +561,12 @@ class AutofocusThing(lt.Thing):
cam: WrappedCamera,
stage: Stage,
) -> tuple[bool, list[CaptureInfo], Optional[int]]:
"""Capture a series of images offset by stack_parameters.stack_dz, and test whether
the sharpest image is towards the centre of the stack.
"""Capture a series of images checking that sharpest image central
The images are seperated in z offset by stack_parameters.stack_dz, as they
are captured the last stack_parameters.min_images_to_test images are checked
to see if the sharpest image is central enough in the stack. If it is the stack
completes.
:param stack_parameters: a StackParams object holding stack parameters
:param cam: Camera Dependency to be passed through from the calling action
@ -644,8 +650,7 @@ class AutofocusThing(lt.Thing):
def check_stack_result(
self, captures: list[CaptureInfo]
) -> tuple[Literal["success", "continue", "restart"], int]:
"""Test a list of captures, to decide whether the sharpest image from a
stack is centrally enough in the stack
"""Check if the sharpest image in a list of captures is central enough.
:param captures: a list of the capture objects to for testing if the
sharpness has converged in the centre

View file

@ -156,8 +156,10 @@ class BaseCamera(lt.Thing):
def start_streaming(
self, main_resolution: tuple[int, int], buffer_count: int
) -> None:
"""Start (or stop and restart) the camera with the given resolution
for the main stream, and buffer_count number of images in the buffer
"""Start (or stop and restart) the camera.
:param main_resolution: the resolution to use for the main stream.
:param buffer_count: number of images in the stream buffer.
"""
raise NotImplementedError(
"CameraThings must define their own start_streaming method"
@ -342,12 +344,14 @@ class BaseCamera(lt.Thing):
metadata_getter: lt.deps.GetThingStates,
logger: lt.deps.InvocationLogger,
) -> Image:
"""Capture an image in memory and return it with metadata
CaptureError raised if the capture fails for any reason
returns tuple with PIL Image, and dict of metadata.
"""Capture an image in memory and return it with metadata.
This robust capturing method attempts to capture the image five times
each time with a 5 second timeout set.
:raises CaptureError: if the capture fails for any reason
:returns: tuple with PIL Image, and dictionary of metadata.
"""
for capture_attempts in range(5):
try:

View file

@ -62,8 +62,9 @@ class PicameraStreamOutput(Output):
class SensorMode(BaseModel):
"""A Pydantic model holding all the information about a specific
sensor mode as reported by the PiCamera.
"""A Pydantic model holding all the information about a specific sensor mode.
This data is as reported by the PiCamera2 module.
"""
unpacked: str
@ -76,10 +77,11 @@ class SensorMode(BaseModel):
class SensorModeSelector(BaseModel):
"""A Pydantic model holding the two values needed to select a PiCamera
Sensor mode. The output size and the bit depth.
"""A Pydantic model holding the two values needed to select a PiCamera Sensor mode.
This is a Pydantic modell so that it can be saved to the disk.
Theses values are the output size and the bit depth.
This is a Pydantic model so that it can be saved to disk.
"""
output_size: tuple[int, int]

View file

@ -477,12 +477,16 @@ def set_static_geq(
tuning: dict,
offset: int = 65535,
) -> None:
"""Update the ``rpi.geq`` section of a camera tuning dict to always use green
equalisation that averages the green pixels in the red and blue rows.
"""Update the ``rpi.geq`` section of a camera tuning dict.
``tuning`` will be updated in-place to set the geq offest to the given value.
The default 65535 is the maximum allowed value. This means
the brightness will always be below the threshold where averaging is used.
:param tuning: the raspberry pi tuning file. This will be updated in-place to
set the geq offest 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
equalisation to averages the green pixels in the red and blue rows due to the
chief ray angle compensation issue when the the stock lens is replaced by an
objective.
"""
geq = Picamera2.find_tuning_algo(tuning, "rpi.geq")
geq["offset"] = offset # max out offset to disable the adaptive green equalisation

View file

@ -311,8 +311,9 @@ class SmartScanThing(lt.Thing):
@_scan_running
def _save_scan_inputs_json(self):
"""Save scan inputs as a JSON file in the scan folder, to allow
the user to review the settings used in the scan
"""Save scan inputs as a JSON file in the scan folder.
This file allows the user to review the settings used in the scan.
"""
# Should this be a method of the scan_data dataclass?
@ -335,11 +336,10 @@ class SmartScanThing(lt.Thing):
@_scan_running
def _update_scan_data_json(self, scan_result: str):
"""Update scan data as a JSON file in the scan folder, with
data only known at the end of the scan.
"""Update scan data JSON file with data only known at the end of the scan.
Takes scan_result, a string that is either "success",
"cancelled by user", or the error that ended the scan.
Takes scan_result, a string that is either "success", "cancelled by user",
or the error that ended the scan.
"""
# Should this be a method of the scan_data dataclass?
current_time = datetime.now().replace(microsecond=0)
@ -378,10 +378,11 @@ class SmartScanThing(lt.Thing):
@_scan_running
def _run_scan(self):
"""Prepare and run the main scan, handling the threads performing
stitching. Uses the result (or exception) from the scanning to
determine whether the scan should be stitched and the microscope
should return to the starting x,y,z position
"""Prepare and run the main scan, and perform final actions on completion.
The result (or exception) from the main scan loop determines whether the
scan should be stitched and whether the microscope should return to the
starting x,y,z position.
"""
# Used to check if finally was reached via exception (except
# cancel by user)
@ -765,8 +766,7 @@ class SmartScanThing(lt.Thing):
@_scan_running
def _preview_stitch_start(self, overlap: float) -> None:
"""Start stitching a preview of the scan in a background subprocess
"""Start stitching a preview of the scan in a background subprocess.
This uses popen and returns immediately
@ -954,8 +954,9 @@ class SmartScanThing(lt.Thing):
self,
scan_name: str,
):
"""Update the zip to include the files left until the end, then return the
zip file as a Blob
"""Return zip after including any files left until the end.
The zipfile is returned as a Blob.
"""
zip_fname = self._scan_dir_manager.zip_scan(scan_name, final_version=True)
return ZipBlob.from_file(zip_fname)

View file

@ -43,8 +43,10 @@ class ErrorCapturingThread(Thread):
)
def join(self, timeout=None):
"""Join when the thread is complete. If the thread ended due to an unhandled exception,
the exception will be raised when this method is called.
"""Join when the thread is complete.
If the thread ended due to an unhandled exception, the exception will be raised
when this method is called.
"""
super().join(timeout)
# If there is an error in the error buffer clear the buffer and raise it