Merge branch 'labthings-fixes' into 'master'

Labthings 0.6.0 fixes

See merge request openflexure/openflexure-microscope-server!58
This commit is contained in:
Joel Collins 2020-05-12 15:04:57 +00:00
commit 3a4975a070
13 changed files with 111 additions and 136 deletions

View file

@ -9,7 +9,6 @@ from labthings.server.decorators import (
PropertySchema, PropertySchema,
ThingAction, ThingAction,
doc_response, doc_response,
marshal_task,
) )
from labthings.server.schema import Schema from labthings.server.schema import Schema
from labthings.server import fields from labthings.server import fields
@ -70,10 +69,8 @@ def timelapse(microscope, n_images, t_between):
@ThingAction @ThingAction
class TimelapseAPI(View): class TimelapseAPI(View):
""" """
Take a series of images in a timelapse, running as a background task Take a series of images in a timelapse
""" """
@marshal_task # Shorthand for marshaling with a pre-made Task object schema
@use_args( @use_args(
{ {
"n_images": fields.Integer( "n_images": fields.Integer(
@ -88,13 +85,11 @@ class TimelapseAPI(View):
# Find our microscope component # Find our microscope component
microscope = find_component("org.openflexure.microscope") microscope = find_component("org.openflexure.microscope")
# Create and start "timelapse", running in a background task # Start "timelapse"
task = taskify(timelapse)( return timelapse(
microscope, args.get("n_images"), args.get("t_between") microscope, args.get("n_images"), args.get("t_between")
) )
return task
## Create extension ## Create extension

View file

@ -9,7 +9,6 @@ from labthings.server.decorators import (
PropertySchema, PropertySchema,
ThingAction, ThingAction,
doc_response, doc_response,
marshal_task,
) )
from labthings.server.schema import Schema from labthings.server.schema import Schema
from labthings.server import fields from labthings.server import fields
@ -74,8 +73,6 @@ class TimelapseAPI(View):
""" """
Take a series of images in a timelapse, running as a background task Take a series of images in a timelapse, running as a background task
""" """
@marshal_task # Shorthand for marshaling with a pre-made Task object schema
@use_args( @use_args(
{ {
"n_images": fields.Integer( "n_images": fields.Integer(
@ -91,12 +88,10 @@ class TimelapseAPI(View):
microscope = find_component("org.openflexure.microscope") microscope = find_component("org.openflexure.microscope")
# Create and start "timelapse", running in a background task # Create and start "timelapse", running in a background task
task = taskify(timelapse)( return timelapse(
microscope, args.get("n_images"), args.get("t_between") microscope, args.get("n_images"), args.get("t_between")
) )
return task
## Extension GUI (OpenFlexure eV) ## Extension GUI (OpenFlexure eV)
# Alternate form without any dynamic parts # Alternate form without any dynamic parts

View file

@ -15,7 +15,7 @@ We get around these issues by making use of background tasks, and component lock
Background tasks Background tasks
---------------- ----------------
Tasks are introduced to manage long-running functions in a way that does not block HTTP requests. Any function can be offloaded to a background task, and this is done through the :py:meth:`labthings.tasks.taskify` function, by calling ``taskify(<long_running_function>)(*args, **kwargs)``. Tasks are introduced to manage long-running functions in a way that does not block HTTP requests. Any API Action will automatically run as a background task (that is, any View that subclasses `ActionView`, or uses the `@ThingAction` decorator).
Internally, the ``tasks`` submodule stores a list of all requested tasks, and their states. This state stores the running status of the task (if itis idle, running, error, or success), information about the start and end times, a unique task ID, and, upon completion, the return value of the long-running function. Internally, the ``tasks`` submodule stores a list of all requested tasks, and their states. This state stores the running status of the task (if itis idle, running, error, or success), information about the start and end times, a unique task ID, and, upon completion, the return value of the long-running function.
@ -23,7 +23,7 @@ By using tasks, a function can be started in the background, and it's return val
API routes have been created to allow checking the state of all tasks (GET ``/tasks``), a particular task by ID (GET ``/tasks/<task_id>``), and terminating or removing individual tasks (DELETE ``/tasks/<task_id>``). API routes have been created to allow checking the state of all tasks (GET ``/tasks``), a particular task by ID (GET ``/tasks/<task_id>``), and terminating or removing individual tasks (DELETE ``/tasks/<task_id>``).
A special ``@marshal_task`` decorator provides shorthand for automatically returning a serialized representation of the task, when your POST request returns. All Actions will return a serialized representation of the task, when your POST request returns. If the task completes within a default timeout period (usually 1 second) then the completed Task representation will be returned. If the task is still running after this timeout period, the "in-progress" Task representation will be returned. The final output value can then be retrieved at a later time.
An example of a long running task may look like: An example of a long running task may look like:
@ -31,15 +31,13 @@ An example of a long running task may look like:
... ...
from labthings.tasks import taskify from labthings.tasks import taskify
from labthings.server.decorators import marshal_task from labthings.server.decorators import ThingAction
@ThingAction
class SlowAPI(View): class SlowAPI(View):
@marshal_task
def post(self): def post(self):
# Run the long-running function as a task # Return the task object.
task = taskify(long_running_function)(function_argument_1, function_argument_2) return long_running_function(function_argument_1, function_argument_2)
# Return the task object. Let @marshal_task handle serialization
return task
After some time, once the task has completed, it could be retreived using: After some time, once the task has completed, it could be retreived using:

View file

@ -177,5 +177,5 @@ if __name__ == "__main__":
from labthings.server.wsgi import Server from labthings.server.wsgi import Server
print("Starting OpenFlexure Microscope Server...") print("Starting OpenFlexure Microscope Server...")
server = Server(app) server = Server(app, log=logger, error_log=logger)
server.run(host="::", port=5000, debug=False, zeroconf=True) server.run(host="::", port=5000, debug=False, zeroconf=True)

View file

@ -1,9 +1,9 @@
from labthings.server.find import find_component from labthings.server.find import find_component
from labthings.server.extensions import BaseExtension from labthings.server.extensions import BaseExtension
from labthings.server.view import View from labthings.server.view import View
from labthings.server.decorators import ThingAction, ThingProperty, marshal_task from labthings.server.decorators import ThingAction, ThingProperty
from openflexure_microscope.devel import JsonResponse, request, taskify, abort from openflexure_microscope.devel import JsonResponse, request, abort
from openflexure_microscope.utilities import set_properties from openflexure_microscope.utilities import set_properties
import time import time
@ -337,8 +337,6 @@ class AutofocusAPI(View):
""" """
Run a standard autofocus Run a standard autofocus
""" """
@marshal_task
def post(self): def post(self):
payload = JsonResponse(request) payload = JsonResponse(request)
microscope = find_component("org.openflexure.microscope") microscope = find_component("org.openflexure.microscope")
@ -351,10 +349,9 @@ class AutofocusAPI(View):
if microscope.has_real_stage(): if microscope.has_real_stage():
logging.debug("Running autofocus...") logging.debug("Running autofocus...")
task = taskify(autofocus)(microscope, dz)
# return a handle on the autofocus task # return a handle on the autofocus task
return task return autofocus(microscope, dz)
else: else:
abort(503, "No stage connected. Unable to autofocus.") abort(503, "No stage connected. Unable to autofocus.")
@ -365,8 +362,6 @@ class FastAutofocusAPI(View):
""" """
Run a fast autofocus Run a fast autofocus
""" """
@marshal_task
def post(self): def post(self):
payload = JsonResponse(request) payload = JsonResponse(request)
microscope = find_component("org.openflexure.microscope") microscope = find_component("org.openflexure.microscope")
@ -382,12 +377,11 @@ class FastAutofocusAPI(View):
if microscope.has_real_stage(): if microscope.has_real_stage():
logging.debug("Running autofocus...") logging.debug("Running autofocus...")
task = taskify(fast_up_down_up_autofocus)(
microscope, dz=dz, mini_backlash=backlash
)
# return a handle on the autofocus task # return a handle on the autofocus task
return task return fast_up_down_up_autofocus(
microscope, dz=dz, mini_backlash=backlash
)
else: else:
abort(503, "No stage connected. Unable to autofocus.") abort(503, "No stage connected. Unable to autofocus.")
@ -405,6 +399,6 @@ autofocus_extension_v2.add_method(
) )
autofocus_extension_v2.add_method(autofocus, "autofocus") autofocus_extension_v2.add_method(autofocus, "autofocus")
autofocus_extension_v2.add_view(MeasureSharpnessAPI, "/measure_sharpness") autofocus_extension_v2.add_view(MeasureSharpnessAPI, "/measure_sharpness", endpoint="measure_sharpness")
autofocus_extension_v2.add_view(AutofocusAPI, "/autofocus") autofocus_extension_v2.add_view(AutofocusAPI, "/autofocus", endpoint="autofocus")
autofocus_extension_v2.add_view(FastAutofocusAPI, "/fast_autofocus") autofocus_extension_v2.add_view(FastAutofocusAPI, "/fast_autofocus", endpoint="fast_autofocus")

View file

@ -266,9 +266,9 @@ def dynamic_form():
} }
autostorage_extension_v2.add_view(GetLocationsView, "list-locations") autostorage_extension_v2.add_view(GetLocationsView, "/list-locations")
autostorage_extension_v2.add_view(PreferredLocationView, "location") autostorage_extension_v2.add_view(PreferredLocationView, "/location")
autostorage_extension_v2.add_view(PreferredLocationGUIView, "location-from-title") autostorage_extension_v2.add_view(PreferredLocationGUIView, "/location-from-title")
autostorage_extension_v2.add_meta( autostorage_extension_v2.add_meta(
"gui", build_gui(dynamic_form, autostorage_extension_v2) "gui", build_gui(dynamic_form, autostorage_extension_v2)
) )

View file

@ -7,14 +7,12 @@ from labthings.server.view import View
from labthings.server.find import find_component from labthings.server.find import find_component
from labthings.server.extensions import BaseExtension from labthings.server.extensions import BaseExtension
from labthings.server.decorators import ( from labthings.server.decorators import (
marshal_task,
ThingAction, ThingAction,
use_args, use_args,
ThingProperty, ThingProperty,
) )
from labthings.server import fields from labthings.server import fields
from labthings.core.tasks import taskify
from labthings.core.utilities import get_by_path, set_by_path, create_from_path from labthings.core.utilities import get_by_path, set_by_path, create_from_path
@ -146,31 +144,25 @@ class Calibrate1DView(View):
@use_args( @use_args(
{"direction": fields.List(fields.Float(), required=True, example=[1, 0, 0])} {"direction": fields.List(fields.Float(), required=True, example=[1, 0, 0])}
) )
@marshal_task
def post(self, args): def post(self, args):
"""Calibrate one axis of the microscope stage against the camera.""" """Calibrate one axis of the microscope stage against the camera."""
direction = np.array(args.get("direction")) direction = np.array(args.get("direction"))
task = taskify(csm_extension.calibrate_1d)(direction) return csm_extension.calibrate_1d(direction)
return task
csm_extension.add_view(Calibrate1DView, "/calibrate_1d") csm_extension.add_view(Calibrate1DView, "/calibrate_1d", endpoint="calibrate_1d")
@ThingAction @ThingAction
class CalibrateXYView(View): class CalibrateXYView(View):
@marshal_task
def post(self): def post(self):
"""Calibrate both axes of the microscope stage against the camera.""" """Calibrate both axes of the microscope stage against the camera."""
task = taskify(csm_extension.calibrate_xy)() return csm_extension.calibrate_xy()
return task
csm_extension.add_view(CalibrateXYView, "/calibrate_xy") csm_extension.add_view(CalibrateXYView, "/calibrate_xy", endpoint="calibrate_xy")
@ThingAction @ThingAction
@ -199,7 +191,7 @@ class MoveInImageCoordinatesView(View):
return csm_extension.microscope.state["stage"]["position"] return csm_extension.microscope.state["stage"]["position"]
csm_extension.add_view(MoveInImageCoordinatesView, "/move_in_image_coordinates") csm_extension.add_view(MoveInImageCoordinatesView, "/move_in_image_coordinates", endpoint="move_in_image_coordinates")
@ThingProperty @ThingProperty
@ -216,4 +208,4 @@ class GetCalibrationFile(View):
return {} return {}
csm_extension.add_view(GetCalibrationFile, "/get_calibration") csm_extension.add_view(GetCalibrationFile, "/get_calibration", endpoint="get_calibration")

View file

@ -1,9 +1,7 @@
from labthings.server.view import View from labthings.server.view import View
from labthings.server.find import find_component from labthings.server.find import find_component
from labthings.server.extensions import BaseExtension from labthings.server.extensions import BaseExtension
from labthings.server.decorators import marshal_task, ThingAction from labthings.server.decorators import ThingAction
from labthings.core.tasks import taskify
from flask import abort from flask import abort
@ -62,7 +60,6 @@ def recalibrate(microscope):
@ThingAction @ThingAction
class RecalibrateView(View): class RecalibrateView(View):
@marshal_task
def post(self): def post(self):
microscope = find_component("org.openflexure.microscope") microscope = find_component("org.openflexure.microscope")
@ -71,7 +68,7 @@ class RecalibrateView(View):
logging.info("Starting microscope recalibration...") logging.info("Starting microscope recalibration...")
return taskify(recalibrate)(microscope) return recalibrate(microscope)
@ThingAction @ThingAction
@ -131,6 +128,6 @@ lst_extension_v2.add_method(
recalibrate, "org.openflexure.calibration.picamera.recalibrate" recalibrate, "org.openflexure.calibration.picamera.recalibrate"
) )
lst_extension_v2.add_view(RecalibrateView, "/recalibrate") lst_extension_v2.add_view(RecalibrateView, "/recalibrate", endpoint="recalibrate")
lst_extension_v2.add_view(FlattenLSTView, "/flatten_lens_shading_table") lst_extension_v2.add_view(FlattenLSTView, "/flatten_lens_shading_table", endpoint="flatten_lens_shading_table")
lst_extension_v2.add_view(DeleteLSTView, "/delete_lens_shading_table") lst_extension_v2.add_view(DeleteLSTView, "/delete_lens_shading_table", endpoint="delete_lens_shading_table")

View file

@ -8,10 +8,10 @@ from functools import reduce
from openflexure_microscope.captures.capture_manager import generate_basename from openflexure_microscope.captures.capture_manager import generate_basename
from labthings.server.find import find_component, find_extension from labthings.server.find import find_component, find_extension
from labthings.server.extensions import BaseExtension from labthings.server.extensions import BaseExtension
from labthings.server.decorators import marshal_task, use_args, ThingAction from labthings.server.decorators import use_args, ThingAction
from labthings.server import fields from labthings.server import fields
from openflexure_microscope.devel import taskify, abort, update_task_progress from openflexure_microscope.devel import abort, update_task_progress
from labthings.server.view import View from labthings.server.view import View
import time import time
@ -309,7 +309,6 @@ class TileScanAPI(View):
"resize": fields.Dict(missing=None), # TODO: Validate keys "resize": fields.Dict(missing=None), # TODO: Validate keys
} }
) )
@marshal_task
def post(self, args): def post(self, args):
microscope = find_component("org.openflexure.microscope") microscope = find_component("org.openflexure.microscope")
@ -327,7 +326,9 @@ class TileScanAPI(View):
abort(404) abort(404)
logging.info("Running tile scan...") logging.info("Running tile scan...")
task = taskify(tile)(
# return a handle on the scan task
return tile(
microscope, microscope,
basename=args.get("filename"), basename=args.get("filename"),
temporary=args.get("temporary"), temporary=args.get("temporary"),
@ -343,10 +344,7 @@ class TileScanAPI(View):
tags=args.get("tags"), tags=args.get("tags"),
) )
# return a handle on the scan task
return task
scan_extension_v2 = BaseExtension("org.openflexure.scan", version="2.0.0") scan_extension_v2 = BaseExtension("org.openflexure.scan", version="2.0.0")
scan_extension_v2.add_view(TileScanAPI, "/tile") scan_extension_v2.add_view(TileScanAPI, "/tile", endpoint="tile")

View file

@ -1,7 +1,6 @@
from openflexure_microscope.devel import ( from openflexure_microscope.devel import (
JsonResponse, JsonResponse,
request, request,
taskify,
update_task_progress, update_task_progress,
) )
@ -22,7 +21,6 @@ from labthings.server.utilities import description_from_view
from labthings.server.decorators import ( from labthings.server.decorators import (
ThingAction, ThingAction,
ThingProperty, ThingProperty,
marshal_task,
marshal_with, marshal_with,
pre_dump, pre_dump,
) )
@ -142,19 +140,16 @@ default_zip_manager = ZipManager()
@ThingAction @ThingAction
class ZipBuilderAPIView(View): class ZipBuilderAPIView(View):
@marshal_task
def post(self): def post(self):
ids = list(JsonResponse(request).json) ids = list(JsonResponse(request).json)
microscope = find_component("org.openflexure.microscope") microscope = find_component("org.openflexure.microscope")
task = taskify(default_zip_manager.marshaled_build_zip_from_capture_ids)( # Return a handle on the autofocus task
return default_zip_manager.marshaled_build_zip_from_capture_ids(
microscope, ids microscope, ids
) )
# Return a handle on the autofocus task
return task
@ThingProperty @ThingProperty
class ZipListAPIView(View): class ZipListAPIView(View):
@ -205,7 +200,7 @@ zip_extension_v2 = BaseExtension(
description="Build and download capture collections as ZIP files", description="Build and download capture collections as ZIP files",
) )
zip_extension_v2.add_view(ZipGetterAPIView, "/get/<string:session_id>") zip_extension_v2.add_view(ZipGetterAPIView, "/get/<string:session_id>", endpoint="get_id")
zip_extension_v2.add_view(ZipListAPIView, "/get") zip_extension_v2.add_view(ZipListAPIView, "/get", endpoint="get")
zip_extension_v2.add_view(ZipBuilderAPIView, "/build") zip_extension_v2.add_view(ZipBuilderAPIView, "/build", endpoint="build")

View file

@ -12,7 +12,6 @@ from labthings.core.tasks import (
current_task, current_task,
update_task_progress, update_task_progress,
update_task_data, update_task_data,
taskify,
) )
@ -24,7 +23,6 @@ __all__ = [
"current_task", "current_task",
"update_task_progress", "update_task_progress",
"update_task_data", "update_task_data",
"taskify",
"abort", "abort",
"escape", "escape",
"Response", "Response",

117
poetry.lock generated
View file

@ -28,15 +28,15 @@ description = "A small Python module for determining appropriate platform-specif
name = "appdirs" name = "appdirs"
optional = false optional = false
python-versions = "*" python-versions = "*"
version = "1.4.3" version = "1.4.4"
[[package]] [[package]]
category = "dev" category = "dev"
description = "An abstract syntax tree for Python with inference support." description = "An abstract syntax tree for Python with inference support."
name = "astroid" name = "astroid"
optional = false optional = false
python-versions = ">=3.5.*" python-versions = ">=3.5"
version = "2.4.0" version = "2.4.1"
[package.dependencies] [package.dependencies]
lazy-object-proxy = ">=1.4.0,<1.5.0" lazy-object-proxy = ">=1.4.0,<1.5.0"
@ -89,6 +89,17 @@ toml = ">=0.9.4"
[package.extras] [package.extras]
d = ["aiohttp (>=3.3.2)"] d = ["aiohttp (>=3.3.2)"]
[[package]]
category = "main"
description = "Pure Python CBOR (de)serializer with extensive tag support"
name = "cbor2"
optional = false
python-versions = "*"
version = "5.1.0"
[package.extras]
test = ["pytest", "pytest-cov"]
[[package]] [[package]]
category = "dev" category = "dev"
description = "Python package for providing Mozilla's CA Bundle." description = "Python package for providing Mozilla's CA Bundle."
@ -179,7 +190,7 @@ description = "Coroutine-based network library"
name = "gevent" name = "gevent"
optional = false 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.*"
version = "20.4.0" version = "20.5.0"
[package.dependencies] [package.dependencies]
cffi = ">=1.12.2" cffi = ">=1.12.2"
@ -279,11 +290,12 @@ description = "Python implementation of LabThings, based on the Flask microframe
name = "labthings" name = "labthings"
optional = false optional = false
python-versions = ">=3.6,<4.0" python-versions = ">=3.6,<4.0"
version = "0.5.3" version = "0.6.0"
[package.dependencies] [package.dependencies]
Flask = ">=1.1.1,<2.0.0" Flask = ">=1.1.1,<2.0.0"
apispec = ">=3.2.0,<4.0.0" apispec = ">=3.2.0,<4.0.0"
cbor2 = ">=5.1.0,<6.0.0"
flask-cors = ">=3.0.8,<4.0.0" flask-cors = ">=3.0.8,<4.0.0"
gevent = ">=1.4,<21.0" gevent = ">=1.4,<21.0"
gevent-websocket = ">=0.10.1,<0.11.0" gevent-websocket = ">=0.10.1,<0.11.0"
@ -313,7 +325,7 @@ description = "A lightweight library for converting complex datatypes to and fro
name = "marshmallow" name = "marshmallow"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
version = "3.5.2" version = "3.6.0"
[package.extras] [package.extras]
dev = ["pytest", "pytz", "simplejson", "mypy (0.770)", "flake8 (3.7.9)", "flake8-bugbear (20.1.4)", "pre-commit (>=1.20,<3.0)", "tox"] dev = ["pytest", "pytz", "simplejson", "mypy (0.770)", "flake8 (3.7.9)", "flake8-bugbear (20.1.4)", "pre-commit (>=1.20,<3.0)", "tox"]
@ -432,7 +444,7 @@ description = "python code static checker"
name = "pylint" name = "pylint"
optional = false optional = false
python-versions = ">=3.5.*" python-versions = ">=3.5.*"
version = "2.5.0" version = "2.5.2"
[package.dependencies] [package.dependencies]
astroid = ">=2.4.0,<=2.5" astroid = ">=2.4.0,<=2.5"
@ -688,17 +700,17 @@ description = "Declarative parsing and validation of HTTP request objects, with
name = "webargs" name = "webargs"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
version = "6.0.0" version = "6.1.0"
[package.dependencies] [package.dependencies]
marshmallow = ">=2.15.2" marshmallow = ">=2.15.2"
[package.extras] [package.extras]
dev = ["pytest", "webtest (2.0.34)", "webtest-aiohttp (2.0.0)", "pytest-aiohttp (>=0.3.0)", "Flask (>=0.12.2)", "Django (>=1.11.16)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "webapp2 (>=3.0.0b1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.0)", "mypy (0.761)", "flake8 (3.7.9)", "flake8-bugbear (20.1.4)", "pre-commit (>=1.20,<3.0)", "tox", "mock"] dev = ["pytest", "webtest (2.0.35)", "webtest-aiohttp (2.0.0)", "pytest-aiohttp (>=0.3.0)", "Flask (>=0.12.2)", "Django (>=1.11.16)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "webapp2 (>=3.0.0b1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.0)", "mypy (0.770)", "flake8 (3.7.9)", "flake8-bugbear (20.1.4)", "pre-commit (>=1.20,<3.0)", "tox", "mock"]
docs = ["Sphinx (2.4.3)", "sphinx-issues (1.2.0)", "sphinx-typlog-theme (0.8.0)", "Flask (>=0.12.2)", "Django (>=1.11.16)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "webapp2 (>=3.0.0b1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.0)"] docs = ["Sphinx (3.0.3)", "sphinx-issues (1.2.0)", "sphinx-typlog-theme (0.8.0)", "Flask (>=0.12.2)", "Django (>=1.11.16)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "webapp2 (>=3.0.0b1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.0)"]
frameworks = ["Flask (>=0.12.2)", "Django (>=1.11.16)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "webapp2 (>=3.0.0b1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.0)"] frameworks = ["Flask (>=0.12.2)", "Django (>=1.11.16)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "webapp2 (>=3.0.0b1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.0)"]
lint = ["mypy (0.761)", "flake8 (3.7.9)", "flake8-bugbear (20.1.4)", "pre-commit (>=1.20,<3.0)"] lint = ["mypy (0.770)", "flake8 (3.7.9)", "flake8-bugbear (20.1.4)", "pre-commit (>=1.20,<3.0)"]
tests = ["pytest", "webtest (2.0.34)", "webtest-aiohttp (2.0.0)", "pytest-aiohttp (>=0.3.0)", "Flask (>=0.12.2)", "Django (>=1.11.16)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "webapp2 (>=3.0.0b1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.0)", "mock"] tests = ["pytest", "webtest (2.0.35)", "webtest-aiohttp (2.0.0)", "pytest-aiohttp (>=0.3.0)", "Flask (>=0.12.2)", "Django (>=1.11.16)", "bottle (>=0.12.13)", "tornado (>=4.5.2)", "pyramid (>=1.9.1)", "webapp2 (>=3.0.0b1)", "falcon (>=2.0.0)", "aiohttp (>=3.0.0)", "mock"]
[[package]] [[package]]
category = "main" category = "main"
@ -726,7 +738,7 @@ description = "Pure Python Multicast DNS Service Discovery Library (Bonjour/Avah
name = "zeroconf" name = "zeroconf"
optional = false optional = false
python-versions = "*" python-versions = "*"
version = "0.26.0" version = "0.26.1"
[package.dependencies] [package.dependencies]
ifaddr = "*" ifaddr = "*"
@ -735,7 +747,7 @@ ifaddr = "*"
rpi = ["picamera", "RPi.GPIO"] rpi = ["picamera", "RPi.GPIO"]
[metadata] [metadata]
content-hash = "78984c9fc264ae453f51e49d12d3d70e663b7d2a0d3a1829529339904d0c5c00" content-hash = "0e7b3418cd280b51aee60e79b190927945d25251f81596629a5a4ee7f47977a7"
python-versions = "^3.6" python-versions = "^3.6"
[metadata.files] [metadata.files]
@ -748,12 +760,12 @@ apispec = [
{file = "apispec-3.3.0.tar.gz", hash = "sha256:419d0564b899e182c2af50483ea074db8cb05fee60838be58bb4542095d5c08d"}, {file = "apispec-3.3.0.tar.gz", hash = "sha256:419d0564b899e182c2af50483ea074db8cb05fee60838be58bb4542095d5c08d"},
] ]
appdirs = [ appdirs = [
{file = "appdirs-1.4.3-py2.py3-none-any.whl", hash = "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"}, {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
{file = "appdirs-1.4.3.tar.gz", hash = "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92"}, {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
] ]
astroid = [ astroid = [
{file = "astroid-2.4.0-py3-none-any.whl", hash = "sha256:2fecea42b20abb1922ed65c7b5be27edfba97211b04b2b6abc6a43549a024ea6"}, {file = "astroid-2.4.1-py3-none-any.whl", hash = "sha256:d8506842a3faf734b81599c8b98dcc423de863adcc1999248480b18bd31a0f38"},
{file = "astroid-2.4.0.tar.gz", hash = "sha256:29fa5d46a2404d01c834fcb802a3943685f1fc538eb2a02a161349f5505ac196"}, {file = "astroid-2.4.1.tar.gz", hash = "sha256:4c17cea3e592c21b6e222f673868961bad77e1f985cb1694ed077475a89229c1"},
] ]
attrs = [ attrs = [
{file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"},
@ -767,6 +779,9 @@ black = [
{file = "black-18.9b0-py36-none-any.whl", hash = "sha256:817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739"}, {file = "black-18.9b0-py36-none-any.whl", hash = "sha256:817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739"},
{file = "black-18.9b0.tar.gz", hash = "sha256:e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"}, {file = "black-18.9b0.tar.gz", hash = "sha256:e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"},
] ]
cbor2 = [
{file = "cbor2-5.1.0.tar.gz", hash = "sha256:43ce11e8c2fe4971d386d1a60cf83bfa0a4a667b97668ba76acbf5e6398821aa"},
]
certifi = [ certifi = [
{file = "certifi-2020.4.5.1-py2.py3-none-any.whl", hash = "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304"}, {file = "certifi-2020.4.5.1-py2.py3-none-any.whl", hash = "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304"},
{file = "certifi-2020.4.5.1.tar.gz", hash = "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"}, {file = "certifi-2020.4.5.1.tar.gz", hash = "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"},
@ -826,30 +841,28 @@ flask-cors = [
{file = "Flask_Cors-3.0.8-py2.py3-none-any.whl", hash = "sha256:f4d97201660e6bbcff2d89d082b5b6d31abee04b1b3003ee073a6fd25ad1d69a"}, {file = "Flask_Cors-3.0.8-py2.py3-none-any.whl", hash = "sha256:f4d97201660e6bbcff2d89d082b5b6d31abee04b1b3003ee073a6fd25ad1d69a"},
] ]
gevent = [ gevent = [
{file = "gevent-20.4.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1ef086264e846371beb5742ebaeb148dc96adf72da2ff350ae5603421cdc2ad9"}, {file = "gevent-20.5.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:efd9546468502a30ddd4699c3124ccb9d3099130f9b5ae1e2a54ad5b46e86120"},
{file = "gevent-20.4.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:2070c65896f89a85b39f49427d6132f7abd047129fc4da88b3670f0ba13b0cf7"}, {file = "gevent-20.5.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3ff477b6d275396123faf8ce2d5b82f96d85ba264e0b9d4b56a2bac49d1b9adc"},
{file = "gevent-20.4.0-cp27-cp27m-win32.whl", hash = "sha256:de6c0cbcb890d0a79323961d3b593a0f2f54dcb9fe38ee5167f2d514e69e3c8c"}, {file = "gevent-20.5.0-cp27-cp27m-win32.whl", hash = "sha256:92edc18a357473e01a4e4a82c073ed3c99ceca6e3ce93c23668dd4a2401f07dc"},
{file = "gevent-20.4.0-cp27-cp27m-win_amd64.whl", hash = "sha256:3b4c4d99f87c0d04b825879c5a91fbfa2b66da7c25b8689e9bdd9f4741d5f80d"}, {file = "gevent-20.5.0-cp27-cp27m-win_amd64.whl", hash = "sha256:1dd95433be45e1115053878366e3f5332ae99c39cb345be23851327c062b9f4a"},
{file = "gevent-20.4.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:8cca7ffd58559f8d51e5605ad73afcc6f348f9747d2fa539b336e70851b69b79"}, {file = "gevent-20.5.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:fcb64f3a28420d1b872b7ef41b12e8a1a4dcadfc8eff3c09993ab0cdf52584a1"},
{file = "gevent-20.4.0-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:b46399f6c9eccc2e6de1dc1057d362be840443e5439b06cce8b01d114ba1a7ec"}, {file = "gevent-20.5.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:4d2729dd4bf9c4d0f29482f53cdf9fc90a498aebb5cd7ae8b45d35657437d2ac"},
{file = "gevent-20.4.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:ee39caf14d66e619709cdfe3962bc68a234518e43ea8c811c0d67a864bc7c196"}, {file = "gevent-20.5.0-cp35-cp35m-win32.whl", hash = "sha256:00b03601b8dd1ee2aa07811cb60a4befe36173b15d91c6e207e37f8d77dd6fac"},
{file = "gevent-20.4.0-cp35-cp35m-win32.whl", hash = "sha256:8a9aba59a3268f20c7b584119215bdc589cb81500d93dad4dab428eb02f72944"}, {file = "gevent-20.5.0-cp35-cp35m-win_amd64.whl", hash = "sha256:937d36730f2b0dee3387712074b1f15b802e2e074a3d7c6dcaf70521236d607c"},
{file = "gevent-20.4.0-cp35-cp35m-win_amd64.whl", hash = "sha256:6088bedd8b6bcdb815be322304a5d1c028ffa837d84e93b349928dadac62f354"}, {file = "gevent-20.5.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:929c33df8e9bcbe31906024fcd21580bd018196dbd3249eb5b2f19d63e11092d"},
{file = "gevent-20.4.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c0b38a654c8fde5b9d9bd27ea3261aeefe36bc9244b170b6d3b11d72a2163bdb"}, {file = "gevent-20.5.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:52e5cd607749ed3b8aa0272cacf2c11deec61fca4c3bec57a9fea8c49316627d"},
{file = "gevent-20.4.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:e7d23d5f32c9db6ae49c4b58585618dcafd6ad0babae251c9c8297afebc4744b"}, {file = "gevent-20.5.0-cp36-cp36m-win32.whl", hash = "sha256:15eae3cd450dac7dae7f4ac59e01db1378965c9ef565c39c5ae78c5a888f9ac9"},
{file = "gevent-20.4.0-cp36-cp36m-win32.whl", hash = "sha256:0b84a8d6f088b29a74402728681c9f11864b95e49f5587a666e6fbf5c683e597"}, {file = "gevent-20.5.0-cp36-cp36m-win_amd64.whl", hash = "sha256:9b4e940fc6071afebb86ba5f48dbb5f1fc3cb96ebeb8cf145eb5b499e9c6ee33"},
{file = "gevent-20.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:b0aea12de542f8fcd6882087bdd5b4d7dc8bb316d28181f6b012dd0b91583285"}, {file = "gevent-20.5.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:e01d5373528e4ebdde66dc47a608d225fa3c4408ccd828d26c49b7ff75d82bd9"},
{file = "gevent-20.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e0990009e7c1624f9a0f3335df1ab8d45678241c852659ac645b70ed8229097c"}, {file = "gevent-20.5.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:31dc5d4ab8172cc00c4ff17cb18edee633babd961f64bf54214244d769bc3a74"},
{file = "gevent-20.4.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:c7a62d51c6dca84f91a91b940037523c926a516f0568f47dc1386bd1682cf4e9"}, {file = "gevent-20.5.0-cp37-cp37m-win32.whl", hash = "sha256:0acc15ba2ac2a555529ad82d5a28fc85dbb6b2ff947657d67bebfd352e2b5c14"},
{file = "gevent-20.4.0-cp37-cp37m-win32.whl", hash = "sha256:d56f36eb98532d2bccc51cb0964c31e9fbd9b2282074c297dc9b006b047e2966"}, {file = "gevent-20.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:a7805934e8ce81610b61f806572c3d504cedd698cc8c9460d78d2893ba598c4a"},
{file = "gevent-20.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2fbe0bc43d8c5540153f06eece6235dda14e5f99bdd9183838396313100815d7"}, {file = "gevent-20.5.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:5c604179cebcc57f10505d8db177b92a715907815a464b066e7eba322d1c33ac"},
{file = "gevent-20.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4572dc7907a0ac3c39b9f0898dbdf390ae3250baaae5f7395661fb844e2e23be"}, {file = "gevent-20.5.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:88c76df4967c5229f853aa67ad1b394d9e4f985b0359c9bc9879416bba3e7c68"},
{file = "gevent-20.4.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:956e82a5d0e90f8d71efe4cecccde602cfb657cd866c58bb953c9c30ca1b3d77"}, {file = "gevent-20.5.0-cp38-cp38-win32.whl", hash = "sha256:d07a2afe4215731eb57d5b257a2e7e7e170d8a7ae1f02f6d0682cd3403debea9"},
{file = "gevent-20.4.0-cp38-cp38-win32.whl", hash = "sha256:38c45d8a3b647f56f8a68769a8ac4953be84a84735c7c7a4d7ca62022bd54036"}, {file = "gevent-20.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:28b7d83b4327ceb79668eca2049bf4b9ce66d5ace18a88335e3035b573f889fd"},
{file = "gevent-20.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:32813de352918fb652a3db805fd6e08e0a1666a1a9304eef95938c9c426f9573"}, {file = "gevent-20.5.0-pp27-pypy_73-win32.whl", hash = "sha256:38db524ea88d81d596b2cbb6948fced26654a15fec40ea4529224e239a6f45e8"},
{file = "gevent-20.4.0-pp27-pypy_73-macosx_10_7_x86_64.whl", hash = "sha256:42cae3be36b7458f411bd589c66aaba27e4e611ec3d3621e37fd732fe383f9b6"}, {file = "gevent-20.5.0.tar.gz", hash = "sha256:1dc7f1f6bc1f67d625e4272b01e717eba0b4fa024d2ff7934c8d320674d6f7fa"},
{file = "gevent-20.4.0-pp27-pypy_73-win32.whl", hash = "sha256:cea28f958bc4206ae092043e0775cd7a2bb2536bcbece292732c6484c1076c01"},
{file = "gevent-20.4.0.tar.gz", hash = "sha256:c516cc5d70c3faf07f271d50930d144339c69fb80f3cac9b687aa964e518535e"},
] ]
gevent-websocket = [ gevent-websocket = [
{file = "gevent-websocket-0.10.1.tar.gz", hash = "sha256:7eaef32968290c9121f7c35b973e2cc302ffb076d018c9068d2f5ca8b2d85fb0"}, {file = "gevent-websocket-0.10.1.tar.gz", hash = "sha256:7eaef32968290c9121f7c35b973e2cc302ffb076d018c9068d2f5ca8b2d85fb0"},
@ -903,8 +916,8 @@ jinja2 = [
{file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"}, {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"},
] ]
labthings = [ labthings = [
{file = "labthings-0.5.3-py3-none-any.whl", hash = "sha256:86ab087945fd1f824b1c1d6f8e05a7d149df5da5be650746a8432837ccc644b9"}, {file = "labthings-0.6.0-py3-none-any.whl", hash = "sha256:e23aa61df3d76b14f8490a43a5343cd77b4927441c81c0e4e7777b95608915b5"},
{file = "labthings-0.5.3.tar.gz", hash = "sha256:dc521e49d69031ef2754fa31c4f246b9b5c6aaed00f5d622c0a877970a8b9721"}, {file = "labthings-0.6.0.tar.gz", hash = "sha256:fa1449d71780e555370ba92d6959740fcf5623127700bc81ddcb355ec10ab9bb"},
] ]
lazy-object-proxy = [ lazy-object-proxy = [
{file = "lazy-object-proxy-1.4.3.tar.gz", hash = "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"}, {file = "lazy-object-proxy-1.4.3.tar.gz", hash = "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"},
@ -965,8 +978,8 @@ markupsafe = [
{file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"},
] ]
marshmallow = [ marshmallow = [
{file = "marshmallow-3.5.2-py2.py3-none-any.whl", hash = "sha256:f12203bf8d94c410ab4b8d66edfde4f8a364892bde1f6747179765559f93d62a"}, {file = "marshmallow-3.6.0-py2.py3-none-any.whl", hash = "sha256:f88fe96434b1f0f476d54224d59333eba8ca1a203a2695683c1855675c4049a7"},
{file = "marshmallow-3.5.2.tar.gz", hash = "sha256:56663fa1d5385c14c6a1236badd166d6dee987a5f64d2b6cc099dadf96eb4f09"}, {file = "marshmallow-3.6.0.tar.gz", hash = "sha256:c2673233aa21dde264b84349dc2fd1dce5f30ed724a0a00e75426734de5b84ab"},
] ]
mccabe = [ mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
@ -1124,8 +1137,8 @@ pygments = [
{file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"}, {file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"},
] ]
pylint = [ pylint = [
{file = "pylint-2.5.0-py3-none-any.whl", hash = "sha256:bd556ba95a4cf55a1fc0004c00cf4560b1e70598a54a74c6904d933c8f3bd5a8"}, {file = "pylint-2.5.2-py3-none-any.whl", hash = "sha256:dd506acce0427e9e08fb87274bcaa953d38b50a58207170dbf5b36cf3e16957b"},
{file = "pylint-2.5.0.tar.gz", hash = "sha256:588e114e3f9a1630428c35b7dd1c82c1c93e1b0e78ee312ae4724c5e1a1e0245"}, {file = "pylint-2.5.2.tar.gz", hash = "sha256:b95e31850f3af163c2283ed40432f053acbc8fc6eba6a069cb518d9dbf71848c"},
] ]
pyparsing = [ pyparsing = [
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
@ -1251,8 +1264,8 @@ urllib3 = [
{file = "urllib3-1.25.9.tar.gz", hash = "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527"}, {file = "urllib3-1.25.9.tar.gz", hash = "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527"},
] ]
webargs = [ webargs = [
{file = "webargs-6.0.0-py2.py3-none-any.whl", hash = "sha256:dfe01cd3711cee9a3651bfd5ca67770ace4a6f35f3cae583aa8400bbf7706bb4"}, {file = "webargs-6.1.0-py2.py3-none-any.whl", hash = "sha256:cf0b5e2fdfb81f28b9332fce15621069d3fbc910a01c7ca8a5e166371699927b"},
{file = "webargs-6.0.0.tar.gz", hash = "sha256:d66a056b3a40c5a3774a650fd349db5970cd91fa8f04ac50d00582c949b45b26"}, {file = "webargs-6.1.0.tar.gz", hash = "sha256:ebb47fb35c3c4fc764213a17d1686e82fec54759ebed2b0715907d566668bb3f"},
] ]
werkzeug = [ werkzeug = [
{file = "Werkzeug-1.0.1-py2.py3-none-any.whl", hash = "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43"}, {file = "Werkzeug-1.0.1-py2.py3-none-any.whl", hash = "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43"},
@ -1262,6 +1275,6 @@ wrapt = [
{file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"}, {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"},
] ]
zeroconf = [ zeroconf = [
{file = "zeroconf-0.26.0-py3-none-any.whl", hash = "sha256:e1e84eb98ce44335190bbb16a237052d07811b1516de69c7983d77162097fdbf"}, {file = "zeroconf-0.26.1-py3-none-any.whl", hash = "sha256:a0cdd43ee8f00e7082f784c4226d2609070ad0b2aeb34b0154466950d2134de6"},
{file = "zeroconf-0.26.0.tar.gz", hash = "sha256:01161f4328d1b3f089008fd0a394524504c87de5a8017ea286c4f1068aea3c09"}, {file = "zeroconf-0.26.1.tar.gz", hash = "sha256:51f25787c27cf7b903e6795e8763bccdaa71199f61b75af97f1bde036fa43b27"},
] ]

View file

@ -39,7 +39,7 @@ opencv-python-headless = [
{version = "4.1.0.25", python = "~3.7"}, # PiWheels build for RPi running Py37 {version = "4.1.0.25", python = "~3.7"}, # PiWheels build for RPi running Py37
{version = "4.2.0.34", python = "^3.8"} # Latest for Py38 systems {version = "4.2.0.34", python = "^3.8"} # Latest for Py38 systems
] ]
labthings = "0.5.3" labthings = "0.6.0"
[tool.poetry.extras] [tool.poetry.extras]
rpi = ["picamera", "RPi.GPIO"] rpi = ["picamera", "RPi.GPIO"]