From 5527c4eb7b27ec88bae26aa75ae1d46b90f7d1db Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Thu, 11 Feb 2021 14:30:20 +0000 Subject: [PATCH 01/23] Fix auto-exposure and AWB The previous "auto calibrate" button for the camera relied on a combination of the built-in autoexposure/AWB and some JPEG-based tweaks to get the exposure settings right. This often led to confusing results, e.g. oscillating between green and pink images. I have rewritten the auto-exposure code to adjust gain and shutter speed based on the raw image. This seems to be very reliable, at the expense of being quite slow. That's a price I'm happy to pay. I have replaced both the camera's auto white balance and my JPEG-based AWB hack with a single-shot AWB method that looks at the raw image. --- .../picamera_autocalibrate/extension.py | 177 ++++++++++++---- .../recalibrate_utils.py | 193 ++++++++++++++++-- 2 files changed, 320 insertions(+), 50 deletions(-) diff --git a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py index 3b7de2ea..5a0f56dc 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py @@ -1,6 +1,8 @@ import logging from contextlib import contextmanager +import labthings.fields as fields +import numpy as np import picamerax from flask import abort from labthings import find_component @@ -13,7 +15,11 @@ from openflexure_microscope.microscope import Microscope from .recalibrate_utils import ( auto_expose_and_freeze_settings, flat_lens_shading_table, + get_channel_percentiles, recalibrate_camera, + adjust_shutter_and_gain_from_raw, + adjust_white_balance_from_raw, + lst_from_camera, ) @@ -61,48 +67,71 @@ class LSTExtension(BaseExtension): "/delete_lens_shading_table", endpoint="delete_lens_shading_table", ) + self.add_view( + GetRawChannelPercentilesView, + "/get_raw_channel_percentiles", + endpoint="get_raw_channel_percentiles", + ) + self.add_view( + AutoExposureFromRawView, + "/auto_exposure_from_raw", + endpoint="auto_exposure_from_raw", + ) + self.add_view( + AutoWhiteBalanceFromRawView, + "/auto_white_balance_from_raw", + endpoint="auto_white_balance_from_raw", + ) + + - def recalibrate(self, microscope: Microscope): + +def find_microscope(): + """Locate the microscope and raise a sensible error if it's missing.""" + microscope = find_component("org.openflexure.microscope") + + if not microscope: + abort( + 503, "No microscope connected. Unable to use camera calibration functions." + ) + + if not hasattr(microscope.camera, "picamera"): + abort( + 503, "The PiCamera calibration plugin requires a Raspberry Pi camera." + ) + + return microscope + + +class RecalibrateView(ActionView): + args = { + "skip_auto_exposure": fields.Bool( + missing=False, + description="Whether to " + ) + } + def post(self, args): """Reset the camera's settings. This generates new gains, exposure time, and lens shading table such that the background is as uniform as possible with a gray level of 230. It takes a little while to run. """ - with pause_stream(microscope.camera) as scamera: - if hasattr(scamera, "picamera"): - picamera_obj: picamerax.PiCamera = getattr(scamera, "picamera") - auto_expose_and_freeze_settings(picamera_obj) - recalibrate_camera(picamera_obj) - microscope.save_settings() - else: - raise RuntimeError( - "Recalibrate can only be used with a Raspberry Pi camera" - ) - - -class RecalibrateView(ActionView): - def post(self): - microscope = find_component("org.openflexure.microscope") - - if not microscope: - abort(503, "No microscope connected. Unable to recalibrate.") - + microscope = find_microscope() logging.info("Starting microscope recalibration...") - - return self.extension.recalibrate(microscope) + picamera = microscope.camera.picamera + if not args.get("skip_auto_exposure"): + adjust_shutter_and_gain_from_raw(picamera) + adjust_white_balance_from_raw(picamera) + lst = lst_from_camera(picamera) + with pause_stream(microscope.camera) as scamera: + scamera.picamera.lens_shading_table = lst + microscope.save_settings() class FlattenLSTView(ActionView): def post(self): - microscope = find_component("org.openflexure.microscope") - - if not microscope: - abort( - 503, - "No microscope connected. Unable to flatten the lens shading table.", - ) - + microscope = find_microscope() with pause_stream(microscope.camera) as scamera: flat_lst = flat_lens_shading_table(scamera.camera) scamera.camera.lens_shading_table = flat_lst @@ -111,14 +140,86 @@ class FlattenLSTView(ActionView): class DeleteLSTView(ActionView): def post(self): - microscope = find_component("org.openflexure.microscope") - - if not microscope: - abort( - 503, - "No microscope connected. Unable to flatten the lens shading table.", - ) - + microscope = find_microscope() with pause_stream(microscope.camera) as scamera: scamera.camera.lens_shading_table = None microscope.save_settings() + + +class AutoExposureFromRawView(ActionView): + args = { + "target_white_level": fields.Int( + missing=700, + example=700, + description=( + "The pixel value (10-bit format) that we aim for when adjusting shutter/gain." + ), + ), + "max_iterations": fields.Int( + missing=20, + description=( + "The number of adjustments to the camera's settings to make before giving up." + ), + ), + "tolerance": fields.Float( + missing=0.05, + example=0.05, + description=( + "We stop adjusting when we get within this fraction of the target " + "value. It is a number between 0 and 1, usually 0.01--0.1." + ), + ), + "percentile": fields.Float( + missing=99.9, + example=99.9, + description=( + "A float between 0 and 100 setting the centile to use " + "to measure the white point of the image. A value " + "of 99.9 allows 0.1% of the pixels to be erroneously " + "bright - this helps stability in low light." + ), + ), + } + + def post(self, args): + camera = find_component("org.openflexure.microscope").camera.picamera + adjust_shutter_and_gain_from_raw( + camera, + **args # I'm relying on the schema to fill in missing values + ) + + +class AutoWhiteBalanceFromRawView(ActionView): + args = { + "percentile": fields.Float( + missing=99.9, + example=99.9, + description=( + "A float between 0 and 100 setting the centile to use " + "to measure the white point of the image. A value " + "of 99.9 allows 0.1% of the pixels to be erroneously " + "bright - this helps stability in low light." + ), + ) + } + + def post(self, args): + camera = find_component("org.openflexure.microscope").camera.picamera + adjust_white_balance_from_raw( + camera, + **args # I'm relying on the schema to fill in missing values + ) + + +class GetRawChannelPercentilesView(ActionView): + args = { + "percentile": fields.Float( + example=99.9, + description="A float between 0 and 100 setting the centile to calculate", + ) + } + schema = fields.List(fields.Integer) + + def post(self, args): + camera = find_component("org.openflexure.microscope").camera.picamera + return get_channel_percentiles(camera, args["percentile"]) 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 0392720e..b8b46044 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py @@ -44,14 +44,66 @@ def adjust_exposure_to_setpoint(camera: PiCamera, setpoint: int): print("done") +def adjust_exposure_and_gain( + camera: PiCamera, target_white_level: int = 700, max_iterations: int = 99 +): + """Adjust exposure and analog gain based on raw images. + + This routine is slow but effective. It uses raw images, so we + are not affected by white balance or digital gain. + """ + # Start by setting fully manual exposure. + camera.exposure_mode = "off" + camera.iso = 0 # We must set ISO=0 (auto) or we can't set gain + camera.analog_gain = 1 + camera.digital_gain = 1 + camera.shutter_speed = 1 # This is not valid - we'll get the minimum + time.sleep(0.5) + + # We start with very low exposure settings and work up in coarse steps + # until either the brightness is high enough, or we can't increase the + # shutter speed any more. + iterations = 0 + while True: + iterations += 1 + + with PiBayerArray(camera) as a: + camera.capture(a, format="jpeg", bayer=True) + max_brightness = np.max(a.array) + + if max_brightness > target_white_level: + break + + previous_shutter_speed = camera.shutter_speed + camera.shutter_speed = previous_shutter_speed * 2 + time.sleep(0.5) + current_shutter_speed = camera.shutter_speed + # NB we save this in a variable in case it changes between + # here and the next loop + logging.info( + f"Max brightness {max_brightness} < {target_white_level}. " + f"Attempting to double exposure from " + f"{previous_shutter_speed} to {current_shutter_speed}" + ) + if current_shutter_speed == previous_shutter_speed: + logging.info(f"Shutter speed has saturated, stopping.") + break + + if max_brightness > target_white_level: + logging.info("Brightness is high enough, fine-tuning") + camera.shutter_speed = int( + current_shutter_speed * target_white_level / max_brightness + ) + + def auto_expose_and_freeze_settings(camera: PiCamera): """Freeze the settings after auto-exposing to white illumination""" logging.info("Allowing the camera to auto-expose") if "greyworld" in camera.AWB_MODES: - print("Calibrating with greyworld AWB") + logging.info("Calibrating with greyworld AWB") camera.awb_mode = "greyworld" else: - print("Calibrating with auto AWB") + logging.warning("Calibrating with auto AWB as greyworld is missing") camera.awb_mode = "auto" camera.exposure_mode = "auto" camera.iso = ( @@ -77,6 +129,112 @@ def auto_expose_and_freeze_settings(camera: PiCamera): adjust_exposure_to_setpoint(camera, 215) +def adjust_shutter_and_gain_from_raw( + camera: PiCamera, + target_white_level: int = 700, + max_iterations: int = 20, + tolerance: float = 0.05, + percentile: float = 99.9, +) -> float: + """Adjust exposure and analog gain based on raw images. + + This routine is slow but effective. It uses raw images, so we + are not affected by white balance or digital gain. + """ + # Start by setting fully manual exposure. + camera.exposure_mode = "off" + camera.iso = 0 # We must set ISO=0 (auto) or we can't set gain + camera.analog_gain = 1 + camera.digital_gain = 1 + camera.shutter_speed = 1 # This is not valid - we'll get the minimum + time.sleep(0.5) + + # We start with very low exposure settings and work up in coarse steps + # until either the brightness is high enough, or we can't increase the + # shutter speed any more. + iterations = 0 + shutter_speed_maximised = False + shutter_speed_increments = [10, 2, None] + shutter_speed_increment = shutter_speed_increments.pop(0) + while iterations < max_iterations: + iterations += 1 + max_brightness = np.max(get_channel_percentiles(camera, percentile)) + tested_shutter_speed = camera.shutter_speed + tested_analog_gain = camera.analog_gain + logging.info( + f"Brightness: {max_brightness: >5.0f}, " + f"Target: {target_white_level: >5.0f}, " + f"Gain: {float(tested_analog_gain): >4.1f}, " + f"Shutter: {float(tested_shutter_speed): >7.0f}" + ) + + if abs(max_brightness - target_white_level) < target_white_level * tolerance: + logging.info( + f"Brightness has converged to within {tolerance * 100 :.0f}% " + f"after {iterations} iterations." + ) + break + + # if not shutter_speed_maximised: # Start by adjusting shutter speed + if max_brightness > target_white_level // 2: + shutter_speed_increment = ( + None + ) # If we're within a factor of 2, trigger fine-tuning + if shutter_speed_increment is None: + logging.info("Fine-tuning shutter speed") + new_shutter_speed = int( + tested_shutter_speed * target_white_level / max_brightness + ) + camera.shutter_speed = new_shutter_speed + else: + if max_brightness > target_white_level: + logging.info("Reducing shutter speed") + camera.shutter_speed = int( + tested_shutter_speed / shutter_speed_increment + ) + else: + logging.info("Increasing shutter speed") + camera.shutter_speed = tested_shutter_speed * shutter_speed_increment + time.sleep(0.5) + + # Check whether the shutter speed is still going up - if not, we've hit a maximum + current_shutter_speed = camera.shutter_speed + if current_shutter_speed == tested_shutter_speed: + camera.analog_gain *= 2 + if camera.analog_gain == tested_analog_gain: + logging.info(f"Shutter speed and gain are both maxed out. Giving up!") + break + logging.info( + f"Shutter speed has maxed out at {current_shutter_speed}, doubled gain." + ) + + max_brightness = np.max(get_channel_percentiles(camera, percentile)) + logging.info(f"Brightness {max_brightness} after {iterations} tries.") + return max_brightness + + +def adjust_white_balance_from_raw( + camera: PiCamera, percentile: float = 99 +) -> (float, float): + """Adjust the white balance in a single shot, based on the raw image. + + NB if ``channels_from_raw_image`` is broken, this will go haywire. + We should probably have better logic to verify the channels really + are BGGR... + """ + blue, g1, g2, red = get_channel_percentiles(camera, percentile) + green = (g1 + g2) / 2.0 + new_awb_gains = (green / red, green / blue) + logging.info( + f"Raw white point is R: {red} G: {green} B: {blue}, " + f"setting AWB gains to ({new_awb_gains[0]:.2f}, " + f"{new_awb_gains[1]:.2f})." + ) + camera.awb_mode = "off" + camera.awb_gains = new_awb_gains + return new_awb_gains + + def channels_from_bayer_array(bayer_array: np.ndarray) -> np.ndarray: """Given the 'array' from a PiBayerArray, return the 4 channels.""" bayer_pattern: List[Tuple[int, int]] = [(0, 0), (0, 1), (1, 0), (1, 1)] @@ -95,6 +253,14 @@ def channels_from_bayer_array(bayer_array: np.ndarray) -> np.ndarray: return channels +def get_channel_percentiles(camera: PiCamera, percentile: float) -> np.ndarray: + """Calculate the brightness percentile of the pixels in each channel""" + with PiBayerArray(camera) as output: + camera.capture(output, format="jpeg", bayer=True) + channels = channels_from_bayer_array(output.array) + return np.percentile(channels, percentile, axis=(1, 2)) + + def lst_from_channels(channels: np.ndarray) -> np.ndarray: """Given the 4 Bayer colour channels from a white image, generate a LST.""" full_resolution: np.ndarray = np.array( @@ -164,6 +330,18 @@ def lst_from_channels(channels: np.ndarray) -> np.ndarray: lens_shading_table: np.ndarray = gains.astype(np.uint8) return lens_shading_table[::-1, :, :].copy() +def lst_from_camera(camera: PiCamera) -> np.ndarray: + """Acquire a raw image and use it to calculate a lens shading table.""" + with PiBayerArray(camera) as a: + camera.capture(a, format="jpeg", bayer=True) + raw_image = a.array.copy() + + # Now we need to calculate a lens shading table that would make this flat. + # raw_image is a 3D array, with full resolution and 3 colour channels. No + # de-mosaicing has been done, so 2/3 of the values are zero (3/4 for R and B + # channels, 1/2 for green because there's twice as many green pixels). + channels = channels_from_bayer_array(raw_image) + return lst_from_channels(channels) def recalibrate_camera(camera: PiCamera): """Reset the lens shading table and exposure settings. @@ -178,16 +356,7 @@ def recalibrate_camera(camera: PiCamera): camera.lens_shading_table = flat_lens_shading_table(camera) _ = rgb_image(camera) # for some reason the camera won't work unless I do this! - with PiBayerArray(camera) as a: - camera.capture(a, format="jpeg", bayer=True) - raw_image = a.array.copy() - - # Now we need to calculate a lens shading table that would make this flat. - # raw_image is a 3D array, with full resolution and 3 colour channels. No - # de-mosaicing has been done, so 2/3 of the values are zero (3/4 for R and B - # channels, 1/2 for green because there's twice as many green pixels). - channels = channels_from_bayer_array(raw_image) - lens_shading_table = lst_from_channels(channels) + lens_shading_table = lst_from_camera(camera) camera.lens_shading_table = lens_shading_table _ = rgb_image(camera) From 4c27d88b37ffd20ca212399422954e943703db2a Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Thu, 11 Feb 2021 15:54:58 +0000 Subject: [PATCH 02/23] Fixed description and linted --- .../picamera_autocalibrate/extension.py | 31 ++++++++----------- .../recalibrate_utils.py | 26 ++++++++++------ 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py index 5a0f56dc..1f8caf88 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py @@ -2,8 +2,6 @@ import logging from contextlib import contextmanager import labthings.fields as fields -import numpy as np -import picamerax from flask import abort from labthings import find_component from labthings.extensions import BaseExtension @@ -13,12 +11,10 @@ from openflexure_microscope.camera.base import BaseCamera from openflexure_microscope.microscope import Microscope from .recalibrate_utils import ( - auto_expose_and_freeze_settings, - flat_lens_shading_table, - get_channel_percentiles, - recalibrate_camera, adjust_shutter_and_gain_from_raw, adjust_white_balance_from_raw, + flat_lens_shading_table, + get_channel_percentiles, lst_from_camera, ) @@ -82,11 +78,9 @@ class LSTExtension(BaseExtension): "/auto_white_balance_from_raw", endpoint="auto_white_balance_from_raw", ) - - -def find_microscope(): +def find_microscope() -> Microscope: """Locate the microscope and raise a sensible error if it's missing.""" microscope = find_component("org.openflexure.microscope") @@ -96,9 +90,7 @@ def find_microscope(): ) if not hasattr(microscope.camera, "picamera"): - abort( - 503, "The PiCamera calibration plugin requires a Raspberry Pi camera." - ) + abort(503, "The PiCamera calibration plugin requires a Raspberry Pi camera.") return microscope @@ -106,10 +98,15 @@ def find_microscope(): class RecalibrateView(ActionView): args = { "skip_auto_exposure": fields.Bool( - missing=False, - description="Whether to " + missing=False, + description=( + "By default we perform an auto-exposure and AWB " + "step before updating the lens shading table. Set " + "this argument to True to disable those steps." + ), ) } + def post(self, args): """Reset the camera's settings. @@ -184,8 +181,7 @@ class AutoExposureFromRawView(ActionView): def post(self, args): camera = find_component("org.openflexure.microscope").camera.picamera adjust_shutter_and_gain_from_raw( - camera, - **args # I'm relying on the schema to fill in missing values + camera, **args # I'm relying on the schema to fill in missing values ) @@ -206,8 +202,7 @@ class AutoWhiteBalanceFromRawView(ActionView): def post(self, args): camera = find_component("org.openflexure.microscope").camera.picamera adjust_white_balance_from_raw( - camera, - **args # I'm relying on the schema to fill in missing values + camera, **args # I'm relying on the schema to fill in missing values ) 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 b8b46044..788d856d 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py @@ -7,6 +7,12 @@ import numpy as np from picamerax import PiCamera from picamerax.array import PiBayerArray, PiRGBArray +# I have used f strings throughout, for readability. +# The performance implication of using them in logging statements +# really doesn't matter here - also, loglevel is usually set to +# INFO to report progress via the LabThings action API +# pylint: disable=logging-fstring-interpolation + def rgb_image( camera: PiCamera, resize: Optional[Tuple[int, int]] = None, **kwargs @@ -66,6 +72,12 @@ def adjust_exposure_and_gain( iterations = 0 while True: iterations += 1 + if iterations > max_iterations: + logging.warning( + f"Auto exposure is stopping after {max_iterations} " + "but it has not converged." + ) + break with PiBayerArray(camera) as a: camera.capture(a, format="jpeg", bayer=True) @@ -86,7 +98,7 @@ def adjust_exposure_and_gain( f"{previous_shutter_speed} to {current_shutter_speed}" ) if current_shutter_speed == previous_shutter_speed: - logging.info(f"Shutter speed has saturated, stopping.") + logging.info("Shutter speed has saturated, stopping.") break if max_brightness > target_white_level: @@ -153,7 +165,6 @@ def adjust_shutter_and_gain_from_raw( # until either the brightness is high enough, or we can't increase the # shutter speed any more. iterations = 0 - shutter_speed_maximised = False shutter_speed_increments = [10, 2, None] shutter_speed_increment = shutter_speed_increments.pop(0) while iterations < max_iterations: @@ -202,14 +213,9 @@ def adjust_shutter_and_gain_from_raw( if current_shutter_speed == tested_shutter_speed: camera.analog_gain *= 2 if camera.analog_gain == tested_analog_gain: - logging.info(f"Shutter speed and gain are both maxed out. Giving up!") + logging.info("Shutter speed and gain are both maxed out. Giving up!") break - logging.info( - f"Shutter speed has maxed out at {current_shutter_speed}, doubled gain." - ) - - max_brightness = np.max(get_channel_percentiles(camera, percentile)) - logging.info(f"Brightness {max_brightness} after {iterations} tries.") + logging.info("Shutter speed has maxed out, doubled gain to compensate.") return max_brightness @@ -330,6 +336,7 @@ def lst_from_channels(channels: np.ndarray) -> np.ndarray: lens_shading_table: np.ndarray = gains.astype(np.uint8) return lens_shading_table[::-1, :, :].copy() + def lst_from_camera(camera: PiCamera) -> np.ndarray: """Acquire a raw image and use it to calculate a lens shading table.""" with PiBayerArray(camera) as a: @@ -343,6 +350,7 @@ def lst_from_camera(camera: PiCamera) -> np.ndarray: channels = channels_from_bayer_array(raw_image) return lst_from_channels(channels) + def recalibrate_camera(camera: PiCamera): """Reset the lens shading table and exposure settings. From ff322decfca35bbf361b29ec5f642aa079ab20b7 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Thu, 11 Feb 2021 19:37:40 +0000 Subject: [PATCH 03/23] updated poetry lockfile --- poetry.lock | 352 ++++++++++++++++++++++++++++------------------------ 1 file changed, 189 insertions(+), 163 deletions(-) diff --git a/poetry.lock b/poetry.lock index 2e5f3cbe..5588bca5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -8,7 +8,7 @@ python-versions = "*" [[package]] name = "apispec" -version = "4.0.0" +version = "4.3.0" description = "A pluggable API specification generator. Currently supports the OpenAPI Specification (f.k.a. the Swagger specification)." category = "main" optional = false @@ -18,12 +18,12 @@ python-versions = ">=3.6" PyYAML = {version = ">=3.10", optional = true, markers = "extra == \"yaml\""} [package.extras] -dev = ["PyYAML (>=3.10)", "prance[osv] (>=0.11)", "marshmallow (>=3.0.0)", "pytest", "mock", "flake8 (==3.8.3)", "flake8-bugbear (==20.1.4)", "pre-commit (>=2.4,<3.0)", "tox"] -docs = ["marshmallow (>=3.0.0)", "pyyaml (==5.3.1)", "sphinx (==3.2.1)", "sphinx-issues (==1.2.0)", "sphinx-rtd-theme (==0.5.0)"] -lint = ["flake8 (==3.8.3)", "flake8-bugbear (==20.1.4)", "pre-commit (>=2.4,<3.0)"] tests = ["PyYAML (>=3.10)", "prance[osv] (>=0.11)", "marshmallow (>=3.0.0)", "pytest", "mock"] -validation = ["prance[osv] (>=0.11)"] +docs = ["marshmallow (>=3.0.0)", "pyyaml (==5.4.1)", "sphinx (==3.4.3)", "sphinx-issues (==1.2.0)", "sphinx-rtd-theme (==0.5.1)"] +lint = ["flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)"] +dev = ["PyYAML (>=3.10)", "prance[osv] (>=0.11)", "marshmallow (>=3.0.0)", "pytest", "mock", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)", "tox"] yaml = ["PyYAML (>=3.10)"] +validation = ["prance[osv] (>=0.11)"] [[package]] name = "apispec-webframeworks" @@ -37,9 +37,9 @@ python-versions = ">=3.6" apispec = {version = ">=2.0.0", extras = ["yaml"]} [package.extras] -dev = ["pytest", "mock", "Flask (==1.1.1)", "tornado", "bottle (==0.12.17)", "flake8 (==3.7.9)", "flake8-bugbear (==19.8.0)", "pre-commit (>=1.18,<2.0)", "tox"] -lint = ["flake8 (==3.7.9)", "flake8-bugbear (==19.8.0)", "pre-commit (>=1.18,<2.0)"] tests = ["pytest", "mock", "Flask (==1.1.1)", "tornado", "bottle (==0.12.17)"] +lint = ["flake8 (==3.7.9)", "flake8-bugbear (==19.8.0)", "pre-commit (>=1.18,<2.0)"] +dev = ["pytest", "mock", "Flask (==1.1.1)", "tornado", "bottle (==0.12.17)", "flake8 (==3.7.9)", "flake8-bugbear (==19.8.0)", "pre-commit (>=1.18,<2.0)", "tox"] [[package]] name = "appdirs" @@ -59,8 +59,8 @@ python-versions = ">=3.5" [package.dependencies] lazy-object-proxy = ">=1.4.0,<1.5.0" -six = ">=1.12,<2.0" typed-ast = {version = ">=1.4.0,<1.5", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""} +six = ">=1.12,<2.0" wrapt = ">=1.11,<2.0" [[package]] @@ -80,10 +80,10 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] docs = ["furo", "sphinx", "zope.interface"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] [[package]] name = "babel" @@ -105,10 +105,10 @@ optional = false python-versions = ">=3.6" [package.dependencies] -appdirs = "*" -attrs = ">=17.4.0" -click = ">=6.5" toml = ">=0.9.4" +attrs = ">=17.4.0" +appdirs = "*" +click = ">=6.5" [package.extras] d = ["aiohttp (>=3.3.2)"] @@ -125,8 +125,8 @@ python-versions = ">=3.6,<4.0" numpy = ">=1.17,<2.0" [package.extras] -correlation = ["opencv-python-headless (>=4.1,<5.0)", "scipy (>=1.4,<2.0)"] all = ["opencv-python-headless (>=4.1,<5.0)", "scipy (>=1.4,<2.0)"] +correlation = ["opencv-python-headless (>=4.1,<5.0)", "scipy (>=1.4,<2.0)"] [[package]] name = "certifi" @@ -169,12 +169,12 @@ optional = false python-versions = "*" [package.extras] -doc = ["sphinx"] test = ["pytest", "coverage", "mock"] +doc = ["sphinx"] [[package]] name = "coverage" -version = "5.3.1" +version = "5.4" description = "Code coverage measurement for Python" category = "main" optional = false @@ -211,15 +211,15 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] -click = ">=5.1" itsdangerous = ">=0.24" Jinja2 = ">=2.10.1" +click = ">=5.1" Werkzeug = ">=0.15" [package.extras] -dev = ["pytest", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] -docs = ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] dotenv = ["python-dotenv"] +docs = ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] +dev = ["pytest", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] [[package]] name = "flask-cors" @@ -235,7 +235,7 @@ Six = "*" [[package]] name = "freezegun" -version = "1.0.0" +version = "1.1.0" description = "Let your Python tests travel through time" category = "dev" optional = false @@ -278,7 +278,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "importlib-metadata" -version = "3.3.0" +version = "3.4.0" description = "Read metadata from Python packages" category = "main" optional = false @@ -289,8 +289,8 @@ typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] [[package]] name = "iniconfig" @@ -309,9 +309,9 @@ optional = false python-versions = ">=3.6,<4.0" [package.extras] +colors = ["colorama (>=0.4.3,<0.5.0)"] pipfile_deprecated_finder = ["pipreqs", "requirementslib"] requirements_deprecated_finder = ["pipreqs", "pip-api"] -colors = ["colorama (>=0.4.3,<0.5.0)"] [[package]] name = "itsdangerous" @@ -323,7 +323,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "jinja2" -version = "2.11.2" +version = "2.11.3" description = "A very fast and expressive template engine." category = "main" optional = false @@ -344,13 +344,13 @@ optional = false python-versions = ">=3.6,<4.0" [package.dependencies] -apispec = ">=3.2,<5.0" apispec_webframeworks = ">=0.5.2,<0.6.0" -Flask = ">=1.1.1,<2.0.0" -flask-cors = ">=3.0.8,<4.0.0" -marshmallow = ">=3.4.0,<4.0.0" webargs = ">=6,<8" +marshmallow = ">=3.4.0,<4.0.0" +Flask = ">=1.1.1,<2.0.0" +apispec = ">=3.2,<5.0" zeroconf = ">=0.24.5,<0.29.0" +flask-cors = ">=3.0.8,<4.0.0" [[package]] name = "lazy-object-proxy" @@ -369,10 +369,10 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" [package.extras] +source = ["Cython (>=0.29.7)"] cssselect = ["cssselect (>=0.7)"] html5 = ["html5lib"] htmlsoup = ["beautifulsoup4"] -source = ["Cython (>=0.29.7)"] [[package]] name = "markupsafe" @@ -391,10 +391,10 @@ optional = false python-versions = ">=3.5" [package.extras] -dev = ["pytest", "pytz", "simplejson", "mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)", "tox"] docs = ["sphinx (==3.3.1)", "sphinx-issues (==1.2.0)", "alabaster (==0.7.12)", "sphinx-version-warning (==1.1.2)", "autodocsumm (==0.2.2)"] -lint = ["mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)"] tests = ["pytest", "pytz", "simplejson"] +lint = ["mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)"] +dev = ["pytest", "pytz", "simplejson", "mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)", "tox"] [[package]] name = "mccabe" @@ -439,15 +439,15 @@ python-versions = ">=3.6" [[package]] name = "numpy-stubs" version = "0.0.1" -description = "" +description = "PEP 561 type stubs for numpy" category = "main" optional = false python-versions = "*" develop = false [package.dependencies] +typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} numpy = ">=1.16.0" -typing_extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} [package.source] type = "git" @@ -463,12 +463,9 @@ category = "main" optional = false python-versions = ">=3.6" -[package.dependencies] -numpy = ">=1.17.3" - [[package]] name = "packaging" -version = "20.8" +version = "20.9" description = "Core utilities for Python packages" category = "main" optional = false @@ -497,9 +494,9 @@ python-versions = "*" colorzero = "*" [package.extras] -array = ["numpy"] -doc = ["sphinx"] test = ["coverage", "pytest", "mock", "pillow", "numpy"] +doc = ["sphinx"] +array = ["numpy"] [[package]] name = "piexif" @@ -564,7 +561,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pygments" -version = "2.7.3" +version = "2.7.4" description = "Pygments is a syntax highlighting package written in Python." category = "dev" optional = false @@ -580,10 +577,10 @@ python-versions = ">=3.5.*" [package.dependencies] astroid = ">=2.4.0,<=2.5" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -isort = ">=4.2.5,<6" mccabe = ">=0.6,<0.7" +colorama = {version = "*", markers = "sys_platform == \"win32\""} toml = ">=0.7.1" +isort = ">=4.2.5,<6" [[package]] name = "pyparsing" @@ -606,37 +603,37 @@ cp2110 = ["hidapi"] [[package]] name = "pytest" -version = "6.2.1" +version = "6.2.2" description = "pytest: simple powerful testing with Python" category = "main" optional = false python-versions = ">=3.6" [package.dependencies] -atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} -attrs = ">=19.2.0" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<1.0.0a1" py = ">=1.8.2" +iniconfig = "*" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +colorama = {version = "*", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" toml = "*" +pluggy = ">=0.12,<1.0.0a1" +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] [[package]] name = "pytest-cov" -version = "2.10.1" +version = "2.11.1" description = "Pytest plugin for measuring coverage." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] -coverage = ">=4.4" pytest = ">=4.6" +coverage = ">=5.2.1" [package.extras] testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"] @@ -654,7 +651,7 @@ six = ">=1.5" [[package]] name = "pytz" -version = "2020.5" +version = "2021.1" description = "World timezone definitions, modern and historical" category = "dev" optional = false @@ -662,11 +659,11 @@ python-versions = "*" [[package]] name = "pyyaml" -version = "5.3.1" +version = "5.4.1" description = "YAML parser and emitter for Python" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [[package]] name = "requests" @@ -677,9 +674,9 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] +idna = ">=2.5,<3" certifi = ">=2017.4.17" chardet = ">=3.0.2,<5" -idna = ">=2.5,<3" urllib3 = ">=1.21.1,<1.27" [package.extras] @@ -736,8 +733,8 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "snowballstemmer" -version = "2.0.0" -description = "This package provides 26 stemmers for 25 languages generated from Snowball algorithms." +version = "2.1.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." category = "dev" optional = false python-versions = "*" @@ -751,27 +748,27 @@ optional = false python-versions = ">=3.5" [package.dependencies] -alabaster = ">=0.7,<0.8" -babel = ">=1.3" -colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.12" +sphinxcontrib-qthelp = "*" imagesize = "*" -Jinja2 = ">=2.3" -packaging = "*" -Pygments = ">=2.0" -requests = ">=2.5.0" -snowballstemmer = ">=1.1" -sphinxcontrib-applehelp = "*" sphinxcontrib-devhelp = "*" +snowballstemmer = ">=1.1" +babel = ">=1.3" +alabaster = ">=0.7,<0.8" +sphinxcontrib-serializinghtml = "*" +packaging = "*" +docutils = ">=0.12" +Pygments = ">=2.0" +Jinja2 = ">=2.3" +colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} +requests = ">=2.5.0" +sphinxcontrib-applehelp = "*" sphinxcontrib-htmlhelp = "*" sphinxcontrib-jsmath = "*" -sphinxcontrib-qthelp = "*" -sphinxcontrib-serializinghtml = "*" [package.extras] +test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"] docs = ["sphinxcontrib-websupport"] lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.790)", "docutils-stubs"] -test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"] [[package]] name = "sphinxcontrib-applehelp" @@ -782,8 +779,8 @@ optional = false python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["flake8", "mypy", "docutils-stubs"] [[package]] name = "sphinxcontrib-devhelp" @@ -794,8 +791,8 @@ optional = false python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["flake8", "mypy", "docutils-stubs"] [[package]] name = "sphinxcontrib-htmlhelp" @@ -806,8 +803,8 @@ optional = false python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest", "html5lib"] +lint = ["flake8", "mypy", "docutils-stubs"] [[package]] name = "sphinxcontrib-httpdomain" @@ -841,8 +838,8 @@ optional = false python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["flake8", "mypy", "docutils-stubs"] [[package]] name = "sphinxcontrib-serializinghtml" @@ -853,8 +850,8 @@ optional = false python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["flake8", "mypy", "docutils-stubs"] [[package]] name = "toml" @@ -890,7 +887,7 @@ python-versions = "*" [[package]] name = "urllib3" -version = "1.26.2" +version = "1.26.3" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "dev" optional = false @@ -913,11 +910,11 @@ python-versions = ">=3.6" marshmallow = ">=3.0.0" [package.extras] -dev = ["pytest", "webtest (==2.0.35)", "webtest-aiohttp (==2.0.0)", "pytest-aiohttp (>=0.3.0)", "Flask (>=0.12.5)", "Django (>=2.2.0)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.8)", "mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)", "tox"] docs = ["Sphinx (==3.3.1)", "sphinx-issues (==1.2.0)", "sphinx-typlog-theme (==0.8.0)", "Flask (>=0.12.5)", "Django (>=2.2.0)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.8)"] -frameworks = ["Flask (>=0.12.5)", "Django (>=2.2.0)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.8)"] -lint = ["mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)"] tests = ["pytest", "webtest (==2.0.35)", "webtest-aiohttp (==2.0.0)", "pytest-aiohttp (>=0.3.0)", "Flask (>=0.12.5)", "Django (>=2.2.0)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.8)"] +lint = ["mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)"] +dev = ["pytest", "webtest (==2.0.35)", "webtest-aiohttp (==2.0.0)", "pytest-aiohttp (>=0.3.0)", "Flask (>=0.12.5)", "Django (>=2.2.0)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.8)", "mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)", "tox"] +frameworks = ["Flask (>=0.12.5)", "Django (>=2.2.0)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.8)"] [[package]] name = "werkzeug" @@ -928,8 +925,8 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.extras] -dev = ["pytest", "pytest-timeout", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinx-issues"] watchdog = ["watchdog"] +dev = ["pytest", "pytest-timeout", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinx-issues"] [[package]] name = "wrapt" @@ -968,7 +965,7 @@ rpi = ["RPi.GPIO"] [metadata] lock-version = "1.1" python-versions = "^3.7.3" -content-hash = "57a3c3fcab4e72f4ff6f37b461967e9c60903baa9f879842c93305069165513c" +content-hash = "701b37eec89567411e3263227a4f23a3372c09feb511cfe491fb0e1cbe14b555" [metadata.files] alabaster = [ @@ -976,8 +973,8 @@ alabaster = [ {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, ] apispec = [ - {file = "apispec-4.0.0-py2.py3-none-any.whl", hash = "sha256:20d271f7c8d130719be223fdb122af391ff8d59fb24958c793f632305b87f8ed"}, - {file = "apispec-4.0.0.tar.gz", hash = "sha256:360e28e5e84a4d7023b16de2b897327fe3da63ddc8e01f9165b9113b7fe1c48a"}, + {file = "apispec-4.3.0-py2.py3-none-any.whl", hash = "sha256:83e4d30edce987f4791f950c847d2b8cbe91e55af3d1ac7e3a35011a5c6fdbdd"}, + {file = "apispec-4.3.0.tar.gz", hash = "sha256:5ec0fe72f1422a1198973fcbb48d0eb5c7390f4b0fbe55474fce999ad6826a9b"}, ] apispec-webframeworks = [ {file = "apispec-webframeworks-0.5.2.tar.gz", hash = "sha256:0db35b267914b3f8c562aca0261957dbcb4176f255eacc22520277010818dcf3"}, @@ -1032,55 +1029,55 @@ colorzero = [ {file = "colorzero-1.1.tar.gz", hash = "sha256:acba47119b5d8555680d3cda9afe6ccc5481385ccc3c00084dd973f7aa184599"}, ] coverage = [ - {file = "coverage-5.3.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:fabeeb121735d47d8eab8671b6b031ce08514c86b7ad8f7d5490a7b6dcd6267d"}, - {file = "coverage-5.3.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:7e4d159021c2029b958b2363abec4a11db0ce8cd43abb0d9ce44284cb97217e7"}, - {file = "coverage-5.3.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:378ac77af41350a8c6b8801a66021b52da8a05fd77e578b7380e876c0ce4f528"}, - {file = "coverage-5.3.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e448f56cfeae7b1b3b5bcd99bb377cde7c4eb1970a525c770720a352bc4c8044"}, - {file = "coverage-5.3.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:cc44e3545d908ecf3e5773266c487ad1877be718d9dc65fc7eb6e7d14960985b"}, - {file = "coverage-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:08b3ba72bd981531fd557f67beee376d6700fba183b167857038997ba30dd297"}, - {file = "coverage-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:8dacc4073c359f40fcf73aede8428c35f84639baad7e1b46fce5ab7a8a7be4bb"}, - {file = "coverage-5.3.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ee2f1d1c223c3d2c24e3afbb2dd38be3f03b1a8d6a83ee3d9eb8c36a52bee899"}, - {file = "coverage-5.3.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9a9d4ff06804920388aab69c5ea8a77525cf165356db70131616acd269e19b36"}, - {file = "coverage-5.3.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:782a5c7df9f91979a7a21792e09b34a658058896628217ae6362088b123c8500"}, - {file = "coverage-5.3.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:fda29412a66099af6d6de0baa6bd7c52674de177ec2ad2630ca264142d69c6c7"}, - {file = "coverage-5.3.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:f2c6888eada180814b8583c3e793f3f343a692fc802546eed45f40a001b1169f"}, - {file = "coverage-5.3.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8f33d1156241c43755137288dea619105477961cfa7e47f48dbf96bc2c30720b"}, - {file = "coverage-5.3.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b239711e774c8eb910e9b1ac719f02f5ae4bf35fa0420f438cdc3a7e4e7dd6ec"}, - {file = "coverage-5.3.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:f54de00baf200b4539a5a092a759f000b5f45fd226d6d25a76b0dff71177a714"}, - {file = "coverage-5.3.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:be0416074d7f253865bb67630cf7210cbc14eb05f4099cc0f82430135aaa7a3b"}, - {file = "coverage-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:c46643970dff9f5c976c6512fd35768c4a3819f01f61169d8cdac3f9290903b7"}, - {file = "coverage-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9a4f66259bdd6964d8cf26142733c81fb562252db74ea367d9beb4f815478e72"}, - {file = "coverage-5.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c6e5174f8ca585755988bc278c8bb5d02d9dc2e971591ef4a1baabdf2d99589b"}, - {file = "coverage-5.3.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3911c2ef96e5ddc748a3c8b4702c61986628bb719b8378bf1e4a6184bbd48fe4"}, - {file = "coverage-5.3.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c5ec71fd4a43b6d84ddb88c1df94572479d9a26ef3f150cef3dacefecf888105"}, - {file = "coverage-5.3.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f51dbba78d68a44e99d484ca8c8f604f17e957c1ca09c3ebc2c7e3bbd9ba0448"}, - {file = "coverage-5.3.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:a2070c5affdb3a5e751f24208c5c4f3d5f008fa04d28731416e023c93b275277"}, - {file = "coverage-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:535dc1e6e68fad5355f9984d5637c33badbdc987b0c0d303ee95a6c979c9516f"}, - {file = "coverage-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:a4857f7e2bc6921dbd487c5c88b84f5633de3e7d416c4dc0bb70256775551a6c"}, - {file = "coverage-5.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fac3c432851038b3e6afe086f777732bcf7f6ebbfd90951fa04ee53db6d0bcdd"}, - {file = "coverage-5.3.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cd556c79ad665faeae28020a0ab3bda6cd47d94bec48e36970719b0b86e4dcf4"}, - {file = "coverage-5.3.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a66ca3bdf21c653e47f726ca57f46ba7fc1f260ad99ba783acc3e58e3ebdb9ff"}, - {file = "coverage-5.3.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ab110c48bc3d97b4d19af41865e14531f300b482da21783fdaacd159251890e8"}, - {file = "coverage-5.3.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e52d3d95df81c8f6b2a1685aabffadf2d2d9ad97203a40f8d61e51b70f191e4e"}, - {file = "coverage-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:fa10fee7e32213f5c7b0d6428ea92e3a3fdd6d725590238a3f92c0de1c78b9d2"}, - {file = "coverage-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ce6f3a147b4b1a8b09aae48517ae91139b1b010c5f36423fa2b866a8b23df879"}, - {file = "coverage-5.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:93a280c9eb736a0dcca19296f3c30c720cb41a71b1f9e617f341f0a8e791a69b"}, - {file = "coverage-5.3.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:3102bb2c206700a7d28181dbe04d66b30780cde1d1c02c5f3c165cf3d2489497"}, - {file = "coverage-5.3.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8ffd4b204d7de77b5dd558cdff986a8274796a1e57813ed005b33fd97e29f059"}, - {file = "coverage-5.3.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:a607ae05b6c96057ba86c811d9c43423f35e03874ffb03fbdcd45e0637e8b631"}, - {file = "coverage-5.3.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:3a3c3f8863255f3c31db3889f8055989527173ef6192a283eb6f4db3c579d830"}, - {file = "coverage-5.3.1-cp38-cp38-win32.whl", hash = "sha256:ff1330e8bc996570221b450e2d539134baa9465f5cb98aff0e0f73f34172e0ae"}, - {file = "coverage-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:3498b27d8236057def41de3585f317abae235dd3a11d33e01736ffedb2ef8606"}, - {file = "coverage-5.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ceb499d2b3d1d7b7ba23abe8bf26df5f06ba8c71127f188333dddcf356b4b63f"}, - {file = "coverage-5.3.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:3b14b1da110ea50c8bcbadc3b82c3933974dbeea1832e814aab93ca1163cd4c1"}, - {file = "coverage-5.3.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:76b2775dda7e78680d688daabcb485dc87cf5e3184a0b3e012e1d40e38527cc8"}, - {file = "coverage-5.3.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:cef06fb382557f66d81d804230c11ab292d94b840b3cb7bf4450778377b592f4"}, - {file = "coverage-5.3.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f61319e33222591f885c598e3e24f6a4be3533c1d70c19e0dc59e83a71ce27d"}, - {file = "coverage-5.3.1-cp39-cp39-win32.whl", hash = "sha256:cc6f8246e74dd210d7e2b56c76ceaba1cc52b025cd75dbe96eb48791e0250e98"}, - {file = "coverage-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:2757fa64e11ec12220968f65d086b7a29b6583d16e9a544c889b22ba98555ef1"}, - {file = "coverage-5.3.1-pp36-none-any.whl", hash = "sha256:723d22d324e7997a651478e9c5a3120a0ecbc9a7e94071f7e1954562a8806cf3"}, - {file = "coverage-5.3.1-pp37-none-any.whl", hash = "sha256:c89b558f8a9a5a6f2cfc923c304d49f0ce629c3bd85cb442ca258ec20366394c"}, - {file = "coverage-5.3.1.tar.gz", hash = "sha256:38f16b1317b8dd82df67ed5daa5f5e7c959e46579840d77a67a4ceb9cef0a50b"}, + {file = "coverage-5.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:6d9c88b787638a451f41f97446a1c9fd416e669b4d9717ae4615bd29de1ac135"}, + {file = "coverage-5.4-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:66a5aae8233d766a877c5ef293ec5ab9520929c2578fd2069308a98b7374ea8c"}, + {file = "coverage-5.4-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9754a5c265f991317de2bac0c70a746efc2b695cf4d49f5d2cddeac36544fb44"}, + {file = "coverage-5.4-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:fbb17c0d0822684b7d6c09915677a32319f16ff1115df5ec05bdcaaee40b35f3"}, + {file = "coverage-5.4-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:b7f7421841f8db443855d2854e25914a79a1ff48ae92f70d0a5c2f8907ab98c9"}, + {file = "coverage-5.4-cp27-cp27m-win32.whl", hash = "sha256:4a780807e80479f281d47ee4af2eb2df3e4ccf4723484f77da0bb49d027e40a1"}, + {file = "coverage-5.4-cp27-cp27m-win_amd64.whl", hash = "sha256:87c4b38288f71acd2106f5d94f575bc2136ea2887fdb5dfe18003c881fa6b370"}, + {file = "coverage-5.4-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:c6809ebcbf6c1049002b9ac09c127ae43929042ec1f1dbd8bb1615f7cd9f70a0"}, + {file = "coverage-5.4-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ba7ca81b6d60a9f7a0b4b4e175dcc38e8fef4992673d9d6e6879fd6de00dd9b8"}, + {file = "coverage-5.4-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:89fc12c6371bf963809abc46cced4a01ca4f99cba17be5e7d416ed7ef1245d19"}, + {file = "coverage-5.4-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a8eb7785bd23565b542b01fb39115a975fefb4a82f23d407503eee2c0106247"}, + {file = "coverage-5.4-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:7e40d3f8eb472c1509b12ac2a7e24158ec352fc8567b77ab02c0db053927e339"}, + {file = "coverage-5.4-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1ccae21a076d3d5f471700f6d30eb486da1626c380b23c70ae32ab823e453337"}, + {file = "coverage-5.4-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:755c56beeacac6a24c8e1074f89f34f4373abce8b662470d3aa719ae304931f3"}, + {file = "coverage-5.4-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:322549b880b2d746a7672bf6ff9ed3f895e9c9f108b714e7360292aa5c5d7cf4"}, + {file = "coverage-5.4-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:60a3307a84ec60578accd35d7f0c71a3a971430ed7eca6567399d2b50ef37b8c"}, + {file = "coverage-5.4-cp35-cp35m-win32.whl", hash = "sha256:1375bb8b88cb050a2d4e0da901001347a44302aeadb8ceb4b6e5aa373b8ea68f"}, + {file = "coverage-5.4-cp35-cp35m-win_amd64.whl", hash = "sha256:16baa799ec09cc0dcb43a10680573269d407c159325972dd7114ee7649e56c66"}, + {file = "coverage-5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2f2cf7a42d4b7654c9a67b9d091ec24374f7c58794858bff632a2039cb15984d"}, + {file = "coverage-5.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:b62046592b44263fa7570f1117d372ae3f310222af1fc1407416f037fb3af21b"}, + {file = "coverage-5.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:812eaf4939ef2284d29653bcfee9665f11f013724f07258928f849a2306ea9f9"}, + {file = "coverage-5.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:859f0add98707b182b4867359e12bde806b82483fb12a9ae868a77880fc3b7af"}, + {file = "coverage-5.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:04b14e45d6a8e159c9767ae57ecb34563ad93440fc1b26516a89ceb5b33c1ad5"}, + {file = "coverage-5.4-cp36-cp36m-win32.whl", hash = "sha256:ebfa374067af240d079ef97b8064478f3bf71038b78b017eb6ec93ede1b6bcec"}, + {file = "coverage-5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:84df004223fd0550d0ea7a37882e5c889f3c6d45535c639ce9802293b39cd5c9"}, + {file = "coverage-5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1b811662ecf72eb2d08872731636aee6559cae21862c36f74703be727b45df90"}, + {file = "coverage-5.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6b588b5cf51dc0fd1c9e19f622457cc74b7d26fe295432e434525f1c0fae02bc"}, + {file = "coverage-5.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3fe50f1cac369b02d34ad904dfe0771acc483f82a1b54c5e93632916ba847b37"}, + {file = "coverage-5.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:32ab83016c24c5cf3db2943286b85b0a172dae08c58d0f53875235219b676409"}, + {file = "coverage-5.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:68fb816a5dd901c6aff352ce49e2a0ffadacdf9b6fae282a69e7a16a02dad5fb"}, + {file = "coverage-5.4-cp37-cp37m-win32.whl", hash = "sha256:a636160680c6e526b84f85d304e2f0bb4e94f8284dd765a1911de9a40450b10a"}, + {file = "coverage-5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:bb32ca14b4d04e172c541c69eec5f385f9a075b38fb22d765d8b0ce3af3a0c22"}, + {file = "coverage-5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4d7165a4e8f41eca6b990c12ee7f44fef3932fac48ca32cecb3a1b2223c21f"}, + {file = "coverage-5.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a565f48c4aae72d1d3d3f8e8fb7218f5609c964e9c6f68604608e5958b9c60c3"}, + {file = "coverage-5.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fff1f3a586246110f34dc762098b5afd2de88de507559e63553d7da643053786"}, + {file = "coverage-5.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:a839e25f07e428a87d17d857d9935dd743130e77ff46524abb992b962eb2076c"}, + {file = "coverage-5.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:6625e52b6f346a283c3d563d1fd8bae8956daafc64bb5bbd2b8f8a07608e3994"}, + {file = "coverage-5.4-cp38-cp38-win32.whl", hash = "sha256:5bee3970617b3d74759b2d2df2f6a327d372f9732f9ccbf03fa591b5f7581e39"}, + {file = "coverage-5.4-cp38-cp38-win_amd64.whl", hash = "sha256:03ed2a641e412e42cc35c244508cf186015c217f0e4d496bf6d7078ebe837ae7"}, + {file = "coverage-5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:14a9f1887591684fb59fdba8feef7123a0da2424b0652e1b58dd5b9a7bb1188c"}, + {file = "coverage-5.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9564ac7eb1652c3701ac691ca72934dd3009997c81266807aef924012df2f4b3"}, + {file = "coverage-5.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:0f48fc7dc82ee14aeaedb986e175a429d24129b7eada1b7e94a864e4f0644dde"}, + {file = "coverage-5.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:107d327071061fd4f4a2587d14c389a27e4e5c93c7cba5f1f59987181903902f"}, + {file = "coverage-5.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:0cdde51bfcf6b6bd862ee9be324521ec619b20590787d1655d005c3fb175005f"}, + {file = "coverage-5.4-cp39-cp39-win32.whl", hash = "sha256:c67734cff78383a1f23ceba3b3239c7deefc62ac2b05fa6a47bcd565771e5880"}, + {file = "coverage-5.4-cp39-cp39-win_amd64.whl", hash = "sha256:c669b440ce46ae3abe9b2d44a913b5fd86bb19eb14a8701e88e3918902ecd345"}, + {file = "coverage-5.4-pp36-none-any.whl", hash = "sha256:c0ff1c1b4d13e2240821ef23c1efb1f009207cb3f56e16986f713c2b0e7cd37f"}, + {file = "coverage-5.4-pp37-none-any.whl", hash = "sha256:cd601187476c6bed26a0398353212684c427e10a903aeafa6da40c63309d438b"}, + {file = "coverage-5.4.tar.gz", hash = "sha256:6d2e262e5e8da6fa56e774fb8e2643417351427604c2b177f8e8c5f75fc928ca"}, ] docutils = [ {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, @@ -1098,8 +1095,8 @@ flask-cors = [ {file = "Flask_Cors-3.0.10-py2.py3-none-any.whl", hash = "sha256:74efc975af1194fc7891ff5cd85b0f7478be4f7f59fe158102e91abb72bb4438"}, ] freezegun = [ - {file = "freezegun-1.0.0-py2.py3-none-any.whl", hash = "sha256:02b35de52f4699a78f6ac4518e4cd3390dddc43b0aeb978335a8f270a2d9668b"}, - {file = "freezegun-1.0.0.tar.gz", hash = "sha256:1cf08e441f913ff5e59b19cc065a8faa9dd1ddc442eaf0375294f344581a0643"}, + {file = "freezegun-1.1.0-py2.py3-none-any.whl", hash = "sha256:2ae695f7eb96c62529f03a038461afe3c692db3465e215355e1bb4b0ab408712"}, + {file = "freezegun-1.1.0.tar.gz", hash = "sha256:177f9dd59861d871e27a484c3332f35a6e3f5d14626f2bf91be37891f18927f3"}, ] future = [ {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, @@ -1117,8 +1114,8 @@ imagesize = [ {file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"}, ] importlib-metadata = [ - {file = "importlib_metadata-3.3.0-py3-none-any.whl", hash = "sha256:bf792d480abbd5eda85794e4afb09dd538393f7d6e6ffef6e9f03d2014cf9450"}, - {file = "importlib_metadata-3.3.0.tar.gz", hash = "sha256:5c5a2720817414a6c41f0a49993908068243ae02c1635a228126519b509c8aed"}, + {file = "importlib_metadata-3.4.0-py3-none-any.whl", hash = "sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771"}, + {file = "importlib_metadata-3.4.0.tar.gz", hash = "sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, @@ -1133,8 +1130,8 @@ itsdangerous = [ {file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19"}, ] jinja2 = [ - {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"}, - {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"}, + {file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"}, + {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"}, ] labthings = [ {file = "labthings-1.2.3-py3-none-any.whl", hash = "sha256:525ac63509554537cfae74f97724b56ea1251ac08a3245fce9a2326dfba9d9fa"}, @@ -1221,20 +1218,39 @@ markupsafe = [ {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b"}, {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-win32.whl", hash = "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8"}, {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, ] marshmallow = [ @@ -1313,8 +1329,8 @@ opencv-python-headless = [ {file = "opencv_python_headless-4.4.0.44-cp38-cp38-win_amd64.whl", hash = "sha256:93f251c28739d8e8ade8898ddcbd75dcec60f779d9534644c568e7e65e2b76de"}, ] packaging = [ - {file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"}, - {file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"}, + {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, + {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, ] pastel = [ {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, @@ -1401,8 +1417,8 @@ py = [ {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, ] pygments = [ - {file = "Pygments-2.7.3-py3-none-any.whl", hash = "sha256:f275b6c0909e5dafd2d6269a656aa90fa58ebf4a74f8fcf9053195d226b24a08"}, - {file = "Pygments-2.7.3.tar.gz", hash = "sha256:ccf3acacf3782cbed4a989426012f1c535c9a90d3a7fc3f16d231b9372d2b716"}, + {file = "Pygments-2.7.4-py3-none-any.whl", hash = "sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435"}, + {file = "Pygments-2.7.4.tar.gz", hash = "sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337"}, ] pylint = [ {file = "pylint-2.6.0-py3-none-any.whl", hash = "sha256:bfe68f020f8a0fece830a22dd4d5dddb4ecc6137db04face4c3420a46a52239f"}, @@ -1417,33 +1433,43 @@ pyserial = [ {file = "pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb"}, ] pytest = [ - {file = "pytest-6.2.1-py3-none-any.whl", hash = "sha256:1969f797a1a0dbd8ccf0fecc80262312729afea9c17f1d70ebf85c5e76c6f7c8"}, - {file = "pytest-6.2.1.tar.gz", hash = "sha256:66e419b1899bc27346cb2c993e12c5e5e8daba9073c1fbce33b9807abc95c306"}, + {file = "pytest-6.2.2-py3-none-any.whl", hash = "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"}, + {file = "pytest-6.2.2.tar.gz", hash = "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9"}, ] pytest-cov = [ - {file = "pytest-cov-2.10.1.tar.gz", hash = "sha256:47bd0ce14056fdd79f93e1713f88fad7bdcc583dcd7783da86ef2f085a0bb88e"}, - {file = "pytest_cov-2.10.1-py2.py3-none-any.whl", hash = "sha256:45ec2d5182f89a81fc3eb29e3d1ed3113b9e9a873bcddb2a71faaab066110191"}, + {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"}, + {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"}, ] python-dateutil = [ {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, ] pytz = [ - {file = "pytz-2020.5-py2.py3-none-any.whl", hash = "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4"}, - {file = "pytz-2020.5.tar.gz", hash = "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"}, + {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, + {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"}, ] pyyaml = [ - {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, - {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, - {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"}, - {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"}, - {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"}, - {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"}, - {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"}, - {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, - {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, - {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, - {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, + {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, + {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, + {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, + {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, + {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, + {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, + {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, + {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, ] requests = [ {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, @@ -1487,8 +1513,8 @@ six = [ {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, ] snowballstemmer = [ - {file = "snowballstemmer-2.0.0-py2.py3-none-any.whl", hash = "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0"}, - {file = "snowballstemmer-2.0.0.tar.gz", hash = "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"}, + {file = "snowballstemmer-2.1.0-py2.py3-none-any.whl", hash = "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2"}, + {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, ] sphinx = [ {file = "Sphinx-3.4.3-py3-none-any.whl", hash = "sha256:c314c857e7cd47c856d2c5adff514ac2e6495f8b8e0f886a8a37e9305dfea0d8"}, @@ -1568,8 +1594,8 @@ typing-extensions = [ {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, ] urllib3 = [ - {file = "urllib3-1.26.2-py2.py3-none-any.whl", hash = "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473"}, - {file = "urllib3-1.26.2.tar.gz", hash = "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08"}, + {file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"}, + {file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"}, ] webargs = [ {file = "webargs-7.0.1-py2.py3-none-any.whl", hash = "sha256:ce8c565789ece1584be7edba41617319eb75de59b2987958bee3f3fdb3669e60"}, From 0f1a4009795b9b56d86ee9d0a244c0ebac41ba0b Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Mon, 15 Feb 2021 17:04:44 +0000 Subject: [PATCH 04/23] Fixed type hint --- .../picamera_autocalibrate/recalibrate_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 788d856d..1f804b2c 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py @@ -221,7 +221,7 @@ def adjust_shutter_and_gain_from_raw( def adjust_white_balance_from_raw( camera: PiCamera, percentile: float = 99 -) -> (float, float): +) -> Tuple[float, float]: """Adjust the white balance in a single shot, based on the raw image. NB if ``channels_from_raw_image`` is broken, this will go haywire. From 2f6049610f3c5152c3cd2dd6af5101f7f31c2568 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Mon, 15 Feb 2021 17:50:39 +0000 Subject: [PATCH 05/23] Added controls for AWB gains, I also increased the precision of the numbers, which makes the step buttons annoyingly useless but allows floating point values without validation errors. --- .../settingsComponents/cameraSettings.vue | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/openflexure_microscope/api/static/src/components/tabContentComponents/settingsComponents/cameraSettings.vue b/openflexure_microscope/api/static/src/components/tabContentComponents/settingsComponents/cameraSettings.vue index 8e37a0b9..67c1fdda 100644 --- a/openflexure_microscope/api/static/src/components/tabContentComponents/settingsComponents/cameraSettings.vue +++ b/openflexure_microscope/api/static/src/components/tabContentComponents/settingsComponents/cameraSettings.vue @@ -3,7 +3,7 @@

