Code cleanup

This commit is contained in:
Joel Collins 2020-10-14 14:56:29 +00:00
parent 2bfb988460
commit 994e83dbeb
46 changed files with 261 additions and 318 deletions

View file

@ -1,4 +1,5 @@
stages:
- analysis
- build
- deploy
@ -9,6 +10,45 @@ cache:
paths:
- node_modules/
pylint:
stage: analysis
image: python:3.7
allow_failure: true
before_script:
- curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
script:
# Install server
- $HOME/.poetry/bin/poetry install
# Run static build script
- $HOME/.poetry/bin/poetry run pylint ./openflexure_microscope/
only:
- branches
- merge_requests
- tags
- web
black:
stage: analysis
image: python:3.7
allow_failure: true
before_script:
- curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
script:
# Install server
- $HOME/.poetry/bin/poetry install
# Run static build script
- $HOME/.poetry/bin/poetry run black --check .
only:
- branches
- merge_requests
- tags
- web
# Electron app build
build:

View file

@ -21,7 +21,7 @@ import os
from datetime import datetime
import pkg_resources
from flask import Flask, abort, send_file
from flask import abort, send_file
from flask_cors import CORS, cross_origin
from labthings import create_app
from labthings.extensions import find_extensions
@ -34,7 +34,6 @@ from openflexure_microscope.paths import (
OPENFLEXURE_EXTENSIONS_PATH,
OPENFLEXURE_VAR_PATH,
logs_file_path,
settings_file_path,
)
# Handle logging
@ -72,7 +71,7 @@ root_log.addHandler(fh)
access_log.addHandler(afh)
# Log server paths being used
logging.info(f"Running with data path {OPENFLEXURE_VAR_PATH}")
logging.info("Running with data path %s", OPENFLEXURE_VAR_PATH)
logging.info("Creating app")
# Create flask app
@ -103,16 +102,16 @@ for extension in find_extensions(OPENFLEXURE_EXTENSIONS_PATH):
labthing.register_extension(extension)
# Attach captures resources
labthing.add_view(views.CaptureList, f"/captures")
labthing.add_view(views.CaptureList, "/captures")
labthing.add_root_link(views.CaptureList, "captures")
labthing.add_view(views.CaptureView, f"/captures/<id>")
labthing.add_view(views.CaptureDownload, f"/captures/<id>/download/<filename>")
labthing.add_view(views.CaptureTags, f"/captures/<id>/tags")
labthing.add_view(views.CaptureAnnotations, f"/captures/<id>/annotations")
labthing.add_view(views.CaptureView, "/captures/<id>")
labthing.add_view(views.CaptureDownload, "/captures/<id>/download/<filename>")
labthing.add_view(views.CaptureTags, "/captures/<id>/tags")
labthing.add_view(views.CaptureAnnotations, "/captures/<id>/annotations")
# Attach settings and state resources
labthing.add_view(views.SettingsProperty, f"/instrument/settings")
labthing.add_view(views.SettingsProperty, "/instrument/settings")
labthing.add_root_link(views.SettingsProperty, "instrumentSettings")
labthing.add_view(views.NestedSettingsProperty, "/instrument/settings/<path:route>")
labthing.add_view(views.StateProperty, "/instrument/state")
@ -129,8 +128,8 @@ labthing.add_view(views.StageTypeProperty, "/instrument/stage/type")
# Attach streams resources
labthing.add_view(views.MjpegStream, f"/streams/mjpeg")
labthing.add_view(views.SnapshotStream, f"/streams/snapshot")
labthing.add_view(views.MjpegStream, "/streams/mjpeg")
labthing.add_view(views.SnapshotStream, "/streams/snapshot")
# Attach microscope action resources
labthing.add_view(views.actions.ActionsView, "/actions")
@ -170,7 +169,7 @@ def err_log():
@app.route("/api/v1/", defaults={"path": ""})
@app.route("/api/v1/<path:path>")
def api_v1_catch_all(path):
def api_v1_catch_all(path): # pylint: disable=W0613
abort(410, "API v1 is no longer in use. Please upgrade your client.")

View file

@ -8,9 +8,11 @@ def handle_extension_error(extension_name):
"""'gracefully' log an error if an extension fails to load."""
try:
yield
except Exception as e:
except Exception: # pylint: disable=W0703
logging.error(
f"Exception loading builtin extension {extension_name}: \n{traceback.format_exc()}"
"Exception loading builtin extension %s: \n%s",
extension_name,
traceback.format_exc(),
)

View file

