From 9f5252194ab3c9097f86b66c248554a1d33e1bcf Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 12 Nov 2020 15:12:17 +0000 Subject: [PATCH] Updated all log strings to new format --- .pre-commit-config.yaml | 1 - .pylintrc | 8 +- docs/source/conf.py | 2 +- .../example_extension/04_properties.py | 2 +- .../example_extension/05_actions.py | 2 +- .../example_extension/06_tasks_locks.py | 12 +- .../extensions/example_extension/07_ev_gui.py | 12 +- openflexure_microscope/api/app.py | 2 +- .../api/default_extensions/__init__.py | 2 +- .../api/default_extensions/autofocus.py | 7 +- .../api/default_extensions/autostorage.py | 6 +- .../recalibrate_utils.py | 19 +- .../api/default_extensions/scan.py | 16 +- .../api/default_extensions/zip_builder.py | 2 +- .../api/dev_extensions/tools.py | 2 +- .../api/utilities/__init__.py | 6 +- openflexure_microscope/api/utilities/gui.py | 2 +- .../api/v2/views/actions/stage.py | 4 +- openflexure_microscope/camera/base.py | 4 +- openflexure_microscope/camera/pi.py | 55 ++-- .../camera/set_picamera_gain.py | 13 +- openflexure_microscope/captures/capture.py | 32 +-- .../captures/capture_manager.py | 10 +- openflexure_microscope/config.py | 12 +- openflexure_microscope/microscope.py | 18 +- .../rescue/check_capture_reload.py | 2 +- .../rescue/monitor_timeout.py | 2 +- openflexure_microscope/stage/mock.py | 4 +- openflexure_microscope/stage/sanga.py | 10 +- openflexure_microscope/utilities.py | 2 +- tests/api_client.py | 101 ------- tests/test_api.py | 112 -------- tests/test_camera.py | 266 ------------------ tests/test_plugins.py | 44 --- tests/test_stage.py | 42 --- 35 files changed, 121 insertions(+), 715 deletions(-) delete mode 100644 tests/api_client.py delete mode 100644 tests/test_api.py delete mode 100644 tests/test_camera.py delete mode 100644 tests/test_plugins.py delete mode 100644 tests/test_stage.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a6643d24..7ff044af 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,6 +14,5 @@ repos: - id: pylint name: pylint entry: poetry run pylint - files: "./openflexure_microscope/" language: system types: [python] diff --git a/.pylintrc b/.pylintrc index 918a4af4..7dc2c3fe 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,4 +1,8 @@ [MESSAGES CONTROL] -disable=C0330,C0326,W1202,W0511,C,R -max-line-length = 88 \ No newline at end of file +disable=fixme,C,R +max-line-length = 88 + +[LOGGING] + +logging-format-style=new \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index fe3f927f..4fd4b0c0 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -38,7 +38,7 @@ sys.modules.update((mod_name, Mock()) for mod_name in mock_imports) # -- Project information ----------------------------------------------------- project = "OpenFlexure Microscope Software" -copyright = "2018, Bath Open Instrumentation Group" +copyright = "2018, Bath Open Instrumentation Group" # pylint: disable=redefined-builtin author = "Bath Open Instrumentation Group" # The short X.Y version diff --git a/docs/source/extensions/example_extension/04_properties.py b/docs/source/extensions/example_extension/04_properties.py index ec13cc3c..3bedbad0 100644 --- a/docs/source/extensions/example_extension/04_properties.py +++ b/docs/source/extensions/example_extension/04_properties.py @@ -1,6 +1,6 @@ from labthings import Schema, fields, find_component from labthings.extensions import BaseExtension -from labthings.views import PropertyView, View +from labthings.views import PropertyView ## Extension methods diff --git a/docs/source/extensions/example_extension/05_actions.py b/docs/source/extensions/example_extension/05_actions.py index 3980e531..52b1b024 100644 --- a/docs/source/extensions/example_extension/05_actions.py +++ b/docs/source/extensions/example_extension/05_actions.py @@ -3,7 +3,7 @@ import io # Used in our capture action from flask import send_file # Used to send images from our server from labthings import Schema, fields, find_component from labthings.extensions import BaseExtension -from labthings.views import ActionView, PropertyView, View +from labthings.views import ActionView, PropertyView ## Extension methods diff --git a/docs/source/extensions/example_extension/06_tasks_locks.py b/docs/source/extensions/example_extension/06_tasks_locks.py index e17ac971..710b4e73 100644 --- a/docs/source/extensions/example_extension/06_tasks_locks.py +++ b/docs/source/extensions/example_extension/06_tasks_locks.py @@ -1,16 +1,8 @@ -import io # Used in our capture action import time # Used in our timelapse function -from flask import send_file # Used to send images from our server -from labthings import ( - Schema, - current_action, - fields, - find_component, - update_action_progress, -) +from labthings import current_action, fields, find_component, update_action_progress from labthings.extensions import BaseExtension -from labthings.views import ActionView, PropertyView, View +from labthings.views import ActionView # Used in our timelapse function from openflexure_microscope.captures.capture_manager import generate_basename diff --git a/docs/source/extensions/example_extension/07_ev_gui.py b/docs/source/extensions/example_extension/07_ev_gui.py index ea2c9c9b..a71d9053 100644 --- a/docs/source/extensions/example_extension/07_ev_gui.py +++ b/docs/source/extensions/example_extension/07_ev_gui.py @@ -1,16 +1,8 @@ -import io # Used in our capture action import time # Used in our timelapse function -from flask import send_file # Used to send images from our server -from labthings import ( - Schema, - current_action, - fields, - find_component, - update_action_progress, -) +from labthings import current_action, fields, find_component, update_action_progress from labthings.extensions import BaseExtension -from labthings.views import ActionView, PropertyView, View +from labthings.views import ActionView # Used to convert our GUI dictionary into a complete eV extension GUI from openflexure_microscope.api.utilities.gui import build_gui diff --git a/openflexure_microscope/api/app.py b/openflexure_microscope/api/app.py index ebb77d19..998be592 100644 --- a/openflexure_microscope/api/app.py +++ b/openflexure_microscope/api/app.py @@ -72,7 +72,7 @@ root_log.addHandler(fh) access_log.addHandler(afh) # Log server paths being used -logging.info("Running with data path %s", OPENFLEXURE_VAR_PATH) +logging.info("Running with data path {}", OPENFLEXURE_VAR_PATH) logging.info("Creating app") # Create flask app diff --git a/openflexure_microscope/api/default_extensions/__init__.py b/openflexure_microscope/api/default_extensions/__init__.py index 561239af..e76733f9 100644 --- a/openflexure_microscope/api/default_extensions/__init__.py +++ b/openflexure_microscope/api/default_extensions/__init__.py @@ -10,7 +10,7 @@ def handle_extension_error(extension_name): yield except Exception: # pylint: disable=W0703 logging.error( - "Exception loading builtin extension %s: \n%s", + "Exception loading builtin extension {}: \n{}", extension_name, traceback.format_exc(), ) diff --git a/openflexure_microscope/api/default_extensions/autofocus.py b/openflexure_microscope/api/default_extensions/autofocus.py index 507a9fac..cc30a908 100644 --- a/openflexure_microscope/api/default_extensions/autofocus.py +++ b/openflexure_microscope/api/default_extensions/autofocus.py @@ -83,7 +83,7 @@ class JPEGSharpnessMonitor: raise e if stop < 1: stop = len(jpeg_times) - logging.debug("changing stop to {}".format(stop)) + logging.debug("changing stop to {}", (stop)) jpeg_times = jpeg_times[start:stop] jpeg_zs = np.interp(jpeg_times, stage_times, stage_zs) return jpeg_times, jpeg_zs, jpeg_sizes[start:stop] @@ -292,9 +292,8 @@ def fast_up_down_up_autofocus( logging.debug("Correction move") correction_move = best_z + target_z - jz[inow] logging.debug( - "Fast autofocus scan: correcting backlash by moving {} steps".format( - correction_move - ) + "Fast autofocus scan: correcting backlash by moving {} steps", + (correction_move), ) m.focus_rel(correction_move) return m.data_dict() diff --git a/openflexure_microscope/api/default_extensions/autostorage.py b/openflexure_microscope/api/default_extensions/autostorage.py index e54f3aea..8a7c4abe 100644 --- a/openflexure_microscope/api/default_extensions/autostorage.py +++ b/openflexure_microscope/api/default_extensions/autostorage.py @@ -95,10 +95,10 @@ class AutostorageExtension(BaseExtension): def on_microscope(self, microscope_obj): """Function to automatically call when the parent LabThing has a microscope attached.""" - logging.debug("Autostorage extension found microscope %s", microscope_obj) + logging.debug("Autostorage extension found microscope {}", microscope_obj) if hasattr(microscope_obj, "captures"): logging.debug( - "Autostorage extension bound to CaptureManager %s", self.capture_manager + "Autostorage extension bound to CaptureManager {}", self.capture_manager ) # Store a reference to the CaptureManager @@ -117,7 +117,7 @@ class AutostorageExtension(BaseExtension): # If preferred path does not exist, or cannot be written to if not (os.path.isdir(location) and check_rw(location)): logging.error( - "Preferred capture path %s is missing or cannot be written to. Restoring defaults.", + "Preferred capture path {} is missing or cannot be written to. Restoring defaults.", location, ) # Reset the storage location to default diff --git a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py index 28b04c02..6aa70937 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py @@ -63,17 +63,15 @@ def auto_expose_and_freeze_settings(camera): logging.info("Freezing the camera settings...") camera.shutter_speed = camera.exposure_speed - logging.info("Shutter speed = {}".format(camera.shutter_speed)) + logging.info("Shutter speed = {}", (camera.shutter_speed)) camera.exposure_mode = "off" logging.info("Auto exposure disabled") g = camera.awb_gains camera.awb_mode = "off" camera.awb_gains = g - logging.info("Auto white balance disabled, gains are {}".format(g)) + logging.info("Auto white balance disabled, gains are {}", (g)) logging.info( - "Analogue gain: {}, Digital gain: {}".format( - camera.analog_gain, camera.digital_gain - ) + "Analogue gain: {}, Digital gain: {}", camera.analog_gain, camera.digital_gain ) adjust_exposure_to_setpoint(camera, 215) @@ -100,7 +98,7 @@ def lst_from_channels(channels): # lst_resolution = list(np.ceil(full_resolution / 64.0).astype(int)) lst_resolution = [(r // 64) + 1 for r in full_resolution] # NB the size of the LST is 1/64th of the image, but rounded UP. - logging.info("Generating a lens shading table at {}x{}".format(*lst_resolution)) + logging.info("Generating a lens shading table at {}x{}", *lst_resolution) lens_shading = np.zeros([channels.shape[0]] + lst_resolution, dtype=np.float) for i in range(lens_shading.shape[0]): image_channel = channels[i, :, :] @@ -118,9 +116,12 @@ def lst_from_channels(channels): image_channel, [(0, lw * 32 - iw), (0, lh * 32 - ih)], mode="edge" ) # Pad image to the right and bottom logging.info( - "Channel shape: {}x{}, shading table shape: {}x{}, after padding {}".format( - iw, ih, lw * 32, lh * 32, padded_image_channel.shape - ) + "Channel shape: {}x{}, shading table shape: {}x{}, after padding {}", + iw, + ih, + lw * 32, + lh * 32, + padded_image_channel.shape, ) # Next, fill the shading table (except edge pixels). Please excuse the # for loop - I know it's not fast but this code needn't be! diff --git a/openflexure_microscope/api/default_extensions/scan.py b/openflexure_microscope/api/default_extensions/scan.py index 9a292f11..52b9e0ec 100644 --- a/openflexure_microscope/api/default_extensions/scan.py +++ b/openflexure_microscope/api/default_extensions/scan.py @@ -216,7 +216,7 @@ class ScanExtension(BaseExtension): for x_y in line: # Move to new grid position without changing z - logging.debug("Moving to step {}".format([x_y[0], x_y[1], next_z])) + logging.debug("Moving to step {}", ([x_y[0], x_y[1], next_z])) microscope.stage.move_abs([x_y[0], x_y[1], next_z]) # Refocus if autofocus_enabled: @@ -270,11 +270,11 @@ class ScanExtension(BaseExtension): # Make sure we use our current best estimate of focus (i.e. the current position) next point next_z = microscope.stage.position[2] - logging.debug("Returning to {}".format(initial_position)) + logging.debug("Returning to {}", (initial_position)) microscope.stage.move_abs(initial_position) end = time.time() - logging.info("Scan took %s seconds", end - start) + logging.info("Scan took {} seconds", end - start) def stack( self, @@ -298,17 +298,17 @@ class ScanExtension(BaseExtension): # Store initial position initial_position = microscope.stage.position - logging.debug("Starting z-stack from position %s", microscope.stage.position) + logging.debug("Starting z-stack from position {}", microscope.stage.position) with microscope.lock: # Move to center scan logging.debug("Moving to z-stack starting position") microscope.stage.move_rel([0, 0, int((-step_size * steps) / 2)]) - logging.debug("Starting scan from position %s", microscope.stage.position) + logging.debug("Starting scan from position {}", microscope.stage.position) for i in range(steps): time.sleep(0.1) - logging.debug("Capturing from position %s", microscope.stage.position) + logging.debug("Capturing from position {}", microscope.stage.position) self.capture( microscope, basename, @@ -328,10 +328,10 @@ class ScanExtension(BaseExtension): return if i != steps - 1: - logging.debug("Moving z by {}".format(step_size)) + logging.debug("Moving z by {}", (step_size)) microscope.stage.move_rel([0, 0, step_size]) if return_to_start: - logging.debug("Returning to {}".format(initial_position)) + logging.debug("Returning to {}", (initial_position)) microscope.stage.move_abs(initial_position) diff --git a/openflexure_microscope/api/default_extensions/zip_builder.py b/openflexure_microscope/api/default_extensions/zip_builder.py index 5db25a70..9ce774da 100644 --- a/openflexure_microscope/api/default_extensions/zip_builder.py +++ b/openflexure_microscope/api/default_extensions/zip_builder.py @@ -156,7 +156,7 @@ class ZipGetterAPIView(View): if not session_id in default_zip_manager.session_zips: return abort(404) # 404 Not Found - logging.info("Session ID: %s", session_id) + logging.info("Session ID: {}", session_id) return send_file( default_zip_manager.zip_fp_from_id(session_id).name, diff --git a/openflexure_microscope/api/dev_extensions/tools.py b/openflexure_microscope/api/dev_extensions/tools.py index ddbadfef..1f6d12cf 100644 --- a/openflexure_microscope/api/dev_extensions/tools.py +++ b/openflexure_microscope/api/dev_extensions/tools.py @@ -17,7 +17,7 @@ class SleepFor(ActionView): def post(self, args): sleep_time = args.get("time") - logging.info("Going to sleep for %s...", sleep_time) + logging.info("Going to sleep for {}...", sleep_time) start = time.time() time.sleep(sleep_time) end = time.time() diff --git a/openflexure_microscope/api/utilities/__init__.py b/openflexure_microscope/api/utilities/__init__.py index 8f72a948..333fa767 100644 --- a/openflexure_microscope/api/utilities/__init__.py +++ b/openflexure_microscope/api/utilities/__init__.py @@ -73,12 +73,10 @@ def init_default_extensions(extension_dir): default_ext_path = os.path.join(extension_dir, "defaults.py") if not os.path.isfile(default_ext_path): # If user extensions file doesn't exist - logging.warning( - "No extension file found at {}. Creating...".format(extension_dir) - ) + logging.warning("No extension file found at {}. Creating...", (extension_dir)) create_file(default_ext_path) - logging.info("Populating {}...".format(default_ext_path)) + logging.info("Populating {}...", (default_ext_path)) with open(default_ext_path, "w") as outfile: outfile.write(_DEFAULT_EXTENSION_INIT) diff --git a/openflexure_microscope/api/utilities/gui.py b/openflexure_microscope/api/utilities/gui.py index 1fc03c1f..5613ae69 100644 --- a/openflexure_microscope/api/utilities/gui.py +++ b/openflexure_microscope/api/utilities/gui.py @@ -27,7 +27,7 @@ def build_gui_from_dict(gui_description, extension_object): if "route" in form and form["route"] in ext_rules.keys(): form["route"] = ext_rules[form["route"]]["urls"][0] else: - logging.warning("No valid expandable route found for %s", form["route"]) + logging.warning("No valid expandable route found for {}", form["route"]) # Inject extension information api_gui["id"] = extension_object.name diff --git a/openflexure_microscope/api/v2/views/actions/stage.py b/openflexure_microscope/api/v2/views/actions/stage.py index cd989f5c..5c7cc416 100644 --- a/openflexure_microscope/api/v2/views/actions/stage.py +++ b/openflexure_microscope/api/v2/views/actions/stage.py @@ -25,11 +25,11 @@ class MoveStageAPI(ActionView): # Handle absolute positioning (calculate a relative move from current position and target) if (args.get("absolute")) and (microscope.stage): # Only if stage exists target_position = axes_to_array(args, ["x", "y", "z"]) - logging.debug("TARGET: {}".format(target_position)) + logging.debug("TARGET: {}", (target_position)) position = [ target_position[i] - microscope.stage.position[i] for i in range(3) ] - logging.debug("DELTA: {}".format(position)) + logging.debug("DELTA: {}", (position)) else: # Get coordinates from payload diff --git a/openflexure_microscope/camera/base.py b/openflexure_microscope/camera/base.py index 4ef6d7b8..d3bdbefa 100644 --- a/openflexure_microscope/camera/base.py +++ b/openflexure_microscope/camera/base.py @@ -113,10 +113,10 @@ class BaseCamera(metaclass=ABCMeta): def close(self): """Close the BaseCamera and all attached StreamObjects.""" - logging.info("Closing {}".format(self)) + logging.info("Closing {}", (self)) # Stop worker thread self.stop_worker() - logging.info("Closed {}".format(self)) + logging.info("Closed {}", (self)) # START AND STOP WORKER THREAD diff --git a/openflexure_microscope/camera/pi.py b/openflexure_microscope/camera/pi.py index 11dafab8..d86957d4 100644 --- a/openflexure_microscope/camera/pi.py +++ b/openflexure_microscope/camera/pi.py @@ -156,10 +156,10 @@ class PiCameraStreamer(BaseCamera): for key in PiCameraStreamer.picamera_settings_keys: try: value = getattr(self.camera, key) - logging.debug("Reading PiCamera().{}: {}".format(key, value)) + logging.debug("Reading PiCamera().{}: {}", key, value) conf_dict["picamera"][key] = value except AttributeError: - logging.debug("Unable to read PiCamera attribute {}".format(key)) + logging.debug("Unable to read PiCamera attribute {}", (key)) # Include a serialised lens shading table if ( @@ -243,26 +243,22 @@ class PiCameraStreamer(BaseCamera): # Set exposure mode if "exposure_mode" in settings_dict: logging.debug( - "Applying exposure_mode: {}".format(settings_dict["exposure_mode"]) + "Applying exposure_mode: {}", (settings_dict["exposure_mode"]) ) self.camera.exposure_mode = settings_dict["exposure_mode"] # Apply gains and let them settle if "analog_gain" in settings_dict: - logging.debug( - "Applying analog_gain: {}".format(settings_dict["analog_gain"]) - ) + logging.debug("Applying analog_gain: {}", (settings_dict["analog_gain"])) set_analog_gain(self.camera, float(settings_dict["analog_gain"])) if "digital_gain" in settings_dict: - logging.debug( - "Applying digital_gain: {}".format(settings_dict["digital_gain"]) - ) + logging.debug("Applying digital_gain: {}", (settings_dict["digital_gain"])) set_digital_gain(self.camera, float(settings_dict["digital_gain"])) # Apply shutter speed if "shutter_speed" in settings_dict: logging.debug( - "Applying shutter_speed: {}".format(settings_dict["shutter_speed"]) + "Applying shutter_speed: {}", (settings_dict["shutter_speed"]) ) self.camera.shutter_speed = int(settings_dict["shutter_speed"]) @@ -272,17 +268,17 @@ class PiCameraStreamer(BaseCamera): if "awb_gains" in settings_dict: logging.debug("Applying awb_mode: off") self.camera.awb_mode = "off" - logging.debug("Applying awb_gains: {}".format(settings_dict["awb_gains"])) + logging.debug("Applying awb_gains: {}", (settings_dict["awb_gains"])) self.camera.awb_gains = settings_dict["awb_gains"] elif "awb_mode" in settings_dict: - logging.debug("Applying awb_mode: {}".format(settings_dict["awb_mode"])) + logging.debug("Applying awb_mode: {}", (settings_dict["awb_mode"])) self.camera.awb_mode = settings_dict["awb_mode"] # Handle some properties that can be quickly applied batched_keys = ["framerate", "saturation"] for key in batched_keys: if (key in settings_dict) and hasattr(self.camera, key): - logging.debug("Applying {}: {}".format(key, settings_dict[key])) + logging.debug("Applying {}: {}", key, settings_dict[key]) setattr(self.camera, key, settings_dict[key]) # Final optional pause to settle @@ -307,7 +303,7 @@ class PiCameraStreamer(BaseCamera): for i in range(2): if np.abs(centre[i] - 0.5) + size / 2 > 0.5: centre[i] = 0.5 + (1.0 - size) / 2 * np.sign(centre[i] - 0.5) - logging.info("setting zoom, centre {}, size {}".format(centre, size)) + logging.info("setting zoom, centre {}, size {}", centre, size) new_fov = (centre[0] - size / 2, centre[1] - size / 2, size, size) self.camera.zoom = new_fov @@ -329,13 +325,12 @@ class PiCameraStreamer(BaseCamera): self.preview_active = True except picamerax.exc.PiCameraMMALError as e: logging.error( - "Suppressed a MMALError in start_preview. Exception: {}".format(e) + "Suppressed a MMALError in start_preview. Exception: {}", (e) ) except picamerax.exc.PiCameraValueError as e: logging.error( - "Suppressed a ValueError exception in start_preview. Exception: {}".format( - e - ) + "Suppressed a ValueError exception in start_preview. Exception: {}", + (e), ) def stop_preview(self): @@ -364,7 +359,7 @@ class PiCameraStreamer(BaseCamera): if not self.record_active: # Start the camera video recording on port 2 - logging.info("Recording to {}".format(output)) + logging.info("Recording to {}", (output)) self.camera.start_recording( output, @@ -406,19 +401,19 @@ class PiCameraStreamer(BaseCamera): """ for k in kwargs.keys(): logging.warning( - "Warning, kwarg %s is invalid for stop_stream_recording.", k + "Warning, kwarg {} is invalid for stop_stream_recording.", k ) with self.lock: # Stop the camera video recording on port 1 try: self.camera.stop_recording(splitter_port=splitter_port) except picamerax.exc.PiCameraNotRecording: - logging.info("Not recording on splitter_port {}".format(splitter_port)) + logging.info("Not recording on splitter_port {}", (splitter_port)) else: logging.info( - "Stopped MJPEG stream on port {1}. Switching to {0}.".format( - self.image_resolution, splitter_port - ) + "Stopped MJPEG stream on port {}. Switching to {}.", + splitter_port, + self.image_resolution, ) # Increase the resolution for taking an image @@ -436,7 +431,7 @@ class PiCameraStreamer(BaseCamera): """ for k in kwargs.keys(): logging.warning( - "Warning, kwarg %s is invalid for stop_stream_recording.", k + "Warning, kwarg {} is invalid for stop_stream_recording.", k ) with self.lock(timeout=None): # Reduce the resolution for video streaming @@ -470,9 +465,9 @@ class PiCameraStreamer(BaseCamera): ) else: logging.debug( - "Started MJPEG stream at {} on port {}".format( - self.stream_resolution, splitter_port - ) + "Started MJPEG stream at {} on port {}", + self.stream_resolution, + splitter_port, ) def capture( @@ -501,7 +496,7 @@ class PiCameraStreamer(BaseCamera): output_object (str/BytesIO): Target object. """ with self.lock: - logging.info("Capturing to {}".format(output)) + logging.info("Capturing to {}", (output)) # Set resolution and stop stream recording if necessary if not use_video_port: @@ -537,7 +532,7 @@ class PiCameraStreamer(BaseCamera): logging.debug("Creating PiRGBArray") with picamerax.array.PiRGBArray(self.camera) as output: - logging.info("Capturing to {}".format(output)) + logging.info("Capturing to {}", (output)) self.camera.capture(output, format="rgb", use_video_port=True) diff --git a/openflexure_microscope/camera/set_picamera_gain.py b/openflexure_microscope/camera/set_picamera_gain.py index d1d7175a..3e92b4b9 100644 --- a/openflexure_microscope/camera/set_picamera_gain.py +++ b/openflexure_microscope/camera/set_picamera_gain.py @@ -55,26 +55,17 @@ if __name__ == "__main__": # fix the shutter speed cam.shutter_speed = cam.exposure_speed - logging.info( - "Current a/d gains: {}, {}".format(cam.analog_gain, cam.digital_gain) - ) + logging.info("Current a/d gains: {}, {}", cam.analog_gain, cam.digital_gain) logging.info("Attempting to set analogue gain to 1") set_analog_gain(cam, 1) logging.info("Attempting to set digital gain to 1") set_digital_gain(cam, 1) - # The old code is left in here in case it is a useful example... - # ret = mmal.mmal_port_parameter_set_rational(cam._camera.control._port, - # MMAL_PARAMETER_DIGITAL_GAIN, - # to_rational(1)) - # print("Return code: {}".format(ret)) try: while True: logging.info( - "Current a/d gains: {}, {}".format( - cam.analog_gain, cam.digital_gain - ) + "Current a/d gains: {}, {}", cam.analog_gain, cam.digital_gain ) time.sleep(1) except KeyboardInterrupt: diff --git a/openflexure_microscope/captures/capture.py b/openflexure_microscope/captures/capture.py index 8f75bf6d..131f84ef 100644 --- a/openflexure_microscope/captures/capture.py +++ b/openflexure_microscope/captures/capture.py @@ -25,23 +25,23 @@ def make_file_list(directory, formats): glob.glob("{}/**/*.{}".format(directory, fmt.lower()), recursive=True) ) - logging.info("{} capture files found on disk".format(len(files))) + logging.info("{} capture files found on disk", (len(files))) return files def build_captures_from_exif(capture_path): - logging.debug("Reloading captures from {}...".format(capture_path)) + logging.debug("Reloading captures from {}...", (capture_path)) files = make_file_list(capture_path, EXIF_FORMATS) captures = OrderedDict() for f in files: - logging.debug("Reloading capture {}...".format(f)) + logging.debug("Reloading capture {}...", (f)) capture = capture_from_path(f) if capture: captures[capture.id] = capture - logging.info("{} capture files successfully reloaded".format(len(captures))) + logging.info("{} capture files successfully reloaded", (len(captures))) return captures @@ -58,7 +58,7 @@ def capture_from_path(path): capture.sync_basic_metadata() return capture except (InvalidImageDataError, json.decoder.JSONDecodeError): - logging.error("Invalid metadata at {}.".format(path)) + logging.error("Invalid metadata at {}.", (path)) return None @@ -75,7 +75,7 @@ class CaptureObject(object): # Store a nice ID self.id = uuid.uuid4() #: str: Unique capture ID - logging.debug("Created CaptureObject {}".format(self.id)) + logging.debug("Created CaptureObject {}", (self.id)) self.time = datetime.datetime.now() @@ -97,17 +97,17 @@ class CaptureObject(object): self.tags = [] def write(self, s): - logging.debug("Writing to %s", self) + logging.debug("Writing to {}", self) self.stream.write(s) def flush(self): - logging.info("Writing image data to disk %s", self.file) + logging.info("Writing image data to disk {}", self.file) with open(self.file, "wb") as outfile: outfile.write(self.stream.getbuffer()) self.stream.close() - logging.info("Writing metadata to disk %s", self.file) + logging.info("Writing metadata to disk {}", self.file) self._init_metadata() - logging.info("Finished writing to disk %s", self.file) + logging.info("Finished writing to disk {}", self.file) def open(self, mode): return open(self.file, mode) @@ -175,7 +175,7 @@ class CaptureObject(object): } def read_full_metadata(self): - logging.info("Reading full capture metadata from %s...", self.file) + logging.info("Reading full capture metadata from {}...", self.file) exif_dict = self._read_exif() return self._decode_usercomment(exif_dict) @@ -271,7 +271,7 @@ class CaptureObject(object): # Write new data to file EXIF, if supported if self.format.upper() in EXIF_FORMATS and self.exists: - logging.info("Writing Exif data to %s", self.file) + logging.info("Writing Exif data to {}", self.file) # Extract current Exif data exif_dict = self._read_exif() @@ -286,7 +286,7 @@ class CaptureObject(object): # Serialize metadata metadata_string = json.dumps(metadata_dict, cls=JSONEncoder) - logging.debug("Saving metadata string to file: %s", metadata_string) + logging.debug("Saving metadata string to file: {}", metadata_string) # Insert metadata into exif_dict exif_dict["Exif"][piexif.ExifIFD.UserComment] = metadata_string.encode() @@ -296,7 +296,7 @@ class CaptureObject(object): # Insert exif into file piexif.insert(exif_bytes, self.file) - logging.info("Finished writing Exif data to %s", self.file) + logging.info("Finished writing Exif data to {}", self.file) # PROPERTIES @@ -316,7 +316,7 @@ class CaptureObject(object): """ if self.exists: # If data file exists - logging.info("Opening from file {}".format(self.file)) + logging.info("Opening from file {}", (self.file)) with open(self.file, "rb") as f: d = io.BytesIO(f.read()) # Load bytes from file d.seek(0) # Rewind loaded bytestream @@ -364,7 +364,7 @@ class CaptureObject(object): """If the StreamObject has been saved, delete the file.""" if os.path.isfile(self.file): - logging.info("Deleting file {}".format(self.file)) + logging.info("Deleting file {}", (self.file)) os.remove(self.file) return True diff --git a/openflexure_microscope/captures/capture_manager.py b/openflexure_microscope/captures/capture_manager.py index e5e290eb..4b435016 100644 --- a/openflexure_microscope/captures/capture_manager.py +++ b/openflexure_microscope/captures/capture_manager.py @@ -61,7 +61,7 @@ class CaptureManager: self.close() def close(self): - logging.info("Closing {}".format(self)) + logging.info("Closing {}", (self)) # Close all StreamObjects for capture_list in [self.images.values(), self.videos.values()]: for stream_object in capture_list: @@ -75,9 +75,9 @@ class CaptureManager: """ if os.path.isdir(self.paths["temp"]): - logging.info("Clearing {}...".format(self.paths["temp"])) + logging.info("Clearing {}...", (self.paths["temp"])) shutil.rmtree(self.paths["temp"]) - logging.debug("Cleared {}.".format(self.paths["temp"])) + logging.debug("Cleared {}.", (self.paths["temp"])) def rebuild_captures(self): self.images = build_captures_from_exif(self.paths["default"]) @@ -158,7 +158,7 @@ class CaptureManager: # Update capture list capture_key = str(output.id) - logging.debug("Adding image %s with key %s", output, capture_key) + logging.debug("Adding image {} with key {}", output, capture_key) self.images[capture_key] = output @@ -205,7 +205,7 @@ class CaptureManager: # Update capture list capture_key = str(output.id) - logging.debug("Adding video %s with key %s", output, capture_key) + logging.debug("Adding video {} with key {}", output, capture_key) self.videos[capture_key] = output return output diff --git a/openflexure_microscope/config.py b/openflexure_microscope/config.py index f4ff2cdb..f13cab2c 100644 --- a/openflexure_microscope/config.py +++ b/openflexure_microscope/config.py @@ -86,7 +86,7 @@ def load_json_file(config_path) -> dict: """ config_path = os.path.expanduser(config_path) - logging.info("Loading {}...".format(config_path)) + logging.info("Loading {}...", config_path) with open(config_path) as config_file: try: @@ -109,7 +109,7 @@ def save_json_file(config_path: str, config_dict: dict): """ config_path = os.path.expanduser(config_path) - logging.info("Saving {}...".format(config_path)) + logging.info("Saving {}...", config_path) logging.debug(config_dict) with open(config_path, "w") as outfile: @@ -142,14 +142,14 @@ def initialise_file(config_path, populate: str = "{}\n"): """ config_path = os.path.expanduser(config_path) - logging.debug("Initialising {}".format(config_path)) - logging.debug("Exists: {}".format(os.path.exists(config_path))) + logging.debug("Initialising {}", (config_path)) + logging.debug("Exists: {}", (os.path.exists(config_path))) if not os.path.exists(config_path): # If user config file doesn't exist - logging.warning("No config file found at {}. Creating...".format(config_path)) + logging.warning("No config file found at {}. Creating...", (config_path)) create_file(config_path) - logging.info("Populating {}...".format(config_path)) + logging.info("Populating {}...", (config_path)) with open(config_path, "w") as outfile: outfile.write(populate) diff --git a/openflexure_microscope/microscope.py b/openflexure_microscope/microscope.py index b9b398e1..bfb22ae1 100644 --- a/openflexure_microscope/microscope.py +++ b/openflexure_microscope/microscope.py @@ -71,7 +71,7 @@ class Microscope: def close(self): """Shut down the microscope hardware.""" - logging.info("Closing {}".format(self)) + logging.info("Closing {}", (self)) if self.camera: try: self.camera.close() @@ -83,7 +83,7 @@ class Microscope: except TimeoutError as e: logging.error(e) self.captures.close() - logging.info("Closed {}".format(self)) + logging.info("Closed {}", (self)) def setup(self, configuration): """ @@ -196,7 +196,7 @@ class Microscope: Applies a settings dictionary to the microscope. Missing parameters will be left untouched. """ with self.lock: - logging.debug("Microscope: Applying settings: {}".format(settings)) + logging.debug("Microscope: Applying settings: {}", (settings)) # If attached to a camera if ("camera" in settings) and self.camera: @@ -329,14 +329,14 @@ class Microscope: # Load cached bits of metadata if cache_key: - logging.debug("Reading cached microscope metadata: %s", cache_key) + logging.debug("Reading cached microscope metadata: {}", cache_key) metadata = self.metadata_cache.get(cache_key, None) if not metadata: - logging.debug("Building and caching microscope metadata: %s", cache_key) + logging.debug("Building and caching microscope metadata: {}", cache_key) metadata = self.force_get_metadata() self.metadata_cache[cache_key] = metadata else: - logging.debug("Building microscope metadata: %s", cache_key) + logging.debug("Building microscope metadata: {}", cache_key) metadata = self.force_get_metadata() # Keys that should never be cached @@ -362,7 +362,7 @@ class Microscope: metadata: dict = None, cache_key: str = None, ): - logging.debug("Microscope capturing to %s", filename) + logging.debug("Microscope capturing to {}", filename) if not annotations: annotations = {} if not metadata: @@ -384,7 +384,7 @@ class Microscope: extras = {} if fmt == "jpeg": extras["thumbnail"] = (*THUMBNAIL_SIZE, 85) - logging.info("Starting microscope capture %s", output.file) + logging.info("Starting microscope capture {}", output.file) self.camera.capture( output, use_video_port=use_video_port, @@ -395,6 +395,6 @@ class Microscope: ) output.put_and_save(tags, annotations, full_metadata) - logging.debug("Finished capture to %s", output.file) + logging.debug("Finished capture to {}", output.file) return output diff --git a/openflexure_microscope/rescue/check_capture_reload.py b/openflexure_microscope/rescue/check_capture_reload.py index e50ae05b..843bc3c2 100644 --- a/openflexure_microscope/rescue/check_capture_reload.py +++ b/openflexure_microscope/rescue/check_capture_reload.py @@ -27,7 +27,7 @@ def main(): logging.info("Loading user settings...") settings = user_settings.load() cap_path = str(settings.get("captures", {}).get("paths", {}).get("default")) - logging.info("Capture path found: %s", cap_path) + logging.info("Capture path found: {}", cap_path) if not cap_path: logging.error( diff --git a/openflexure_microscope/rescue/monitor_timeout.py b/openflexure_microscope/rescue/monitor_timeout.py index 49af1169..879f6063 100644 --- a/openflexure_microscope/rescue/monitor_timeout.py +++ b/openflexure_microscope/rescue/monitor_timeout.py @@ -24,7 +24,7 @@ def launch_timeout_test_process(target, args=(), kwargs=None, timeout=10): # If thread is still active if p.is_alive(): logging.error( - "Function %s reached timeout after %s seconds. Terminating.", + "Function {} reached timeout after {} seconds. Terminating.", target, timeout, ) diff --git a/openflexure_microscope/stage/mock.py b/openflexure_microscope/stage/mock.py index 9e9eb20f..6d6dd856 100644 --- a/openflexure_microscope/stage/mock.py +++ b/openflexure_microscope/stage/mock.py @@ -80,13 +80,13 @@ class MissingStage(BaseStage): self._position = list(np.array(self._position) + np.array(initial_move)) logging.debug(np.array(self._position) + np.array(initial_move)) - logging.debug("New position: %s", self._position) + logging.debug("New position: {}", self._position) def move_abs(self, final, **kwargs): time.sleep(0.5) self._position = list(final) - logging.debug("New position: %s", self._position) + logging.debug("New position: {}", self._position) def zero_position(self): """Set the current position to zero""" diff --git a/openflexure_microscope/stage/sanga.py b/openflexure_microscope/stage/sanga.py index 549ab5b9..3389d83d 100644 --- a/openflexure_microscope/stage/sanga.py +++ b/openflexure_microscope/stage/sanga.py @@ -81,7 +81,7 @@ class SangaStage(BaseStage): @backlash.setter def backlash(self, blsh): - logging.debug("Setting backlash to {}".format(blsh)) + logging.debug("Setting backlash to {}", (blsh)) if blsh is None: self._backlash = None elif isinstance(blsh, Iterable): @@ -118,7 +118,7 @@ class SangaStage(BaseStage): backlash: (default: True) whether to correct for backlash. """ with self.lock: - logging.debug("Moving sangaboard by %s", displacement) + logging.debug("Moving sangaboard by {}", displacement) if not backlash or self.backlash is None: return self.board.move_rel(displacement, axis=axis) if axis is not None: @@ -158,7 +158,7 @@ class SangaStage(BaseStage): """Make an absolute move to a position """ with self.lock: - logging.debug("Moving sangaboard to %s", final) + logging.debug("Moving sangaboard to {}", final) self.board.move_abs(final, **kwargs) # Settle outside of the stage lock so that another move request # can just take over before settling @@ -262,7 +262,7 @@ class SangaDeltaStage(SangaStage): # Transform into delta coordinates displacement = np.dot(self.Tdv, displacement) - logging.debug("Delta displacement: {}".format(displacement)) + logging.debug("Delta displacement: {}", (displacement)) # Do the move SangaStage.move_rel(self, displacement, axis=None, backlash=backlash) @@ -274,7 +274,7 @@ class SangaDeltaStage(SangaStage): # Transform into delta coordinates final = np.dot(self.Tdv, final) - logging.debug("Delta final: {}".format(final)) + logging.debug("Delta final: {}", (final)) # Do the move SangaStage.move_abs(self, final, **kwargs) diff --git a/openflexure_microscope/utilities.py b/openflexure_microscope/utilities.py index e0756303..4ded53c7 100644 --- a/openflexure_microscope/utilities.py +++ b/openflexure_microscope/utilities.py @@ -22,7 +22,7 @@ class Timer(object): def __exit__(self, type_, value, traceback): self.end = time.time() - logging.debug("%s time: %s", self.name, self.end - self.start) + logging.debug("{} time: {}", self.name, self.end - self.start) def deserialise_array_b64(b64_string, dtype, shape): diff --git a/tests/api_client.py b/tests/api_client.py deleted file mode 100644 index 43ad4942..00000000 --- a/tests/api_client.py +++ /dev/null @@ -1,101 +0,0 @@ -from io import BytesIO - -import numpy as np -import requests -from PIL import Image - - -class APIconnection: - def __init__(self, host="localhost", port=5000, api_ver="v1"): - self.base = self.build_base(host=host, port=port, api_ver=api_ver) - - def build_base(self, host, port, api_ver): - return "http://{}:{}/api/{}".format(host, port, api_ver) - - def uri(self, suffix): - return self.base + suffix - - def get(self, route, json=True, timeout=5): - r = requests.get(self.uri(route), timeout=timeout) - if json: - return r.json() - else: - return r - - def post(self, route, json=None, timeout=5): - r = requests.post(self.uri(route), json=json, timeout=timeout) - return r.json() - - def delete(self, route, timeout=5): - r = requests.delete(self.uri(route), timeout=timeout) - return r.json() - - def set_overlay(self, message="", size=50): - json = {"text": message, "size": size} - return self.post("/camera/overlay", json=json) - - def get_overlay(self): - return self.get("/camera/overlay") - - def get_config(self): - return self.get("/config") - - def set_config(self, config_dict): - return self.post("/config", json=config_dict) - - def get_state(self): - return self.get("/state") - - def start_preview(self): - return self.post("/camera/preview/start") - - def stop_preview(self): - return self.post("/camera/preview/stop") - - def move_by(self, x=0, y=0, z=0): - json = {"x": x, "y": y, "z": z} - return self.post("/stage/position", json=json) - - def new_capture(self, use_video_port=True, keep_on_disk=False, resize=None): - json = {"keep_on_disk": keep_on_disk, "use_video_port": use_video_port} - - if resize: - json["size"] = {"width": resize[0], "height": resize[1]} - - return self.post("/camera/capture", json=json) - - def get_capture(self, capture_id): - uri_route = "/camera/capture/{}/download".format(capture_id) - r = self.get(uri_route, json=False) - img = Image.open(BytesIO(r.content)) - array = np.asarray(img, dtype=np.int32) - return array - - def del_capture(self, capture_id): - uri_route = "/camera/capture/{}".format(capture_id) - return self.delete(uri_route) - - def capture( - self, - use_video_port=True, - keep_on_disk=False, - delete_after_use=True, - resize=None, - ): - p = self.new_capture( - use_video_port=use_video_port, keep_on_disk=keep_on_disk, resize=resize - ) - capture_id = p["metadata"]["id"] - img_array = self.get_capture(capture_id) - - if delete_after_use: - self.del_capture(capture_id) - - return img_array - - def set_zoom(self, zoom_value=1.0): - json = {"zoom_value": zoom_value} - return self.post("/camera/zoom", json=json) - - def get_zoom(self): - return self.get("/camera/zoom") diff --git a/tests/test_api.py b/tests/test_api.py deleted file mode 100644 index c7b38e35..00000000 --- a/tests/test_api.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env python -import logging -import sys -import unittest - -import numpy as np -from api_client import APIconnection - -logging.basicConfig(stream=sys.stderr, level=logging.INFO) - - -class TestCapture(unittest.TestCase): - def test_capture_config(self): - connection = APIconnection(host="localhost", port=5000, api_ver="v1") - config = connection.get_config() - - expected_keys = ["image_resolution", "stream_resolution", "numpy_resolution"] - - for key in expected_keys: - self.assertTrue(key in config) - - def test_capture_videoport(self): - connection = APIconnection(host="localhost", port=5000, api_ver="v1") - resolution = connection.get_config()["stream_resolution"] - - for resize in [None, (640, 480)]: - - if resize: - resolution = resize - - capture_array = connection.capture( - use_video_port=True, - keep_on_disk=False, - delete_after_use=True, - resize=resize, - ) - - self.assertTrue(capture_array.shape == (resolution[1], resolution[0], 3)) - - def test_capture_full(self): - connection = APIconnection(host="localhost", port=5000, api_ver="v1") - resolution = connection.get_config()["image_resolution"] - - for resize in [None, (640, 480)]: - - if resize: - resolution = resize - - capture_array = connection.capture( - use_video_port=False, - keep_on_disk=False, - delete_after_use=True, - resize=resize, - ) - - self.assertTrue(capture_array.shape == (resolution[1], resolution[0], 3)) - - -class TestStage(unittest.TestCase): - def test_stage_config(self): - connection = APIconnection(host="localhost", port=5000, api_ver="v1") - config = connection.get_config() - - expected_keys = ["backlash"] - - for key in expected_keys: - self.assertTrue(key in config) - - def test_stage_state(self): - connection = APIconnection(host="localhost", port=5000, api_ver="v1") - state = connection.get_state() - - self.assertTrue("stage" in state) - - expected_keys = ["position"] - - for key in expected_keys: - self.assertTrue(key in state["stage"]) - - def test_stage_movement(self): - connection = APIconnection(host="localhost", port=5000, api_ver="v1") - - move_distance = 500 - for axis in range(3): - for direction in [1, -1]: - pos_i_dict = connection.get_state()["stage"]["position"] - pos_i = [pos_i_dict["x"], pos_i_dict["y"], pos_i_dict["z"]] - - move = [0, 0, 0] - move[axis] = move_distance * direction - - connection.move_by(*move) - - pos_f_dict = connection.get_state()["stage"]["position"] - pos_f = [pos_f_dict["x"], pos_f_dict["y"], pos_f_dict["z"]] - - diff = np.subtract(pos_f, pos_i) - logging.debug("{} > {}".format(pos_i, pos_f)) - - self.assertTrue(np.array_equal(diff, move)) - - -if __name__ == "__main__": - - suites = [ - unittest.TestLoader().loadTestsFromTestCase(TestCapture), - unittest.TestLoader().loadTestsFromTestCase(TestStage), - ] - - alltests = unittest.TestSuite(suites) - - result = unittest.TextTestRunner(verbosity=2).run(alltests) diff --git a/tests/test_camera.py b/tests/test_camera.py deleted file mode 100644 index 1d2723fb..00000000 --- a/tests/test_camera.py +++ /dev/null @@ -1,266 +0,0 @@ -#!/usr/bin/env python -import io -import logging -import os -import sys -import time -import unittest - -import numpy as np -from PIL import Image - -from openflexure_microscope.camera.pi import CaptureObject, PiCameraStreamer - -logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) - - -class TestCaptureMethods(unittest.TestCase): - def test_still_capture(self): - """Tests capturing still images to a BytesIO stream.""" - global camera - - for use_video_port in [True, False]: - for resize in [None, (640, 480)]: - - # Wait for camera - camera.wait_for_camera() - - # Capture to a context (auto-deletes files when done) - with camera.new_image() as output: - - camera.capture(output, use_video_port=use_video_port, resize=resize) - - # Ensure file deletion fails and returns False - self.assertFalse(output.delete_file()) - # Ensure capture not stored to file - self.assertFalse(os.path.isfile(output.file)) - - # BEFORE DELETE: Ensure StreamObject 'stream' has - # a valid BytesIO object and byte string - self.assertTrue(isinstance(output.data, io.IOBase)) - self.assertTrue(isinstance(output.binary, (bytes, bytearray))) - - # Save capture to file - output.save_file() - # Check file got saved - self.assertTrue(os.path.isfile(output.file)) - - # Delete file - output.delete_file() - # Check file got deleted - self.assertFalse(os.path.isfile(output.file)) - - # AFTER DELETE: Ensure StreamObject 'stream' has - # a valid BytesIO object and byte string - self.assertTrue(isinstance(output.data, io.IOBase)) - self.assertTrue(isinstance(output.binary, (bytes, bytearray))) - - # Create a PIL image from stream - image = Image.open(output.data) - - # Ensure a valid PIL image was created - self.assertTrue(isinstance(image, Image.Image)) - - # Calculate expected dimensions - if resize: - dims = resize - else: - if use_video_port: - dims = camera.stream_resolution - else: - dims = camera.image_resolution - - # Ensure PIL image size matches expected size - print(image.size, dims) - self.assertTrue(image.size == dims) - - def test_still_store(self): - """Tests capturing still images to a file on disk.""" - global camera - - for use_video_port in [True, False]: - for resize in [None, (640, 480)]: - # Wait for camera - camera.wait_for_camera() - - # Capture - output = camera.capture( - camera.new_image().file, - use_video_port=use_video_port, - resize=resize, - ) - - # Check file got saved - self.assertTrue(os.path.isfile(output.file)) - statinfo = os.stat(output.file) - self.assertTrue(statinfo.st_size > 0) - - # Ensure StreamObject 'stream' has - # a valid BytesIO object and byte string - self.assertTrue(isinstance(output.data, io.IOBase)) - self.assertTrue(isinstance(output.binary, (bytes, bytearray))) - - # Ensure file deletion completes and returns True - self.assertTrue(output.delete_file()) - - # Check file got deleted - self.assertFalse(os.path.isfile(output.file)) - - -class TestUnencodedMethods(unittest.TestCase): - def test_yuv_array(self): - """Tests capturing unencoded YUV data to a Numpy array.""" - global camera - - for use_video_port in [True, False]: - for resize in [None, (640, 480)]: - - print("{}, {}, {}".format(id, resize, use_video_port)) - - # Wait for camera - camera.wait_for_camera() - - # Capture RGB array - yuv = camera.yuv(use_video_port=use_video_port, resize=resize) - - # Ensure capture output is a valid numpy array - self.assertTrue(isinstance(yuv, np.ndarray)) - - # Calculate expected dimensions - if resize: - dims = resize - else: - if use_video_port: - dims = camera.stream_resolution - else: - dims = camera.numpy_resolution - - # Ensure array shape matches expected dimensions - self.assertTrue(yuv.shape == (dims[1], dims[0], 3)) - - def test_rgb_array(self): - """Tests capturing unencoded YUV/RGB data to a Numpy array.""" - global camera - - for use_video_port in [True, False]: - for resize in [None, (640, 480)]: - - print("{}, {}, {}".format(id, resize, use_video_port)) - - # Wait for camera - camera.wait_for_camera() - - # Capture RGB array - rgb = camera.array(use_video_port=use_video_port, resize=resize) - - # Ensure capture output is a valid numpy array - self.assertTrue(isinstance(rgb, np.ndarray)) - - # Calculate expected dimensions - if resize: - dims = resize - else: - if use_video_port: - dims = camera.stream_resolution - else: - dims = camera.numpy_resolution - - # Ensure array shape matches expected dimensions - self.assertTrue(rgb.shape == (dims[1], dims[0], 3)) - - -class TestRecordMethods(unittest.TestCase): - def test_video_record(self): - """Tests recording videos to BytesIO stream, and to file on disk.""" - global camera - - # Wait for camera - camera.wait_for_camera() - - with camera.new_video() as output: - - # Start recording - camera.start_recording(output) - - # Record for 2 seconds - time.sleep(2) - # Stop recording - camera.stop_recording() - - # Check stream - self.assertTrue(isinstance(output.data, io.IOBase)) - self.assertTrue(isinstance(output.binary, (bytes, bytearray))) - - # Check file - statinfo = os.stat(output.file) - self.assertTrue(statinfo.st_size > 0) - - # Log path - temp_path = output.file - - # Check file got deleted on __exit__ - self.assertFalse(os.path.isfile(temp_path)) - - time.sleep(0.25) - - def test_video_store(self): - """Tests recording videos to file on disk, without context manager.""" - global camera - - # Wait for camera - camera.wait_for_camera() - - # Start recording - output = camera.start_recording(camera.new_video()) - - # Record for 2 seconds - time.sleep(2) - # Stop recording - camera.stop_recording() - - # Check file - statinfo = os.stat(output.file) - self.assertTrue(statinfo.st_size > 0) - - # Ensure file deletion completes and returns True - self.assertTrue(output.delete_file()) - - # Check file got deleted - self.assertFalse(os.path.isfile(output.file)) - - -class TestThreadStarting(unittest.TestCase): - def test_restarting_stream(self): - """Tests that a capture call restarts the camera worker thread.""" - global camera - - # Wait for camera - camera.wait_for_camera() - - # Force-stop the camera worker thread (should return True) - self.assertTrue(camera.stop_worker()) - - # Check Pi camera has been disconnected - self.assertTrue(camera.camera) - - # Restart worker thread - self.assertTrue(camera.start_worker()) - - self.assertTrue(camera.camera) - self.assertTrue(camera.thread) - self.assertIsInstance(camera.stream, io.IOBase) - - -if __name__ == "__main__": - with PiCameraStreamer() as camera: - - suites = [ - unittest.TestLoader().loadTestsFromTestCase(TestCaptureMethods), - unittest.TestLoader().loadTestsFromTestCase(TestUnencodedMethods), - unittest.TestLoader().loadTestsFromTestCase(TestThreadStarting), - unittest.TestLoader().loadTestsFromTestCase(TestRecordMethods), - ] - - alltests = unittest.TestSuite(suites) - - result = unittest.TextTestRunner(verbosity=2).run(alltests) diff --git a/tests/test_plugins.py b/tests/test_plugins.py deleted file mode 100644 index 431e70a8..00000000 --- a/tests/test_plugins.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python -import atexit -import logging -import sys -import unittest - -from openflexure_stage import OpenFlexureStage - -from openflexure_microscope import Microscope, config -from openflexure_microscope.camera.pi import PiCameraStreamer - -logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) - - -class TestPluginMethods(unittest.TestCase): - def test_plugin_load(self): - plugin_arr = microscope.plugins.plugins - - plugin_names = [plugin[0] for plugin in plugin_arr] - - self.assertTrue("testing" in plugin_names) - - def test_camera_access(self): - identify = microscope.plugins.testing.identify() - self.assertTrue(identify[0] is microscope.camera) - - def test_stage_access(self): - identify = microscope.plugins.testing.identify() - self.assertTrue(identify[1] is microscope.stage) - - -if __name__ == "__main__": - - with Microscope() as microscope: - - microscope.attach(PiCameraStreamer(), OpenFlexureStage()) - - microscope.plugins.attach("openflexure_microscope.plugins.testing:Plugin") - - suites = [unittest.TestLoader().loadTestsFromTestCase(TestPluginMethods)] - - alltests = unittest.TestSuite(suites) - - result = unittest.TextTestRunner(verbosity=2).run(alltests) diff --git a/tests/test_stage.py b/tests/test_stage.py deleted file mode 100644 index 90ee10d4..00000000 --- a/tests/test_stage.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python -import logging -import sys -import unittest - -import numpy as np -from openflexure_stage import OpenFlexureStage - -logging.basicConfig(stream=sys.stderr, level=logging.INFO) - - -class TestMicroscope(unittest.TestCase): - def test_movement(self): - move_distance = 500 - for axis in range(3): - for direction in [1, -1]: - pos_i = stage.position - logging.debug(pos_i) - - logging.info( - "Moving axis {} by {}".format(axis, move_distance * direction) - ) - move = [0, 0, 0] - move[axis] = move_distance * direction - - stage.move_rel(move) - - pos_f = stage.position - diff = np.subtract(pos_f, pos_i) - logging.debug("{} > {}".format(pos_i, pos_f)) - - self.assertTrue(np.array_equal(diff, move)) - - -if __name__ == "__main__": - with OpenFlexureStage("/dev/ttyUSB0") as stage: - - suites = [unittest.TestLoader().loadTestsFromTestCase(TestMicroscope)] - - alltests = unittest.TestSuite(suites) - - result = unittest.TextTestRunner(verbosity=2).run(alltests)