Manual camera settings

-
+
  • @@ -31,7 +31,7 @@ v-model="picamera.analog_gain" class="uk-input uk-form-small" type="number" - step="0.000001" + step="0.0000000000000001" />
@@ -45,7 +45,29 @@ v-model="picamera.digital_gain" class="uk-input uk-form-small" type="number" - step="0.000001" + step="0.0000000000000001" + /> +
+ + +
+ +
+ + + +
@@ -194,7 +216,8 @@ export default { shutter_speed: undefined, analog_gain: undefined, digital_gain: undefined, - framerate: undefined + framerate: undefined, + awb_gains: undefined }, mjpeg_bitrate: undefined, stream_resolution: undefined, @@ -244,6 +267,7 @@ export default { this.picamera.digital_gain = cameraSettings.picamera.digital_gain; this.picamera.shutter_speed = cameraSettings.picamera.shutter_speed; this.picamera.framerate = cameraSettings.picamera.framerate; + this.picamera.awb_gains = cameraSettings.picamera.awb_gains; } }) .catch(error => { @@ -263,7 +287,11 @@ export default { shutter_speed: parseFloat(this.picamera.shutter_speed), analog_gain: parseFloat(this.picamera.analog_gain), digital_gain: parseFloat(this.picamera.digital_gain), - framerate: parseInt(this.picamera.framerate) + framerate: parseInt(this.picamera.framerate), + awb_gains: [ + parseFloat(this.picamera.awb_gains[0]), + parseFloat(this.picamera.awb_gains[1]), + ], } } }; From 36284c10bebc2576a1c3e7bb5897035fc0dc97c1 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Mon, 15 Feb 2021 19:17:20 +0000 Subject: [PATCH 06/23] UI Improvements I've added buttons to perform the separate calibrations (gain, AWB) under the full recalibrate link. It would be really nice to have a modal dialog though. --- .../cameraCalibrationSettings.vue | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/openflexure_microscope/api/static/src/components/tabContentComponents/settingsComponents/cameraSettingsComponents/cameraCalibrationSettings.vue b/openflexure_microscope/api/static/src/components/tabContentComponents/settingsComponents/cameraSettingsComponents/cameraCalibrationSettings.vue index e6f3c8d8..09e38e15 100644 --- a/openflexure_microscope/api/static/src/components/tabContentComponents/settingsComponents/cameraSettingsComponents/cameraCalibrationSettings.vue +++ b/openflexure_microscope/api/static/src/components/tabContentComponents/settingsComponents/cameraSettingsComponents/cameraCalibrationSettings.vue @@ -9,7 +9,29 @@ 'Start recalibration? This may take a while, and the microscope will be locked during this time.' " :submit-url="recalibrationLinks.recalibrate.href" - :submit-label="'Auto-Calibrate'" + :submit-label="'Full Auto-Calibrate'" + @response="onRecalibrateResponse" + @error="modalError" + > + + +
+ + +
+
+ @@ -24,14 +46,6 @@ > Disable flat-field correction - -
From a9709b14eaacfd1c947ce359071bf39234145643 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Mon, 15 Feb 2021 19:21:27 +0000 Subject: [PATCH 07/23] Added locking to recalibrate functions When testing the UI I noticed that the different recalibrate routines don't use locks properly, so they try to run concurrently. This commit isn't tested yet... --- .../picamera_autocalibrate/extension.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py index 1f8caf88..5ff1a4dc 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py @@ -117,13 +117,14 @@ class RecalibrateView(ActionView): microscope = find_microscope() logging.info("Starting microscope recalibration...") picamera = microscope.camera.picamera - if not args.get("skip_auto_exposure"): - adjust_shutter_and_gain_from_raw(picamera) - adjust_white_balance_from_raw(picamera) - lst = lst_from_camera(picamera) - with pause_stream(microscope.camera) as scamera: - scamera.picamera.lens_shading_table = lst - microscope.save_settings() + with microscope.camera.lock: + if not args.get("skip_auto_exposure"): + adjust_shutter_and_gain_from_raw(picamera) + adjust_white_balance_from_raw(picamera) + lst = lst_from_camera(picamera) + with pause_stream(microscope.camera) as scamera: + scamera.picamera.lens_shading_table = lst + microscope.save_settings() class FlattenLSTView(ActionView): @@ -179,10 +180,12 @@ class AutoExposureFromRawView(ActionView): } def post(self, args): - camera = find_component("org.openflexure.microscope").camera.picamera - adjust_shutter_and_gain_from_raw( - camera, **args # I'm relying on the schema to fill in missing values - ) + camera = find_component("org.openflexure.microscope").camera + + with camera.lock: + adjust_shutter_and_gain_from_raw( + camera.picamera, **args # I'm relying on the schema to fill in missing values + ) class AutoWhiteBalanceFromRawView(ActionView): From 069d27bf153779cdc7ffd73006af1e1063285ce4 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Mon, 15 Feb 2021 20:15:55 +0000 Subject: [PATCH 08/23] Lock access to picamera The picamera object isn't thread safe; I have now wrapped all access in context managers that acquire the BaseCamera.lock I also added an endpoint to reset the LST without auto-gain. --- .../picamera_autocalibrate/extension.py | 108 +++++++++++------- 1 file changed, 64 insertions(+), 44 deletions(-) diff --git a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py index 5ff1a4dc..bdb0022b 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py @@ -6,6 +6,8 @@ from flask import abort from labthings import find_component from labthings.extensions import BaseExtension from labthings.views import ActionView +from picamerax import PiCamera +from typing import Tuple from openflexure_microscope.camera.base import BaseCamera from openflexure_microscope.microscope import Microscope @@ -78,9 +80,14 @@ class LSTExtension(BaseExtension): "/auto_white_balance_from_raw", endpoint="auto_white_balance_from_raw", ) + self.add_view( + AutoLensShadingTableView, + "/auto_lens_shading_table", + endpoint="auto_lens_shading_table", + ) - -def find_microscope() -> Microscope: +@contextmanager +def find_picamera() -> Tuple[PiCamera, BaseCamera, Microscope]: """Locate the microscope and raise a sensible error if it's missing.""" microscope = find_component("org.openflexure.microscope") @@ -89,23 +96,17 @@ def find_microscope() -> Microscope: 503, "No microscope connected. Unable to use camera calibration functions." ) - if not hasattr(microscope.camera, "picamera"): + scamera = microscope.camera + + if not hasattr(scamera, "picamera"): abort(503, "The PiCamera calibration plugin requires a Raspberry Pi camera.") - return microscope + with scamera.lock: + yield scamera.picamera, scamera, microscope class RecalibrateView(ActionView): - args = { - "skip_auto_exposure": fields.Bool( - missing=False, - description=( - "By default we perform an auto-exposure and AWB " - "step before updating the lens shading table. Set " - "this argument to True to disable those steps." - ), - ) - } + args = {} def post(self, args): """Reset the camera's settings. @@ -113,35 +114,60 @@ class RecalibrateView(ActionView): This generates new gains, exposure time, and lens shading table such that the background is as uniform as possible with a gray level of 230. It takes a little while to run. + + This consists of three steps: + + * Reset gain and exposure time, then increase them until + we have a suitably bright image + * Set the auto white balance based on a raw image + * Set the lens shading table based on a raw image + + Each of these steps has its own endpoint if you want to + perform them separately. """ - microscope = find_microscope() - logging.info("Starting microscope recalibration...") - picamera = microscope.camera.picamera - with microscope.camera.lock: - if not args.get("skip_auto_exposure"): - adjust_shutter_and_gain_from_raw(picamera) - adjust_white_balance_from_raw(picamera) + with find_picamera() as (picamera, scamera, microscope): + logging.info("Starting microscope recalibration...") + adjust_shutter_and_gain_from_raw(picamera) + adjust_white_balance_from_raw(picamera) lst = lst_from_camera(picamera) - with pause_stream(microscope.camera) as scamera: - scamera.picamera.lens_shading_table = lst + with pause_stream(scamera): + picamera.lens_shading_table = lst microscope.save_settings() +class AutoLensShadingTableView(ActionView): + args = {} + + def post(self, args): + """Perform flat-field correction + + This routine acquires a new image (which should be an + empty field of view, i.e. a perfect microscope would + record a uniform white image), and then uses it to set + the "lens shading table" such that future images will + be corrected for vignetting. + """ + with find_picamera() as (picamera, scamera, microscope): + logging.info("Generating lens shading table") + lst = lst_from_camera(picamera) + with pause_stream(scamera): + picamera.lens_shading_table = lst + microscope.save_settings() class FlattenLSTView(ActionView): def post(self): - microscope = find_microscope() - 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() + with find_picamera() as (picamera, scamera, microscope): + with pause_stream(scamera): + flat_lst = flat_lens_shading_table(picamera) + picamera.lens_shading_table = flat_lst + microscope.save_settings() class DeleteLSTView(ActionView): def post(self): - microscope = find_microscope() - with pause_stream(microscope.camera) as scamera: - scamera.camera.lens_shading_table = None - microscope.save_settings() + with find_picamera() as (picamera, scamera, microscope): + with pause_stream(scamera): + picamera.lens_shading_table = None + microscope.save_settings() class AutoExposureFromRawView(ActionView): @@ -180,12 +206,8 @@ class AutoExposureFromRawView(ActionView): } def post(self, args): - camera = find_component("org.openflexure.microscope").camera - - with camera.lock: - adjust_shutter_and_gain_from_raw( - camera.picamera, **args # I'm relying on the schema to fill in missing values - ) + with find_picamera() as (picamera, scamera, microscope): + adjust_shutter_and_gain_from_raw(picamera, **args) class AutoWhiteBalanceFromRawView(ActionView): @@ -203,10 +225,8 @@ class AutoWhiteBalanceFromRawView(ActionView): } def post(self, args): - camera = find_component("org.openflexure.microscope").camera.picamera - adjust_white_balance_from_raw( - camera, **args # I'm relying on the schema to fill in missing values - ) + with find_picamera() as (picamera, scamera, microscope): + adjust_white_balance_from_raw(picamera, **args) class GetRawChannelPercentilesView(ActionView): @@ -219,5 +239,5 @@ class GetRawChannelPercentilesView(ActionView): schema = fields.List(fields.Integer) def post(self, args): - camera = find_component("org.openflexure.microscope").camera.picamera - return get_channel_percentiles(camera, args["percentile"]) + with find_picamera() as (picamera, scamera, microscope): + return get_channel_percentiles(picamera, args["percentile"]) From b06dd187ec52f3b2ff05c624095cfb142ddb3602 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Mon, 15 Feb 2021 20:18:34 +0000 Subject: [PATCH 09/23] Fixed args in endpoints --- .../picamera_autocalibrate/extension.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py index bdb0022b..28f51352 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py @@ -106,9 +106,7 @@ def find_picamera() -> Tuple[PiCamera, BaseCamera, Microscope]: class RecalibrateView(ActionView): - args = {} - - def post(self, args): + def post(self): """Reset the camera's settings. This generates new gains, exposure time, and lens shading @@ -135,9 +133,7 @@ class RecalibrateView(ActionView): microscope.save_settings() class AutoLensShadingTableView(ActionView): - args = {} - - def post(self, args): + def post(self): """Perform flat-field correction This routine acquires a new image (which should be an From 1e45640195524968092f9b32f04dd58c1ae9f69e Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Mon, 15 Feb 2021 20:19:39 +0000 Subject: [PATCH 10/23] Added control to reset LST You can now recalibrate the LST without running auto-gain. --- .../cameraCalibrationSettings.vue | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/openflexure_microscope/api/static/src/components/tabContentComponents/settingsComponents/cameraSettingsComponents/cameraCalibrationSettings.vue b/openflexure_microscope/api/static/src/components/tabContentComponents/settingsComponents/cameraSettingsComponents/cameraCalibrationSettings.vue index 09e38e15..c306e87b 100644 --- a/openflexure_microscope/api/static/src/components/tabContentComponents/settingsComponents/cameraSettingsComponents/cameraCalibrationSettings.vue +++ b/openflexure_microscope/api/static/src/components/tabContentComponents/settingsComponents/cameraSettingsComponents/cameraCalibrationSettings.vue @@ -37,6 +37,21 @@ >
+
+ + +
From 20001e49465e0292192a9618695aa65e561e39b2 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Thu, 18 Feb 2021 13:29:02 +0000 Subject: [PATCH 11/23] Force installation only from piwheels Forcing the use of piwheels avoids trying to install numpy/scipy from source, which is the Right Answer. Unfortunately, sometimes packages fail on piwheels, but poetry still tries to install them. I've had to fix pyyaml at 5.4.0 for that reason. --- poetry.lock | 1039 +++++++++++++++++++++++++----------------------- pyproject.toml | 18 +- 2 files changed, 562 insertions(+), 495 deletions(-) diff --git a/poetry.lock b/poetry.lock index 5588bca5..a900e866 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6,6 +6,11 @@ category = "dev" optional = false python-versions = "*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "apispec" version = "4.3.0" @@ -18,12 +23,17 @@ python-versions = ">=3.6" PyYAML = {version = ">=3.10", optional = true, markers = "extra == \"yaml\""} [package.extras] -tests = ["PyYAML (>=3.10)", "prance[osv] (>=0.11)", "marshmallow (>=3.0.0)", "pytest", "mock"] +dev = ["PyYAML (>=3.10)", "prance[osv] (>=0.11)", "marshmallow (>=3.0.0)", "pytest", "mock", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)", "tox"] docs = ["marshmallow (>=3.0.0)", "pyyaml (==5.4.1)", "sphinx (==3.4.3)", "sphinx-issues (==1.2.0)", "sphinx-rtd-theme (==0.5.1)"] lint = ["flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)"] -dev = ["PyYAML (>=3.10)", "prance[osv] (>=0.11)", "marshmallow (>=3.0.0)", "pytest", "mock", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)", "tox"] -yaml = ["PyYAML (>=3.10)"] +tests = ["PyYAML (>=3.10)", "prance[osv] (>=0.11)", "marshmallow (>=3.0.0)", "pytest", "mock"] validation = ["prance[osv] (>=0.11)"] +yaml = ["PyYAML (>=3.10)"] + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "apispec-webframeworks" @@ -37,9 +47,14 @@ python-versions = ">=3.6" apispec = {version = ">=2.0.0", extras = ["yaml"]} [package.extras] -tests = ["pytest", "mock", "Flask (==1.1.1)", "tornado", "bottle (==0.12.17)"] -lint = ["flake8 (==3.7.9)", "flake8-bugbear (==19.8.0)", "pre-commit (>=1.18,<2.0)"] dev = ["pytest", "mock", "Flask (==1.1.1)", "tornado", "bottle (==0.12.17)", "flake8 (==3.7.9)", "flake8-bugbear (==19.8.0)", "pre-commit (>=1.18,<2.0)", "tox"] +lint = ["flake8 (==3.7.9)", "flake8-bugbear (==19.8.0)", "pre-commit (>=1.18,<2.0)"] +tests = ["pytest", "mock", "Flask (==1.1.1)", "tornado", "bottle (==0.12.17)"] + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "appdirs" @@ -49,6 +64,11 @@ category = "dev" optional = false python-versions = "*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "astroid" version = "2.4.2" @@ -59,10 +79,15 @@ python-versions = ">=3.5" [package.dependencies] lazy-object-proxy = ">=1.4.0,<1.5.0" -typed-ast = {version = ">=1.4.0,<1.5", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""} six = ">=1.12,<2.0" +typed-ast = {version = ">=1.4.0,<1.5", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""} wrapt = ">=1.11,<2.0" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "atomicwrites" version = "1.4.0" @@ -71,6 +96,11 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "attrs" version = "20.3.0" @@ -80,10 +110,15 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] docs = ["furo", "sphinx", "zope.interface"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "babel" @@ -96,6 +131,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.dependencies] pytz = ">=2015.7" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "black" version = "18.9b0" @@ -105,14 +145,19 @@ optional = false python-versions = ">=3.6" [package.dependencies] -toml = ">=0.9.4" -attrs = ">=17.4.0" appdirs = "*" +attrs = ">=17.4.0" click = ">=6.5" +toml = ">=0.9.4" [package.extras] d = ["aiohttp (>=3.3.2)"] +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "camera-stage-mapping" version = "0.1.4" @@ -125,8 +170,13 @@ python-versions = ">=3.6,<4.0" numpy = ">=1.17,<2.0" [package.extras] -all = ["opencv-python-headless (>=4.1,<5.0)", "scipy (>=1.4,<2.0)"] correlation = ["opencv-python-headless (>=4.1,<5.0)", "scipy (>=1.4,<2.0)"] +all = ["opencv-python-headless (>=4.1,<5.0)", "scipy (>=1.4,<2.0)"] + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "certifi" @@ -136,6 +186,11 @@ category = "dev" optional = false python-versions = "*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "chardet" version = "4.0.0" @@ -144,6 +199,11 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "click" version = "7.1.2" @@ -152,6 +212,11 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "colorama" version = "0.4.4" @@ -160,6 +225,11 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "colorzero" version = "1.1" @@ -169,8 +239,13 @@ optional = false python-versions = "*" [package.extras] -test = ["pytest", "coverage", "mock"] doc = ["sphinx"] +test = ["pytest", "coverage", "mock"] + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "coverage" @@ -183,6 +258,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] toml = ["toml"] +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "docutils" version = "0.16" @@ -191,6 +271,11 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "expiringdict" version = "1.2.1" @@ -202,6 +287,11 @@ python-versions = "*" [package.extras] tests = ["dill", "coverage", "coveralls", "mock", "nose"] +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "flask" version = "1.1.2" @@ -211,15 +301,20 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] +click = ">=5.1" itsdangerous = ">=0.24" Jinja2 = ">=2.10.1" -click = ">=5.1" Werkzeug = ">=0.15" [package.extras] -dotenv = ["python-dotenv"] -docs = ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] dev = ["pytest", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] +docs = ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] +dotenv = ["python-dotenv"] + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "flask-cors" @@ -233,6 +328,11 @@ python-versions = "*" Flask = ">=0.9" Six = "*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "freezegun" version = "1.1.0" @@ -244,6 +344,11 @@ python-versions = ">=3.5" [package.dependencies] python-dateutil = ">=2.7" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "future" version = "0.18.2" @@ -252,6 +357,11 @@ category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "idna" version = "2.10" @@ -260,6 +370,11 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "ifaddr" version = "0.1.7" @@ -268,6 +383,11 @@ category = "main" optional = false python-versions = "*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "imagesize" version = "1.2.0" @@ -276,6 +396,11 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "importlib-metadata" version = "3.4.0" @@ -292,6 +417,11 @@ zipp = ">=0.5" docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "iniconfig" version = "1.1.1" @@ -300,6 +430,11 @@ category = "main" optional = false python-versions = "*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "isort" version = "5.7.0" @@ -310,8 +445,13 @@ python-versions = ">=3.6,<4.0" [package.extras] colors = ["colorama (>=0.4.3,<0.5.0)"] +requirements_deprecated_finder = ["pip-api", "pipreqs"] pipfile_deprecated_finder = ["pipreqs", "requirementslib"] -requirements_deprecated_finder = ["pipreqs", "pip-api"] + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "itsdangerous" @@ -321,6 +461,11 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "jinja2" version = "2.11.3" @@ -335,6 +480,11 @@ MarkupSafe = ">=0.23" [package.extras] i18n = ["Babel (>=0.8)"] +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "labthings" version = "1.2.3" @@ -344,13 +494,18 @@ optional = false python-versions = ">=3.6,<4.0" [package.dependencies] -apispec_webframeworks = ">=0.5.2,<0.6.0" -webargs = ">=6,<8" -marshmallow = ">=3.4.0,<4.0.0" -Flask = ">=1.1.1,<2.0.0" apispec = ">=3.2,<5.0" -zeroconf = ">=0.24.5,<0.29.0" +apispec_webframeworks = ">=0.5.2,<0.6.0" +Flask = ">=1.1.1,<2.0.0" flask-cors = ">=3.0.8,<4.0.0" +marshmallow = ">=3.4.0,<4.0.0" +webargs = ">=6,<8" +zeroconf = ">=0.24.5,<0.29.0" + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "lazy-object-proxy" @@ -360,6 +515,11 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "lxml" version = "4.6.2" @@ -369,10 +529,15 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" [package.extras] -source = ["Cython (>=0.29.7)"] cssselect = ["cssselect (>=0.7)"] html5 = ["html5lib"] htmlsoup = ["beautifulsoup4"] +source = ["Cython (>=0.29.7)"] + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "markupsafe" @@ -382,6 +547,11 @@ category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "marshmallow" version = "3.10.0" @@ -391,10 +561,15 @@ optional = false python-versions = ">=3.5" [package.extras] -docs = ["sphinx (==3.3.1)", "sphinx-issues (==1.2.0)", "alabaster (==0.7.12)", "sphinx-version-warning (==1.1.2)", "autodocsumm (==0.2.2)"] -tests = ["pytest", "pytz", "simplejson"] -lint = ["mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)"] dev = ["pytest", "pytz", "simplejson", "mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)", "tox"] +docs = ["sphinx (==3.3.1)", "sphinx-issues (==1.2.0)", "alabaster (==0.7.12)", "sphinx-version-warning (==1.1.2)", "autodocsumm (==0.2.2)"] +lint = ["mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)"] +tests = ["pytest", "pytz", "simplejson"] + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "mccabe" @@ -404,6 +579,11 @@ category = "dev" optional = false python-versions = "*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "mypy" version = "0.790" @@ -420,6 +600,11 @@ typing-extensions = ">=3.7.4" [package.extras] dmypy = ["psutil (>=4.0)"] +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "mypy-extensions" version = "0.4.3" @@ -428,6 +613,11 @@ category = "dev" optional = false python-versions = "*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "numpy" version = "1.19.2" @@ -436,18 +626,23 @@ category = "main" optional = false python-versions = ">=3.6" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "numpy-stubs" version = "0.0.1" -description = "PEP 561 type stubs for numpy" +description = "" category = "main" optional = false python-versions = "*" develop = false [package.dependencies] -typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} numpy = ">=1.16.0" +typing_extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} [package.source] type = "git" @@ -457,12 +652,20 @@ resolved_reference = "c49d2d6875971a669a166ea93ef998911af283a1" [[package]] name = "opencv-python-headless" -version = "4.4.0.44" +version = "4.5.1.48" description = "Wrapper package for OpenCV python bindings." category = "main" optional = false python-versions = ">=3.6" +[package.dependencies] +numpy = ">=1.14.5" + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "packaging" version = "20.9" @@ -474,6 +677,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.dependencies] pyparsing = ">=2.0.2" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "pastel" version = "0.2.1" @@ -482,6 +690,11 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "picamerax" version = "20.9.1" @@ -494,9 +707,14 @@ python-versions = "*" colorzero = "*" [package.extras] -test = ["coverage", "pytest", "mock", "pillow", "numpy"] -doc = ["sphinx"] array = ["numpy"] +doc = ["sphinx"] +test = ["coverage", "pytest", "mock", "pillow", "numpy"] + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "piexif" @@ -506,6 +724,11 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "pillow" version = "7.2.0" @@ -514,6 +737,11 @@ category = "main" optional = false python-versions = ">=3.5" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "pluggy" version = "0.13.1" @@ -528,6 +756,11 @@ importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [package.extras] dev = ["pre-commit", "tox"] +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "poethepoet" version = "0.9.0" @@ -540,6 +773,11 @@ python-versions = ">=3.6,<4.0" pastel = ">=0.2.0,<0.3.0" tomlkit = ">=0.6.0,<1.0.0" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "psutil" version = "5.8.0" @@ -551,6 +789,11 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"] +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "py" version = "1.10.0" @@ -559,28 +802,43 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "pygments" -version = "2.7.4" +version = "2.8.0" description = "Pygments is a syntax highlighting package written in Python." category = "dev" optional = false python-versions = ">=3.5" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "pylint" -version = "2.6.0" +version = "2.6.2" description = "python code static checker" category = "dev" optional = false python-versions = ">=3.5.*" [package.dependencies] -astroid = ">=2.4.0,<=2.5" -mccabe = ">=0.6,<0.7" +astroid = ">=2.4.0,<2.5" colorama = {version = "*", markers = "sys_platform == \"win32\""} -toml = ">=0.7.1" isort = ">=4.2.5,<6" +mccabe = ">=0.6,<0.7" +toml = ">=0.7.1" + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "pyparsing" @@ -590,6 +848,11 @@ category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "pyserial" version = "3.5" @@ -601,6 +864,11 @@ python-versions = "*" [package.extras] cp2110 = ["hidapi"] +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "pytest" version = "6.2.2" @@ -610,19 +878,24 @@ optional = false python-versions = ">=3.6" [package.dependencies] -packaging = "*" -py = ">=1.8.2" -iniconfig = "*" -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -colorama = {version = "*", markers = "sys_platform == \"win32\""} -attrs = ">=19.2.0" -toml = "*" -pluggy = ">=0.12,<1.0.0a1" atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<1.0.0a1" +py = ">=1.8.2" +toml = "*" [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "pytest-cov" version = "2.11.1" @@ -632,12 +905,17 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] -pytest = ">=4.6" coverage = ">=5.2.1" +pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"] +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "python-dateutil" version = "2.8.1" @@ -649,6 +927,11 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" [package.dependencies] six = ">=1.5" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "pytz" version = "2021.1" @@ -657,14 +940,24 @@ category = "dev" optional = false python-versions = "*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "pyyaml" -version = "5.4.1" +version = "5.4" description = "YAML parser and emitter for Python" category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "requests" version = "2.25.1" @@ -674,15 +967,20 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] -idna = ">=2.5,<3" certifi = ">=2017.4.17" chardet = ">=3.0.2,<5" +idna = ">=2.5,<3" urllib3 = ">=1.21.1,<1.27" [package.extras] security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "rope" version = "0.14.0" @@ -691,6 +989,11 @@ category = "dev" optional = false python-versions = "*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "rpi.gpio" version = "0.7.0" @@ -699,6 +1002,11 @@ category = "main" optional = true python-versions = "*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "sangaboard" version = "0.2.3" @@ -712,16 +1020,26 @@ future = "*" numpy = "*" pyserial = "*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "scipy" -version = "1.5.3" +version = "1.6.1" description = "SciPy: Scientific Library for Python" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] -numpy = ">=1.14.5" +numpy = ">=1.16.5" + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "six" @@ -731,6 +1049,11 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "snowballstemmer" version = "2.1.0" @@ -739,36 +1062,46 @@ category = "dev" optional = false python-versions = "*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "sphinx" -version = "3.4.3" +version = "3.5.1" description = "Python documentation generator" category = "dev" optional = false python-versions = ">=3.5" [package.dependencies] -sphinxcontrib-qthelp = "*" -imagesize = "*" -sphinxcontrib-devhelp = "*" -snowballstemmer = ">=1.1" -babel = ">=1.3" alabaster = ">=0.7,<0.8" -sphinxcontrib-serializinghtml = "*" -packaging = "*" -docutils = ">=0.12" -Pygments = ">=2.0" -Jinja2 = ">=2.3" +babel = ">=1.3" colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.12" +imagesize = "*" +Jinja2 = ">=2.3" +packaging = "*" +Pygments = ">=2.0" requests = ">=2.5.0" +snowballstemmer = ">=1.1" sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" sphinxcontrib-htmlhelp = "*" sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = "*" [package.extras] -test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"] docs = ["sphinxcontrib-websupport"] -lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.790)", "docutils-stubs"] +lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.800)", "docutils-stubs"] +test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"] + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "sphinxcontrib-applehelp" @@ -779,8 +1112,13 @@ optional = false python-versions = ">=3.5" [package.extras] -test = ["pytest"] lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest"] + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "sphinxcontrib-devhelp" @@ -791,8 +1129,13 @@ optional = false python-versions = ">=3.5" [package.extras] -test = ["pytest"] lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest"] + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "sphinxcontrib-htmlhelp" @@ -803,8 +1146,13 @@ optional = false python-versions = ">=3.5" [package.extras] -test = ["pytest", "html5lib"] lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest", "html5lib"] + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "sphinxcontrib-httpdomain" @@ -818,6 +1166,11 @@ python-versions = "*" six = "*" Sphinx = ">=1.5" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" @@ -829,6 +1182,11 @@ python-versions = ">=3.5" [package.extras] test = ["pytest", "flake8", "mypy"] +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "sphinxcontrib-qthelp" version = "1.0.3" @@ -838,8 +1196,13 @@ optional = false python-versions = ">=3.5" [package.extras] -test = ["pytest"] lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest"] + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "sphinxcontrib-serializinghtml" @@ -850,8 +1213,13 @@ optional = false python-versions = ">=3.5" [package.extras] -test = ["pytest"] lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest"] + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "toml" @@ -861,6 +1229,11 @@ category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "tomlkit" version = "0.7.0" @@ -869,6 +1242,11 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "typed-ast" version = "1.4.2" @@ -877,6 +1255,11 @@ category = "dev" optional = false python-versions = "*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "typing-extensions" version = "3.7.4.3" @@ -885,6 +1268,11 @@ category = "main" optional = false python-versions = "*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "urllib3" version = "1.26.3" @@ -898,6 +1286,11 @@ brotli = ["brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "webargs" version = "7.0.1" @@ -910,11 +1303,16 @@ python-versions = ">=3.6" marshmallow = ">=3.0.0" [package.extras] -docs = ["Sphinx (==3.3.1)", "sphinx-issues (==1.2.0)", "sphinx-typlog-theme (==0.8.0)", "Flask (>=0.12.5)", "Django (>=2.2.0)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.8)"] -tests = ["pytest", "webtest (==2.0.35)", "webtest-aiohttp (==2.0.0)", "pytest-aiohttp (>=0.3.0)", "Flask (>=0.12.5)", "Django (>=2.2.0)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.8)"] -lint = ["mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)"] dev = ["pytest", "webtest (==2.0.35)", "webtest-aiohttp (==2.0.0)", "pytest-aiohttp (>=0.3.0)", "Flask (>=0.12.5)", "Django (>=2.2.0)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.8)", "mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)", "tox"] +docs = ["Sphinx (==3.3.1)", "sphinx-issues (==1.2.0)", "sphinx-typlog-theme (==0.8.0)", "Flask (>=0.12.5)", "Django (>=2.2.0)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.8)"] frameworks = ["Flask (>=0.12.5)", "Django (>=2.2.0)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.8)"] +lint = ["mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)"] +tests = ["pytest", "webtest (==2.0.35)", "webtest-aiohttp (==2.0.0)", "pytest-aiohttp (>=0.3.0)", "Flask (>=0.12.5)", "Django (>=2.2.0)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.8)"] + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "werkzeug" @@ -925,8 +1323,13 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.extras] -watchdog = ["watchdog"] dev = ["pytest", "pytest-timeout", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinx-issues"] +watchdog = ["watchdog"] + +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" [[package]] name = "wrapt" @@ -936,6 +1339,11 @@ category = "dev" optional = false python-versions = "*" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "zeroconf" version = "0.28.8" @@ -947,6 +1355,11 @@ python-versions = "*" [package.dependencies] ifaddr = ">=0.1.7" +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [[package]] name = "zipp" version = "3.4.0" @@ -959,660 +1372,304 @@ python-versions = ">=3.6" docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] +[package.source] +type = "legacy" +url = "https://www.piwheels.org/simple" +reference = "piwheels" + [extras] rpi = ["RPi.GPIO"] [metadata] lock-version = "1.1" python-versions = "^3.7.3" -content-hash = "701b37eec89567411e3263227a4f23a3372c09feb511cfe491fb0e1cbe14b555" +content-hash = "7144f1f0652692911988bfc402489aa1d693d818e4152c57dca592d1c4f65fe1" [metadata.files] alabaster = [ {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, - {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, ] apispec = [ {file = "apispec-4.3.0-py2.py3-none-any.whl", hash = "sha256:83e4d30edce987f4791f950c847d2b8cbe91e55af3d1ac7e3a35011a5c6fdbdd"}, - {file = "apispec-4.3.0.tar.gz", hash = "sha256:5ec0fe72f1422a1198973fcbb48d0eb5c7390f4b0fbe55474fce999ad6826a9b"}, ] apispec-webframeworks = [ - {file = "apispec-webframeworks-0.5.2.tar.gz", hash = "sha256:0db35b267914b3f8c562aca0261957dbcb4176f255eacc22520277010818dcf3"}, {file = "apispec_webframeworks-0.5.2-py2.py3-none-any.whl", hash = "sha256:482c563abbcc2a261439476cb3f1a7c7284cc997c322c574d48c111643e9c04e"}, ] appdirs = [ {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, ] astroid = [ - {file = "astroid-2.4.2-py3-none-any.whl", hash = "sha256:bc58d83eb610252fd8de6363e39d4f1d0619c894b0ed24603b881c02e64c7386"}, - {file = "astroid-2.4.2.tar.gz", hash = "sha256:2f4078c2a41bf377eea06d71c9d2ba4eb8f6b1af2135bec27bbbb7d8f12bb703"}, + {file = "astroid-2.4.2-py3-none-any.whl", hash = "sha256:ba1e4f22868598dd8d6871a7db5f6c2acf974855fed93d2e3ababadb399962b3"}, ] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, - {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, ] attrs = [ {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, - {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, ] babel = [ {file = "Babel-2.9.0-py2.py3-none-any.whl", hash = "sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5"}, - {file = "Babel-2.9.0.tar.gz", hash = "sha256:da031ab54472314f210b0adcff1588ee5d1d1d0ba4dbd07b94dba82bde791e05"}, ] black = [ {file = "black-18.9b0-py36-none-any.whl", hash = "sha256:817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739"}, - {file = "black-18.9b0.tar.gz", hash = "sha256:e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"}, ] camera-stage-mapping = [ - {file = "camera-stage-mapping-0.1.4.tar.gz", hash = "sha256:02a409bd2cb193b20d9eb015ca31400bd3e1721ec5d702e1b96de54608ff5a8f"}, {file = "camera_stage_mapping-0.1.4-py3-none-any.whl", hash = "sha256:d39c3afebb59ec865fb1247dfd951d7090c159ab0cf3e4291e0869d3be33776c"}, ] certifi = [ {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, - {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, ] chardet = [ {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, - {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, ] click = [ - {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, - {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, + {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:a3747c864f8e400a3664f5f4fd6dae11b4605bf6b727dae7b6f22ba9bd0a194a"}, ] colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, - {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] colorzero = [ {file = "colorzero-1.1-py2.py3-none-any.whl", hash = "sha256:e3c36d15b293de2b2f77ff54a5bd243fffac941ed0a5332d0697a6612a26a0a3"}, - {file = "colorzero-1.1.tar.gz", hash = "sha256:acba47119b5d8555680d3cda9afe6ccc5481385ccc3c00084dd973f7aa184599"}, ] coverage = [ - {file = "coverage-5.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:6d9c88b787638a451f41f97446a1c9fd416e669b4d9717ae4615bd29de1ac135"}, - {file = "coverage-5.4-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:66a5aae8233d766a877c5ef293ec5ab9520929c2578fd2069308a98b7374ea8c"}, - {file = "coverage-5.4-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9754a5c265f991317de2bac0c70a746efc2b695cf4d49f5d2cddeac36544fb44"}, - {file = "coverage-5.4-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:fbb17c0d0822684b7d6c09915677a32319f16ff1115df5ec05bdcaaee40b35f3"}, - {file = "coverage-5.4-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:b7f7421841f8db443855d2854e25914a79a1ff48ae92f70d0a5c2f8907ab98c9"}, - {file = "coverage-5.4-cp27-cp27m-win32.whl", hash = "sha256:4a780807e80479f281d47ee4af2eb2df3e4ccf4723484f77da0bb49d027e40a1"}, - {file = "coverage-5.4-cp27-cp27m-win_amd64.whl", hash = "sha256:87c4b38288f71acd2106f5d94f575bc2136ea2887fdb5dfe18003c881fa6b370"}, - {file = "coverage-5.4-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:c6809ebcbf6c1049002b9ac09c127ae43929042ec1f1dbd8bb1615f7cd9f70a0"}, - {file = "coverage-5.4-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ba7ca81b6d60a9f7a0b4b4e175dcc38e8fef4992673d9d6e6879fd6de00dd9b8"}, - {file = "coverage-5.4-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:89fc12c6371bf963809abc46cced4a01ca4f99cba17be5e7d416ed7ef1245d19"}, - {file = "coverage-5.4-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a8eb7785bd23565b542b01fb39115a975fefb4a82f23d407503eee2c0106247"}, - {file = "coverage-5.4-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:7e40d3f8eb472c1509b12ac2a7e24158ec352fc8567b77ab02c0db053927e339"}, - {file = "coverage-5.4-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1ccae21a076d3d5f471700f6d30eb486da1626c380b23c70ae32ab823e453337"}, - {file = "coverage-5.4-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:755c56beeacac6a24c8e1074f89f34f4373abce8b662470d3aa719ae304931f3"}, - {file = "coverage-5.4-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:322549b880b2d746a7672bf6ff9ed3f895e9c9f108b714e7360292aa5c5d7cf4"}, - {file = "coverage-5.4-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:60a3307a84ec60578accd35d7f0c71a3a971430ed7eca6567399d2b50ef37b8c"}, - {file = "coverage-5.4-cp35-cp35m-win32.whl", hash = "sha256:1375bb8b88cb050a2d4e0da901001347a44302aeadb8ceb4b6e5aa373b8ea68f"}, - {file = "coverage-5.4-cp35-cp35m-win_amd64.whl", hash = "sha256:16baa799ec09cc0dcb43a10680573269d407c159325972dd7114ee7649e56c66"}, - {file = "coverage-5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2f2cf7a42d4b7654c9a67b9d091ec24374f7c58794858bff632a2039cb15984d"}, - {file = "coverage-5.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:b62046592b44263fa7570f1117d372ae3f310222af1fc1407416f037fb3af21b"}, - {file = "coverage-5.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:812eaf4939ef2284d29653bcfee9665f11f013724f07258928f849a2306ea9f9"}, - {file = "coverage-5.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:859f0add98707b182b4867359e12bde806b82483fb12a9ae868a77880fc3b7af"}, - {file = "coverage-5.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:04b14e45d6a8e159c9767ae57ecb34563ad93440fc1b26516a89ceb5b33c1ad5"}, - {file = "coverage-5.4-cp36-cp36m-win32.whl", hash = "sha256:ebfa374067af240d079ef97b8064478f3bf71038b78b017eb6ec93ede1b6bcec"}, - {file = "coverage-5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:84df004223fd0550d0ea7a37882e5c889f3c6d45535c639ce9802293b39cd5c9"}, - {file = "coverage-5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1b811662ecf72eb2d08872731636aee6559cae21862c36f74703be727b45df90"}, - {file = "coverage-5.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6b588b5cf51dc0fd1c9e19f622457cc74b7d26fe295432e434525f1c0fae02bc"}, - {file = "coverage-5.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3fe50f1cac369b02d34ad904dfe0771acc483f82a1b54c5e93632916ba847b37"}, - {file = "coverage-5.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:32ab83016c24c5cf3db2943286b85b0a172dae08c58d0f53875235219b676409"}, - {file = "coverage-5.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:68fb816a5dd901c6aff352ce49e2a0ffadacdf9b6fae282a69e7a16a02dad5fb"}, - {file = "coverage-5.4-cp37-cp37m-win32.whl", hash = "sha256:a636160680c6e526b84f85d304e2f0bb4e94f8284dd765a1911de9a40450b10a"}, - {file = "coverage-5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:bb32ca14b4d04e172c541c69eec5f385f9a075b38fb22d765d8b0ce3af3a0c22"}, - {file = "coverage-5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4d7165a4e8f41eca6b990c12ee7f44fef3932fac48ca32cecb3a1b2223c21f"}, - {file = "coverage-5.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a565f48c4aae72d1d3d3f8e8fb7218f5609c964e9c6f68604608e5958b9c60c3"}, - {file = "coverage-5.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fff1f3a586246110f34dc762098b5afd2de88de507559e63553d7da643053786"}, - {file = "coverage-5.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:a839e25f07e428a87d17d857d9935dd743130e77ff46524abb992b962eb2076c"}, - {file = "coverage-5.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:6625e52b6f346a283c3d563d1fd8bae8956daafc64bb5bbd2b8f8a07608e3994"}, - {file = "coverage-5.4-cp38-cp38-win32.whl", hash = "sha256:5bee3970617b3d74759b2d2df2f6a327d372f9732f9ccbf03fa591b5f7581e39"}, - {file = "coverage-5.4-cp38-cp38-win_amd64.whl", hash = "sha256:03ed2a641e412e42cc35c244508cf186015c217f0e4d496bf6d7078ebe837ae7"}, - {file = "coverage-5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:14a9f1887591684fb59fdba8feef7123a0da2424b0652e1b58dd5b9a7bb1188c"}, - {file = "coverage-5.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9564ac7eb1652c3701ac691ca72934dd3009997c81266807aef924012df2f4b3"}, - {file = "coverage-5.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:0f48fc7dc82ee14aeaedb986e175a429d24129b7eada1b7e94a864e4f0644dde"}, - {file = "coverage-5.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:107d327071061fd4f4a2587d14c389a27e4e5c93c7cba5f1f59987181903902f"}, - {file = "coverage-5.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:0cdde51bfcf6b6bd862ee9be324521ec619b20590787d1655d005c3fb175005f"}, - {file = "coverage-5.4-cp39-cp39-win32.whl", hash = "sha256:c67734cff78383a1f23ceba3b3239c7deefc62ac2b05fa6a47bcd565771e5880"}, - {file = "coverage-5.4-cp39-cp39-win_amd64.whl", hash = "sha256:c669b440ce46ae3abe9b2d44a913b5fd86bb19eb14a8701e88e3918902ecd345"}, - {file = "coverage-5.4-pp36-none-any.whl", hash = "sha256:c0ff1c1b4d13e2240821ef23c1efb1f009207cb3f56e16986f713c2b0e7cd37f"}, - {file = "coverage-5.4-pp37-none-any.whl", hash = "sha256:cd601187476c6bed26a0398353212684c427e10a903aeafa6da40c63309d438b"}, - {file = "coverage-5.4.tar.gz", hash = "sha256:6d2e262e5e8da6fa56e774fb8e2643417351427604c2b177f8e8c5f75fc928ca"}, + {file = "coverage-5.4-cp35-cp35m-linux_armv6l.whl", hash = "sha256:4b40b794775df10d7e3ea677108dd581bfec796235750c617d461874178a67f6"}, + {file = "coverage-5.4-cp35-cp35m-linux_armv7l.whl", hash = "sha256:4b40b794775df10d7e3ea677108dd581bfec796235750c617d461874178a67f6"}, + {file = "coverage-5.4-cp37-cp37m-linux_armv6l.whl", hash = "sha256:ae9702c099546e72000d76758b5efec2dd937ba5d746ec8d0563d2fca0f9bc2e"}, + {file = "coverage-5.4-cp37-cp37m-linux_armv7l.whl", hash = "sha256:ae9702c099546e72000d76758b5efec2dd937ba5d746ec8d0563d2fca0f9bc2e"}, ] docutils = [ {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, - {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"}, ] expiringdict = [ - {file = "expiringdict-1.2.1.tar.gz", hash = "sha256:fe2ba427220425c3c8a3d29f6d2e2985bcee323f8bcd4021e68ebefbd90d8250"}, + {file = "expiringdict-1.2.1-py3-none-any.whl", hash = "sha256:3eb293ca7ff15ed283d8e3ae9bbc5b1cbd2d60c906b08b38c7437380e8a2f697"}, ] flask = [ {file = "Flask-1.1.2-py2.py3-none-any.whl", hash = "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"}, - {file = "Flask-1.1.2.tar.gz", hash = "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060"}, ] flask-cors = [ - {file = "Flask-Cors-3.0.10.tar.gz", hash = "sha256:b60839393f3b84a0f3746f6cdca56c1ad7426aa738b70d6c61375857823181de"}, {file = "Flask_Cors-3.0.10-py2.py3-none-any.whl", hash = "sha256:74efc975af1194fc7891ff5cd85b0f7478be4f7f59fe158102e91abb72bb4438"}, ] freezegun = [ {file = "freezegun-1.1.0-py2.py3-none-any.whl", hash = "sha256:2ae695f7eb96c62529f03a038461afe3c692db3465e215355e1bb4b0ab408712"}, - {file = "freezegun-1.1.0.tar.gz", hash = "sha256:177f9dd59861d871e27a484c3332f35a6e3f5d14626f2bf91be37891f18927f3"}, ] future = [ - {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, + {file = "future-0.18.2-py3-none-any.whl", hash = "sha256:83c4f8cec14cd08179d8b4bfe8c5e48724efadac8add187f385c68d6ae2345e6"}, ] idna = [ - {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, - {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, + {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:4a57a6379512ade94fa99e2fa46d3cd0f2f553040548d0e2958c6ed90ee48226"}, ] ifaddr = [ {file = "ifaddr-0.1.7-py2.py3-none-any.whl", hash = "sha256:d1f603952f0a71c9ab4e705754511e4e03b02565bc4cec7188ad6415ff534cd3"}, - {file = "ifaddr-0.1.7.tar.gz", hash = "sha256:1f9e8a6ca6f16db5a37d3356f07b6e52344f6f9f7e806d618537731669eb1a94"}, ] imagesize = [ {file = "imagesize-1.2.0-py2.py3-none-any.whl", hash = "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1"}, - {file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"}, ] importlib-metadata = [ {file = "importlib_metadata-3.4.0-py3-none-any.whl", hash = "sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771"}, - {file = "importlib_metadata-3.4.0.tar.gz", hash = "sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d"}, ] iniconfig = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:8647b85c03813b8680f4ae9c9db2fd7293f8591ea536a10d73d90f6eb4b10aac"}, ] isort = [ {file = "isort-5.7.0-py3-none-any.whl", hash = "sha256:fff4f0c04e1825522ce6949973e83110a6e907750cd92d128b0d14aaaadbffdc"}, - {file = "isort-5.7.0.tar.gz", hash = "sha256:c729845434366216d320e936b8ad6f9d681aab72dc7cbc2d51bedc3582f3ad1e"}, ] itsdangerous = [ {file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"}, - {file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19"}, ] jinja2 = [ {file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"}, - {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"}, ] labthings = [ {file = "labthings-1.2.3-py3-none-any.whl", hash = "sha256:525ac63509554537cfae74f97724b56ea1251ac08a3245fce9a2326dfba9d9fa"}, - {file = "labthings-1.2.3.tar.gz", hash = "sha256:127e0ea6d5fdbd72c35f9990529f0b43e167cb2ae2ca516344c24798cc373e74"}, ] lazy-object-proxy = [ - {file = "lazy-object-proxy-1.4.3.tar.gz", hash = "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"}, - {file = "lazy_object_proxy-1.4.3-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442"}, - {file = "lazy_object_proxy-1.4.3-cp27-cp27m-win32.whl", hash = "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4"}, - {file = "lazy_object_proxy-1.4.3-cp27-cp27m-win_amd64.whl", hash = "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a"}, - {file = "lazy_object_proxy-1.4.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d"}, - {file = "lazy_object_proxy-1.4.3-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a"}, - {file = "lazy_object_proxy-1.4.3-cp34-cp34m-win32.whl", hash = "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e"}, - {file = "lazy_object_proxy-1.4.3-cp34-cp34m-win_amd64.whl", hash = "sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357"}, - {file = "lazy_object_proxy-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50"}, - {file = "lazy_object_proxy-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db"}, - {file = "lazy_object_proxy-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449"}, - {file = "lazy_object_proxy-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156"}, - {file = "lazy_object_proxy-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531"}, - {file = "lazy_object_proxy-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb"}, - {file = "lazy_object_proxy-1.4.3-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08"}, - {file = "lazy_object_proxy-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383"}, - {file = "lazy_object_proxy-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142"}, - {file = "lazy_object_proxy-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea"}, - {file = "lazy_object_proxy-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62"}, - {file = "lazy_object_proxy-1.4.3-cp38-cp38-win32.whl", hash = "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd"}, - {file = "lazy_object_proxy-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239"}, + {file = "lazy_object_proxy-1.4.3-cp35-cp35m-linux_armv6l.whl", hash = "sha256:c9b1297e959c61d41e049e6dec4ea2622783b3a7e32fd7a7f2390c0d65fe1ab5"}, + {file = "lazy_object_proxy-1.4.3-cp35-cp35m-linux_armv7l.whl", hash = "sha256:c9b1297e959c61d41e049e6dec4ea2622783b3a7e32fd7a7f2390c0d65fe1ab5"}, + {file = "lazy_object_proxy-1.4.3-cp37-cp37m-linux_armv6l.whl", hash = "sha256:f8895ded48ba1319a2b2a6247024cfa7a4e593f6d5e2711b4f957fb777dbb6ea"}, + {file = "lazy_object_proxy-1.4.3-cp37-cp37m-linux_armv7l.whl", hash = "sha256:f8895ded48ba1319a2b2a6247024cfa7a4e593f6d5e2711b4f957fb777dbb6ea"}, ] lxml = [ - {file = "lxml-4.6.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a9d6bc8642e2c67db33f1247a77c53476f3a166e09067c0474facb045756087f"}, - {file = "lxml-4.6.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:791394449e98243839fa822a637177dd42a95f4883ad3dec2a0ce6ac99fb0a9d"}, - {file = "lxml-4.6.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:68a5d77e440df94011214b7db907ec8f19e439507a70c958f750c18d88f995d2"}, - {file = "lxml-4.6.2-cp27-cp27m-win32.whl", hash = "sha256:fc37870d6716b137e80d19241d0e2cff7a7643b925dfa49b4c8ebd1295eb506e"}, - {file = "lxml-4.6.2-cp27-cp27m-win_amd64.whl", hash = "sha256:69a63f83e88138ab7642d8f61418cf3180a4d8cd13995df87725cb8b893e950e"}, - {file = "lxml-4.6.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:42ebca24ba2a21065fb546f3e6bd0c58c3fe9ac298f3a320147029a4850f51a2"}, - {file = "lxml-4.6.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:f83d281bb2a6217cd806f4cf0ddded436790e66f393e124dfe9731f6b3fb9afe"}, - {file = "lxml-4.6.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:535f067002b0fd1a4e5296a8f1bf88193080ff992a195e66964ef2a6cfec5388"}, - {file = "lxml-4.6.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:366cb750140f221523fa062d641393092813b81e15d0e25d9f7c6025f910ee80"}, - {file = "lxml-4.6.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:97db258793d193c7b62d4e2586c6ed98d51086e93f9a3af2b2034af01450a74b"}, - {file = "lxml-4.6.2-cp35-cp35m-win32.whl", hash = "sha256:648914abafe67f11be7d93c1a546068f8eff3c5fa938e1f94509e4a5d682b2d8"}, - {file = "lxml-4.6.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4e751e77006da34643ab782e4a5cc21ea7b755551db202bc4d3a423b307db780"}, - {file = "lxml-4.6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:681d75e1a38a69f1e64ab82fe4b1ed3fd758717bed735fb9aeaa124143f051af"}, - {file = "lxml-4.6.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:127f76864468d6630e1b453d3ffbbd04b024c674f55cf0a30dc2595137892d37"}, - {file = "lxml-4.6.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4fb85c447e288df535b17ebdebf0ec1cf3a3f1a8eba7e79169f4f37af43c6b98"}, - {file = "lxml-4.6.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:5be4a2e212bb6aa045e37f7d48e3e1e4b6fd259882ed5a00786f82e8c37ce77d"}, - {file = "lxml-4.6.2-cp36-cp36m-win32.whl", hash = "sha256:8c88b599e226994ad4db29d93bc149aa1aff3dc3a4355dd5757569ba78632bdf"}, - {file = "lxml-4.6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:6e4183800f16f3679076dfa8abf2db3083919d7e30764a069fb66b2b9eff9939"}, - {file = "lxml-4.6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d8d3d4713f0c28bdc6c806a278d998546e8efc3498949e3ace6e117462ac0a5e"}, - {file = "lxml-4.6.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:8246f30ca34dc712ab07e51dc34fea883c00b7ccb0e614651e49da2c49a30711"}, - {file = "lxml-4.6.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:923963e989ffbceaa210ac37afc9b906acebe945d2723e9679b643513837b089"}, - {file = "lxml-4.6.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:1471cee35eba321827d7d53d104e7b8c593ea3ad376aa2df89533ce8e1b24a01"}, - {file = "lxml-4.6.2-cp37-cp37m-win32.whl", hash = "sha256:2363c35637d2d9d6f26f60a208819e7eafc4305ce39dc1d5005eccc4593331c2"}, - {file = "lxml-4.6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:f4822c0660c3754f1a41a655e37cb4dbbc9be3d35b125a37fab6f82d47674ebc"}, - {file = "lxml-4.6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0448576c148c129594d890265b1a83b9cd76fd1f0a6a04620753d9a6bcfd0a4d"}, - {file = "lxml-4.6.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:60a20bfc3bd234d54d49c388950195d23a5583d4108e1a1d47c9eef8d8c042b3"}, - {file = "lxml-4.6.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2e5cc908fe43fe1aa299e58046ad66981131a66aea3129aac7770c37f590a644"}, - {file = "lxml-4.6.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:50c348995b47b5a4e330362cf39fc503b4a43b14a91c34c83b955e1805c8e308"}, - {file = "lxml-4.6.2-cp38-cp38-win32.whl", hash = "sha256:94d55bd03d8671686e3f012577d9caa5421a07286dd351dfef64791cf7c6c505"}, - {file = "lxml-4.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:7a7669ff50f41225ca5d6ee0a1ec8413f3a0d8aa2b109f86d540887b7ec0d72a"}, - {file = "lxml-4.6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e0bfe9bb028974a481410432dbe1b182e8191d5d40382e5b8ff39cdd2e5c5931"}, - {file = "lxml-4.6.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:6fd8d5903c2e53f49e99359b063df27fdf7acb89a52b6a12494208bf61345a03"}, - {file = "lxml-4.6.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7e9eac1e526386df7c70ef253b792a0a12dd86d833b1d329e038c7a235dfceb5"}, - {file = "lxml-4.6.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:7ee8af0b9f7de635c61cdd5b8534b76c52cd03536f29f51151b377f76e214a1a"}, - {file = "lxml-4.6.2-cp39-cp39-win32.whl", hash = "sha256:2e6fd1b8acd005bd71e6c94f30c055594bbd0aa02ef51a22bbfa961ab63b2d75"}, - {file = "lxml-4.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:535332fe9d00c3cd455bd3dd7d4bacab86e2d564bdf7606079160fa6251caacf"}, - {file = "lxml-4.6.2.tar.gz", hash = "sha256:cd11c7e8d21af997ee8079037fff88f16fda188a9776eb4b81c7e4c9c0a7d7fc"}, + {file = "lxml-4.6.2-cp35-cp35m-linux_armv6l.whl", hash = "sha256:e1dbb88a937126ab14d219a000728224702e0ec0fc7ceb7131c53606b7a76772"}, + {file = "lxml-4.6.2-cp35-cp35m-linux_armv7l.whl", hash = "sha256:e1dbb88a937126ab14d219a000728224702e0ec0fc7ceb7131c53606b7a76772"}, + {file = "lxml-4.6.2-cp37-cp37m-linux_armv6l.whl", hash = "sha256:91d6dace31b07ab47eeadd3f4384ded2f77b94b30446410cb2c3e660e047f7a7"}, + {file = "lxml-4.6.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:91d6dace31b07ab47eeadd3f4384ded2f77b94b30446410cb2c3e660e047f7a7"}, ] markupsafe = [ - {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"}, - {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"}, - {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-win32.whl", hash = "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8"}, - {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-linux_armv6l.whl", hash = "sha256:19536834abffb3fa155017053c607cb835b2ecc6a3a2554a88043d991dffb736"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-linux_armv7l.whl", hash = "sha256:19536834abffb3fa155017053c607cb835b2ecc6a3a2554a88043d991dffb736"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-linux_armv6l.whl", hash = "sha256:7952deddf24b85c88dab48f6ec366ac6e39d2761b5280f2f9594911e03fcd064"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-linux_armv7l.whl", hash = "sha256:7952deddf24b85c88dab48f6ec366ac6e39d2761b5280f2f9594911e03fcd064"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-linux_armv6l.whl", hash = "sha256:3d61f15e39611aacd91b7e71d903787da86d9e80896e683c0103fced9add7834"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:3d61f15e39611aacd91b7e71d903787da86d9e80896e683c0103fced9add7834"}, ] marshmallow = [ {file = "marshmallow-3.10.0-py2.py3-none-any.whl", hash = "sha256:eca81d53aa4aafbc0e20566973d0d2e50ce8bf0ee15165bb799bec0df1e50177"}, - {file = "marshmallow-3.10.0.tar.gz", hash = "sha256:4ab2fdb7f36eb61c3665da67a7ce281c8900db08d72ba6bf0e695828253581f7"}, ] mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] mypy = [ - {file = "mypy-0.790-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:bd03b3cf666bff8d710d633d1c56ab7facbdc204d567715cb3b9f85c6e94f669"}, - {file = "mypy-0.790-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:2170492030f6faa537647d29945786d297e4862765f0b4ac5930ff62e300d802"}, - {file = "mypy-0.790-cp35-cp35m-win_amd64.whl", hash = "sha256:e86bdace26c5fe9cf8cb735e7cedfe7850ad92b327ac5d797c656717d2ca66de"}, - {file = "mypy-0.790-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e97e9c13d67fbe524be17e4d8025d51a7dca38f90de2e462243ab8ed8a9178d1"}, - {file = "mypy-0.790-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0d34d6b122597d48a36d6c59e35341f410d4abfa771d96d04ae2c468dd201abc"}, - {file = "mypy-0.790-cp36-cp36m-win_amd64.whl", hash = "sha256:72060bf64f290fb629bd4a67c707a66fd88ca26e413a91384b18db3876e57ed7"}, - {file = "mypy-0.790-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:eea260feb1830a627fb526d22fbb426b750d9f5a47b624e8d5e7e004359b219c"}, - {file = "mypy-0.790-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c614194e01c85bb2e551c421397e49afb2872c88b5830e3554f0519f9fb1c178"}, - {file = "mypy-0.790-cp37-cp37m-win_amd64.whl", hash = "sha256:0a0d102247c16ce93c97066443d11e2d36e6cc2a32d8ccc1f705268970479324"}, - {file = "mypy-0.790-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cf4e7bf7f1214826cf7333627cb2547c0db7e3078723227820d0a2490f117a01"}, - {file = "mypy-0.790-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:af4e9ff1834e565f1baa74ccf7ae2564ae38c8df2a85b057af1dbbc958eb6666"}, - {file = "mypy-0.790-cp38-cp38-win_amd64.whl", hash = "sha256:da56dedcd7cd502ccd3c5dddc656cb36113dd793ad466e894574125945653cea"}, {file = "mypy-0.790-py3-none-any.whl", hash = "sha256:2842d4fbd1b12ab422346376aad03ff5d0805b706102e475e962370f874a5122"}, - {file = "mypy-0.790.tar.gz", hash = "sha256:2b21ba45ad9ef2e2eb88ce4aeadd0112d0f5026418324176fd494a6824b74975"}, ] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] numpy = [ - {file = "numpy-1.19.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b594f76771bc7fc8a044c5ba303427ee67c17a09b36e1fa32bde82f5c419d17a"}, - {file = "numpy-1.19.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:e6ddbdc5113628f15de7e4911c02aed74a4ccff531842c583e5032f6e5a179bd"}, - {file = "numpy-1.19.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3733640466733441295b0d6d3dcbf8e1ffa7e897d4d82903169529fd3386919a"}, - {file = "numpy-1.19.2-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:4339741994c775396e1a274dba3609c69ab0f16056c1077f18979bec2a2c2e6e"}, - {file = "numpy-1.19.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7c6646314291d8f5ea900a7ea9c4261f834b5b62159ba2abe3836f4fa6705526"}, - {file = "numpy-1.19.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:7118f0a9f2f617f921ec7d278d981244ba83c85eea197be7c5a4f84af80a9c3c"}, - {file = "numpy-1.19.2-cp36-cp36m-win32.whl", hash = "sha256:9a3001248b9231ed73894c773142658bab914645261275f675d86c290c37f66d"}, - {file = "numpy-1.19.2-cp36-cp36m-win_amd64.whl", hash = "sha256:967c92435f0b3ba37a4257c48b8715b76741410467e2bdb1097e8391fccfae15"}, - {file = "numpy-1.19.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d526fa58ae4aead839161535d59ea9565863bb0b0bdb3cc63214613fb16aced4"}, - {file = "numpy-1.19.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:eb25c381d168daf351147713f49c626030dcff7a393d5caa62515d415a6071d8"}, - {file = "numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:62139af94728d22350a571b7c82795b9d59be77fc162414ada6c8b6a10ef5d02"}, - {file = "numpy-1.19.2-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:0c66da1d202c52051625e55a249da35b31f65a81cb56e4c69af0dfb8fb0125bf"}, - {file = "numpy-1.19.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:2117536e968abb7357d34d754e3733b0d7113d4c9f1d921f21a3d96dec5ff716"}, - {file = "numpy-1.19.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:54045b198aebf41bf6bf4088012777c1d11703bf74461d70cd350c0af2182e45"}, - {file = "numpy-1.19.2-cp37-cp37m-win32.whl", hash = "sha256:aba1d5daf1144b956bc87ffb87966791f5e9f3e1f6fab3d7f581db1f5b598f7a"}, - {file = "numpy-1.19.2-cp37-cp37m-win_amd64.whl", hash = "sha256:addaa551b298052c16885fc70408d3848d4e2e7352de4e7a1e13e691abc734c1"}, - {file = "numpy-1.19.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:58d66a6b3b55178a1f8a5fe98df26ace76260a70de694d99577ddeab7eaa9a9d"}, - {file = "numpy-1.19.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:59f3d687faea7a4f7f93bd9665e5b102f32f3fa28514f15b126f099b7997203d"}, - {file = "numpy-1.19.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cebd4f4e64cfe87f2039e4725781f6326a61f095bc77b3716502bed812b385a9"}, - {file = "numpy-1.19.2-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c35a01777f81e7333bcf276b605f39c872e28295441c265cd0c860f4b40148c1"}, - {file = "numpy-1.19.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d7ac33585e1f09e7345aa902c281bd777fdb792432d27fca857f39b70e5dd31c"}, - {file = "numpy-1.19.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:04c7d4ebc5ff93d9822075ddb1751ff392a4375e5885299445fcebf877f179d5"}, - {file = "numpy-1.19.2-cp38-cp38-win32.whl", hash = "sha256:51ee93e1fac3fe08ef54ff1c7f329db64d8a9c5557e6c8e908be9497ac76374b"}, - {file = "numpy-1.19.2-cp38-cp38-win_amd64.whl", hash = "sha256:1669ec8e42f169ff715a904c9b2105b6640f3f2a4c4c2cb4920ae8b2785dac65"}, - {file = "numpy-1.19.2-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:0bfd85053d1e9f60234f28f63d4a5147ada7f432943c113a11afcf3e65d9d4c8"}, - {file = "numpy-1.19.2.zip", hash = "sha256:0d310730e1e793527065ad7dde736197b705d0e4c9999775f212b03c44a8484c"}, + {file = "numpy-1.19.2-cp37-cp37m-linux_armv6l.whl", hash = "sha256:bff0719af5134d472546e601f83e5335ec8919464084b34d602541fc80a33698"}, + {file = "numpy-1.19.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:bff0719af5134d472546e601f83e5335ec8919464084b34d602541fc80a33698"}, ] numpy-stubs = [] opencv-python-headless = [ - {file = "opencv-python-headless-4.4.0.44.tar.gz", hash = "sha256:6eefcacfb9b2da305277e1a93c7bf074dcd10b7aa154a0c963ded08fc0ffc02e"}, - {file = "opencv_python_headless-4.4.0.44-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:0896413b35b4b64acae42b84f740a458e0520d51f806946adf185e710a2ab300"}, - {file = "opencv_python_headless-4.4.0.44-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:b9ac818c9b000389cfc6892c3109dd98d1eab63a6038084c6f8a560c8a696f17"}, - {file = "opencv_python_headless-4.4.0.44-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9fe62a18ddf18d80271f67c36b019044b33941c11aa2c13ba549c3142bdc3a50"}, - {file = "opencv_python_headless-4.4.0.44-cp36-cp36m-win32.whl", hash = "sha256:a70c4d986c90d76514098af979344f7f3eeea694beb44ecdebf6a29e3d0c3774"}, - {file = "opencv_python_headless-4.4.0.44-cp36-cp36m-win_amd64.whl", hash = "sha256:6d3d2efeedd13f452d59caf84f4eb507edf1763edb8e79de2e9aa47c05713b25"}, - {file = "opencv_python_headless-4.4.0.44-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:aa8cb3fe425b637879026661c53985fcabb3c1184bb6c17e2c1257e893b2e4c9"}, - {file = "opencv_python_headless-4.4.0.44-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:b5d38a1100fdf1992170a3190176c3b99321f0e96e32a383f3b60b852e3f599f"}, - {file = "opencv_python_headless-4.4.0.44-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:6003e57e9824a9f098d85ae833268f51356c6f23ea4669dd3ee2092bcbd2c800"}, - {file = "opencv_python_headless-4.4.0.44-cp37-cp37m-win32.whl", hash = "sha256:4ef242b8923c693ad6b2e0ae7023582ebd983e6d487771639d0e9550e01fd120"}, - {file = "opencv_python_headless-4.4.0.44-cp37-cp37m-win_amd64.whl", hash = "sha256:19cd76fcf886033a08556e203c49fdc4bfb4a95325f0b2d18562e6411f7bd475"}, - {file = "opencv_python_headless-4.4.0.44-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:01e54dd61fc849f2c4dea9eb62e056f4332612616fc4d0576cb5a9096ae5818b"}, - {file = "opencv_python_headless-4.4.0.44-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:0c6ab08883e4f026c863a9d785f1ab7546bf2620d873d30fbedebf8e607d8ffc"}, - {file = "opencv_python_headless-4.4.0.44-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4bc859120ce9271669cd5e7a01ac2a479b6f21e81900f1b7325d266c6e3a0e5f"}, - {file = "opencv_python_headless-4.4.0.44-cp38-cp38-win32.whl", hash = "sha256:54dd417aba4bfcc2a6a2110bb6e6fed8a0f8eef2d432f86a1739cfa3e6d6caea"}, - {file = "opencv_python_headless-4.4.0.44-cp38-cp38-win_amd64.whl", hash = "sha256:93f251c28739d8e8ade8898ddcbd75dcec60f779d9534644c568e7e65e2b76de"}, + {file = "opencv_python_headless-4.5.1.48-cp37-cp37m-linux_armv6l.whl", hash = "sha256:e61c8df818d79c6bd62a5a18a9b3bfdf389502798e3c22f95c67f8dcd09ce97b"}, + {file = "opencv_python_headless-4.5.1.48-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e61c8df818d79c6bd62a5a18a9b3bfdf389502798e3c22f95c67f8dcd09ce97b"}, ] packaging = [ {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, - {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, ] pastel = [ {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, - {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, ] picamerax = [ {file = "picamerax-20.9.1-py3-none-any.whl", hash = "sha256:8dc45542644ed9c67e3b331e90bcffa04098e8b056a42d0fedb25769b0b1f17e"}, - {file = "picamerax-20.9.1.tar.gz", hash = "sha256:fccb201ad9e2ab67946111d6bf875289c141ef900fd5875de6d435d219c59440"}, ] piexif = [ {file = "piexif-1.1.3-py2.py3-none-any.whl", hash = "sha256:3bc435d171720150b81b15d27e05e54b8abbde7b4242cddd81ef160d283108b6"}, - {file = "piexif-1.1.3.zip", hash = "sha256:83cb35c606bf3a1ea1a8f0a25cb42cf17e24353fd82e87ae3884e74a302a5f1b"}, ] pillow = [ - {file = "Pillow-7.2.0-cp35-cp35m-macosx_10_10_intel.whl", hash = "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae"}, - {file = "Pillow-7.2.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c92302a33138409e8f1ad16731568c55c9053eee71bb05b6b744067e1b62380f"}, - {file = "Pillow-7.2.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38"}, - {file = "Pillow-7.2.0-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5"}, - {file = "Pillow-7.2.0-cp35-cp35m-win32.whl", hash = "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad"}, - {file = "Pillow-7.2.0-cp35-cp35m-win_amd64.whl", hash = "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f"}, - {file = "Pillow-7.2.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d"}, - {file = "Pillow-7.2.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233"}, - {file = "Pillow-7.2.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f"}, - {file = "Pillow-7.2.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8"}, - {file = "Pillow-7.2.0-cp36-cp36m-win32.whl", hash = "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a"}, - {file = "Pillow-7.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce"}, - {file = "Pillow-7.2.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4"}, - {file = "Pillow-7.2.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727"}, - {file = "Pillow-7.2.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b"}, - {file = "Pillow-7.2.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d"}, - {file = "Pillow-7.2.0-cp37-cp37m-win32.whl", hash = "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63"}, - {file = "Pillow-7.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1"}, - {file = "Pillow-7.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6"}, - {file = "Pillow-7.2.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9"}, - {file = "Pillow-7.2.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41"}, - {file = "Pillow-7.2.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8"}, - {file = "Pillow-7.2.0-cp38-cp38-win32.whl", hash = "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f"}, - {file = "Pillow-7.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6"}, - {file = "Pillow-7.2.0-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:9c87ef410a58dd54b92424ffd7e28fd2ec65d2f7fc02b76f5e9b2067e355ebf6"}, - {file = "Pillow-7.2.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:e901964262a56d9ea3c2693df68bc9860b8bdda2b04768821e4c44ae797de117"}, - {file = "Pillow-7.2.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d"}, - {file = "Pillow-7.2.0.tar.gz", hash = "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626"}, + {file = "Pillow-7.2.0-cp35-cp35m-linux_armv6l.whl", hash = "sha256:39b8830992872e00605ec6eb0d83efad488553f10d79066b8b47c8be38e126b3"}, + {file = "Pillow-7.2.0-cp35-cp35m-linux_armv7l.whl", hash = "sha256:39b8830992872e00605ec6eb0d83efad488553f10d79066b8b47c8be38e126b3"}, + {file = "Pillow-7.2.0-cp37-cp37m-linux_armv6l.whl", hash = "sha256:e6d08a4d39b8a3a503c6e97e53ee2cc1dcd4fd05c4f87e8991f53e979608d0f6"}, + {file = "Pillow-7.2.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e6d08a4d39b8a3a503c6e97e53ee2cc1dcd4fd05c4f87e8991f53e979608d0f6"}, ] pluggy = [ {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, - {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] poethepoet = [ {file = "poethepoet-0.9.0-py3-none-any.whl", hash = "sha256:6b1df9a755c297d5b10749cd4713924055b41edfa62055770c8bd6b5da8e2c69"}, - {file = "poethepoet-0.9.0.tar.gz", hash = "sha256:ab2263fd7be81d16d38a4b4fe42a055d992d04421e61cad36498b1e4bd8ee2a6"}, ] psutil = [ - {file = "psutil-5.8.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64"}, - {file = "psutil-5.8.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0ae6f386d8d297177fd288be6e8d1afc05966878704dad9847719650e44fc49c"}, - {file = "psutil-5.8.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:12d844996d6c2b1d3881cfa6fa201fd635971869a9da945cf6756105af73d2df"}, - {file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:02b8292609b1f7fcb34173b25e48d0da8667bc85f81d7476584d889c6e0f2131"}, - {file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6ffe81843131ee0ffa02c317186ed1e759a145267d54fdef1bc4ea5f5931ab60"}, - {file = "psutil-5.8.0-cp27-none-win32.whl", hash = "sha256:ea313bb02e5e25224e518e4352af4bf5e062755160f77e4b1767dd5ccb65f876"}, - {file = "psutil-5.8.0-cp27-none-win_amd64.whl", hash = "sha256:5da29e394bdedd9144c7331192e20c1f79283fb03b06e6abd3a8ae45ffecee65"}, - {file = "psutil-5.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:74fb2557d1430fff18ff0d72613c5ca30c45cdbfcddd6a5773e9fc1fe9364be8"}, - {file = "psutil-5.8.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:74f2d0be88db96ada78756cb3a3e1b107ce8ab79f65aa885f76d7664e56928f6"}, - {file = "psutil-5.8.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:99de3e8739258b3c3e8669cb9757c9a861b2a25ad0955f8e53ac662d66de61ac"}, - {file = "psutil-5.8.0-cp36-cp36m-win32.whl", hash = "sha256:36b3b6c9e2a34b7d7fbae330a85bf72c30b1c827a4366a07443fc4b6270449e2"}, - {file = "psutil-5.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:52de075468cd394ac98c66f9ca33b2f54ae1d9bff1ef6b67a212ee8f639ec06d"}, - {file = "psutil-5.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c6a5fd10ce6b6344e616cf01cc5b849fa8103fbb5ba507b6b2dee4c11e84c935"}, - {file = "psutil-5.8.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:61f05864b42fedc0771d6d8e49c35f07efd209ade09a5afe6a5059e7bb7bf83d"}, - {file = "psutil-5.8.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:0dd4465a039d343925cdc29023bb6960ccf4e74a65ad53e768403746a9207023"}, - {file = "psutil-5.8.0-cp37-cp37m-win32.whl", hash = "sha256:1bff0d07e76114ec24ee32e7f7f8d0c4b0514b3fae93e3d2aaafd65d22502394"}, - {file = "psutil-5.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:fcc01e900c1d7bee2a37e5d6e4f9194760a93597c97fee89c4ae51701de03563"}, - {file = "psutil-5.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6223d07a1ae93f86451d0198a0c361032c4c93ebd4bf6d25e2fb3edfad9571ef"}, - {file = "psutil-5.8.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d225cd8319aa1d3c85bf195c4e07d17d3cd68636b8fc97e6cf198f782f99af28"}, - {file = "psutil-5.8.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:28ff7c95293ae74bf1ca1a79e8805fcde005c18a122ca983abf676ea3466362b"}, - {file = "psutil-5.8.0-cp38-cp38-win32.whl", hash = "sha256:ce8b867423291cb65cfc6d9c4955ee9bfc1e21fe03bb50e177f2b957f1c2469d"}, - {file = "psutil-5.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:90f31c34d25b1b3ed6c40cdd34ff122b1887a825297c017e4cbd6796dd8b672d"}, - {file = "psutil-5.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6323d5d845c2785efb20aded4726636546b26d3b577aded22492908f7c1bdda7"}, - {file = "psutil-5.8.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:245b5509968ac0bd179287d91210cd3f37add77dad385ef238b275bad35fa1c4"}, - {file = "psutil-5.8.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:90d4091c2d30ddd0a03e0b97e6a33a48628469b99585e2ad6bf21f17423b112b"}, - {file = "psutil-5.8.0-cp39-cp39-win32.whl", hash = "sha256:ea372bcc129394485824ae3e3ddabe67dc0b118d262c568b4d2602a7070afdb0"}, - {file = "psutil-5.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3"}, - {file = "psutil-5.8.0.tar.gz", hash = "sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6"}, + {file = "psutil-5.8.0-cp35-cp35m-linux_armv6l.whl", hash = "sha256:1ef2ab49f9138c6fc67ec9c3c5eac1b1345cf9c990b0c37af50e70d8a0a7b30e"}, + {file = "psutil-5.8.0-cp35-cp35m-linux_armv7l.whl", hash = "sha256:1ef2ab49f9138c6fc67ec9c3c5eac1b1345cf9c990b0c37af50e70d8a0a7b30e"}, + {file = "psutil-5.8.0-cp37-cp37m-linux_armv6l.whl", hash = "sha256:3379573a8f2447c496eec470a09b2071414aa4e527f80a44eacb3068f3b76ff3"}, + {file = "psutil-5.8.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:3379573a8f2447c496eec470a09b2071414aa4e527f80a44eacb3068f3b76ff3"}, ] py = [ {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, - {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, ] pygments = [ - {file = "Pygments-2.7.4-py3-none-any.whl", hash = "sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435"}, - {file = "Pygments-2.7.4.tar.gz", hash = "sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337"}, + {file = "Pygments-2.8.0-py3-none-any.whl", hash = "sha256:b21b072d0ccdf29297a82a2363359d99623597b8a265b8081760e4d0f7153c88"}, ] pylint = [ - {file = "pylint-2.6.0-py3-none-any.whl", hash = "sha256:bfe68f020f8a0fece830a22dd4d5dddb4ecc6137db04face4c3420a46a52239f"}, - {file = "pylint-2.6.0.tar.gz", hash = "sha256:bb4a908c9dadbc3aac18860550e870f58e1a02c9f2c204fdf5693d73be061210"}, + {file = "pylint-2.6.2-py3-none-any.whl", hash = "sha256:e71c2e9614a4f06e36498f310027942b0f4f2fde20aebb01655b31edc63b9eaf"}, ] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, - {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] pyserial = [ {file = "pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0"}, - {file = "pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb"}, ] pytest = [ {file = "pytest-6.2.2-py3-none-any.whl", hash = "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"}, - {file = "pytest-6.2.2.tar.gz", hash = "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9"}, ] pytest-cov = [ - {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"}, {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"}, ] python-dateutil = [ - {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, ] pytz = [ {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, - {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"}, ] pyyaml = [ - {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, - {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, - {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, - {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, - {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, - {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, - {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, - {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, - {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, - {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, - {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, - {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, - {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, - {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, - {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, - {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, - {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, + {file = "PyYAML-5.4-cp37-cp37m-linux_armv6l.whl", hash = "sha256:64a89e4ab1e56c6bd2dcb4578df6a29b480abffcc78eba175fba87eb140da5d0"}, + {file = "PyYAML-5.4-cp37-cp37m-linux_armv7l.whl", hash = "sha256:64a89e4ab1e56c6bd2dcb4578df6a29b480abffcc78eba175fba87eb140da5d0"}, ] requests = [ {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, - {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, ] rope = [ - {file = "rope-0.14.0-py2-none-any.whl", hash = "sha256:6b728fdc3e98a83446c27a91fc5d56808a004f8beab7a31ab1d7224cecc7d969"}, {file = "rope-0.14.0-py3-none-any.whl", hash = "sha256:f0dcf719b63200d492b85535ebe5ea9b29e0d0b8aebeb87fe03fc1a65924fdaf"}, - {file = "rope-0.14.0.tar.gz", hash = "sha256:c5c5a6a87f7b1a2095fb311135e2a3d1f194f5ecb96900fdd0a9100881f48aaf"}, ] "rpi.gpio" = [ - {file = "RPi.GPIO-0.7.0.tar.gz", hash = "sha256:7424bc6c205466764f30f666c18187a0824077daf20b295c42f08aea2cb87d3f"}, + {file = "RPi.GPIO-0.7.0-cp34-cp34m-linux_armv6l.whl", hash = "sha256:5cd2f3c88d0b1e2f4780c905702c0474768c2bc113fb793121c2e117b401ab16"}, + {file = "RPi.GPIO-0.7.0-cp34-cp34m-linux_armv7l.whl", hash = "sha256:5cd2f3c88d0b1e2f4780c905702c0474768c2bc113fb793121c2e117b401ab16"}, + {file = "RPi.GPIO-0.7.0-cp35-cp35m-linux_armv6l.whl", hash = "sha256:7da5235aeba20da39ad4fb97f3ba3a2c6b2d60bba8958b00a4df42f608c6d42e"}, + {file = "RPi.GPIO-0.7.0-cp35-cp35m-linux_armv7l.whl", hash = "sha256:7da5235aeba20da39ad4fb97f3ba3a2c6b2d60bba8958b00a4df42f608c6d42e"}, + {file = "RPi.GPIO-0.7.0-cp37-cp37m-linux_armv6l.whl", hash = "sha256:6a4791f41cafc2ee6e4cb70e5bd31fadc66a0cfab29b38df8723a98f6f73ad5a"}, + {file = "RPi.GPIO-0.7.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:6a4791f41cafc2ee6e4cb70e5bd31fadc66a0cfab29b38df8723a98f6f73ad5a"}, ] sangaboard = [ {file = "sangaboard-0.2.3-py3-none-any.whl", hash = "sha256:3791159d57a749571f89acdce70aa9c1de82afef71f5186d5e2845c7be44698c"}, - {file = "sangaboard-0.2.3.tar.gz", hash = "sha256:ed0c5a9864f5901d11bed0ed2387da3b5555f75fef72a9f35997304447f9b08c"}, ] scipy = [ - {file = "scipy-1.5.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f574558f1b774864516f3c3fe072ebc90a29186f49b720f60ed339294b7f32ac"}, - {file = "scipy-1.5.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:e527c9221b6494bcd06a17f9f16874406b32121385f9ab353b8a9545be458f0b"}, - {file = "scipy-1.5.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:b9751b39c52a3fa59312bd2e1f40144ee26b51404db5d2f0d5259c511ff6f614"}, - {file = "scipy-1.5.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d5e3cc60868f396b78fc881d2c76460febccfe90f6d2f082b9952265c79a8788"}, - {file = "scipy-1.5.3-cp36-cp36m-win32.whl", hash = "sha256:1fee28b6641ecbff6e80fe7788e50f50c5576157d278fa40f36c851940eb0aff"}, - {file = "scipy-1.5.3-cp36-cp36m-win_amd64.whl", hash = "sha256:ffcbd331f1ffa82e22f1d408e93c37463c9a83088243158635baec61983aaacf"}, - {file = "scipy-1.5.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:07b083128beae040f1129bd8a82b01804f5e716a7fd2962c1053fa683433e4ab"}, - {file = "scipy-1.5.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:e2602f79c85924e4486f684aa9bbab74afff90606100db88d0785a0088be7edb"}, - {file = "scipy-1.5.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:aebb69bcdec209d874fc4b0c7ac36f509d50418a431c1422465fa34c2c0143ea"}, - {file = "scipy-1.5.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:bc0e63daf43bf052aefbbd6c5424bc03f629d115ece828e87303a0bcc04a37e4"}, - {file = "scipy-1.5.3-cp37-cp37m-win32.whl", hash = "sha256:8cc5c39ed287a8b52a5509cd6680af078a40b0e010e2657eca01ffbfec929468"}, - {file = "scipy-1.5.3-cp37-cp37m-win_amd64.whl", hash = "sha256:0edd67e8a00903aaf7a29c968555a2e27c5a69fea9d1dcfffda80614281a884f"}, - {file = "scipy-1.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:66ec29348444ed6e8a14c9adc2de65e74a8fc526dc2c770741725464488ede1f"}, - {file = "scipy-1.5.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:12fdcbfa56cac926a0a9364a30cbf4ad03c2c7b59f75b14234656a5e4fd52bf3"}, - {file = "scipy-1.5.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:a1a13858b10d41beb0413c4378462b43eafef88a1948d286cb357eadc0aec024"}, - {file = "scipy-1.5.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:5163200ab14fd2b83aba8f0c4ddcc1fa982a43192867264ab0f4c8065fd10d17"}, - {file = "scipy-1.5.3-cp38-cp38-win32.whl", hash = "sha256:33e6a7439f43f37d4c1135bc95bcd490ffeac6ef4b374892c7005ce2c729cf4a"}, - {file = "scipy-1.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:a3db1fe7c6cb29ca02b14c9141151ebafd11e06ffb6da8ecd330eee5c8283a8a"}, - {file = "scipy-1.5.3.tar.gz", hash = "sha256:ddae76784574cc4c172f3d5edd7308be16078dd3b977e8746860c76c195fa707"}, + {file = "scipy-1.6.1-cp37-cp37m-linux_armv6l.whl", hash = "sha256:e3a56a9a68a3ad4a9d72ecc8570bf83106dd42237271460dd74079b449b9f6ea"}, + {file = "scipy-1.6.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3a56a9a68a3ad4a9d72ecc8570bf83106dd42237271460dd74079b449b9f6ea"}, ] six = [ {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, - {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, ] snowballstemmer = [ {file = "snowballstemmer-2.1.0-py2.py3-none-any.whl", hash = "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2"}, - {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, ] sphinx = [ - {file = "Sphinx-3.4.3-py3-none-any.whl", hash = "sha256:c314c857e7cd47c856d2c5adff514ac2e6495f8b8e0f886a8a37e9305dfea0d8"}, - {file = "Sphinx-3.4.3.tar.gz", hash = "sha256:41cad293f954f7d37f803d97eb184158cfd90f51195131e94875bc07cd08b93c"}, + {file = "Sphinx-3.5.1-py3-none-any.whl", hash = "sha256:e90161222e4d80ce5fc811ace7c6787a226b4f5951545f7f42acf97277bfc35c"}, ] sphinxcontrib-applehelp = [ - {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, ] sphinxcontrib-devhelp = [ - {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, ] sphinxcontrib-htmlhelp = [ - {file = "sphinxcontrib-htmlhelp-1.0.3.tar.gz", hash = "sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b"}, {file = "sphinxcontrib_htmlhelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f"}, ] sphinxcontrib-httpdomain = [ - {file = "sphinxcontrib-httpdomain-1.7.0.tar.gz", hash = "sha256:ac40b4fba58c76b073b03931c7b8ead611066a6aebccafb34dc19694f4eb6335"}, {file = "sphinxcontrib_httpdomain-1.7.0-py2.py3-none-any.whl", hash = "sha256:1fb5375007d70bf180cdd1c79e741082be7aa2d37ba99efe561e1c2e3f38191e"}, ] sphinxcontrib-jsmath = [ - {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, ] sphinxcontrib-qthelp = [ - {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, ] sphinxcontrib-serializinghtml = [ - {file = "sphinxcontrib-serializinghtml-1.1.4.tar.gz", hash = "sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc"}, {file = "sphinxcontrib_serializinghtml-1.1.4-py2.py3-none-any.whl", hash = "sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a"}, ] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] tomlkit = [ {file = "tomlkit-0.7.0-py2.py3-none-any.whl", hash = "sha256:6babbd33b17d5c9691896b0e68159215a9387ebfa938aa3ac42f4a4beeb2b831"}, - {file = "tomlkit-0.7.0.tar.gz", hash = "sha256:ac57f29693fab3e309ea789252fcce3061e19110085aa31af5446ca749325618"}, ] typed-ast = [ - {file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70"}, - {file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487"}, - {file = "typed_ast-1.4.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412"}, - {file = "typed_ast-1.4.2-cp35-cp35m-win32.whl", hash = "sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400"}, - {file = "typed_ast-1.4.2-cp35-cp35m-win_amd64.whl", hash = "sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606"}, - {file = "typed_ast-1.4.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64"}, - {file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07"}, - {file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc"}, - {file = "typed_ast-1.4.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a"}, - {file = "typed_ast-1.4.2-cp36-cp36m-win32.whl", hash = "sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151"}, - {file = "typed_ast-1.4.2-cp36-cp36m-win_amd64.whl", hash = "sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3"}, - {file = "typed_ast-1.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41"}, - {file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f"}, - {file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581"}, - {file = "typed_ast-1.4.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37"}, - {file = "typed_ast-1.4.2-cp37-cp37m-win32.whl", hash = "sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd"}, - {file = "typed_ast-1.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496"}, - {file = "typed_ast-1.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc"}, - {file = "typed_ast-1.4.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10"}, - {file = "typed_ast-1.4.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea"}, - {file = "typed_ast-1.4.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787"}, - {file = "typed_ast-1.4.2-cp38-cp38-win32.whl", hash = "sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2"}, - {file = "typed_ast-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937"}, - {file = "typed_ast-1.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1"}, - {file = "typed_ast-1.4.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6"}, - {file = "typed_ast-1.4.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166"}, - {file = "typed_ast-1.4.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d"}, - {file = "typed_ast-1.4.2-cp39-cp39-win32.whl", hash = "sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b"}, - {file = "typed_ast-1.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440"}, - {file = "typed_ast-1.4.2.tar.gz", hash = "sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a"}, + {file = "typed_ast-1.4.2-cp35-cp35m-linux_armv6l.whl", hash = "sha256:4822449fb865605582a22d23997444ff3eaa3d0920b04b9902e37f8edbeadca6"}, + {file = "typed_ast-1.4.2-cp35-cp35m-linux_armv7l.whl", hash = "sha256:4822449fb865605582a22d23997444ff3eaa3d0920b04b9902e37f8edbeadca6"}, + {file = "typed_ast-1.4.2-cp37-cp37m-linux_armv6l.whl", hash = "sha256:a93908d202f52ec64e24a41031f1336a14b66706be2c9e51949647f7ac4cbd22"}, + {file = "typed_ast-1.4.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:a93908d202f52ec64e24a41031f1336a14b66706be2c9e51949647f7ac4cbd22"}, ] typing-extensions = [ - {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, - {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, ] urllib3 = [ {file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"}, - {file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"}, ] webargs = [ {file = "webargs-7.0.1-py2.py3-none-any.whl", hash = "sha256:ce8c565789ece1584be7edba41617319eb75de59b2987958bee3f3fdb3669e60"}, - {file = "webargs-7.0.1.tar.gz", hash = "sha256:2f3d883ce9f348fa884889440fcc1b207e7c67b04be5e90be00a5be73e2cd91d"}, ] werkzeug = [ {file = "Werkzeug-1.0.1-py2.py3-none-any.whl", hash = "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43"}, - {file = "Werkzeug-1.0.1.tar.gz", hash = "sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c"}, ] wrapt = [ - {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"}, + {file = "wrapt-1.12.1-cp34-cp34m-linux_armv6l.whl", hash = "sha256:c16fd7cad3d27a5db392ae0e6215533a912a7d3d83a40b8cf275744276989780"}, + {file = "wrapt-1.12.1-cp34-cp34m-linux_armv7l.whl", hash = "sha256:c16fd7cad3d27a5db392ae0e6215533a912a7d3d83a40b8cf275744276989780"}, + {file = "wrapt-1.12.1-cp35-cp35m-linux_armv6l.whl", hash = "sha256:1ba3a6bcb5014ed4d9c98afe8aa47b5ed320f84292b4f1b9f6afa0f4b2b91351"}, + {file = "wrapt-1.12.1-cp35-cp35m-linux_armv7l.whl", hash = "sha256:1ba3a6bcb5014ed4d9c98afe8aa47b5ed320f84292b4f1b9f6afa0f4b2b91351"}, + {file = "wrapt-1.12.1-cp37-cp37m-linux_armv6l.whl", hash = "sha256:d3294c918c5529e50b5454d39b589648d9baf6152e380067606d5566d71369a0"}, + {file = "wrapt-1.12.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:d3294c918c5529e50b5454d39b589648d9baf6152e380067606d5566d71369a0"}, ] zeroconf = [ {file = "zeroconf-0.28.8-py3-none-any.whl", hash = "sha256:3608be2db58f6f0dc70665e02ab420fb8bf428016f2c78403d879e066ecc9bff"}, - {file = "zeroconf-0.28.8.tar.gz", hash = "sha256:4be24a10aa9c73406f48d42a8b3b077c217b0e8d7ed1e57639630da520c25959"}, ] zipp = [ {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"}, - {file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"}, ] diff --git a/pyproject.toml b/pyproject.toml index d7bccc65..81609ef2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,21 +28,31 @@ packages = [ { include = "openflexure_microscope" }, ] +# Installing numpy/scipy/etc. from source is unlikely to work. +# The block below forces Poetry to use piwheels. +# We can add other repositories if we need them. +# Setting default=true disables fallback to PyPI. + +[[tool.poetry.source]] +name = "piwheels" +url = "https://www.piwheels.org/simple" +default = true + [tool.poetry.dependencies] python = "^3.7.3" Flask = "^1.0" numpy = "1.19.2" # Exact version so we can guarantee a wheel Pillow = "^7.2.0" -scipy = "1.5.3" # Exact version so we can guarantee a wheel +scipy = "1.6.1" # Exact version so we can guarantee a wheel "RPi.GPIO" = { version = "^0.7.0", optional = true } python-dateutil = "^2.8" psutil = "^5.6.7" # Autostorage extension -opencv-python-headless = "4.4.0.44" +opencv-python-headless = "4.5.1.48" sangaboard = "^0.2" expiringdict = "^1.2.1" camera-stage-mapping = "0.1.4" picamerax = ">=20.9.1" -pyyaml = "^5.3.1" +pyyaml = "5.4.0" # pyyaml 5.4.1 is broken in piwheels pytest-cov = "^2.10.1" piexif = "^1.1.3" labthings = "^1.2.2" @@ -64,7 +74,7 @@ pytest = "^6.1.2" mypy = "^0.790" poethepoet = "^0.9.0" freezegun = "^1.0.0" -lxml = "^4.6.2" +lxml = "^4.6.2" # Fix version so piwheels works [tool.black] exclude = '(\.eggs|\.git|\.venv|\venv|node_modules/)' From 414fd55c5be92e6e023a53f7832194884580f4b4 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Thu, 18 Feb 2021 14:33:07 +0000 Subject: [PATCH 12/23] Upgrade numpy numpy/scipy were mismatched in version, which led to a confusing import error. I've removed the numpy version specification entirely and left it to scipy to ensure numpy is installed. This could do with review in the future... --- poetry.lock | 40 +++++++++++++++++++++++++++------------- pyproject.toml | 11 ++++++----- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/poetry.lock b/poetry.lock index a900e866..5c309797 100644 --- a/poetry.lock +++ b/poetry.lock @@ -620,11 +620,11 @@ reference = "piwheels" [[package]] name = "numpy" -version = "1.19.2" +version = "1.20.1" description = "NumPy is the fundamental package for array computing with Python." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.source] type = "legacy" @@ -947,17 +947,12 @@ reference = "piwheels" [[package]] name = "pyyaml" -version = "5.4" +version = "5.4.1" description = "YAML parser and emitter for Python" category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - [[package]] name = "requests" version = "2.25.1" @@ -1383,7 +1378,7 @@ rpi = ["RPi.GPIO"] [metadata] lock-version = "1.1" python-versions = "^3.7.3" -content-hash = "7144f1f0652692911988bfc402489aa1d693d818e4152c57dca592d1c4f65fe1" +content-hash = "26a514da40cebddf91dd6020cb4a8f130e8684063d3aaf6a984e548b16338575" [metadata.files] alabaster = [ @@ -1515,8 +1510,8 @@ mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, ] numpy = [ - {file = "numpy-1.19.2-cp37-cp37m-linux_armv6l.whl", hash = "sha256:bff0719af5134d472546e601f83e5335ec8919464084b34d602541fc80a33698"}, - {file = "numpy-1.19.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:bff0719af5134d472546e601f83e5335ec8919464084b34d602541fc80a33698"}, + {file = "numpy-1.20.1-cp37-cp37m-linux_armv6l.whl", hash = "sha256:23d6802e7f2ceb9a1572f375470478251e821bdb4babbbbf3f2420c858d5dbde"}, + {file = "numpy-1.20.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:23d6802e7f2ceb9a1572f375470478251e821bdb4babbbbf3f2420c858d5dbde"}, ] numpy-stubs = [] opencv-python-headless = [ @@ -1581,8 +1576,27 @@ pytz = [ {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, ] pyyaml = [ - {file = "PyYAML-5.4-cp37-cp37m-linux_armv6l.whl", hash = "sha256:64a89e4ab1e56c6bd2dcb4578df6a29b480abffcc78eba175fba87eb140da5d0"}, - {file = "PyYAML-5.4-cp37-cp37m-linux_armv7l.whl", hash = "sha256:64a89e4ab1e56c6bd2dcb4578df6a29b480abffcc78eba175fba87eb140da5d0"}, + {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, + {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, + {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, + {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, + {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, + {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, + {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, + {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, ] requests = [ {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, diff --git a/pyproject.toml b/pyproject.toml index 81609ef2..5124bbb5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,19 +31,20 @@ packages = [ # Installing numpy/scipy/etc. from source is unlikely to work. # The block below forces Poetry to use piwheels. # We can add other repositories if we need them. -# Setting default=true disables fallback to PyPI. +# Setting default=true disables fallback to PyPI. I have removed +# this for now, because it will prevent installation on non-Pi +# platforms. [[tool.poetry.source]] name = "piwheels" url = "https://www.piwheels.org/simple" -default = true [tool.poetry.dependencies] python = "^3.7.3" Flask = "^1.0" -numpy = "1.19.2" # Exact version so we can guarantee a wheel +# let scipy specify numpy?? numpy = "1.19.2" # Exact version so we can guarantee a wheel Pillow = "^7.2.0" -scipy = "1.6.1" # Exact version so we can guarantee a wheel +scipy = "^1.6.1" # Exact version so we can guarantee a wheel "RPi.GPIO" = { version = "^0.7.0", optional = true } python-dateutil = "^2.8" psutil = "^5.6.7" # Autostorage extension @@ -52,7 +53,7 @@ sangaboard = "^0.2" expiringdict = "^1.2.1" camera-stage-mapping = "0.1.4" picamerax = ">=20.9.1" -pyyaml = "5.4.0" # pyyaml 5.4.1 is broken in piwheels +pyyaml = "^5.4.0" # pyyaml 5.4.1 is broken in piwheels pytest-cov = "^2.10.1" piexif = "^1.1.3" labthings = "^1.2.2" From 3b435ab2ee0cf0140aa8d80fd1d5be582c8099ad Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Thu, 18 Feb 2021 15:56:11 +0000 Subject: [PATCH 13/23] Deleted obsolete functions --- .../recalibrate_utils.py | 111 ++++-------------- 1 file changed, 20 insertions(+), 91 deletions(-) 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 1f804b2c..50eb3905 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py @@ -50,97 +50,6 @@ def adjust_exposure_to_setpoint(camera: PiCamera, setpoint: int): print("done") -def adjust_exposure_and_gain( - camera: PiCamera, target_white_level: int = 700, max_iterations: int = 99 -): - """Adjust exposure and analog gain based on raw images. - - This routine is slow but effective. It uses raw images, so we - are not affected by white balance or digital gain. - """ - # Start by setting fully manual exposure. - camera.exposure_mode = "off" - camera.iso = 0 # We must set ISO=0 (auto) or we can't set gain - camera.analog_gain = 1 - camera.digital_gain = 1 - camera.shutter_speed = 1 # This is not valid - we'll get the minimum - time.sleep(0.5) - - # We start with very low exposure settings and work up in coarse steps - # until either the brightness is high enough, or we can't increase the - # shutter speed any more. - iterations = 0 - while True: - iterations += 1 - if iterations > max_iterations: - logging.warning( - f"Auto exposure is stopping after {max_iterations} " - "but it has not converged." - ) - break - - with PiBayerArray(camera) as a: - camera.capture(a, format="jpeg", bayer=True) - max_brightness = np.max(a.array) - - if max_brightness > target_white_level: - break - - previous_shutter_speed = camera.shutter_speed - camera.shutter_speed = previous_shutter_speed * 2 - time.sleep(0.5) - current_shutter_speed = camera.shutter_speed - # NB we save this in a variable in case it changes between - # here and the next loop - logging.info( - f"Max brightness {max_brightness} < {target_white_level}. " - f"Attempting to double exposure from " - f"{previous_shutter_speed} to {current_shutter_speed}" - ) - if current_shutter_speed == previous_shutter_speed: - logging.info("Shutter speed has saturated, stopping.") - break - - if max_brightness > target_white_level: - logging.info("Brightness is high enough, fine-tuning") - camera.shutter_speed = int( - current_shutter_speed * target_white_level / max_brightness - ) - - -def auto_expose_and_freeze_settings(camera: PiCamera): - """Freeze the settings after auto-exposing to white illumination""" - logging.info("Allowing the camera to auto-expose") - if "greyworld" in camera.AWB_MODES: - logging.info("Calibrating with greyworld AWB") - camera.awb_mode = "greyworld" - else: - logging.warning("Calibrating with auto AWB as greyworld is missing") - 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 _ in range(6): - print(".", end="") - time.sleep(0.5) - logging.info("done") - - logging.info("Freezing the camera settings...") - camera.shutter_speed = camera.exposure_speed - logging.info("Shutter speed = %s", (camera.shutter_speed)) - camera.exposure_mode = "off" - logging.info("Auto exposure disabled") - g: Tuple[Fraction, Fraction] = camera.awb_gains - camera.awb_mode = "off" - camera.awb_gains = g - logging.info("Auto white balance disabled, gains are %s", (g)) - logging.info( - "Analogue gain: %s, Digital gain: %s", camera.analog_gain, camera.digital_gain - ) - adjust_exposure_to_setpoint(camera, 215) - - def adjust_shutter_and_gain_from_raw( camera: PiCamera, target_white_level: int = 700, @@ -152,6 +61,26 @@ def adjust_shutter_and_gain_from_raw( This routine is slow but effective. It uses raw images, so we are not affected by white balance or digital gain. + + + Arguments: + target_white_level: + The raw, 10-bit value we aim for. The brightest pixels + should be approximately this bright. Maximum possible + is 1023, 700 is reasonable. + max_iterations: + We will terminate once we perform this many iterations, + whether or not we converge. More than 10 shouldn't happen. + tolerance: + How close to the target value we consider "done". Expressed + as a fraction of the ``target_white_level`` so 0.05 means + +/- 5% + percentile: + Rather then use the maximum value for each channel, we + calculate a percentile. This makes us robust to single + pixels that are bright/noisy. 99.9% still picks the top + of the brightness range, but seems much more reliable + than just ``np.max()``. """ # Start by setting fully manual exposure. camera.exposure_mode = "off" From bc0995d0cf9bdbc849dd694be1dde1e33ba42df1 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Thu, 18 Feb 2021 18:03:42 +0000 Subject: [PATCH 14/23] Added poe serve To save some typing ``poe serve`` now runs a debug server. --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 5124bbb5..3476e253 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -99,6 +99,7 @@ isort = "isort ." pylint = "pylint openflexure_microscope" mypy = "mypy --cobertura-xml-report openflexure_microscope openflexure_microscope" test = "pytest ." +serve = "python -m openflexure_microscope.api.app" format = ["black", "isort"] lint = ["pylint", "mypy"] From a373afd955cadca3ffdf22ac1a9e313ed46df1b0 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Tue, 16 Mar 2021 11:01:57 +0000 Subject: [PATCH 15/23] swap print for logging in old function --- .../picamera_autocalibrate/recalibrate_utils.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 50eb3905..58a340a1 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py @@ -40,14 +40,12 @@ def flat_lens_shading_table(camera: PiCamera) -> np.ndarray: def adjust_exposure_to_setpoint(camera: PiCamera, setpoint: int): """Adjust the camera's exposure time until the maximum pixel value is .""" - print("Adjusting shutter speed to hit setpoint {}".format(setpoint), end="") + logging.info("Adjusting shutter speed to hit setpoint {}".format(setpoint)) for _ in range(3): - print(".", end="") camera.shutter_speed = int( camera.shutter_speed * setpoint / np.max(rgb_image(camera)) ) time.sleep(1) - print("done") def adjust_shutter_and_gain_from_raw( From 64f2263e81899c4f3065dd359d5a977179c02b9c Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Wed, 21 Apr 2021 12:35:49 +0100 Subject: [PATCH 16/23] Remove poetry.lock We've switched to pipenv, poetry.lock is now superfluous --- poetry.lock | 1689 --------------------------------------------------- 1 file changed, 1689 deletions(-) delete mode 100644 poetry.lock diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index 5c309797..00000000 --- a/poetry.lock +++ /dev/null @@ -1,1689 +0,0 @@ -[[package]] -name = "alabaster" -version = "0.7.12" -description = "A configurable sidebar-enabled Sphinx theme" -category = "dev" -optional = false -python-versions = "*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "apispec" -version = "4.3.0" -description = "A pluggable API specification generator. Currently supports the OpenAPI Specification (f.k.a. the Swagger specification)." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -PyYAML = {version = ">=3.10", optional = true, markers = "extra == \"yaml\""} - -[package.extras] -dev = ["PyYAML (>=3.10)", "prance[osv] (>=0.11)", "marshmallow (>=3.0.0)", "pytest", "mock", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)", "tox"] -docs = ["marshmallow (>=3.0.0)", "pyyaml (==5.4.1)", "sphinx (==3.4.3)", "sphinx-issues (==1.2.0)", "sphinx-rtd-theme (==0.5.1)"] -lint = ["flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)"] -tests = ["PyYAML (>=3.10)", "prance[osv] (>=0.11)", "marshmallow (>=3.0.0)", "pytest", "mock"] -validation = ["prance[osv] (>=0.11)"] -yaml = ["PyYAML (>=3.10)"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "apispec-webframeworks" -version = "0.5.2" -description = "Web framework plugins for apispec." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -apispec = {version = ">=2.0.0", extras = ["yaml"]} - -[package.extras] -dev = ["pytest", "mock", "Flask (==1.1.1)", "tornado", "bottle (==0.12.17)", "flake8 (==3.7.9)", "flake8-bugbear (==19.8.0)", "pre-commit (>=1.18,<2.0)", "tox"] -lint = ["flake8 (==3.7.9)", "flake8-bugbear (==19.8.0)", "pre-commit (>=1.18,<2.0)"] -tests = ["pytest", "mock", "Flask (==1.1.1)", "tornado", "bottle (==0.12.17)"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = "*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "astroid" -version = "2.4.2" -description = "An abstract syntax tree for Python with inference support." -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -lazy-object-proxy = ">=1.4.0,<1.5.0" -six = ">=1.12,<2.0" -typed-ast = {version = ">=1.4.0,<1.5", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""} -wrapt = ">=1.11,<2.0" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "atomicwrites" -version = "1.4.0" -description = "Atomic file writes." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "attrs" -version = "20.3.0" -description = "Classes Without Boilerplate" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] -docs = ["furo", "sphinx", "zope.interface"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "babel" -version = "2.9.0" -description = "Internationalization utilities" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -pytz = ">=2015.7" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "black" -version = "18.9b0" -description = "The uncompromising code formatter." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -appdirs = "*" -attrs = ">=17.4.0" -click = ">=6.5" -toml = ">=0.9.4" - -[package.extras] -d = ["aiohttp (>=3.3.2)"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "camera-stage-mapping" -version = "0.1.4" -description = "Calibration and mapping between stage and camera coordinates in a microscope" -category = "main" -optional = false -python-versions = ">=3.6,<4.0" - -[package.dependencies] -numpy = ">=1.17,<2.0" - -[package.extras] -correlation = ["opencv-python-headless (>=4.1,<5.0)", "scipy (>=1.4,<2.0)"] -all = ["opencv-python-headless (>=4.1,<5.0)", "scipy (>=1.4,<2.0)"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "certifi" -version = "2020.12.5" -description = "Python package for providing Mozilla's CA Bundle." -category = "dev" -optional = false -python-versions = "*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "chardet" -version = "4.0.0" -description = "Universal encoding detector for Python 2 and 3" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "click" -version = "7.1.2" -description = "Composable command line interface toolkit" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "colorama" -version = "0.4.4" -description = "Cross-platform colored terminal text." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "colorzero" -version = "1.1" -description = "Yet another Python color library." -category = "main" -optional = false -python-versions = "*" - -[package.extras] -doc = ["sphinx"] -test = ["pytest", "coverage", "mock"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "coverage" -version = "5.4" -description = "Code coverage measurement for Python" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" - -[package.extras] -toml = ["toml"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "docutils" -version = "0.16" -description = "Docutils -- Python Documentation Utilities" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "expiringdict" -version = "1.2.1" -description = "Dictionary with auto-expiring values for caching purposes" -category = "main" -optional = false -python-versions = "*" - -[package.extras] -tests = ["dill", "coverage", "coveralls", "mock", "nose"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "flask" -version = "1.1.2" -description = "A simple framework for building complex web applications." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.dependencies] -click = ">=5.1" -itsdangerous = ">=0.24" -Jinja2 = ">=2.10.1" -Werkzeug = ">=0.15" - -[package.extras] -dev = ["pytest", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] -docs = ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] -dotenv = ["python-dotenv"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "flask-cors" -version = "3.0.10" -description = "A Flask extension adding a decorator for CORS support" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -Flask = ">=0.9" -Six = "*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "freezegun" -version = "1.1.0" -description = "Let your Python tests travel through time" -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -python-dateutil = ">=2.7" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "future" -version = "0.18.2" -description = "Clean single-source support for Python 3 and 2" -category = "main" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "idna" -version = "2.10" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "ifaddr" -version = "0.1.7" -description = "Cross-platform network interface and IP address enumeration library" -category = "main" -optional = false -python-versions = "*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "imagesize" -version = "1.2.0" -description = "Getting image size from png/jpeg/jpeg2000/gif file" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "importlib-metadata" -version = "3.4.0" -description = "Read metadata from Python packages" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} -zipp = ">=0.5" - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" -category = "main" -optional = false -python-versions = "*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "isort" -version = "5.7.0" -description = "A Python utility / library to sort Python imports." -category = "dev" -optional = false -python-versions = ">=3.6,<4.0" - -[package.extras] -colors = ["colorama (>=0.4.3,<0.5.0)"] -requirements_deprecated_finder = ["pip-api", "pipreqs"] -pipfile_deprecated_finder = ["pipreqs", "requirementslib"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "itsdangerous" -version = "1.1.0" -description = "Various helpers to pass data to untrusted environments and back." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "jinja2" -version = "2.11.3" -description = "A very fast and expressive template engine." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.dependencies] -MarkupSafe = ">=0.23" - -[package.extras] -i18n = ["Babel (>=0.8)"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "labthings" -version = "1.2.3" -description = "Python implementation of LabThings, based on the Flask microframework" -category = "main" -optional = false -python-versions = ">=3.6,<4.0" - -[package.dependencies] -apispec = ">=3.2,<5.0" -apispec_webframeworks = ">=0.5.2,<0.6.0" -Flask = ">=1.1.1,<2.0.0" -flask-cors = ">=3.0.8,<4.0.0" -marshmallow = ">=3.4.0,<4.0.0" -webargs = ">=6,<8" -zeroconf = ">=0.24.5,<0.29.0" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "lazy-object-proxy" -version = "1.4.3" -description = "A fast and thorough lazy object proxy." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "lxml" -version = "4.6.2" -description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" - -[package.extras] -cssselect = ["cssselect (>=0.7)"] -html5 = ["html5lib"] -htmlsoup = ["beautifulsoup4"] -source = ["Cython (>=0.29.7)"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "markupsafe" -version = "1.1.1" -description = "Safely add untrusted strings to HTML/XML markup." -category = "main" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "marshmallow" -version = "3.10.0" -description = "A lightweight library for converting complex datatypes to and from native Python datatypes." -category = "main" -optional = false -python-versions = ">=3.5" - -[package.extras] -dev = ["pytest", "pytz", "simplejson", "mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)", "tox"] -docs = ["sphinx (==3.3.1)", "sphinx-issues (==1.2.0)", "alabaster (==0.7.12)", "sphinx-version-warning (==1.1.2)", "autodocsumm (==0.2.2)"] -lint = ["mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)"] -tests = ["pytest", "pytz", "simplejson"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "mccabe" -version = "0.6.1" -description = "McCabe checker, plugin for flake8" -category = "dev" -optional = false -python-versions = "*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "mypy" -version = "0.790" -description = "Optional static typing for Python" -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -mypy-extensions = ">=0.4.3,<0.5.0" -typed-ast = ">=1.4.0,<1.5.0" -typing-extensions = ">=3.7.4" - -[package.extras] -dmypy = ["psutil (>=4.0)"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "mypy-extensions" -version = "0.4.3" -description = "Experimental type system extensions for programs checked with the mypy typechecker." -category = "dev" -optional = false -python-versions = "*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "numpy" -version = "1.20.1" -description = "NumPy is the fundamental package for array computing with Python." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "numpy-stubs" -version = "0.0.1" -description = "" -category = "main" -optional = false -python-versions = "*" -develop = false - -[package.dependencies] -numpy = ">=1.16.0" -typing_extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} - -[package.source] -type = "git" -url = "https://github.com/numpy/numpy-stubs.git" -reference = "master" -resolved_reference = "c49d2d6875971a669a166ea93ef998911af283a1" - -[[package]] -name = "opencv-python-headless" -version = "4.5.1.48" -description = "Wrapper package for OpenCV python bindings." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -numpy = ">=1.14.5" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "packaging" -version = "20.9" -description = "Core utilities for Python packages" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -pyparsing = ">=2.0.2" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "pastel" -version = "0.2.1" -description = "Bring colors to your terminal." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "picamerax" -version = "20.9.1" -description = "A pure Python interface for the Raspberry Pi camera module, with extra features and fixes." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -colorzero = "*" - -[package.extras] -array = ["numpy"] -doc = ["sphinx"] -test = ["coverage", "pytest", "mock", "pillow", "numpy"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "piexif" -version = "1.1.3" -description = "To simplify exif manipulations with python. Writing, reading, and more..." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "pillow" -version = "7.2.0" -description = "Python Imaging Library (Fork)" -category = "main" -optional = false -python-versions = ">=3.5" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "pluggy" -version = "0.13.1" -description = "plugin and hook calling mechanisms for python" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} - -[package.extras] -dev = ["pre-commit", "tox"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "poethepoet" -version = "0.9.0" -description = "A task runner that works well with poetry." -category = "dev" -optional = false -python-versions = ">=3.6,<4.0" - -[package.dependencies] -pastel = ">=0.2.0,<0.3.0" -tomlkit = ">=0.6.0,<1.0.0" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "psutil" -version = "5.8.0" -description = "Cross-platform lib for process and system monitoring in Python." -category = "main" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.extras] -test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "py" -version = "1.10.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "pygments" -version = "2.8.0" -description = "Pygments is a syntax highlighting package written in Python." -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "pylint" -version = "2.6.2" -description = "python code static checker" -category = "dev" -optional = false -python-versions = ">=3.5.*" - -[package.dependencies] -astroid = ">=2.4.0,<2.5" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -isort = ">=4.2.5,<6" -mccabe = ">=0.6,<0.7" -toml = ">=0.7.1" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "pyparsing" -version = "2.4.7" -description = "Python parsing module" -category = "main" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "pyserial" -version = "3.5" -description = "Python Serial Port Extension" -category = "main" -optional = false -python-versions = "*" - -[package.extras] -cp2110 = ["hidapi"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "pytest" -version = "6.2.2" -description = "pytest: simple powerful testing with Python" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} -attrs = ">=19.2.0" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<1.0.0a1" -py = ">=1.8.2" -toml = "*" - -[package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "pytest-cov" -version = "2.11.1" -description = "Pytest plugin for measuring coverage." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.dependencies] -coverage = ">=5.2.1" -pytest = ">=4.6" - -[package.extras] -testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "python-dateutil" -version = "2.8.1" -description = "Extensions to the standard Python datetime module" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" - -[package.dependencies] -six = ">=1.5" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "pytz" -version = "2021.1" -description = "World timezone definitions, modern and historical" -category = "dev" -optional = false -python-versions = "*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "pyyaml" -version = "5.4.1" -description = "YAML parser and emitter for Python" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" - -[[package]] -name = "requests" -version = "2.25.1" -description = "Python HTTP for Humans." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.dependencies] -certifi = ">=2017.4.17" -chardet = ">=3.0.2,<5" -idna = ">=2.5,<3" -urllib3 = ">=1.21.1,<1.27" - -[package.extras] -security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] -socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "rope" -version = "0.14.0" -description = "a python refactoring library..." -category = "dev" -optional = false -python-versions = "*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "rpi.gpio" -version = "0.7.0" -description = "A module to control Raspberry Pi GPIO channels" -category = "main" -optional = true -python-versions = "*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "sangaboard" -version = "0.2.3" -description = "Communication to the Sangaboard unipolar motor driver" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -future = "*" -numpy = "*" -pyserial = "*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "scipy" -version = "1.6.1" -description = "SciPy: Scientific Library for Python" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -numpy = ">=1.16.5" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "six" -version = "1.15.0" -description = "Python 2 and 3 compatibility utilities" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "snowballstemmer" -version = "2.1.0" -description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -category = "dev" -optional = false -python-versions = "*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "sphinx" -version = "3.5.1" -description = "Python documentation generator" -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -alabaster = ">=0.7,<0.8" -babel = ">=1.3" -colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.12" -imagesize = "*" -Jinja2 = ">=2.3" -packaging = "*" -Pygments = ">=2.0" -requests = ">=2.5.0" -snowballstemmer = ">=1.1" -sphinxcontrib-applehelp = "*" -sphinxcontrib-devhelp = "*" -sphinxcontrib-htmlhelp = "*" -sphinxcontrib-jsmath = "*" -sphinxcontrib-qthelp = "*" -sphinxcontrib-serializinghtml = "*" - -[package.extras] -docs = ["sphinxcontrib-websupport"] -lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.800)", "docutils-stubs"] -test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "sphinxcontrib-applehelp" -version = "1.0.2" -description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] -test = ["pytest"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "sphinxcontrib-devhelp" -version = "1.0.2" -description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] -test = ["pytest"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "1.0.3" -description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] -test = ["pytest", "html5lib"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "sphinxcontrib-httpdomain" -version = "1.7.0" -description = "Sphinx domain for documenting HTTP APIs" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -six = "*" -Sphinx = ">=1.5" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -description = "A sphinx extension which renders display math in HTML via JavaScript" -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -test = ["pytest", "flake8", "mypy"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "sphinxcontrib-qthelp" -version = "1.0.3" -description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] -test = ["pytest"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "1.1.4" -description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] -test = ["pytest"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -category = "main" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "tomlkit" -version = "0.7.0" -description = "Style preserving TOML library" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "typed-ast" -version = "1.4.2" -description = "a fork of Python 2 and 3 ast modules with type comment support" -category = "dev" -optional = false -python-versions = "*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "typing-extensions" -version = "3.7.4.3" -description = "Backported and Experimental Type Hints for Python 3.5+" -category = "main" -optional = false -python-versions = "*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "urllib3" -version = "1.26.3" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" - -[package.extras] -brotli = ["brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "webargs" -version = "7.0.1" -description = "Declarative parsing and validation of HTTP request objects, with built-in support for popular web frameworks, including Flask, Django, Bottle, Tornado, Pyramid, Falcon, and aiohttp." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -marshmallow = ">=3.0.0" - -[package.extras] -dev = ["pytest", "webtest (==2.0.35)", "webtest-aiohttp (==2.0.0)", "pytest-aiohttp (>=0.3.0)", "Flask (>=0.12.5)", "Django (>=2.2.0)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.8)", "mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)", "tox"] -docs = ["Sphinx (==3.3.1)", "sphinx-issues (==1.2.0)", "sphinx-typlog-theme (==0.8.0)", "Flask (>=0.12.5)", "Django (>=2.2.0)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.8)"] -frameworks = ["Flask (>=0.12.5)", "Django (>=2.2.0)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.8)"] -lint = ["mypy (==0.790)", "flake8 (==3.8.4)", "flake8-bugbear (==20.11.1)", "pre-commit (>=2.4,<3.0)"] -tests = ["pytest", "webtest (==2.0.35)", "webtest-aiohttp (==2.0.0)", "pytest-aiohttp (>=0.3.0)", "Flask (>=0.12.5)", "Django (>=2.2.0)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.8)"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "werkzeug" -version = "1.0.1" -description = "The comprehensive WSGI web application library." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.extras] -dev = ["pytest", "pytest-timeout", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinx-issues"] -watchdog = ["watchdog"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "wrapt" -version = "1.12.1" -description = "Module for decorators, wrappers and monkey patching." -category = "dev" -optional = false -python-versions = "*" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "zeroconf" -version = "0.28.8" -description = "Pure Python Multicast DNS Service Discovery Library (Bonjour/Avahi compatible)" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -ifaddr = ">=0.1.7" - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[[package]] -name = "zipp" -version = "3.4.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] - -[package.source] -type = "legacy" -url = "https://www.piwheels.org/simple" -reference = "piwheels" - -[extras] -rpi = ["RPi.GPIO"] - -[metadata] -lock-version = "1.1" -python-versions = "^3.7.3" -content-hash = "26a514da40cebddf91dd6020cb4a8f130e8684063d3aaf6a984e548b16338575" - -[metadata.files] -alabaster = [ - {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, -] -apispec = [ - {file = "apispec-4.3.0-py2.py3-none-any.whl", hash = "sha256:83e4d30edce987f4791f950c847d2b8cbe91e55af3d1ac7e3a35011a5c6fdbdd"}, -] -apispec-webframeworks = [ - {file = "apispec_webframeworks-0.5.2-py2.py3-none-any.whl", hash = "sha256:482c563abbcc2a261439476cb3f1a7c7284cc997c322c574d48c111643e9c04e"}, -] -appdirs = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, -] -astroid = [ - {file = "astroid-2.4.2-py3-none-any.whl", hash = "sha256:ba1e4f22868598dd8d6871a7db5f6c2acf974855fed93d2e3ababadb399962b3"}, -] -atomicwrites = [ - {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, -] -attrs = [ - {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, -] -babel = [ - {file = "Babel-2.9.0-py2.py3-none-any.whl", hash = "sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5"}, -] -black = [ - {file = "black-18.9b0-py36-none-any.whl", hash = "sha256:817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739"}, -] -camera-stage-mapping = [ - {file = "camera_stage_mapping-0.1.4-py3-none-any.whl", hash = "sha256:d39c3afebb59ec865fb1247dfd951d7090c159ab0cf3e4291e0869d3be33776c"}, -] -certifi = [ - {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, -] -chardet = [ - {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, -] -click = [ - {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:a3747c864f8e400a3664f5f4fd6dae11b4605bf6b727dae7b6f22ba9bd0a194a"}, -] -colorama = [ - {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, -] -colorzero = [ - {file = "colorzero-1.1-py2.py3-none-any.whl", hash = "sha256:e3c36d15b293de2b2f77ff54a5bd243fffac941ed0a5332d0697a6612a26a0a3"}, -] -coverage = [ - {file = "coverage-5.4-cp35-cp35m-linux_armv6l.whl", hash = "sha256:4b40b794775df10d7e3ea677108dd581bfec796235750c617d461874178a67f6"}, - {file = "coverage-5.4-cp35-cp35m-linux_armv7l.whl", hash = "sha256:4b40b794775df10d7e3ea677108dd581bfec796235750c617d461874178a67f6"}, - {file = "coverage-5.4-cp37-cp37m-linux_armv6l.whl", hash = "sha256:ae9702c099546e72000d76758b5efec2dd937ba5d746ec8d0563d2fca0f9bc2e"}, - {file = "coverage-5.4-cp37-cp37m-linux_armv7l.whl", hash = "sha256:ae9702c099546e72000d76758b5efec2dd937ba5d746ec8d0563d2fca0f9bc2e"}, -] -docutils = [ - {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, -] -expiringdict = [ - {file = "expiringdict-1.2.1-py3-none-any.whl", hash = "sha256:3eb293ca7ff15ed283d8e3ae9bbc5b1cbd2d60c906b08b38c7437380e8a2f697"}, -] -flask = [ - {file = "Flask-1.1.2-py2.py3-none-any.whl", hash = "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"}, -] -flask-cors = [ - {file = "Flask_Cors-3.0.10-py2.py3-none-any.whl", hash = "sha256:74efc975af1194fc7891ff5cd85b0f7478be4f7f59fe158102e91abb72bb4438"}, -] -freezegun = [ - {file = "freezegun-1.1.0-py2.py3-none-any.whl", hash = "sha256:2ae695f7eb96c62529f03a038461afe3c692db3465e215355e1bb4b0ab408712"}, -] -future = [ - {file = "future-0.18.2-py3-none-any.whl", hash = "sha256:83c4f8cec14cd08179d8b4bfe8c5e48724efadac8add187f385c68d6ae2345e6"}, -] -idna = [ - {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:4a57a6379512ade94fa99e2fa46d3cd0f2f553040548d0e2958c6ed90ee48226"}, -] -ifaddr = [ - {file = "ifaddr-0.1.7-py2.py3-none-any.whl", hash = "sha256:d1f603952f0a71c9ab4e705754511e4e03b02565bc4cec7188ad6415ff534cd3"}, -] -imagesize = [ - {file = "imagesize-1.2.0-py2.py3-none-any.whl", hash = "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1"}, -] -importlib-metadata = [ - {file = "importlib_metadata-3.4.0-py3-none-any.whl", hash = "sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771"}, -] -iniconfig = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:8647b85c03813b8680f4ae9c9db2fd7293f8591ea536a10d73d90f6eb4b10aac"}, -] -isort = [ - {file = "isort-5.7.0-py3-none-any.whl", hash = "sha256:fff4f0c04e1825522ce6949973e83110a6e907750cd92d128b0d14aaaadbffdc"}, -] -itsdangerous = [ - {file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"}, -] -jinja2 = [ - {file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"}, -] -labthings = [ - {file = "labthings-1.2.3-py3-none-any.whl", hash = "sha256:525ac63509554537cfae74f97724b56ea1251ac08a3245fce9a2326dfba9d9fa"}, -] -lazy-object-proxy = [ - {file = "lazy_object_proxy-1.4.3-cp35-cp35m-linux_armv6l.whl", hash = "sha256:c9b1297e959c61d41e049e6dec4ea2622783b3a7e32fd7a7f2390c0d65fe1ab5"}, - {file = "lazy_object_proxy-1.4.3-cp35-cp35m-linux_armv7l.whl", hash = "sha256:c9b1297e959c61d41e049e6dec4ea2622783b3a7e32fd7a7f2390c0d65fe1ab5"}, - {file = "lazy_object_proxy-1.4.3-cp37-cp37m-linux_armv6l.whl", hash = "sha256:f8895ded48ba1319a2b2a6247024cfa7a4e593f6d5e2711b4f957fb777dbb6ea"}, - {file = "lazy_object_proxy-1.4.3-cp37-cp37m-linux_armv7l.whl", hash = "sha256:f8895ded48ba1319a2b2a6247024cfa7a4e593f6d5e2711b4f957fb777dbb6ea"}, -] -lxml = [ - {file = "lxml-4.6.2-cp35-cp35m-linux_armv6l.whl", hash = "sha256:e1dbb88a937126ab14d219a000728224702e0ec0fc7ceb7131c53606b7a76772"}, - {file = "lxml-4.6.2-cp35-cp35m-linux_armv7l.whl", hash = "sha256:e1dbb88a937126ab14d219a000728224702e0ec0fc7ceb7131c53606b7a76772"}, - {file = "lxml-4.6.2-cp37-cp37m-linux_armv6l.whl", hash = "sha256:91d6dace31b07ab47eeadd3f4384ded2f77b94b30446410cb2c3e660e047f7a7"}, - {file = "lxml-4.6.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:91d6dace31b07ab47eeadd3f4384ded2f77b94b30446410cb2c3e660e047f7a7"}, -] -markupsafe = [ - {file = "MarkupSafe-1.1.1-cp34-cp34m-linux_armv6l.whl", hash = "sha256:19536834abffb3fa155017053c607cb835b2ecc6a3a2554a88043d991dffb736"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-linux_armv7l.whl", hash = "sha256:19536834abffb3fa155017053c607cb835b2ecc6a3a2554a88043d991dffb736"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-linux_armv6l.whl", hash = "sha256:7952deddf24b85c88dab48f6ec366ac6e39d2761b5280f2f9594911e03fcd064"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-linux_armv7l.whl", hash = "sha256:7952deddf24b85c88dab48f6ec366ac6e39d2761b5280f2f9594911e03fcd064"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-linux_armv6l.whl", hash = "sha256:3d61f15e39611aacd91b7e71d903787da86d9e80896e683c0103fced9add7834"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:3d61f15e39611aacd91b7e71d903787da86d9e80896e683c0103fced9add7834"}, -] -marshmallow = [ - {file = "marshmallow-3.10.0-py2.py3-none-any.whl", hash = "sha256:eca81d53aa4aafbc0e20566973d0d2e50ce8bf0ee15165bb799bec0df1e50177"}, -] -mccabe = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, -] -mypy = [ - {file = "mypy-0.790-py3-none-any.whl", hash = "sha256:2842d4fbd1b12ab422346376aad03ff5d0805b706102e475e962370f874a5122"}, -] -mypy-extensions = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, -] -numpy = [ - {file = "numpy-1.20.1-cp37-cp37m-linux_armv6l.whl", hash = "sha256:23d6802e7f2ceb9a1572f375470478251e821bdb4babbbbf3f2420c858d5dbde"}, - {file = "numpy-1.20.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:23d6802e7f2ceb9a1572f375470478251e821bdb4babbbbf3f2420c858d5dbde"}, -] -numpy-stubs = [] -opencv-python-headless = [ - {file = "opencv_python_headless-4.5.1.48-cp37-cp37m-linux_armv6l.whl", hash = "sha256:e61c8df818d79c6bd62a5a18a9b3bfdf389502798e3c22f95c67f8dcd09ce97b"}, - {file = "opencv_python_headless-4.5.1.48-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e61c8df818d79c6bd62a5a18a9b3bfdf389502798e3c22f95c67f8dcd09ce97b"}, -] -packaging = [ - {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, -] -pastel = [ - {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, -] -picamerax = [ - {file = "picamerax-20.9.1-py3-none-any.whl", hash = "sha256:8dc45542644ed9c67e3b331e90bcffa04098e8b056a42d0fedb25769b0b1f17e"}, -] -piexif = [ - {file = "piexif-1.1.3-py2.py3-none-any.whl", hash = "sha256:3bc435d171720150b81b15d27e05e54b8abbde7b4242cddd81ef160d283108b6"}, -] -pillow = [ - {file = "Pillow-7.2.0-cp35-cp35m-linux_armv6l.whl", hash = "sha256:39b8830992872e00605ec6eb0d83efad488553f10d79066b8b47c8be38e126b3"}, - {file = "Pillow-7.2.0-cp35-cp35m-linux_armv7l.whl", hash = "sha256:39b8830992872e00605ec6eb0d83efad488553f10d79066b8b47c8be38e126b3"}, - {file = "Pillow-7.2.0-cp37-cp37m-linux_armv6l.whl", hash = "sha256:e6d08a4d39b8a3a503c6e97e53ee2cc1dcd4fd05c4f87e8991f53e979608d0f6"}, - {file = "Pillow-7.2.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e6d08a4d39b8a3a503c6e97e53ee2cc1dcd4fd05c4f87e8991f53e979608d0f6"}, -] -pluggy = [ - {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, -] -poethepoet = [ - {file = "poethepoet-0.9.0-py3-none-any.whl", hash = "sha256:6b1df9a755c297d5b10749cd4713924055b41edfa62055770c8bd6b5da8e2c69"}, -] -psutil = [ - {file = "psutil-5.8.0-cp35-cp35m-linux_armv6l.whl", hash = "sha256:1ef2ab49f9138c6fc67ec9c3c5eac1b1345cf9c990b0c37af50e70d8a0a7b30e"}, - {file = "psutil-5.8.0-cp35-cp35m-linux_armv7l.whl", hash = "sha256:1ef2ab49f9138c6fc67ec9c3c5eac1b1345cf9c990b0c37af50e70d8a0a7b30e"}, - {file = "psutil-5.8.0-cp37-cp37m-linux_armv6l.whl", hash = "sha256:3379573a8f2447c496eec470a09b2071414aa4e527f80a44eacb3068f3b76ff3"}, - {file = "psutil-5.8.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:3379573a8f2447c496eec470a09b2071414aa4e527f80a44eacb3068f3b76ff3"}, -] -py = [ - {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, -] -pygments = [ - {file = "Pygments-2.8.0-py3-none-any.whl", hash = "sha256:b21b072d0ccdf29297a82a2363359d99623597b8a265b8081760e4d0f7153c88"}, -] -pylint = [ - {file = "pylint-2.6.2-py3-none-any.whl", hash = "sha256:e71c2e9614a4f06e36498f310027942b0f4f2fde20aebb01655b31edc63b9eaf"}, -] -pyparsing = [ - {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, -] -pyserial = [ - {file = "pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0"}, -] -pytest = [ - {file = "pytest-6.2.2-py3-none-any.whl", hash = "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"}, -] -pytest-cov = [ - {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"}, -] -python-dateutil = [ - {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, -] -pytz = [ - {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, -] -pyyaml = [ - {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, - {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, - {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, - {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, - {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, - {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, - {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, - {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, - {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, - {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, - {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, - {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, - {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, - {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, - {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, - {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, - {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, -] -requests = [ - {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, -] -rope = [ - {file = "rope-0.14.0-py3-none-any.whl", hash = "sha256:f0dcf719b63200d492b85535ebe5ea9b29e0d0b8aebeb87fe03fc1a65924fdaf"}, -] -"rpi.gpio" = [ - {file = "RPi.GPIO-0.7.0-cp34-cp34m-linux_armv6l.whl", hash = "sha256:5cd2f3c88d0b1e2f4780c905702c0474768c2bc113fb793121c2e117b401ab16"}, - {file = "RPi.GPIO-0.7.0-cp34-cp34m-linux_armv7l.whl", hash = "sha256:5cd2f3c88d0b1e2f4780c905702c0474768c2bc113fb793121c2e117b401ab16"}, - {file = "RPi.GPIO-0.7.0-cp35-cp35m-linux_armv6l.whl", hash = "sha256:7da5235aeba20da39ad4fb97f3ba3a2c6b2d60bba8958b00a4df42f608c6d42e"}, - {file = "RPi.GPIO-0.7.0-cp35-cp35m-linux_armv7l.whl", hash = "sha256:7da5235aeba20da39ad4fb97f3ba3a2c6b2d60bba8958b00a4df42f608c6d42e"}, - {file = "RPi.GPIO-0.7.0-cp37-cp37m-linux_armv6l.whl", hash = "sha256:6a4791f41cafc2ee6e4cb70e5bd31fadc66a0cfab29b38df8723a98f6f73ad5a"}, - {file = "RPi.GPIO-0.7.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:6a4791f41cafc2ee6e4cb70e5bd31fadc66a0cfab29b38df8723a98f6f73ad5a"}, -] -sangaboard = [ - {file = "sangaboard-0.2.3-py3-none-any.whl", hash = "sha256:3791159d57a749571f89acdce70aa9c1de82afef71f5186d5e2845c7be44698c"}, -] -scipy = [ - {file = "scipy-1.6.1-cp37-cp37m-linux_armv6l.whl", hash = "sha256:e3a56a9a68a3ad4a9d72ecc8570bf83106dd42237271460dd74079b449b9f6ea"}, - {file = "scipy-1.6.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3a56a9a68a3ad4a9d72ecc8570bf83106dd42237271460dd74079b449b9f6ea"}, -] -six = [ - {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, -] -snowballstemmer = [ - {file = "snowballstemmer-2.1.0-py2.py3-none-any.whl", hash = "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2"}, -] -sphinx = [ - {file = "Sphinx-3.5.1-py3-none-any.whl", hash = "sha256:e90161222e4d80ce5fc811ace7c6787a226b4f5951545f7f42acf97277bfc35c"}, -] -sphinxcontrib-applehelp = [ - {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, -] -sphinxcontrib-devhelp = [ - {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, -] -sphinxcontrib-htmlhelp = [ - {file = "sphinxcontrib_htmlhelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f"}, -] -sphinxcontrib-httpdomain = [ - {file = "sphinxcontrib_httpdomain-1.7.0-py2.py3-none-any.whl", hash = "sha256:1fb5375007d70bf180cdd1c79e741082be7aa2d37ba99efe561e1c2e3f38191e"}, -] -sphinxcontrib-jsmath = [ - {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, -] -sphinxcontrib-qthelp = [ - {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, -] -sphinxcontrib-serializinghtml = [ - {file = "sphinxcontrib_serializinghtml-1.1.4-py2.py3-none-any.whl", hash = "sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a"}, -] -toml = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, -] -tomlkit = [ - {file = "tomlkit-0.7.0-py2.py3-none-any.whl", hash = "sha256:6babbd33b17d5c9691896b0e68159215a9387ebfa938aa3ac42f4a4beeb2b831"}, -] -typed-ast = [ - {file = "typed_ast-1.4.2-cp35-cp35m-linux_armv6l.whl", hash = "sha256:4822449fb865605582a22d23997444ff3eaa3d0920b04b9902e37f8edbeadca6"}, - {file = "typed_ast-1.4.2-cp35-cp35m-linux_armv7l.whl", hash = "sha256:4822449fb865605582a22d23997444ff3eaa3d0920b04b9902e37f8edbeadca6"}, - {file = "typed_ast-1.4.2-cp37-cp37m-linux_armv6l.whl", hash = "sha256:a93908d202f52ec64e24a41031f1336a14b66706be2c9e51949647f7ac4cbd22"}, - {file = "typed_ast-1.4.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:a93908d202f52ec64e24a41031f1336a14b66706be2c9e51949647f7ac4cbd22"}, -] -typing-extensions = [ - {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, -] -urllib3 = [ - {file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"}, -] -webargs = [ - {file = "webargs-7.0.1-py2.py3-none-any.whl", hash = "sha256:ce8c565789ece1584be7edba41617319eb75de59b2987958bee3f3fdb3669e60"}, -] -werkzeug = [ - {file = "Werkzeug-1.0.1-py2.py3-none-any.whl", hash = "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43"}, -] -wrapt = [ - {file = "wrapt-1.12.1-cp34-cp34m-linux_armv6l.whl", hash = "sha256:c16fd7cad3d27a5db392ae0e6215533a912a7d3d83a40b8cf275744276989780"}, - {file = "wrapt-1.12.1-cp34-cp34m-linux_armv7l.whl", hash = "sha256:c16fd7cad3d27a5db392ae0e6215533a912a7d3d83a40b8cf275744276989780"}, - {file = "wrapt-1.12.1-cp35-cp35m-linux_armv6l.whl", hash = "sha256:1ba3a6bcb5014ed4d9c98afe8aa47b5ed320f84292b4f1b9f6afa0f4b2b91351"}, - {file = "wrapt-1.12.1-cp35-cp35m-linux_armv7l.whl", hash = "sha256:1ba3a6bcb5014ed4d9c98afe8aa47b5ed320f84292b4f1b9f6afa0f4b2b91351"}, - {file = "wrapt-1.12.1-cp37-cp37m-linux_armv6l.whl", hash = "sha256:d3294c918c5529e50b5454d39b589648d9baf6152e380067606d5566d71369a0"}, - {file = "wrapt-1.12.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:d3294c918c5529e50b5454d39b589648d9baf6152e380067606d5566d71369a0"}, -] -zeroconf = [ - {file = "zeroconf-0.28.8-py3-none-any.whl", hash = "sha256:3608be2db58f6f0dc70665e02ab420fb8bf428016f2c78403d879e066ecc9bff"}, -] -zipp = [ - {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"}, -] From fd32c22e30846660e2015032cd9b99a356f500e7 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Wed, 21 Apr 2021 12:40:10 +0100 Subject: [PATCH 17/23] Fixed formatting --- .../default_extensions/picamera_autocalibrate/extension.py | 5 ++++- setup.py | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py index 28f51352..146bc8d7 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py @@ -1,5 +1,6 @@ import logging from contextlib import contextmanager +from typing import Tuple import labthings.fields as fields from flask import abort @@ -7,7 +8,6 @@ from labthings import find_component from labthings.extensions import BaseExtension from labthings.views import ActionView from picamerax import PiCamera -from typing import Tuple from openflexure_microscope.camera.base import BaseCamera from openflexure_microscope.microscope import Microscope @@ -86,6 +86,7 @@ class LSTExtension(BaseExtension): endpoint="auto_lens_shading_table", ) + @contextmanager def find_picamera() -> Tuple[PiCamera, BaseCamera, Microscope]: """Locate the microscope and raise a sensible error if it's missing.""" @@ -132,6 +133,7 @@ class RecalibrateView(ActionView): picamera.lens_shading_table = lst microscope.save_settings() + class AutoLensShadingTableView(ActionView): def post(self): """Perform flat-field correction @@ -149,6 +151,7 @@ class AutoLensShadingTableView(ActionView): picamera.lens_shading_table = lst microscope.save_settings() + class FlattenLSTView(ActionView): def post(self): with find_picamera() as (picamera, scamera, microscope): diff --git a/setup.py b/setup.py index 73269b1a..adf729ff 100644 --- a/setup.py +++ b/setup.py @@ -6,10 +6,11 @@ https://packaging.python.org/guides/distributing-packages-using-setuptools/ """ -# the following imports, from the guide above, prefer `setuptools` to `distutils` -from setuptools import setup, find_packages from os import path +# the following imports, from the guide above, prefer `setuptools` to `distutils` +from setuptools import find_packages, setup + here = path.abspath(path.dirname(__file__)) # Get the long description from the README file From 8c50f73b347815902195c280012618244d38d71a Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Wed, 21 Apr 2021 12:54:53 +0100 Subject: [PATCH 18/23] Fixed type of context manager --- .../default_extensions/picamera_autocalibrate/extension.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py index 146bc8d7..e8dc726e 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py @@ -1,6 +1,6 @@ import logging from contextlib import contextmanager -from typing import Tuple +from typing import Tuple, Generator import labthings.fields as fields from flask import abort @@ -88,7 +88,7 @@ class LSTExtension(BaseExtension): @contextmanager -def find_picamera() -> Tuple[PiCamera, BaseCamera, Microscope]: +def find_picamera() -> Generator[Tuple[PiCamera, BaseCamera, Microscope], None, None]: """Locate the microscope and raise a sensible error if it's missing.""" microscope = find_component("org.openflexure.microscope") From 88de3089eabc1aaf5880488ceb7fa9d8474697d5 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Wed, 21 Apr 2021 13:38:14 +0100 Subject: [PATCH 19/23] Fixed linter errors --- .../default_extensions/picamera_autocalibrate/extension.py | 6 +++--- .../picamera_autocalibrate/recalibrate_utils.py | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py index e8dc726e..5f0d9c7e 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py @@ -205,7 +205,7 @@ class AutoExposureFromRawView(ActionView): } def post(self, args): - with find_picamera() as (picamera, scamera, microscope): + with find_picamera() as (picamera, _, _): adjust_shutter_and_gain_from_raw(picamera, **args) @@ -224,7 +224,7 @@ class AutoWhiteBalanceFromRawView(ActionView): } def post(self, args): - with find_picamera() as (picamera, scamera, microscope): + with find_picamera() as (picamera, _, _): adjust_white_balance_from_raw(picamera, **args) @@ -238,5 +238,5 @@ class GetRawChannelPercentilesView(ActionView): schema = fields.List(fields.Integer) def post(self, args): - with find_picamera() as (picamera, scamera, microscope): + with find_picamera() as (picamera, _, _): return get_channel_percentiles(picamera, args["percentile"]) 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 e6c6b5fe..352d2354 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py @@ -1,6 +1,5 @@ import logging import time -from fractions import Fraction from typing import List, Optional, Tuple import numpy as np @@ -40,7 +39,7 @@ def flat_lens_shading_table(camera: PiCamera) -> np.ndarray: def adjust_exposure_to_setpoint(camera: PiCamera, setpoint: int): """Adjust the camera's exposure time until the maximum pixel value is .""" - logging.info("Adjusting shutter speed to hit setpoint {}".format(setpoint)) + logging.info(f"Adjusting shutter speed to hit setpoint {setpoint}") for _ in range(3): camera.shutter_speed = int( camera.shutter_speed * setpoint / np.max(rgb_image(camera)) From 79b0d48cf4463fbdd0aecdb3a2bdfac159b2ab24 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Tue, 27 Apr 2021 17:13:40 +0100 Subject: [PATCH 20/23] Swap Generator annotation for Iterator --- .../default_extensions/picamera_autocalibrate/extension.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py index 5f0d9c7e..af31741e 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py @@ -1,6 +1,6 @@ import logging from contextlib import contextmanager -from typing import Tuple, Generator +from typing import Tuple, Iterator import labthings.fields as fields from flask import abort @@ -88,7 +88,7 @@ class LSTExtension(BaseExtension): @contextmanager -def find_picamera() -> Generator[Tuple[PiCamera, BaseCamera, Microscope], None, None]: +def find_picamera() -> Iterator[Tuple[PiCamera, BaseCamera, Microscope]]: """Locate the microscope and raise a sensible error if it's missing.""" microscope = find_component("org.openflexure.microscope") From bb303d4766392565cc9ae3dc0d63f4e77ddcb945 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Tue, 27 Apr 2021 18:54:21 +0100 Subject: [PATCH 21/23] Simplified auto gain/exposure algorithm adjust_shutter_and_gain_from_raw now uses a simpler algorithm. The code is longer overall but hides some details in fuctions. I've also split the loop into one for shutter speed and one for gain. This isn't the algorithm that I originally wrote, but it seems to work and is more readable. --- .../recalibrate_utils.py | 219 +++++++++++++----- 1 file changed, 155 insertions(+), 64 deletions(-) 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 352d2354..a6112cfe 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py @@ -1,17 +1,44 @@ +""" +Functions to set up a Raspberry Pi Camera v2 for scientific use + +This module provides slower, simpler functions to set the +gain, exposure, and white balance of a Raspberry Pi camera, using +`picamerax` (a fork of `picamera`) to get as-manual-as-possible +control over the camera. It's mostly used by the OpenFlexure +Microscope, though it deliberately has no hard dependencies on +said software, so that it's useful on its own. + +There are three main calibration steps: + +* Setting exposure time and gain to get a reasonably bright + image. +* Fixing the white balance to get a neutral image +* Taking a uniform white image and using it to calibrate + the Lens Shading Table + +The most reliable way to do this, avoiding any issues relating +to "memory" or nonlinearities in the camera's image processing +pipeline, is to use raw images. This is quite slow, but very +reliable. The three steps above can be accomplished by: + +``` +picamera = picamerax.PiCamera() + +adjust_shutter_and_gain_from_raw(picamera) +adjust_white_balance_from_raw(picamera) +lst = lst_from_camera(picamera) +picamera.lens_shading_table = lst +``` +""" + import logging import time -from typing import List, Optional, Tuple +from typing import List, Optional, Tuple, NamedTuple import numpy as np from picamerax import PiCamera from picamerax.array import PiBayerArray, PiRGBArray -# I have used f strings throughout, for readability. -# The performance implication of using them in logging statements -# really doesn't matter here - also, loglevel is usually set to -# INFO to report progress via the LabThings action API -# pylint: disable=logging-fstring-interpolation - def rgb_image( camera: PiCamera, resize: Optional[Tuple[int, int]] = None, **kwargs @@ -38,7 +65,10 @@ def flat_lens_shading_table(camera: PiCamera) -> np.ndarray: def adjust_exposure_to_setpoint(camera: PiCamera, setpoint: int): - """Adjust the camera's exposure time until the maximum pixel value is .""" + """Adjust the camera's exposure time until the maximum pixel value is . + + NB this method uses RGB images (i.e. processed ones) not raw images. + """ logging.info(f"Adjusting shutter speed to hit setpoint {setpoint}") for _ in range(3): camera.shutter_speed = int( @@ -47,6 +77,69 @@ def adjust_exposure_to_setpoint(camera: PiCamera, setpoint: int): time.sleep(1) +def set_minimum_exposure( + camera: PiCamera +): + """Enable manual exposure, with low gain and shutter speed + + We set exposure mode to manual, analog and digital gain + to 1, and shutter speed to the minimum (8us for Pi Camera v2) + NB ISO is left at auto, because this is needed for the gains + to be set correctly. + """ + camera.exposure_mode = "off" + camera.iso = 0 # We must set ISO=0 (auto) or we can't set gain + camera.analog_gain = 1 + camera.digital_gain = 1 + # Setting the shutter speed to 1us will result in it being set + # to the minimum possible, which is probably 8us for PiCamera v2 + camera.shutter_speed = 1 + time.sleep(0.5) + +class ExposureTest(NamedTuple): + """Record the results of testing the camera's current exposure settings""" + level: int + shutter_speed: int + analog_gain: float + + +def test_exposure_settings( + camera: PiCamera, + percentile: float +) -> ExposureTest: + """Evaluate current exposure settings using a raw image + + We will acquire a raw image and calculate the given percentile + of the pixel values. We return a dictionary containing the + percentile (which will be compared to the target), as well as + the camera's shutter and gain values. + """ + max_brightness = np.max(get_channel_percentiles(camera, percentile)) + # The reported brightness can, theoretically, be negative or zero + # because of black level compensation. The line below forces a + # minimum value of 1 which will keep things well-behaved! + if max_brightness < 1: + logging.warning( + f"Measured brightness of {max_brightness}. " + "This should normally be >= 1, and may indicate the " + "camera's black level compensation has gone wrong." + ) + max_brightness = 1 + shutter_speed = float(camera.shutter_speed) + analog_gain = float(camera.analog_gain) + logging.info( + f"Brightness: {max_brightness: >5.0f}, " + f"Gain: {analog_gain: >4.1f}, " + f"Shutter: {shutter_speed: >7.0f}" + ) + return ExposureTest(max_brightness, shutter_speed, analog_gain) + +def check_convergence(test: ExposureTest, target: int, tolerance: float): + """Check whether the brightness is within the specified target range""" + converged = abs(test.level - target) < target * tolerance + return converged + + def adjust_shutter_and_gain_from_raw( camera: PiCamera, target_white_level: int = 700, @@ -64,7 +157,7 @@ def adjust_shutter_and_gain_from_raw( target_white_level: The raw, 10-bit value we aim for. The brightest pixels should be approximately this bright. Maximum possible - is 1023, 700 is reasonable. + is about 900, 700 is reasonable. max_iterations: We will terminate once we perform this many iterations, whether or not we converge. More than 10 shouldn't happen. @@ -79,70 +172,61 @@ def adjust_shutter_and_gain_from_raw( of the brightness range, but seems much more reliable than just ``np.max()``. """ - # Start by setting fully manual exposure. - camera.exposure_mode = "off" - camera.iso = 0 # We must set ISO=0 (auto) or we can't set gain - camera.analog_gain = 1 - camera.digital_gain = 1 - camera.shutter_speed = 1 # This is not valid - we'll get the minimum - time.sleep(0.5) + if target_white_level * (tolerance + 1) >= 959: + raise ValueError("The target level is too high - a saturated image would be considered successful. target_white_level * (tolerance + 1) must be less than 959.") + + set_minimum_exposure(camera) - # We start with very low exposure settings and work up in coarse steps + # We start with very low exposure settings and work up # until either the brightness is high enough, or we can't increase the # shutter speed any more. iterations = 0 - shutter_speed_increments = [10, 2, None] - shutter_speed_increment = shutter_speed_increments.pop(0) while iterations < max_iterations: - iterations += 1 - max_brightness = np.max(get_channel_percentiles(camera, percentile)) - tested_shutter_speed = camera.shutter_speed - tested_analog_gain = camera.analog_gain - logging.info( - f"Brightness: {max_brightness: >5.0f}, " - f"Target: {target_white_level: >5.0f}, " - f"Gain: {float(tested_analog_gain): >4.1f}, " - f"Shutter: {float(tested_shutter_speed): >7.0f}" - ) + test = test_exposure_settings(camera, percentile) - if abs(max_brightness - target_white_level) < target_white_level * tolerance: - logging.info( - f"Brightness has converged to within {tolerance * 100 :.0f}% " - f"after {iterations} iterations." - ) + if check_convergence(test, target_white_level, tolerance): break + iterations += 1 - # if not shutter_speed_maximised: # Start by adjusting shutter speed - if max_brightness > target_white_level // 2: - shutter_speed_increment = ( - None - ) # If we're within a factor of 2, trigger fine-tuning - if shutter_speed_increment is None: - logging.info("Fine-tuning shutter speed") - new_shutter_speed = int( - tested_shutter_speed * target_white_level / max_brightness - ) - camera.shutter_speed = new_shutter_speed - else: - if max_brightness > target_white_level: - logging.info("Reducing shutter speed") - camera.shutter_speed = int( - tested_shutter_speed / shutter_speed_increment - ) - else: - logging.info("Increasing shutter speed") - camera.shutter_speed = tested_shutter_speed * shutter_speed_increment + # Adjust shutter speed so that the brightness approximates the target + # NB we put a maximum of 32 on this, to stop it increasing too quickly. + camera.shutter_speed = int( + test.shutter_speed * min(target_white_level / test.level, 32) + ) time.sleep(0.5) # Check whether the shutter speed is still going up - if not, we've hit a maximum - current_shutter_speed = camera.shutter_speed - if current_shutter_speed == tested_shutter_speed: - camera.analog_gain *= 2 - if camera.analog_gain == tested_analog_gain: - logging.info("Shutter speed and gain are both maxed out. Giving up!") - break - logging.info("Shutter speed has maxed out, doubled gain to compensate.") - return max_brightness + if camera.shutter_speed == test.shutter_speed: + logging.info("Shutter speed has maxed out.") + break + + # Now, if we've not converged, increase gain until we converge or run out of options. + while iterations < max_iterations: + test = test_exposure_settings(camera, percentile) + if check_convergence(test, target_white_level, tolerance): + break + iterations += 1 + + # Adjust gain to make the white level hit the target, again with a maximum + camera.analog_gain *= min(target_white_level / test.level, 2) + time.sleep(0.5) + + # Check the gain is still changing - if not, we have probably hit the maximum + if camera.analog_gain == test.analog_gain: + logging.info("Gain has maxed out.") + break + + if check_convergence(test, target_white_level, tolerance): + logging.info( + f"Brightness has converged to within {tolerance * 100 :.0f}%." + ) + else: + logging.warning( + f"Failed to reach target brightness of {target_white_level}." + f"Brightness reached {test.level} after {iterations} iterations." + ) + + return test.level def adjust_white_balance_from_raw( @@ -186,11 +270,18 @@ def channels_from_bayer_array(bayer_array: np.ndarray) -> np.ndarray: def get_channel_percentiles(camera: PiCamera, percentile: float) -> np.ndarray: - """Calculate the brightness percentile of the pixels in each channel""" + """Calculate the brightness percentile of the pixels in each channel + + This is a number between -64 and 959 for each channel, because the + camera takes 10-bit images (maximum=1023) and its zero level is set + at 64 for denoising purposes (there's black level compensation built + in, and to avoid skewing the noise, the black level is set as 64 to + leave some room for negative values. + """ with PiBayerArray(camera) as output: camera.capture(output, format="jpeg", bayer=True) channels = channels_from_bayer_array(output.array) - return np.percentile(channels, percentile, axis=(1, 2)) + return np.percentile(channels, percentile, axis=(1, 2)) - 64 def lst_from_channels(channels: np.ndarray) -> np.ndarray: From 87f636ecc09d4bf90b336089a225e77a4e96a99e Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Tue, 27 Apr 2021 19:56:54 +0100 Subject: [PATCH 22/23] Code format and linting --- .../picamera_autocalibrate/extension.py | 2 +- .../recalibrate_utils.py | 28 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py index af31741e..1a0bf539 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/extension.py @@ -1,6 +1,6 @@ import logging from contextlib import contextmanager -from typing import Tuple, Iterator +from typing import Iterator, Tuple import labthings.fields as fields from flask import abort 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 a6112cfe..c1aba3c7 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py @@ -33,7 +33,7 @@ picamera.lens_shading_table = lst import logging import time -from typing import List, Optional, Tuple, NamedTuple +from typing import List, NamedTuple, Optional, Tuple import numpy as np from picamerax import PiCamera @@ -77,9 +77,7 @@ def adjust_exposure_to_setpoint(camera: PiCamera, setpoint: int): time.sleep(1) -def set_minimum_exposure( - camera: PiCamera -): +def set_minimum_exposure(camera: PiCamera): """Enable manual exposure, with low gain and shutter speed We set exposure mode to manual, analog and digital gain @@ -96,17 +94,16 @@ def set_minimum_exposure( camera.shutter_speed = 1 time.sleep(0.5) + class ExposureTest(NamedTuple): """Record the results of testing the camera's current exposure settings""" + level: int shutter_speed: int analog_gain: float -def test_exposure_settings( - camera: PiCamera, - percentile: float -) -> ExposureTest: +def test_exposure_settings(camera: PiCamera, percentile: float) -> ExposureTest: """Evaluate current exposure settings using a raw image We will acquire a raw image and calculate the given percentile @@ -134,6 +131,7 @@ def test_exposure_settings( ) return ExposureTest(max_brightness, shutter_speed, analog_gain) + def check_convergence(test: ExposureTest, target: int, tolerance: float): """Check whether the brightness is within the specified target range""" converged = abs(test.level - target) < target * tolerance @@ -173,8 +171,12 @@ def adjust_shutter_and_gain_from_raw( than just ``np.max()``. """ if target_white_level * (tolerance + 1) >= 959: - raise ValueError("The target level is too high - a saturated image would be considered successful. target_white_level * (tolerance + 1) must be less than 959.") - + raise ValueError( + "The target level is too high - a saturated image would be " + "considered successful. target_white_level * (tolerance + 1) " + "must be less than 959." + ) + set_minimum_exposure(camera) # We start with very low exposure settings and work up @@ -215,11 +217,9 @@ def adjust_shutter_and_gain_from_raw( if camera.analog_gain == test.analog_gain: logging.info("Gain has maxed out.") break - + if check_convergence(test, target_white_level, tolerance): - logging.info( - f"Brightness has converged to within {tolerance * 100 :.0f}%." - ) + logging.info(f"Brightness has converged to within {tolerance * 100 :.0f}%.") else: logging.warning( f"Failed to reach target brightness of {target_white_level}." From 82eba1e99a0948274b1971ffefa4728a1ca2a304 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Tue, 27 Apr 2021 20:17:07 +0100 Subject: [PATCH 23/23] fixed typing error --- .../picamera_autocalibrate/recalibrate_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c1aba3c7..450584ed 100644 --- a/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py +++ b/openflexure_microscope/api/default_extensions/picamera_autocalibrate/recalibrate_utils.py @@ -122,7 +122,7 @@ def test_exposure_settings(camera: PiCamera, percentile: float) -> ExposureTest: "camera's black level compensation has gone wrong." ) max_brightness = 1 - shutter_speed = float(camera.shutter_speed) + shutter_speed = int(camera.shutter_speed) analog_gain = float(camera.analog_gain) logging.info( f"Brightness: {max_brightness: >5.0f}, "