@ -6,10 +6,10 @@ from threading import Event, Thread
import numpy as np
from labthings import current_action, fields, find_component
from labthings.extensions import BaseExtension
from labthings.views import ActionView, PropertyView, View
from labthings.views import ActionView, View
from scipy import ndimage
from openflexure_microscope.devel import JsonResponse, abort, request
from openflexure_microscope.devel import abort
from openflexure_microscope.utilities import set_properties
### Autofocus utilities
@ -36,19 +36,15 @@ class JPEGSharpnessMonitor:
return self.background_thread.is_alive()
def should_stop(self):
import time
return time.time() - self.kept_alive > self.timeout
def keep_alive(self):
import time
self.kept_alive = time.time()
def start(self):
"Start monitoring sharpness by looking at JPEG size"
if not self.camera.stream_active:
logging.warn(
logging.warning(
"Autofocus sharpness monitor was started but the camera isn't streaming. Attempting to start the stream..."
)
self.camera.start_stream_recording()
@ -66,7 +62,7 @@ class JPEGSharpnessMonitor:
"Function that runs in a background thread to record sharpness"
logging.debug("Starting sharpness measurement in background thread")
self.keep_alive()
logging.debug(f"_measure_jpegs stop_event: {self.stop_event.is_set()}")
logging.debug("_measure_jpegs stop_event: %s", self.stop_event.is_set())
while not self.stop_event.is_set() and not self.should_stop():
size_now = self.jpeg_size()
time_now = time.time()
@ -107,7 +103,7 @@ class JPEGSharpnessMonitor:
if np.sum(jpeg_times > stage_times[0]) == 0:
raise ValueError(
"No images were captured during the move of the stage. Perhaps the camera is not streaming images?"
)
) from e
else:
raise e
if stop < 1:
@ -119,7 +115,7 @@ class JPEGSharpnessMonitor:
def sharpest_z_on_move(self, index):
"""Return the z position of the sharpest image on a given move"""
jt, jz, js = self.move_data(index)
_, jz, js = self.move_data(index)
if len(js) == 0:
raise ValueError(
"No images were captured during the move of the stage. Perhaps the camera is not streaming images?"
@ -226,7 +222,7 @@ def fast_autofocus(microscope, dz=2000, backlash=None):
"""Perform a down-up-down-up autofocus"""
with monitor_sharpness(
microscope
) as m, microscope.camera.lock, microscope.stage.lock:
) as m, microscope.camera.lock as _, microscope.stage.lock as _:
i, z = m.focus_rel(-dz / 2)
i, z = m.focus_rel(dz)
fz = m.sharpest_z_on_move(i)
@ -278,7 +274,7 @@ def fast_up_down_up_autofocus(
"""
with monitor_sharpness(
microscope
) as m, microscope.camera.lock, microscope.stage.lock:
) as m, microscope.camera.lock as _, microscope.stage.lock as _:
# Ensure the MJPEG stream has started
microscope.camera.start_stream_recording()
@ -291,12 +287,8 @@ def fast_up_down_up_autofocus(
i, z = m.focus_rel(-df)
# now inspect where the sharpest point is, and estimate the sharpness
# (JPEG size) that we should find at the start of the Z stack
logging.debug("Get target_s")
jt, jz, js = m.move_data(i)
_, jz, js = m.move_data(i)
best_z = jz[np.argmax(js)]
target_s = np.interp(
[best_z + target_z], jz[::-1], js[::-1]
) # NB jz is decreasing
# now move to the start of the z stack
logging.debug("Move to the start of the z stack")

View file

@ -1,6 +1,5 @@
import logging
import os
from sys import platform
import psutil
from flask import abort
@ -9,9 +8,7 @@ from labthings.extensions import BaseExtension
from labthings.views import PropertyView, View
from openflexure_microscope.api.utilities.gui import build_gui
from openflexure_microscope.captures.capture import build_captures_from_exif
from openflexure_microscope.captures.capture_manager import BASE_CAPTURE_PATH
from openflexure_microscope.config import OpenflexureSettingsFile
from openflexure_microscope.paths import check_rw, settings_file_path
AS_SETTINGS_PATH = settings_file_path("autostorage_settings.json")
@ -97,10 +94,10 @@ class AutostorageExtension(BaseExtension):
def on_microscope(self, microscope_obj):
"""Function to automatically call when the parent LabThing has a microscope attached."""
logging.debug(f"Autostorage extension found microscope {microscope_obj}")
logging.debug("Autostorage extension found microscope %s", microscope_obj)
if hasattr(microscope_obj, "captures"):
logging.debug(
f"Autostorage extension bound to CaptureManager {self.capture_manager}"
"Autostorage extension bound to CaptureManager %s", self.capture_manager
)
# Store a reference to the CaptureManager
@ -119,7 +116,8 @@ 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(
f"Preferred capture path {location} is missing or cannot be written to. Restoring defaults."
"Preferred capture path %s is missing or cannot be written to. Restoring defaults.",
location,
)
# Reset the storage location to default
set_current_location(self.capture_manager, get_default_location())
@ -187,8 +185,6 @@ autostorage_extension_v2 = AutostorageExtension()
class GetLocationsView(PropertyView):
def get(self):
global autostorage_extension_v2
autostorage_extension_v2.check_location()
return autostorage_extension_v2.get_locations()
@ -197,13 +193,10 @@ class PreferredLocationView(PropertyView):
schema = fields.String(required=True, example="Default")
def get(self):
global autostorage_extension_v2
autostorage_extension_v2.check_location()
return autostorage_extension_v2.get_preferred_key()
def post(self, new_path_key):
global autostorage_extension_v2
microscope = find_component("org.openflexure.microscope")
if not microscope:
@ -218,13 +211,11 @@ class PreferredLocationGUIView(View):
args = {"new_path_title": fields.String(required=True)}
def post(self, args):
global autostorage_extension_v2
new_path_title = args.get("new_path_title")
logging.debug(new_path_title)
new_path_key = autostorage_extension_v2.title_to_key(new_path_title)
logging.debug(f"{new_path_key}")
logging.debug(new_path_key)
autostorage_extension_v2.check_location()
autostorage_extension_v2.set_preferred_key(new_path_key)
@ -233,7 +224,6 @@ class PreferredLocationGUIView(View):
def dynamic_form():
global autostorage_extension_v2
autostorage_extension_v2.check_location()
return {
"icon": "sd_storage",

View file

@ -7,7 +7,7 @@ from typing import Tuple
from flask import abort
from labthings import find_component
from labthings.extensions import BaseExtension
from labthings.views import ActionView, View
from labthings.views import ActionView
from .recalibrate_utils import (
auto_expose_and_freeze_settings,
@ -78,17 +78,10 @@ class FlattenLSTView(ActionView):
"No microscope connected. Unable to flatten the lens shading table.",
)
try:
with pause_stream(microscope.camera) as scamera:
flat_lst = flat_lens_shading_table(scamera.camera)
scamera.camera.lens_shading_table = flat_lst
microscope.save_settings()
except:
logging.exception("Error flattening the lens shading table.")
abort(
503,
"Couldn't flatten the lens shading table - do you have the forked PiCamera library installed?",
)
with pause_stream(microscope.camera) as scamera:
flat_lst = flat_lens_shading_table(scamera.camera)
scamera.camera.lens_shading_table = flat_lst
microscope.save_settings()
class DeleteLSTView(ActionView):
@ -101,16 +94,9 @@ class DeleteLSTView(ActionView):
"No microscope connected. Unable to flatten the lens shading table.",
)
try:
with pause_stream(microscope.camera) as scamera:
scamera.camera.lens_shading_table = None
microscope.save_settings()
except:
logging.exception("Error deleting the lens shading table.")
abort(
503,
"Couldn't flatten the lens shading table - do you have the forked PiCamera library installed?",
)
with pause_stream(microscope.camera) as scamera:
scamera.camera.lens_shading_table = None
microscope.save_settings()
lst_extension_v2 = BaseExtension(

View file

@ -24,13 +24,15 @@ def flat_lens_shading_table(camera):
raise ImportError(
"This program requires the forked picamera library with lens shading support"
)
return np.zeros(camera._lens_shading_table_shape(), dtype=np.uint8) + 32
return (
np.zeros(camera._lens_shading_table_shape(), dtype=np.uint8) + 32
) # pylint: disable=W0212
def adjust_exposure_to_setpoint(camera, setpoint):
"""Adjust the camera's exposure time until the maximum pixel value is <setpoint>."""
print("Adjusting shutter speed to hit setpoint {}".format(setpoint), end="")
for i in range(3):
for _ in range(3):
print(".", end="")
camera.shutter_speed = int(
camera.shutter_speed * setpoint / np.max(rgb_image(camera))
@ -50,9 +52,9 @@ def auto_expose_and_freeze_settings(camera):
camera.awb_mode = "auto"
camera.exposure_mode = "auto"
camera.iso = (
0
) # This is important, if it's on a fixed ISO, gain might not set properly.
for i in range(6):
0 # This is important, if it's on a fixed ISO, gain might not set properly.
)
for _ in range(6):
print(".", end="")
time.sleep(0.5)
logging.info("done")
@ -188,10 +190,10 @@ def recalibrate_camera(camera):
if __name__ == "__main__":
with PiCamera() as camera:
camera.start_preview()
with PiCamera() as main_camera:
main_camera.start_preview()
time.sleep(3)
logging.info("Recalibrating...")
recalibrate_camera(camera)
recalibrate_camera(main_camera)
logging.info("Done.")
time.sleep(2)

View file

@ -14,7 +14,7 @@ from labthings import (
update_action_progress,
)
from labthings.extensions import BaseExtension
from labthings.views import ActionView, View
from labthings.views import ActionView
from openflexure_microscope.captures.capture_manager import generate_basename
from openflexure_microscope.devel import abort
@ -74,10 +74,13 @@ class ScanExtension(BaseExtension):
use_video_port: bool = False,
resize: Tuple[int, int] = None,
bayer: bool = False,
metadata: dict = {},
annotations: dict = {},
tags: list = [],
metadata: dict = None,
annotations: dict = None,
tags: list = None,
):
metadata = metadata or {}
annotations = annotations or {}
tags = tags or []
# Construct a tile filename
if namemode == "coordinates":
@ -117,18 +120,22 @@ class ScanExtension(BaseExtension):
basename: str = None,
namemode: str = "coordinates",
temporary: bool = False,
stride_size: int = [2000, 1500, 100],
grid: list = [3, 3, 5],
stride_size: list = (2000, 1500, 100),
grid: list = (3, 3, 5),
style="raster",
autofocus_dz: int = 50,
use_video_port: bool = False,
resize: Tuple[int, int] = None,
bayer: bool = False,
fast_autofocus=False,
metadata: dict = {},
annotations: dict = {},
tags: list = [],
metadata: dict = None,
annotations: dict = None,
tags: list = None,
):
metadata = metadata or {}
annotations = annotations or {}
tags = tags or []
start = time.time()
# Keep task progress
@ -210,7 +217,7 @@ class ScanExtension(BaseExtension):
# If we're not doing a z-stack, just capture
if grid[2] <= 1:
capture(
self.capture(
microscope,
basename,
namemode=namemode,
@ -250,7 +257,7 @@ class ScanExtension(BaseExtension):
microscope.stage.move_abs(initial_position)
end = time.time()
logging.info(f"Scan took {end - start} seconds")
logging.info("Scan took %s seconds", end - start)
def stack(
self,
@ -264,24 +271,27 @@ class ScanExtension(BaseExtension):
use_video_port: bool = False,
resize: Tuple[int, int] = None,
bayer: bool = False,
metadata: dict = {},
annotations: dict = {},
tags: list = [],
metadata: dict = None,
annotations: dict = None,
tags: list = None,
):
metadata = metadata or {}
annotations = annotations or {}
tags = tags or []
# Store initial position
initial_position = microscope.stage.position
logging.debug(f"Starting z-stack from position {microscope.stage.position}")
logging.debug("Starting z-stack from position %s", 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(f"Starting scan from position {microscope.stage.position}")
logging.debug("Starting scan from position %s", microscope.stage.position)
for i in range(steps):
time.sleep(0.1)
logging.debug(f"Capturing from position {microscope.stage.position}")
logging.debug("Capturing from position %s", microscope.stage.position)
self.capture(
microscope,
basename,

View file

@ -21,7 +21,7 @@ class ZipObjectSchema(Schema):
links = fields.Dict()
@pre_dump
def generate_links(self, data, **kwargs):
def generate_links(self, data, **_):
data.links = {
"download": {
"href": url_for(
@ -34,8 +34,8 @@ class ZipObjectSchema(Schema):
class ZipObjectDescription:
def __init__(self, id, file_pointer, data_size=None):
self.id = id
def __init__(self, id_, file_pointer, data_size=None):
self.id = id_
self.fp = file_pointer
self.data_size = data_size
self.zip_size = os.path.getsize(self.fp.name) * 1e-6
@ -155,7 +155,7 @@ class ZipGetterAPIView(View):
if not session_id in default_zip_manager.session_zips:
return abort(404) # 404 Not Found
logging.info(f"Session ID: {session_id}")
logging.info("Session ID: %s", session_id)
return send_file(
default_zip_manager.zip_fp_from_id(session_id).name,

View file

@ -17,7 +17,7 @@ class SleepFor(ActionView):
def post(self, args):
sleep_time = args.get("time")
logging.info(f"Going to sleep for {sleep_time}...")
logging.info("Going to sleep for %s...", sleep_time)
start = time.time()
time.sleep(sleep_time)
end = time.time()

View file

@ -116,7 +116,6 @@ def create_file(config_path):
def init_default_extensions(extension_dir):
global _DEFAULT_EXTENSION_INIT
os.makedirs(extension_dir, exist_ok=True)
default_ext_path = os.path.join(extension_dir, "defaults.py")

View file

@ -12,9 +12,10 @@ def clean_rule(rule: str):
def build_gui_from_dict(gui_description, extension_object):
# Make a working copy of GUI description
api_gui = copy.deepcopy(gui_description)
logging.debug(extension_object)
logging.debug(extension_object._rules)
# Grab the extensions rules dictionary
# NOTE: The LabThings extension object should probably have a non-private
# way to get the rules dictionary. For now we need to ignore pylint W0212
ext_rules = extension_object._rules # pylint: disable=W0212
# Expand shorthand routes into full relative URLs
if "forms" in gui_description and isinstance(api_gui["forms"], list):
@ -23,12 +24,10 @@ def build_gui_from_dict(gui_description, extension_object):
if "route" in form:
form["route"] = clean_rule(form["route"])
# Match rule in extension object
if "route" in form and form["route"] in extension_object._rules.keys():
form["route"] = extension_object._rules[form["route"]]["urls"][0]
if "route" in form and form["route"] in ext_rules.keys():
form["route"] = ext_rules[form["route"]]["urls"][0]
else:
logging.warn(
"No valid expandable route found for {}".format(form["route"])
)
logging.warning("No valid expandable route found for %s", form["route"])
# Inject extension information
api_gui["id"] = extension_object.name

View file

@ -53,7 +53,6 @@ _actions = {
def enabled_root_actions():
global _actions
return {k: v for k, v in _actions.items() if v["conditions"]}
@ -65,8 +64,6 @@ class ActionsView(View):
This list does not include any actions added by LabThings extensions, only
those part of the default OpenFlexure Microscope API.
"""
global _actions
actions = {}
for name, action in enabled_root_actions().items():
d = {

View file

@ -1,13 +1,11 @@
import io
import logging
from flask import abort, redirect, request, send_file, url_for
from flask import abort, send_file
from labthings import fields, find_component
from labthings.views import ActionView, View
from labthings.views import ActionView
from openflexure_microscope.api.utilities import JsonResponse, get_bool
from openflexure_microscope.api.v2.views.captures import CaptureSchema
from openflexure_microscope.utilities import filter_dict
class CaptureAPI(ActionView):

View file

@ -1,11 +1,9 @@
import logging
from flask import Blueprint, request
from labthings import fields, find_component
from labthings.views import ActionView, View
from labthings.views import ActionView
from openflexure_microscope.api.utilities import JsonResponse
from openflexure_microscope.utilities import axes_to_array, filter_dict
from openflexure_microscope.utilities import axes_to_array
class MoveStageAPI(ActionView):

View file

@ -1,11 +1,10 @@
import os
import subprocess
from sys import platform
from labthings.views import ActionView, View
from labthings.views import ActionView
def is_raspberrypi(raise_on_errors=False):
def is_raspberrypi():
"""
Checks if Raspberry Pi.
"""

View file

@ -45,9 +45,8 @@ class CaptureSchema(Schema):
links = fields.Dict()
# TODO: Automate this somewhat
@pre_dump
def generate_links(self, data, **kwargs):
def generate_links(self, data, **_):
data.links = {
"self": {
"href": url_for(CaptureView.endpoint, id=data.id, _external=True),
@ -97,24 +96,24 @@ class CaptureView(View):
tags = ["captures"]
@marshal_with(CaptureSchema())
def get(self, id):
def get(self, id_):
"""
Description of a single image capture
"""
microscope = find_component("org.openflexure.microscope")
capture_obj = microscope.captures.images.get(id)
capture_obj = microscope.captures.images.get(id_)
if not capture_obj:
return abort(404) # 404 Not Found
return capture_obj
def delete(self, id):
def delete(self, id_):
"""
Delete a single image capture
"""
microscope = find_component("org.openflexure.microscope")
capture_obj = microscope.captures.images.get(id)
capture_obj = microscope.captures.images.get(id_)
if not capture_obj:
return abort(404) # 404 Not Found
@ -122,7 +121,7 @@ class CaptureView(View):
# Delete the capture file
capture_obj.delete()
# Delete from capture list
del microscope.captures.images[id]
del microscope.captures.images[id_]
return "", 204
@ -131,12 +130,12 @@ class CaptureDownload(View):
tags = ["captures"]
responses = {200: {"content_type": "image/jpeg"}}
def get(self, id, filename):
def get(self, id_, filename):
"""
Image data for a single image capture
"""
microscope = find_component("org.openflexure.microscope")
capture_obj = microscope.captures.images.get(id)
capture_obj = microscope.captures.images.get(id_)
if not capture_obj:
return abort(404) # 404 Not Found
@ -148,7 +147,7 @@ class CaptureDownload(View):
return redirect(
url_for(
"DownloadAPI",
id=id,
id=id_,
filename=capture_obj.filename,
thumbnail=thumbnail,
),
@ -167,24 +166,24 @@ class CaptureDownload(View):
class CaptureTags(View):
tags = ["captures"]
def get(self, id):
def get(self, id_):
"""
Get tags associated with a single image capture
"""
microscope = find_component("org.openflexure.microscope")
capture_obj = microscope.captures.images.get(id)
capture_obj = microscope.captures.images.get(id_)
if not capture_obj:
return abort(404) # 404 Not Found
return capture_obj.tags
def put(self, id):
def put(self, id_):
"""
Add tags to a single image capture
"""
microscope = find_component("org.openflexure.microscope")
capture_obj = microscope.captures.images.get(id)
capture_obj = microscope.captures.images.get(id_)
if not capture_obj:
return abort(404) # 404 Not Found
@ -199,12 +198,12 @@ class CaptureTags(View):
return capture_obj.tags
def delete(self, id):
def delete(self, id_):
"""
Delete tags from a single image capture
"""
microscope = find_component("org.openflexure.microscope")
capture_obj = microscope.captures.images.get(id)
capture_obj = microscope.captures.images.get(id_)
if not capture_obj:
return abort(404) # 404 Not Found
@ -223,24 +222,24 @@ class CaptureTags(View):
class CaptureAnnotations(View):
tags = ["captures"]
def get(self, id):
def get(self, id_):
"""
Get annotations associated with a single image capture
"""
microscope = find_component("org.openflexure.microscope")
capture_obj = microscope.captures.images.get(id)
capture_obj = microscope.captures.images.get(id_)
if not capture_obj:
return abort(404) # 404 Not Found
return capture_obj.annotations
def put(self, id):
def put(self, id_):
"""
Update metadata for a single image capture
"""
microscope = find_component("org.openflexure.microscope")
capture_obj = microscope.captures.images.get(id)
capture_obj = microscope.captures.images.get(id_)
if not capture_obj:
return abort(404) # 404 Not Found

View file

@ -1,6 +1,4 @@
import logging
from labthings import fields, find_component, schema
from labthings import fields, find_component
from labthings.views import PropertyView

View file

@ -1,9 +1,8 @@
from flask import Response
from labthings import find_component
from labthings.utilities import create_from_path, get_by_path, set_by_path
from labthings.views import PropertyView, View
from labthings.views import PropertyView
from openflexure_microscope.api.utilities import JsonResponse, gen
from openflexure_microscope.api.utilities import gen
class MjpegStream(PropertyView):

View file

@ -1,8 +1,5 @@
# -*- coding: utf-8 -*-
import datetime
import logging
import os
import shutil
import threading
import time
from abc import ABCMeta, abstractmethod
@ -21,6 +18,7 @@ class BaseCamera(metaclass=ABCMeta):
self.lock = StrictLock(name="Camera", timeout=None)
self.frames_iterator = None
self.frame = None
self.last_access = 0
self.event = ClientEvent()
@ -34,13 +32,11 @@ class BaseCamera(metaclass=ABCMeta):
@abstractmethod
def configuration(self):
"""The current camera configuration."""
pass
@property
@abstractmethod
def state(self):
"""The current read-only camera state."""
pass
@property
def settings(self):
@ -132,7 +128,6 @@ class BaseCamera(metaclass=ABCMeta):
@abstractmethod
def frames(self):
"""Create generator that returns frames from the camera."""
pass
# WORKER THREAD

View file

@ -1,8 +1,5 @@
# -*- coding: utf-8 -*-
"""
"""
from __future__ import division
import io
@ -11,18 +8,13 @@ import time
from datetime import datetime
# Type hinting
from typing import Tuple
import numpy as np
from PIL import Image, ImageDraw, ImageFont
from PIL import Image, ImageDraw
from openflexure_microscope.camera.base import BaseCamera
from openflexure_microscope.captures import CaptureObject
"""
PIL spams the logger with debug-level information. This is a pain when debugging api.app.
We override the logging settings in api.app by setting a level for PIL here.
"""
# PIL spams the logger with debug-level information. This is a pain when debugging api.app.
# We override the logging settings in api.app by setting a level for PIL here.
pil_logger = logging.getLogger("PIL")
pil_logger.setLevel(logging.INFO)
@ -79,7 +71,6 @@ class MissingCamera(BaseCamera):
def initialisation(self):
"""Run any initialisation code when the frame iterator starts."""
pass
def close(self):
"""Close the Raspberry Pi PiCameraStreamer."""
@ -134,7 +125,7 @@ class MissingCamera(BaseCamera):
"Cannot update camera config while recording is active."
)
def set_zoom(self, zoom_value: float = 1.0) -> None:
def set_zoom(self, *_) -> None:
"""
Change the camera zoom, handling re-centering and scaling.
"""
@ -142,7 +133,7 @@ class MissingCamera(BaseCamera):
# LAUNCH ACTIONS
def start_preview(self, fullscreen=True, window=None):
def start_preview(self, *_):
"""Start the on board GPU camera preview."""
logging.warning("GPU preview not implemented in mock camera")
@ -150,7 +141,7 @@ class MissingCamera(BaseCamera):
"""Stop the on board GPU camera preview."""
logging.warning("GPU preview not implemented in mock camera")
def start_recording(self, output, fmt: str = "h264", quality: int = 15):
def start_recording(self, *_):
"""Start recording.
Start a new video recording, writing to a output object.
@ -173,14 +164,7 @@ class MissingCamera(BaseCamera):
with self.lock:
logging.warning("Recording not implemented in mock camera")
def capture(
self,
output,
fmt: str = "jpeg",
use_video_port: bool = False,
resize: Tuple[int, int] = None,
bayer: bool = True,
):
def capture(self, output, *_):
"""
Capture a still image to a StreamObject.
@ -189,10 +173,6 @@ class MissingCamera(BaseCamera):
Args:
output: String or file-like object to write capture data to
use_video_port (bool): Capture from the video port used for streaming. Lower resolution, faster.
fmt (str): Format of the capture.
resize ((int, int)): Resize the captured image.
bayer (bool): Store raw bayer data in capture
"""
with self.lock:

View file

@ -30,7 +30,6 @@ from __future__ import division
import io
import logging
import os
import time
# Type hinting
@ -43,13 +42,8 @@ import picamerax
import picamerax.array
from openflexure_microscope.camera.base import BaseCamera
from openflexure_microscope.captures import CaptureObject
from openflexure_microscope.paths import settings_file_path
from openflexure_microscope.utilities import (
json_to_ndarray,
ndarray_to_json,
serialise_array_b64,
)
from openflexure_microscope.utilities import json_to_ndarray, ndarray_to_json
# Richard's fix gain
from .set_picamera_gain import set_analog_gain, set_digital_gain
@ -117,6 +111,7 @@ class PiCameraStreamer(BaseCamera):
) #: str: Path of .npy lens shading table file
# Start the stream worker on init
self.stream = None
self.start_worker()
@property
@ -131,7 +126,6 @@ class PiCameraStreamer(BaseCamera):
def initialisation(self):
"""Run any initialisation code when the frame iterator starts."""
pass
def close(self):
"""Close the Raspberry Pi PiCameraStreamer."""
@ -466,7 +460,7 @@ class PiCameraStreamer(BaseCamera):
# Reduce the resolution for video streaming
try:
self.camera._check_recording_stopped()
self.camera._check_recording_stopped() # pylint: disable=W0212
except picamerax.exc.PiCameraRuntimeError:
logging.info(
"Error while changing resolution: Recording already running."

View file

@ -39,7 +39,7 @@ def read_exif_from_file(filename):
head = data[2:6]
HEAD_LENGTH = 4
exif = None
while 1:
while len(head) == HEAD_LENGTH:
length = struct.unpack(">H", head[2:4])[0]
if head[:2] == b"\xff\xe1":

View file

@ -2,8 +2,8 @@ import copy
import numbers
import struct
from ._common import *
from ._exif import *
from ._common import split_into_segments
from ._exif import ExifIFD, ImageIFD, TAGS, TYPES
TIFF_HEADER_LENGTH = 8
@ -243,11 +243,11 @@ def _value_to_bytes(raw_value, value_type, offset):
elif value_type == TYPES.Ascii:
try:
new_value = raw_value.encode("latin1") + b"\x00"
except:
except: # pylint: disable=W0702
try:
new_value = raw_value + b"\x00"
except TypeError:
raise ValueError("Got invalid type to convert.")
except TypeError as e:
raise ValueError("Got invalid type to convert.") from e
length = len(new_value)
if length > 4:
value_str = struct.pack(">I", offset)
@ -262,7 +262,7 @@ def _value_to_bytes(raw_value, value_type, offset):
elif isinstance(raw_value[0], tuple):
length = len(raw_value)
new_value = b""
for n, val in enumerate(raw_value):
for _, val in enumerate(raw_value):
num, den = val
new_value += struct.pack(">L", num) + struct.pack(">L", den)
value_str = struct.pack(">I", offset)
@ -275,7 +275,7 @@ def _value_to_bytes(raw_value, value_type, offset):
elif isinstance(raw_value[0], tuple):
length = len(raw_value)
new_value = b""
for n, val in enumerate(raw_value):
for _, val in enumerate(raw_value):
num, den = val
new_value += struct.pack(">l", num) + struct.pack(">l", den)
value_str = struct.pack(">I", offset)
@ -286,13 +286,13 @@ def _value_to_bytes(raw_value, value_type, offset):
value_str = struct.pack(">I", offset)
try:
four_bytes_over = b"" + raw_value
except TypeError:
raise ValueError("Got invalid type to convert.")
except TypeError as e:
raise ValueError("Got invalid type to convert.") from e
else:
try:
value_str = raw_value + b"\x00" * (4 - length)
except TypeError:
raise ValueError("Got invalid type to convert.")
except TypeError as e:
raise ValueError("Got invalid type to convert.") from e
elif value_type == TYPES.SByte: # Signed Byte
length = len(raw_value)
if length <= 4:
@ -333,7 +333,7 @@ def _dict_to_bytes(ifd_dict, ifd, ifd_offset):
entries = b""
values = b""
for n, key in enumerate(sorted(ifd_dict)):
for _, key in enumerate(sorted(ifd_dict)):
if (ifd == "0th") and (key in (ImageIFD.ExifTag, ImageIFD.GPSTag)):
continue
elif (ifd == "Exif") and (key == ExifIFD.InteroperabilityTag):
@ -358,11 +358,11 @@ def _dict_to_bytes(ifd_dict, ifd, ifd_offset):
length_str, value_str, four_bytes_over = _value_to_bytes(
raw_value, value_type, offset
)
except ValueError:
except ValueError as e:
raise ValueError(
'"dump" got wrong type of exif value.\n'
+ "{0} in {1} IFD. Got as {2}.".format(key, ifd, type(ifd_dict[key]))
)
) from e
entries += key_str + type_str + length_str + value_str
values += four_bytes_over

View file

@ -3,7 +3,7 @@ import struct
import sys
from . import _webp
from ._common import *
from ._common import merge_segments, split_into_segments
from ._exceptions import InvalidImageDataError

View file

@ -2,9 +2,9 @@ import struct
import sys
from . import _webp
from ._common import *
from ._common import get_exif_seg, read_exif_from_file, split_into_segments
from ._exceptions import InvalidImageDataError
from ._exif import *
from ._exif import ExifIFD, ImageIFD, TAGS, TYPES
LITTLE_ENDIAN = b"\x49\x49"
@ -109,6 +109,8 @@ class _ExifReader(object):
else:
raise InvalidImageDataError("Given file is neither JPEG nor TIFF.")
self.endian_mark = None
def get_ifd_dict(self, pointer, ifd_name, read_unknown=False):
ifd_dict = {}
tag_count = struct.unpack(

View file

@ -1,7 +1,7 @@
import io
from . import _webp
from ._common import *
from ._common import get_exif_seg, split_into_segments
def remove(src, new_file=None):
@ -42,7 +42,7 @@ def remove(src, new_file=None):
new_data = src_data
except Exception as e:
print(e)
raise ValueError("Error occurred.")
raise ValueError("Error occurred.") from e
if isinstance(new_file, io.BytesIO):
new_file.write(new_data)

View file

@ -1,6 +1,6 @@
import io
from ._common import *
from ._common import get_exif_seg, merge_segments, split_into_segments
def transplant(exif_src, image, new_file=None):

View file

@ -204,8 +204,6 @@ def get_exif(data):
CHUNK_FOURCC_LENGTH = 4
LENGTH_BYTES_LENGTH = 4
chunks = []
exif = b""
while pointer < file_size:
fourcc = data[pointer : pointer + CHUNK_FOURCC_LENGTH]
pointer += CHUNK_FOURCC_LENGTH

View file

@ -46,8 +46,8 @@ class UserComment:
cls._JIS_PREFIX: cls._JIS,
cls._UNICODE_PREFIX: cls._UNICODE,
}[prefix]
except KeyError:
raise ValueError("unable to determine appropriate encoding")
except KeyError as e:
raise ValueError("unable to determine appropriate encoding") from e
return body.decode(encoding, errors="replace")
@classmethod

View file

@ -4,7 +4,7 @@ import logging
import time
import picamerax
from picamerax import exc, mmal, mmalobj
from picamerax import exc, mmal
from picamerax.mmalobj import to_rational
MMAL_PARAMETER_ANALOG_GAIN = mmal.MMAL_PARAMETER_GROUP_CAMERA + 0x59
@ -21,7 +21,7 @@ def set_gain(camera, gain, value):
if gain not in [MMAL_PARAMETER_ANALOG_GAIN, MMAL_PARAMETER_DIGITAL_GAIN]:
raise ValueError("The gain parameter was not valid")
ret = mmal.mmal_port_parameter_set_rational(
camera._camera.control._port, gain, to_rational(value)
camera._camera.control._port, gain, to_rational(value) # pylint: disable=W0212
)
if ret == 4:
raise exc.PiCameraMMALError(

View file

@ -1,11 +1,9 @@
import atexit
import datetime
import glob
import io
import json
import logging
import os
import shutil
import uuid
from collections import OrderedDict
@ -37,7 +35,8 @@ def pull_usercomment_dict(filepath):
return json.loads(exif_dict["Exif"][piexif.ExifIFD.UserComment].decode())
except json.decoder.JSONDecodeError:
logging.error(
f"Capture {filepath} has old, corrupt, or missing OpenFlexure metadata. Unable to reload to server."
"Capture %s has old, corrupt, or missing OpenFlexure metadata. Unable to reload to server.",
filepath,
)
else:
return None
@ -56,8 +55,6 @@ def make_file_list(directory, formats):
def build_captures_from_exif(capture_path):
global EXIF_FORMATS
logging.debug("Reloading captures from {}...".format(capture_path))
files = make_file_list(capture_path, EXIF_FORMATS)
captures = OrderedDict()
@ -97,9 +94,9 @@ def capture_from_exif(path, exif_dict):
# Image metadata
try:
image_metadata = exif_dict.pop("image")
except KeyError as e:
except KeyError:
logging.error(
f"Unable to obtain valid 2.0 OpenFlexure metadata from file {path}"
"Unable to obtain valid 2.0 OpenFlexure metadata from file %s", path
)
return None
@ -111,7 +108,7 @@ def capture_from_exif(path, exif_dict):
capture.annotations = image_metadata.get("annotations")
# Since we popped the "image" key, we dump whatever is left in _metadata
capture._metadata = exif_dict
capture.set_metadata(exif_dict)
return capture
@ -133,6 +130,7 @@ class CaptureObject(object):
self.datetime = datetime.datetime.now()
# Create file name. Default to UUID
self.format = None
self.file = filepath
self.split_file_path(self.file)
@ -151,15 +149,15 @@ class CaptureObject(object):
self.thumb_bytes = None
def write(self, s):
logging.debug(f"Writing to {self}")
logging.debug("Writing to %s", self)
self.stream.write(s)
def flush(self):
logging.info(f"Writing to disk {self.file}")
logging.info("Writing to disk %s", self.file)
with open(self.file, "wb") as outfile:
outfile.write(self.stream.getbuffer())
self.stream.close()
logging.info(f"Finished writing to disk {self.file}")
logging.info("Finished writing to disk %s", self.file)
def open(self, mode):
return open(self.file, mode)
@ -233,6 +231,15 @@ class CaptureObject(object):
self._metadata.update(data)
self.save_metadata()
def set_metadata(self, data: dict) -> None:
"""
Write root metadata from a passed dictionary into the capture metadata.
Args:
data (dict): Dictionary of metadata to be added
"""
self._metadata = data
def put_and_save(
self, tags: list = None, annotations: dict = None, metadata: dict = None
):
@ -261,22 +268,20 @@ class CaptureObject(object):
"""
Save metadata to exif, if supported
"""
global EXIF_FORMATS
if self.format.upper() in EXIF_FORMATS and self.exists:
logging.debug("Writing exif data to capture file")
# Extract current Exif data
exif_dict = piexif.load(self.file)
# Serialize metadata
metadata_string = json.dumps(self.metadata, cls=JSONEncoder)
logging.debug(f"Saving metadata string to file: {metadata_string}")
logging.debug("Saving metadata string to file: %s", metadata_string)
# Insert metadata into exif_dict
exif_dict["Exif"][piexif.ExifIFD.UserComment] = metadata_string.encode()
# Convert new exif dict to exif bytes
exif_bytes = piexif.dump(exif_dict)
# Insert exif into file
piexif.insert(exif_bytes, self.file)
logging.info(f"Finished saving metadata to {self.file}")
logging.info("Finished saving metadata to %s", self.file)
@property
def metadata(self) -> dict:
@ -344,7 +349,6 @@ class CaptureObject(object):
"""
Returns a thumbnail of the capture data, for supported image formats.
"""
global THUMBNAIL_SIZE
# If no thumbnail exists, try and make one
if not self.thumb_bytes:
logging.info("Building thumbnail")

View file

@ -158,7 +158,8 @@ class CaptureManager:
# Update capture list
capture_key = str(output.id)
logging.debug(f"Adding image {output} with key {capture_key}")
logging.debug("Adding image %s with key %s", output, capture_key)
self.images[capture_key] = output
return output
@ -204,7 +205,7 @@ class CaptureManager:
# Update capture list
capture_key = str(output.id)
logging.debug(f"Adding video {output} with key {capture_key}")
logging.debug("Adding video %s with key %s", output, capture_key)
self.videos[capture_key] = output
return output

View file

@ -6,7 +6,6 @@ import shutil
from fractions import Fraction
from uuid import UUID
import flask
import numpy as np
from labthings.json import LabThingsJSONEncoder
@ -27,8 +26,8 @@ class OpenflexureSettingsFile:
expand (bool): Expand paths to valid auxillary config files.
"""
def __init__(self, path: str, defaults: dict = {}):
global DEFAULT_SETTINGS
def __init__(self, path: str, defaults: dict = None):
defaults = defaults or {}
# Set arguments
self.path = path
@ -84,7 +83,7 @@ class JSONEncoder(LabThingsJSONEncoder):
A custom JSON encoder, with type conversions for PiCamera fractions, Numpy integers, and Numpy arrays
"""
def default(self, o, markers=None):
def default(self, o):
if isinstance(o, UUID):
return str(o)
# PiCamera fractions

View file

@ -1,23 +0,0 @@
from threading import ThreadError
class LockError(ThreadError):
ERROR_CODES = {
"ACQUIRE_ERROR": "Unable to acquire. Lock in use by another thread.",
"IN_USE_ERROR": "Lock in use by another thread.",
}
def __init__(self, code, lock):
self.code = code
if code in LockError.ERROR_CODES:
self.message = LockError.ERROR_CODES[code]
else:
self.message = "Unknown error."
self.string = "{}: {}".format(self.code, self.message)
print(self.string)
ThreadError.__init__(self)
def __str__(self):
return self.string

View file

@ -16,14 +16,12 @@ from openflexure_microscope.stage.sanga import SangaDeltaStage, SangaStage
try:
from openflexure_microscope.camera.pi import PiCameraStreamer
except Exception as e:
except Exception as e: # pylint: disable=W0703
logging.error(e)
logging.warning("Unable to import PiCameraStreamer")
from labthings import CompositeLock
from openflexure_microscope.camera.mock import MissingCamera
from openflexure_microscope.config import user_configuration, user_settings
from openflexure_microscope.utilities import Timer, serialise_array_b64
class Microscope:
@ -99,7 +97,7 @@ class Microscope:
if camera_type in ("PiCamera", "PiCameraStreamer"):
try:
self.camera = PiCameraStreamer()
except Exception as e:
except Exception as e: # pylint: disable=W0703
logging.error(e)
logging.warning("No compatible camera hardware found.")
@ -146,7 +144,7 @@ class Microscope:
try:
logging.info("Trying SangaStage")
self.stage = SangaStage(port=stage_port)
except Exception as e:
except Exception as e: # pylint: disable=W0703
logging.error(e)
logging.warning("No compatible Sangaboard hardware found.")
elif stage_type in ("SangaDeltaStage"):
@ -154,7 +152,7 @@ class Microscope:
logging.info("Trying SangaDeltaStage")
self.stage = SangaDeltaStage(port=stage_port)
except Exception as e:
except Exception as e: # pylint: disable=W0703
logging.error(e)
logging.warning("No compatible Sangaboard hardware found.")
else:
@ -334,14 +332,14 @@ class Microscope:
# Load cached bits of metadata
if cache_key:
logging.debug(f"Reading cached microscope metadata: {cache_key}")
logging.debug("Reading cached microscope metadata: %s", cache_key)
metadata = self.metadata_cache.get(cache_key, None)
if not metadata:
logging.debug(f"Building and caching microscope metadata: {cache_key}")
logging.debug("Building and caching microscope metadata: %s", cache_key)
metadata = self.force_get_metadata()
self.metadata_cache[cache_key] = metadata
else:
logging.debug(f"Building microscope metadata: {cache_key}")
logging.debug("Building microscope metadata: %s", cache_key)
metadata = self.force_get_metadata()
# Keys that should never be cached
@ -367,7 +365,7 @@ class Microscope:
metadata: dict = None,
cache_key: str = None,
):
logging.debug(f"Microscope capturing to {filename}")
logging.debug("Microscope capturing to %s", filename)
if not annotations:
annotations = {}
if not metadata:
@ -386,8 +384,7 @@ class Microscope:
)
# Capture to output object
logging.info(f"Starting microscope capture {output.file}")
print(output)
logging.info("Starting microscope capture %s", output.file)
self.camera.capture(
output,
use_video_port=use_video_port,
@ -397,6 +394,6 @@ class Microscope:
)
output.put_and_save(tags, annotations, full_metadata)
logging.debug(f"Finished capture to {output.file}")
logging.debug("Finished capture to %s", output.file)
return output

View file

@ -1,4 +1,3 @@
import logging
import os
# UTILITIES

View file

@ -6,12 +6,12 @@ from openflexure_microscope.config import user_settings
from openflexure_microscope.rescue.monitor_timeout import launch_timeout_test_process
def check_capture_rebuild(timeout=10):
def check_capture_rebuild():
logging.info("Loading user settings...")
settings = user_settings.load()
cap_path = str(settings.get("captures", {}).get("paths", {}).get("default"))
logging.info(f"Capture path found: {cap_path}")
logging.info("Capture path found: %s", cap_path)
if not cap_path:
logging.error(
"No capture path defined in settings. This is unusual for anything other than a first-run. \nFalling back to default path."

View file

@ -16,7 +16,7 @@ def trace_config_exceptions():
default_config = json.load(DEFAULT_CONFIGURATION_FILE_PATH)
if not default_config:
error_sources.append("default_config_empty")
except Exception as e:
except Exception as e: # pylint: disable=W0703
logging.error("Error parsing config:")
logging.error(e)
error_sources.append("default_config_error")
@ -25,7 +25,7 @@ def trace_config_exceptions():
default_settings = json.load(SETTINGS_FILE_PATH)
if not default_settings:
error_sources.append("default_settings_empty")
except Exception as e:
except Exception as e: # pylint: disable=W0703
logging.error("Error parsing settings:")
logging.error(e)
error_sources.append("default_settings_error")
@ -37,8 +37,8 @@ def main():
error_sources = []
logging.info("Attempting default settings and config import...")
try:
from openflexure_microscope import config
except Exception as e:
from openflexure_microscope import config as _
except Exception as e: # pylint: disable=W0703
error_sources.append("config_settings_import_error")
logging.error("Error importing config:")
logging.error(e)

View file

@ -21,7 +21,6 @@
#
import subprocess
import sys
class ServiceMonitor(object):

View file

@ -24,7 +24,9 @@ def launch_timeout_test_process(target, args=(), kwargs=None, timeout=10):
# If thread is still active
if p.is_alive():
logging.error(
f"Function {target} reached timeout after {timeout} seconds. Terminating."
"Function %s reached timeout after %s seconds. Terminating.",
target,
timeout,
)
# Terminate

View file

@ -17,36 +17,30 @@ class BaseStage(metaclass=ABCMeta):
@abstractmethod
def update_settings(self, config: dict):
"""Update settings from a config dictionary"""
pass
@abstractmethod
def read_settings(self):
"""Return the current settings as a dictionary"""
pass
@property
@abstractmethod
def state(self):
"""The general state dictionary of the board."""
pass
@property
@abstractmethod
def configuration(self):
"""The general stage configuration."""
pass
@property
@abstractmethod
def n_axes(self):
"""The number of axes this stage has."""
pass
@property
@abstractmethod
def position(self):
"""The current position, as a list"""
pass
@property
def position_map(self):
@ -56,36 +50,30 @@ class BaseStage(metaclass=ABCMeta):
@abstractmethod
def backlash(self):
"""Get the distance used for backlash compensation."""
pass
@backlash.setter
@abstractmethod
def backlash(self):
"""Set the distance used for backlash compensation."""
pass
@abstractmethod
def move_rel(self, displacement, backlash=True):
def move_rel(self, displacement: list, axis=None, backlash=True):
"""Make a relative move, optionally correcting for backlash.
displacement: integer or array/list of 3 integers
backlash: (default: True) whether to correct for backlash.
"""
pass
@abstractmethod
def move_abs(self, final, **kwargs):
"""Make an absolute move to a position"""
pass
@abstractmethod
def zero_position(self):
"""Set the current position to zero"""
pass
@abstractmethod
def close(self):
"""Cleanly close communication with the stage"""
pass
def scan_linear(self, rel_positions, backlash=True, return_to_start=True):
"""

View file

@ -9,7 +9,7 @@ from openflexure_microscope.utilities import axes_to_array
class MissingStage(BaseStage):
def __init__(self, port=None, **kwargs):
def __init__(self, *args, **kwargs): # pylint: disable=unused-argument
BaseStage.__init__(self)
self._position = [0, 0, 0]
self._n_axis = 3
@ -66,11 +66,8 @@ class MissingStage(BaseStage):
else:
self._backlash = np.array([int(blsh)] * self.n_axes, dtype=np.int)
def move_rel(
self, displacement: list, axis=None, backlash=True, simulate_time: bool = True
):
if simulate_time:
time.sleep(0.5)
def move_rel(self, displacement: list, axis=None, backlash=True):
time.sleep(0.5)
if axis is not None:
assert axis in self.axis_names, "axis must be one of {}".format(
self.axis_names
@ -83,14 +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(f"New position: {self._position}")
logging.debug("New position: %s", self._position)
def move_abs(self, final, simulate_time: bool = True, **kwargs):
if simulate_time:
time.sleep(0.5)
def move_abs(self, final, **kwargs):
time.sleep(0.5)
self._position = list(final)
logging.debug(f"New position: {self._position}")
logging.debug("New position: %s", self._position)
def zero_position(self):
"""Set the current position to zero"""

View file

@ -29,10 +29,12 @@ class SangaStage(BaseStage):
self.board = Sangaboard(port, **kwargs)
self._backlash = (
None
) # Initialise backlash storage, used by property setter/getter
None # Initialise backlash storage, used by property setter/getter
)
self.axis_names = ["x", "y", "z"] # Assume all sangaboards are 3 axis
self._position_on_enter = None
@property
def state(self):
"""The general state dictionary of the board."""
@ -111,7 +113,7 @@ class SangaStage(BaseStage):
backlash: (default: True) whether to correct for backlash.
"""
with self.lock:
logging.debug(f"Moving sangaboard by {displacement}")
logging.debug("Moving sangaboard by %s", displacement)
if not backlash or self.backlash is None:
return self.board.move_rel(displacement, axis=axis)
if axis is not None:
@ -148,7 +150,7 @@ class SangaStage(BaseStage):
"""Make an absolute move to a position
"""
with self.lock:
logging.debug(f"Moving sangaboard to {final}")
logging.debug("Moving sangaboard to %s", final)
self.board.move_abs(final, **kwargs)
def zero_position(self):
@ -171,12 +173,12 @@ class SangaStage(BaseStage):
self._position_on_enter = self.position
return self
def __exit__(self, type, value, traceback):
def __exit__(self, type_, value, traceback):
"""The end of the with statement. Reset position if it went wrong.
NB the instrument is closed when the object is deleted, so we don't
need to worry about that here.
"""
if type is not None:
if type_ is not None:
print(
"An exception occurred inside a with block, resetting position \
to its value at the start of the with block"
@ -184,7 +186,7 @@ class SangaStage(BaseStage):
try:
time.sleep(0.5)
self.move_abs(self._position_on_enter)
except Exception as e:
except Exception as e: # pylint: disable=W0703
print(
"A further exception occurred when resetting position: {}".format(e)
)

View file

@ -2,7 +2,6 @@ import base64
import copy
import logging
import operator
import re
import time
from collections import abc
from contextlib import contextmanager
@ -21,9 +20,9 @@ class Timer(object):
def __enter__(self):
self.start = time.time()
def __exit__(self, type, value, traceback):
def __exit__(self, type_, value, traceback):
self.end = time.time()
logging.debug(f"{self.name} time: {self.end - self.start}")
logging.debug("%s time: %s", self.name, self.end - self.start)
def deserialise_array_b64(b64_string, dtype, shape):

View file

@ -72,3 +72,7 @@ force_grid_wrap = 0
use_parentheses = true
ensure_newline_before_comments = true
line_length = 88
[tool.pylint.'MESSAGES CONTROL']
disable = "C0330, C0326, W1202, W0511, C, R"
max-line-length = 88