Merge branch 'optimised-capture-threadpools' into 'master'
Optimised capture IO See merge request openflexure/openflexure-microscope-server!62
This commit is contained in:
commit
aa9f621476
12 changed files with 220 additions and 175 deletions
|
|
@ -3,13 +3,20 @@ from gevent import monkey
|
|||
|
||||
monkey.patch_all()
|
||||
|
||||
import sys
|
||||
import time
|
||||
import atexit
|
||||
import logging, logging.handlers
|
||||
|
||||
# Look for debug flag
|
||||
if "-d" in sys.argv or "--debug" in sys.argv:
|
||||
log_level = logging.DEBUG
|
||||
else:
|
||||
log_level = logging.INFO
|
||||
|
||||
# Set root logger level
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.INFO)
|
||||
logger.setLevel(log_level)
|
||||
|
||||
import os
|
||||
import pkg_resources
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class MjpegStream(PropertyView):
|
|||
frame from the camera object, passed to the Flask response, and then repeats until
|
||||
the connection is closed.
|
||||
|
||||
Without monkey patching with gevent, or using a native threaded server, the stream
|
||||
Without monkey patching, or using a native threaded server, the stream
|
||||
will block all proceeding requests.
|
||||
"""
|
||||
microscope = find_component("org.openflexure.microscope")
|
||||
|
|
|
|||
|
|
@ -2,12 +2,10 @@
|
|||
import time
|
||||
import os
|
||||
import shutil
|
||||
import threading
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
import threading
|
||||
import gevent
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
|
|
@ -25,7 +23,7 @@ class BaseCamera(metaclass=ABCMeta):
|
|||
self.thread = None
|
||||
self.camera = None
|
||||
|
||||
self.lock = StrictLock(timeout=1, name="Camera")
|
||||
self.lock = StrictLock(name="Camera")
|
||||
|
||||
self.frame = None
|
||||
self.last_access = 0
|
||||
|
|
@ -93,9 +91,10 @@ class BaseCamera(metaclass=ABCMeta):
|
|||
self.stop = False
|
||||
|
||||
if not self.stream_active:
|
||||
# Spawn a greenlet to handle stream
|
||||
# start background frame thread
|
||||
self.thread = gevent.spawn(self._thread)
|
||||
# Start background frame thread
|
||||
self.thread = threading.Thread(target=self._thread)
|
||||
self.thread.daemon = True
|
||||
self.thread.start()
|
||||
|
||||
# wait until frames are available
|
||||
logging.info("Waiting for frames")
|
||||
|
|
|
|||
|
|
@ -197,19 +197,14 @@ class MissingCamera(BaseCamera):
|
|||
bayer (bool): Store raw bayer data in capture
|
||||
"""
|
||||
|
||||
if isinstance(output, CaptureObject):
|
||||
target = output.file
|
||||
else:
|
||||
target = output
|
||||
|
||||
with self.lock:
|
||||
if isinstance(target, str):
|
||||
target = open(target, "wb")
|
||||
if isinstance(output, str):
|
||||
output = open(output, "wb")
|
||||
|
||||
target.write(self.stream.getvalue())
|
||||
output.write(self.stream.getvalue())
|
||||
|
||||
if isinstance(target, str):
|
||||
target.close()
|
||||
if isinstance(output, str):
|
||||
output.close()
|
||||
|
||||
# HANDLE STREAM FRAMES
|
||||
|
||||
|
|
|
|||
|
|
@ -108,18 +108,14 @@ class PiCameraStreamer(BaseCamera):
|
|||
1312,
|
||||
976,
|
||||
) #: tuple: Resolution for numpy array captures
|
||||
self.jpeg_quality = 75 #: int: JPEG quality
|
||||
self.jpeg_quality = 100 #: int: JPEG quality
|
||||
self.mjpeg_quality = 75 #: int: MJPEG quality
|
||||
|
||||
# Set default lens shading table path
|
||||
self.picamera_lst_path = settings_file_path(
|
||||
"picamera_lst.npy"
|
||||
) #: str: Path of .npy lens shading table file
|
||||
|
||||
# Create an empty stream
|
||||
self.stream = io.BytesIO()
|
||||
|
||||
# Start streaming
|
||||
self.start_worker()
|
||||
|
||||
@property
|
||||
def configuration(self):
|
||||
|
|
@ -159,6 +155,7 @@ class PiCameraStreamer(BaseCamera):
|
|||
"image_resolution": self.image_resolution,
|
||||
"numpy_resolution": self.numpy_resolution,
|
||||
"jpeg_quality": self.jpeg_quality,
|
||||
"mjpeg_quality": self.mjpeg_quality,
|
||||
"picamera": {},
|
||||
}
|
||||
)
|
||||
|
|
@ -479,7 +476,7 @@ class PiCameraStreamer(BaseCamera):
|
|||
self.camera.start_recording(
|
||||
self.stream,
|
||||
format="mjpeg",
|
||||
quality=self.jpeg_quality,
|
||||
quality=self.mjpeg_quality,
|
||||
bitrate=-1, # RWB: disable bitrate control
|
||||
# (bitrate control makes JPEG size less good as a focus
|
||||
# metric)
|
||||
|
|
@ -520,12 +517,6 @@ class PiCameraStreamer(BaseCamera):
|
|||
Returns:
|
||||
output_object (str/BytesIO): Target object.
|
||||
"""
|
||||
|
||||
if isinstance(output, CaptureObject):
|
||||
target = output.file
|
||||
else:
|
||||
target = output
|
||||
|
||||
with self.lock:
|
||||
logging.info("Capturing to {}".format(output))
|
||||
|
||||
|
|
@ -535,20 +526,20 @@ class PiCameraStreamer(BaseCamera):
|
|||
time.sleep(0.1)
|
||||
|
||||
self.camera.capture(
|
||||
target,
|
||||
output,
|
||||
format=fmt,
|
||||
quality=100,
|
||||
quality=self.jpeg_quality,
|
||||
resize=resize,
|
||||
bayer=(not use_video_port) and bayer,
|
||||
use_video_port=use_video_port,
|
||||
)
|
||||
time.sleep(0.1)
|
||||
#time.sleep(0.1)
|
||||
|
||||
# Set resolution and start stream recording if necessary
|
||||
if not use_video_port:
|
||||
self.start_stream_recording()
|
||||
|
||||
return target
|
||||
return output
|
||||
|
||||
def yuv(
|
||||
self, use_video_port: bool = True, resize: Tuple[int, int] = None
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ from PIL import Image
|
|||
import dateutil.parser
|
||||
import atexit
|
||||
|
||||
from gevent.fileobject import FileObjectThread
|
||||
import gevent
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from openflexure_microscope.camera import piexif
|
||||
|
|
@ -118,12 +121,18 @@ def capture_from_exif(path, exif_dict):
|
|||
|
||||
class CaptureObject(object):
|
||||
"""
|
||||
StreamObject used to store and process on-disk capture data, and metadata.
|
||||
File-like object used to store and process on-disk capture data, and metadata.
|
||||
Serves to simplify modifying properties of on-disk capture data.
|
||||
"""
|
||||
|
||||
def __init__(self, filepath) -> None:
|
||||
"""Create a new StreamObject, to manage capture data."""
|
||||
# Stream for buffering capture data
|
||||
self.stream = io.BytesIO()
|
||||
# Event to notify when the stream has finished writing to disk
|
||||
self.file_ready = gevent.event.Event()
|
||||
# Access lock for the disk file
|
||||
self.lock = gevent.lock.BoundedSemaphore()
|
||||
|
||||
# Store a nice ID
|
||||
self.id = uuid.uuid4() #: str: Unique capture ID
|
||||
|
|
@ -148,6 +157,23 @@ class CaptureObject(object):
|
|||
# Thumbnail (populated only for PIL captures)
|
||||
self.thumb_bytes = None
|
||||
|
||||
def write(self, s):
|
||||
self.stream.write(s)
|
||||
|
||||
def _stream_to_file(self):
|
||||
with self.lock:
|
||||
logging.info(f"Writing to disk {self.file}")
|
||||
with FileObjectThread(open(self.file, "wb"), 'wb') as outfile:
|
||||
outfile.write(self.stream.getbuffer())
|
||||
self.stream.close()
|
||||
self.file_ready.set()
|
||||
logging.info(f"Finished writing to disk {self.file}")
|
||||
|
||||
def flush(self):
|
||||
logging.debug(f"Flushing {self.file}")
|
||||
gevent.spawn(self._stream_to_file)
|
||||
logging.debug(f"Returning flushing {self.file}")
|
||||
|
||||
def open(self, mode):
|
||||
return open(self.file, mode)
|
||||
|
||||
|
|
@ -227,18 +253,20 @@ class CaptureObject(object):
|
|||
global EXIF_FORMATS
|
||||
|
||||
if self.format.upper() in EXIF_FORMATS and self.exists:
|
||||
logging.debug("Writing exif data to capture file")
|
||||
# Extract current Exif data
|
||||
exif_dict = piexif.load(self.file)
|
||||
# Serialize metadata
|
||||
metadata_string = json.dumps(self.metadata, cls=JSONEncoder)
|
||||
logging.debug(f"Saving metadata string to file: {metadata_string}")
|
||||
# Insert metadata into exif_dict
|
||||
exif_dict["Exif"][piexif.ExifIFD.UserComment] = metadata_string.encode()
|
||||
# Convert new exif dict to exif bytes
|
||||
exif_bytes = piexif.dump(exif_dict)
|
||||
# Insert exif into file
|
||||
piexif.insert(exif_bytes, self.file)
|
||||
self.file_ready.wait()
|
||||
with self.lock:
|
||||
logging.debug("Writing exif data to capture file")
|
||||
# Extract current Exif data
|
||||
exif_dict = piexif.load(self.file)
|
||||
# Serialize metadata
|
||||
metadata_string = json.dumps(self.metadata, cls=JSONEncoder)
|
||||
logging.debug(f"Saving metadata string to file: {metadata_string}")
|
||||
# Insert metadata into exif_dict
|
||||
exif_dict["Exif"][piexif.ExifIFD.UserComment] = metadata_string.encode()
|
||||
# Convert new exif dict to exif bytes
|
||||
exif_bytes = piexif.dump(exif_dict)
|
||||
# Insert exif into file
|
||||
piexif.insert(exif_bytes, self.file)
|
||||
|
||||
@property
|
||||
def metadata(self) -> dict:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import pkg_resources
|
|||
import uuid
|
||||
from typing import Tuple
|
||||
|
||||
import gevent
|
||||
|
||||
from openflexure_microscope.captures import CaptureManager
|
||||
|
||||
from openflexure_microscope.stage.mock import MissingStage
|
||||
|
|
@ -151,39 +153,41 @@ class Microscope:
|
|||
Return:
|
||||
dict: Dictionary containing complete microscope state
|
||||
"""
|
||||
state = {"camera": self.camera.state, "stage": self.stage.state}
|
||||
return state
|
||||
with self.lock:
|
||||
state = {"camera": self.camera.state, "stage": self.stage.state}
|
||||
return state
|
||||
|
||||
def update_settings(self, settings: dict):
|
||||
"""
|
||||
Applies a settings dictionary to the microscope. Missing parameters will be left untouched.
|
||||
"""
|
||||
logging.debug("Microscope: Applying settings: {}".format(settings))
|
||||
with self.lock:
|
||||
logging.debug("Microscope: Applying settings: {}".format(settings))
|
||||
|
||||
# If attached to a camera
|
||||
if ("camera" in settings) and self.camera:
|
||||
self.camera.update_settings(settings.get("camera", {}))
|
||||
# If attached to a camera
|
||||
if ("camera" in settings) and self.camera:
|
||||
self.camera.update_settings(settings.get("camera", {}))
|
||||
|
||||
# If attached to a stage
|
||||
if ("stage" in settings) and self.stage:
|
||||
self.stage.update_settings(settings.get("stage", {}))
|
||||
# If attached to a stage
|
||||
if ("stage" in settings) and self.stage:
|
||||
self.stage.update_settings(settings.get("stage", {}))
|
||||
|
||||
# Capture manager
|
||||
self.captures.update_settings(settings.get("captures", {}))
|
||||
# Capture manager
|
||||
self.captures.update_settings(settings.get("captures", {}))
|
||||
|
||||
# Microscope settings
|
||||
if "id" in settings:
|
||||
self.id = settings["id"]
|
||||
if "name" in settings:
|
||||
self.name = settings["name"]
|
||||
if "fov" in settings:
|
||||
self.fov = settings["fov"]
|
||||
# Microscope settings
|
||||
if "id" in settings:
|
||||
self.id = settings["id"]
|
||||
if "name" in settings:
|
||||
self.name = settings["name"]
|
||||
if "fov" in settings:
|
||||
self.fov = settings["fov"]
|
||||
|
||||
# Extension settings
|
||||
if "extensions" in settings:
|
||||
self.extension_settings.update(settings["extensions"])
|
||||
# Extension settings
|
||||
if "extensions" in settings:
|
||||
self.extension_settings.update(settings["extensions"])
|
||||
|
||||
# TODO: warn if there are settings that we silently ignore
|
||||
# TODO: warn if there are settings that we silently ignore
|
||||
|
||||
def read_settings(self, full: bool = True):
|
||||
"""
|
||||
|
|
@ -195,49 +199,50 @@ class Microscope:
|
|||
This is to ensure that settings for currently disconnected hardware
|
||||
don't get removed from the settings file.
|
||||
"""
|
||||
with self.lock:
|
||||
|
||||
settings_current = {
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"fov": self.fov,
|
||||
"extensions": self.extension_settings,
|
||||
}
|
||||
settings_current = {
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"fov": self.fov,
|
||||
"extensions": self.extension_settings,
|
||||
}
|
||||
|
||||
# If attached to a camera
|
||||
if self.camera:
|
||||
settings_current_camera = self.camera.read_settings()
|
||||
settings_current["camera"] = settings_current_camera
|
||||
# If attached to a camera
|
||||
if self.camera:
|
||||
settings_current_camera = self.camera.read_settings()
|
||||
settings_current["camera"] = settings_current_camera
|
||||
|
||||
# Store an encoded copy of the PiCamera lens shading table, if it exists
|
||||
if hasattr(self.camera, "read_lens_shading_table"):
|
||||
# Read LST. Returns None if no LST is active
|
||||
lst_arr = self.camera.read_lens_shading_table()
|
||||
# Store an encoded copy of the PiCamera lens shading table, if it exists
|
||||
if hasattr(self.camera, "read_lens_shading_table"):
|
||||
# Read LST. Returns None if no LST is active
|
||||
lst_arr = self.camera.read_lens_shading_table()
|
||||
|
||||
if lst_arr is not None:
|
||||
b64_string, dtype, shape = serialise_array_b64(lst_arr)
|
||||
if lst_arr is not None:
|
||||
b64_string, dtype, shape = serialise_array_b64(lst_arr)
|
||||
|
||||
settings_current["camera"]["lens_shading_table"] = {
|
||||
"@type": "ndarray",
|
||||
"b64_string": b64_string,
|
||||
"dtype": dtype,
|
||||
"shape": shape,
|
||||
}
|
||||
settings_current["camera"]["lens_shading_table"] = {
|
||||
"@type": "ndarray",
|
||||
"b64_string": b64_string,
|
||||
"dtype": dtype,
|
||||
"shape": shape,
|
||||
}
|
||||
|
||||
# If attached to a stage
|
||||
if self.stage:
|
||||
settings_current_stage = self.stage.read_settings()
|
||||
settings_current["stage"] = settings_current_stage
|
||||
# If attached to a stage
|
||||
if self.stage:
|
||||
settings_current_stage = self.stage.read_settings()
|
||||
settings_current["stage"] = settings_current_stage
|
||||
|
||||
# Capture manager
|
||||
settings_current_captures = self.captures.read_settings()
|
||||
settings_current["captures"] = settings_current_captures
|
||||
# Capture manager
|
||||
settings_current_captures = self.captures.read_settings()
|
||||
settings_current["captures"] = settings_current_captures
|
||||
|
||||
settings_full = self.settings_file.merge(settings_current)
|
||||
settings_full = self.settings_file.merge(settings_current)
|
||||
|
||||
if full:
|
||||
return settings_full
|
||||
else:
|
||||
return settings_current
|
||||
if full:
|
||||
return settings_full
|
||||
else:
|
||||
return settings_current
|
||||
|
||||
def save_settings(self):
|
||||
"""
|
||||
|
|
@ -286,6 +291,21 @@ class Microscope:
|
|||
|
||||
return system_metadata
|
||||
|
||||
def add_metadata_to_capture(self, output, metadata, annotations, tags):
|
||||
logging.debug(f"Waiting for {output.file}")
|
||||
# Wait for the file to be written to disk
|
||||
output.file_ready.wait()
|
||||
logging.info(f"Asynchronously injecting EXIF data into {output.file}")
|
||||
# Inject system metadata
|
||||
output.put_metadata({"instrument": self.metadata})
|
||||
# Insert custom metadata
|
||||
output.put_metadata(metadata)
|
||||
# Insert custom metadata
|
||||
output.put_annotations(annotations)
|
||||
# Insert custom tags
|
||||
output.put_tags(tags)
|
||||
logging.info(f"Finished injecting EXIF data into {output.file}")
|
||||
|
||||
def capture(
|
||||
self,
|
||||
filename: str = None,
|
||||
|
|
@ -299,6 +319,7 @@ class Microscope:
|
|||
tags: list = None,
|
||||
metadata: dict = None,
|
||||
):
|
||||
logging.debug(f"Microscope capturing to {filename}")
|
||||
if not annotations:
|
||||
annotations = {}
|
||||
if not metadata:
|
||||
|
|
@ -313,21 +334,18 @@ class Microscope:
|
|||
)
|
||||
|
||||
# Capture to output object
|
||||
logging.info("Starting microscope capture...")
|
||||
self.camera.capture(
|
||||
output.file,
|
||||
output,
|
||||
use_video_port=use_video_port,
|
||||
resize=resize,
|
||||
bayer=bayer,
|
||||
fmt=fmt,
|
||||
)
|
||||
|
||||
# Inject system metadata
|
||||
output.put_metadata({"instrument": self.metadata})
|
||||
# Insert custom metadata
|
||||
output.put_metadata(metadata)
|
||||
# Insert custom metadata
|
||||
output.put_annotations(annotations)
|
||||
# Insert custom tags
|
||||
output.put_tags(tags)
|
||||
# Gether metadata from hardware in a greenlet
|
||||
gevent.get_hub().threadpool.spawn(self.add_metadata_to_capture, output, metadata, annotations, tags)
|
||||
|
||||
logging.info(f"Finished capture to {output.file}")
|
||||
|
||||
return output
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ class BaseStage(metaclass=ABCMeta):
|
|||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.lock = StrictLock(timeout=5)
|
||||
self.lock = StrictLock(name="Stage")
|
||||
|
||||
@abstractmethod
|
||||
def update_settings(self, config: dict):
|
||||
|
|
|
|||
|
|
@ -51,7 +51,8 @@ class SangaStage(BaseStage):
|
|||
|
||||
@property
|
||||
def position(self):
|
||||
return self.board.position
|
||||
with self.lock:
|
||||
return self.board.position
|
||||
|
||||
@property
|
||||
def backlash(self):
|
||||
|
|
@ -108,6 +109,7 @@ class SangaStage(BaseStage):
|
|||
backlash: (default: True) whether to correct for backlash.
|
||||
"""
|
||||
with self.lock:
|
||||
logging.info(f"Moving sangaboard by {displacement}")
|
||||
if not backlash or self.backlash is None:
|
||||
return self.board.move_rel(displacement, axis=axis)
|
||||
|
||||
|
|
@ -145,6 +147,7 @@ class SangaStage(BaseStage):
|
|||
"""Make an absolute move to a position
|
||||
"""
|
||||
with self.lock:
|
||||
logging.info(f"Moving sangaboard to {final}")
|
||||
self.board.move_abs(final, **kwargs)
|
||||
|
||||
def zero_position(self):
|
||||
|
|
|
|||
115
poetry.lock
generated
115
poetry.lock
generated
|
|
@ -12,12 +12,12 @@ description = "A pluggable API specification generator. Currently supports the O
|
|||
name = "apispec"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
version = "3.3.0"
|
||||
version = "3.3.1"
|
||||
|
||||
[package.extras]
|
||||
dev = ["PyYAML (>=3.10)", "prance (>=0.11)", "marshmallow (>=2.19.2)", "pytest", "mock", "flake8 (3.7.9)", "flake8-bugbear (20.1.4)", "pre-commit (>=1.20,<3.0)", "tox"]
|
||||
docs = ["marshmallow (>=2.19.2)", "pyyaml (5.3)", "sphinx (2.4.1)", "sphinx-issues (1.2.0)", "sphinx-rtd-theme (0.4.3)"]
|
||||
lint = ["flake8 (3.7.9)", "flake8-bugbear (20.1.4)", "pre-commit (>=1.20,<3.0)"]
|
||||
dev = ["PyYAML (>=3.10)", "prance (>=0.11)", "marshmallow (>=2.19.2)", "pytest", "mock", "flake8 (3.8.2)", "flake8-bugbear (20.1.4)", "pre-commit (>=2.4,<3.0)", "tox"]
|
||||
docs = ["marshmallow (>=2.19.2)", "pyyaml (5.3.1)", "sphinx (3.0.4)", "sphinx-issues (1.2.0)", "sphinx-rtd-theme (0.4.3)"]
|
||||
lint = ["flake8 (3.8.2)", "flake8-bugbear (20.1.4)", "pre-commit (>=2.4,<3.0)"]
|
||||
tests = ["PyYAML (>=3.10)", "prance (>=0.11)", "marshmallow (>=2.19.2)", "pytest", "mock"]
|
||||
validation = ["prance (>=0.11)"]
|
||||
yaml = ["PyYAML (>=3.10)"]
|
||||
|
|
@ -36,7 +36,7 @@ description = "An abstract syntax tree for Python with inference support."
|
|||
name = "astroid"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
version = "2.4.1"
|
||||
version = "2.4.2"
|
||||
|
||||
[package.dependencies]
|
||||
lazy-object-proxy = ">=1.4.0,<1.5.0"
|
||||
|
|
@ -110,6 +110,7 @@ ofmclient = []
|
|||
reference = "f1803f5f655b405a6a902f6c697c9f720f6a18da"
|
||||
type = "git"
|
||||
url = "https://gitlab.com/openflexure/microscope-extensions/camera-stage-mapping.git"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Pure Python CBOR (de)serializer with extensive tag support"
|
||||
|
|
@ -127,7 +128,7 @@ description = "Python package for providing Mozilla's CA Bundle."
|
|||
name = "certifi"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "2020.4.5.1"
|
||||
version = "2020.4.5.2"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
|
|
@ -219,11 +220,11 @@ description = "Coroutine-based network library"
|
|||
name = "gevent"
|
||||
optional = false
|
||||
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
|
||||
version = "20.5.2"
|
||||
version = "20.6.0"
|
||||
|
||||
[package.dependencies]
|
||||
cffi = ">=1.12.2"
|
||||
greenlet = ">=0.4.14"
|
||||
greenlet = ">=0.4.16"
|
||||
setuptools = "*"
|
||||
"zope.event" = "*"
|
||||
"zope.interface" = "*"
|
||||
|
|
@ -232,8 +233,8 @@ setuptools = "*"
|
|||
dnspython = ["dnspython (>=1.16.0)", "idna"]
|
||||
docs = ["repoze.sphinx.autointerface", "sphinxcontrib-programoutput"]
|
||||
monitor = ["psutil (>=5.6.1)", "psutil (5.6.3)"]
|
||||
recommended = ["dnspython (>=1.16.0)", "idna", "cffi (>=1.12.2)", "psutil (>=5.6.1)", "psutil (5.6.3)"]
|
||||
test = ["dnspython (>=1.16.0)", "idna", "requests", "objgraph", "cffi (>=1.12.2)", "psutil (>=5.6.1)", "psutil (5.6.3)", "futures", "mock", "contextvars (2.4)", "coverage (<5.0)", "coveralls (>=1.7.0)"]
|
||||
recommended = ["dnspython (>=1.16.0)", "idna", "cffi (>=1.12.2)", "psutil (>=5.6.1)", "psutil (5.6.3)", "selectors2", "backports.socketpair"]
|
||||
test = ["dnspython (>=1.16.0)", "idna", "requests", "objgraph", "cffi (>=1.12.2)", "psutil (>=5.6.1)", "psutil (5.6.3)", "selectors2", "futures", "mock", "backports.socketpair", "contextvars (2.4)", "coverage (<5.0)", "coveralls (>=1.7.0)"]
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
|
|
@ -265,11 +266,11 @@ version = "2.9"
|
|||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Enumerates all IP addresses on all network adapters of the system."
|
||||
description = "Cross-platform network interface and IP address enumeration library"
|
||||
name = "ifaddr"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
|
|
@ -321,7 +322,7 @@ description = "Python implementation of LabThings, based on the Flask microframe
|
|||
name = "labthings"
|
||||
optional = false
|
||||
python-versions = ">=3.6,<4.0"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
|
||||
[package.dependencies]
|
||||
Flask = ">=1.1.1,<2.0.0"
|
||||
|
|
@ -332,7 +333,7 @@ gevent = ">=1.4,<21.0"
|
|||
gevent-websocket = ">=0.10.1,<0.11.0"
|
||||
marshmallow = ">=3.4.0,<4.0.0"
|
||||
webargs = ">=6.0.0,<7.0.0"
|
||||
zeroconf = ">=0.24.5,<0.27.0"
|
||||
zeroconf = ">=0.24.5,<0.28.0"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
|
|
@ -433,6 +434,7 @@ test = ["coverage", "pytest", "mock", "pillow", "numpy"]
|
|||
reference = ""
|
||||
type = "url"
|
||||
url = "https://github.com/rwb27/picamera/releases/download/v1.13.2b0/picamera-1.13.2b0-py3-none-any.whl"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Python Imaging Library (Fork)"
|
||||
|
|
@ -475,7 +477,7 @@ description = "python code static checker"
|
|||
name = "pylint"
|
||||
optional = false
|
||||
python-versions = ">=3.5.*"
|
||||
version = "2.5.2"
|
||||
version = "2.5.3"
|
||||
|
||||
[package.dependencies]
|
||||
astroid = ">=2.4.0,<=2.5"
|
||||
|
|
@ -572,7 +574,7 @@ description = "Communication to the Sangaboard unipolar motor driver"
|
|||
name = "sangaboard"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "0.2.0"
|
||||
version = "0.2.3"
|
||||
|
||||
[package.dependencies]
|
||||
future = "*"
|
||||
|
|
@ -612,7 +614,7 @@ description = "Python documentation generator"
|
|||
name = "sphinx"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
version = "3.0.4"
|
||||
version = "3.1.1"
|
||||
|
||||
[package.dependencies]
|
||||
Jinja2 = ">=2.3"
|
||||
|
|
@ -635,7 +637,7 @@ sphinxcontrib-serializinghtml = "*"
|
|||
|
||||
[package.extras]
|
||||
docs = ["sphinxcontrib-websupport"]
|
||||
lint = ["flake8 (>=3.5.0)", "flake8-import-order", "mypy (>=0.770)", "docutils-stubs"]
|
||||
lint = ["flake8 (>=3.5.0)", "flake8-import-order", "mypy (>=0.780)", "docutils-stubs"]
|
||||
test = ["pytest", "pytest-cov", "html5lib", "typed-ast", "cython"]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -835,7 +837,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
|
|||
rpi = ["RPi.GPIO"]
|
||||
|
||||
[metadata]
|
||||
content-hash = "1c577dec4c5e870bacd76d24991f49cb0c7fed212ace179bea075e2849c91e00"
|
||||
content-hash = "46a88e0322847fc9c13602f72b7de7f10be81bbddc5c0f87e174a540fb427a7c"
|
||||
python-versions = "^3.6"
|
||||
|
||||
[metadata.files]
|
||||
|
|
@ -844,16 +846,16 @@ alabaster = [
|
|||
{file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"},
|
||||
]
|
||||
apispec = [
|
||||
{file = "apispec-3.3.0-py2.py3-none-any.whl", hash = "sha256:9bf4e51d56c9067c60668b78210ae213894f060f85593dc2ad8805eb7d875a2a"},
|
||||
{file = "apispec-3.3.0.tar.gz", hash = "sha256:419d0564b899e182c2af50483ea074db8cb05fee60838be58bb4542095d5c08d"},
|
||||
{file = "apispec-3.3.1-py2.py3-none-any.whl", hash = "sha256:b65063e11968a8c26dbbe9b4100ee24026a41cc358dd204df279bdedd7d8dea2"},
|
||||
{file = "apispec-3.3.1.tar.gz", hash = "sha256:f5244ccca33f7a81309f6b3c9d458e33e869050c2d861b9f8cee24b3ad739d2b"},
|
||||
]
|
||||
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.1-py3-none-any.whl", hash = "sha256:d8506842a3faf734b81599c8b98dcc423de863adcc1999248480b18bd31a0f38"},
|
||||
{file = "astroid-2.4.1.tar.gz", hash = "sha256:4c17cea3e592c21b6e222f673868961bad77e1f985cb1694ed077475a89229c1"},
|
||||
{file = "astroid-2.4.2-py3-none-any.whl", hash = "sha256:bc58d83eb610252fd8de6363e39d4f1d0619c894b0ed24603b881c02e64c7386"},
|
||||
{file = "astroid-2.4.2.tar.gz", hash = "sha256:2f4078c2a41bf377eea06d71c9d2ba4eb8f6b1af2135bec27bbbb7d8f12bb703"},
|
||||
]
|
||||
attrs = [
|
||||
{file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"},
|
||||
|
|
@ -872,8 +874,8 @@ cbor2 = [
|
|||
{file = "cbor2-5.1.0.tar.gz", hash = "sha256:43ce11e8c2fe4971d386d1a60cf83bfa0a4a667b97668ba76acbf5e6398821aa"},
|
||||
]
|
||||
certifi = [
|
||||
{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.2-py2.py3-none-any.whl", hash = "sha256:9cd41137dc19af6a5e03b630eefe7d1f458d964d406342dd3edf625839b944cc"},
|
||||
{file = "certifi-2020.4.5.2.tar.gz", hash = "sha256:5ad7e9a056d25ffa5082862e36f119f7f7cec6457fa07ee2f8c339814b80c9b1"},
|
||||
]
|
||||
cffi = [
|
||||
{file = "cffi-1.14.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1cae98a7054b5c9391eb3249b86e0e99ab1e02bb0cc0575da191aedadbdf4384"},
|
||||
|
|
@ -933,28 +935,28 @@ future = [
|
|||
{file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
|
||||
]
|
||||
gevent = [
|
||||
{file = "gevent-20.5.2-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:4a69373a07a0d4ff74401274d3b30aa47430e76d7e314f23bb9f399da1bbed6e"},
|
||||
{file = "gevent-20.5.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a55ee5c388511362b1f886be57f06062334bc44b91ef215b997435d21901def7"},
|
||||
{file = "gevent-20.5.2-cp27-cp27m-win32.whl", hash = "sha256:2436dfbc1f0cfa2c2fc8ec9e824dcc13f8501085c29605b4488c981456f12ed6"},
|
||||
{file = "gevent-20.5.2-cp27-cp27m-win_amd64.whl", hash = "sha256:c2c2e0e275b375941be80a174cd47b3ee20ccf457fbec31863808448ec4fcf59"},
|
||||
{file = "gevent-20.5.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1bb8330f1f86460b845b589eeea73b3e2dd52f84bee30178a8eaa0ddd61622a6"},
|
||||
{file = "gevent-20.5.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:2863c2899f0a379322cb07724c67eb608652e99ab5bdf5bfce936c9bf89f4a87"},
|
||||
{file = "gevent-20.5.2-cp35-cp35m-win32.whl", hash = "sha256:de44cb7bf1f74043e7976f2dbf9780da7a99a2b58799675817d1df77ccfde519"},
|
||||
{file = "gevent-20.5.2-cp35-cp35m-win_amd64.whl", hash = "sha256:9cec7379b540cd7a8cdffec9ebe799943c07d0f575e6f222bf3515e7655544d7"},
|
||||
{file = "gevent-20.5.2-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:9c4601f67f26e534237d1cbaded3157b2a70a89e7760c819e8afb20e32878553"},
|
||||
{file = "gevent-20.5.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f2932f05b8cfef3d7c285ddbd77008d642bd843c88625e1da4e96a643d4de885"},
|
||||
{file = "gevent-20.5.2-cp36-cp36m-win32.whl", hash = "sha256:08de7d46923ab04529e70ff0601522bc7897b7bcef8ac40e8a9f906168518875"},
|
||||
{file = "gevent-20.5.2-cp36-cp36m-win_amd64.whl", hash = "sha256:ae6787acd68bd2f8d40e10fd8d031a5da2ac9dae6ddd35390cf4c18261a8bd38"},
|
||||
{file = "gevent-20.5.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:12276c30ce4d6da84eba674dade04b0cd80fb5808dbfdef01ba49b75d50b3816"},
|
||||
{file = "gevent-20.5.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:4a3ee667456ccec0c1d215aeec8d150449d0a6da5d58ba2c6be741249af1f139"},
|
||||
{file = "gevent-20.5.2-cp37-cp37m-win32.whl", hash = "sha256:2f138b82527c520fff432e3aa9f606335d23eebfa1e8aeb0e36822e34a91afdd"},
|
||||
{file = "gevent-20.5.2-cp37-cp37m-win_amd64.whl", hash = "sha256:899e1cdbfaa7aa0d53a430f2f4cb8d03c00db0ee94872ce2b4bb918817a719f0"},
|
||||
{file = "gevent-20.5.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:bd261c12724e3e4be81ce4bd978c3ba47d31bd56444ba9f30332b8e7d4f09b81"},
|
||||
{file = "gevent-20.5.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:1886197bcf8aea097c066e212c32a0aed9ad6a0ed245c124f3ff6b7dce7db354"},
|
||||
{file = "gevent-20.5.2-cp38-cp38-win32.whl", hash = "sha256:9146860dd8ac0d0b675abc64025ea8dcd3d399de9290a8eec23bbed6d3993be4"},
|
||||
{file = "gevent-20.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:67820fd68ba14a38a1645261fc8f3530c4cfad6c45e29f40cc1dda8d5dcc17fb"},
|
||||
{file = "gevent-20.5.2-pp27-pypy_73-win32.whl", hash = "sha256:91bfe6c3a2ec4d0027b30eb1aa678ab1f828c7d8961e64ac000d8c4a80fc8806"},
|
||||
{file = "gevent-20.5.2.tar.gz", hash = "sha256:2756de36f56b33c46f6cc7146a74ba65afcd1471922c95b6771ce87b279d689c"},
|
||||
{file = "gevent-20.6.0-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:ccac571d5bb7a85be736c0c5e241a5263a36904b358edc2dcceaa3b54a18eb25"},
|
||||
{file = "gevent-20.6.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:eb74efa2486b8c2576eaf2ef6a3a782b1a38fe414390a4a3ab411efca91e54f5"},
|
||||
{file = "gevent-20.6.0-cp27-cp27m-win32.whl", hash = "sha256:2701ac60993fb74f1f548348641c78160215aa9707b08e37caf10fe63712f5d9"},
|
||||
{file = "gevent-20.6.0-cp27-cp27m-win_amd64.whl", hash = "sha256:1a449ab1390fd48cc509d846a62d867e7c548f8f9d5632ca012af2741c97e6d6"},
|
||||
{file = "gevent-20.6.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:e963584dd93b8268764297b13d6d99e24d9f1d63d921d5260286b3cde92f2851"},
|
||||
{file = "gevent-20.6.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:56f2949b068e922bce3695e80bbf484115536901ad77e833b0e1ac7e4d996654"},
|
||||
{file = "gevent-20.6.0-cp35-cp35m-win32.whl", hash = "sha256:d4376e9d8f6ecf68de182fed99eb7d7f255b15170edb0bc7d06720552f541c6f"},
|
||||
{file = "gevent-20.6.0-cp35-cp35m-win_amd64.whl", hash = "sha256:6d172744705aa401b0014142343e866f98668bd86fc5ba070ace22f4e98f76dd"},
|
||||
{file = "gevent-20.6.0-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:664f061993e1104f901801dc7b305824389f5c05936e7daeabee56bf76837726"},
|
||||
{file = "gevent-20.6.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f9521f4721f50f7e1ade81bf61c17387d2cd901de65701038e74c5e87e2785cc"},
|
||||
{file = "gevent-20.6.0-cp36-cp36m-win32.whl", hash = "sha256:779ab592efc30550879546bfe83ee6cb42538080fcd495817868ed7b7b17c3ff"},
|
||||
{file = "gevent-20.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:94241c8e962b5eab49d5459b319454161bdb1a0c53ffe883a94a9952ab916345"},
|
||||
{file = "gevent-20.6.0-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:77d9237d17a64054e1a03883d3265d27fe98e49f41cf1dd22b42ee3206f99b53"},
|
||||
{file = "gevent-20.6.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:5520e72522906483559bd12eacabf72f130972dabe8e05d159e227eca97cbcf9"},
|
||||
{file = "gevent-20.6.0-cp37-cp37m-win32.whl", hash = "sha256:8625256ec3acc9b095050d15c256ad9ce3f8bac833982bd712af14a83991b067"},
|
||||
{file = "gevent-20.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e5e1f2024fbf753549703cde287f6cc940248d860f617d657c3b3ea0638035f0"},
|
||||
{file = "gevent-20.6.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:3165510ed37f8ea38345a8a2464d5c700f7cc8828194612889f6c583e2889ff1"},
|
||||
{file = "gevent-20.6.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b5f73ef6442e72774359afcf892c47cbdcb0811a4577fc1b4b11191aa362e353"},
|
||||
{file = "gevent-20.6.0-cp38-cp38-win32.whl", hash = "sha256:6e232e1870dfd2e32aaa26083488c116492e9378d288dffca0e38c1521ba502d"},
|
||||
{file = "gevent-20.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:dffc2d2bb815a6ab0370e3e478094e0c43f610a3466e241bc150af21293763db"},
|
||||
{file = "gevent-20.6.0-pp27-pypy_73-win32.whl", hash = "sha256:ea79e58f7927939ac60d3c73aa9e5d6186c3fea77e4414d768d658e1f58ef7a3"},
|
||||
{file = "gevent-20.6.0.tar.gz", hash = "sha256:51cab8f792961a75da39452586bb3474eaa1607ab2c83398745ef1b9ba09cb92"},
|
||||
]
|
||||
gevent-websocket = [
|
||||
{file = "gevent-websocket-0.10.1.tar.gz", hash = "sha256:7eaef32968290c9121f7c35b973e2cc302ffb076d018c9068d2f5ca8b2d85fb0"},
|
||||
|
|
@ -984,7 +986,8 @@ idna = [
|
|||
{file = "idna-2.9.tar.gz", hash = "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"},
|
||||
]
|
||||
ifaddr = [
|
||||
{file = "ifaddr-0.1.6.tar.gz", hash = "sha256:c19c64882a7ad51a394451dabcbbed72e98b5625ec1e79789924d5ea3e3ecb93"},
|
||||
{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"},
|
||||
|
|
@ -1003,8 +1006,8 @@ jinja2 = [
|
|||
{file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"},
|
||||
]
|
||||
labthings = [
|
||||
{file = "labthings-0.6.4-py3-none-any.whl", hash = "sha256:61e540e4635278008b8f851a1b8104199cae92affd043f229f0ffa9f78c0479e"},
|
||||
{file = "labthings-0.6.4.tar.gz", hash = "sha256:d734e74716b2348350d31b48f67cbf96c01c88b6b3185bace287e81fd3aa5826"},
|
||||
{file = "labthings-0.6.5-py3-none-any.whl", hash = "sha256:f28fb8b6fa05eabc105c5dc987835ebed8784f9357ccd1f6ef9eb32b6280216c"},
|
||||
{file = "labthings-0.6.5.tar.gz", hash = "sha256:adadcf9a1588d1d38c12d36939fe902b30e8f56d50c196fc79161ad58e0331a0"},
|
||||
]
|
||||
lazy-object-proxy = [
|
||||
{file = "lazy-object-proxy-1.4.3.tar.gz", hash = "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"},
|
||||
|
|
@ -1219,8 +1222,8 @@ pygments = [
|
|||
{file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"},
|
||||
]
|
||||
pylint = [
|
||||
{file = "pylint-2.5.2-py3-none-any.whl", hash = "sha256:dd506acce0427e9e08fb87274bcaa953d38b50a58207170dbf5b36cf3e16957b"},
|
||||
{file = "pylint-2.5.2.tar.gz", hash = "sha256:b95e31850f3af163c2283ed40432f053acbc8fc6eba6a069cb518d9dbf71848c"},
|
||||
{file = "pylint-2.5.3-py3-none-any.whl", hash = "sha256:d0ece7d223fe422088b0e8f13fa0a1e8eb745ebffcb8ed53d3e95394b6101a1c"},
|
||||
{file = "pylint-2.5.3.tar.gz", hash = "sha256:7dd78437f2d8d019717dbf287772d0b2dbdfd13fc016aa7faa08d67bccc46adc"},
|
||||
]
|
||||
pynpm = [
|
||||
{file = "pynpm-0.1.2-py2.py3-none-any.whl", hash = "sha256:3f03fbf667549f8b8b7e0419eef88d1b21833ce288f96de66fbb761b9f4c4061"},
|
||||
|
|
@ -1255,8 +1258,8 @@ rope = [
|
|||
{file = "RPi.GPIO-0.6.5.tar.gz", hash = "sha256:a4210ad63bfe844e43995286de0d3950dfacfa0f3799bb9392770ac54a7d2e47"},
|
||||
]
|
||||
sangaboard = [
|
||||
{file = "sangaboard-0.2.0-py3-none-any.whl", hash = "sha256:510cb95a56e070ef7a9214f6a0673a92f2269e71e125c54b8bb1c16b1abfb095"},
|
||||
{file = "sangaboard-0.2.0.tar.gz", hash = "sha256:3636203179e9748cd27a1856d44de2b1c7adf398d033a8d5d382748903a098f0"},
|
||||
{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.4.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:c5cac0c0387272ee0e789e94a570ac51deb01c796b37fb2aad1fb13f85e2f97d"},
|
||||
|
|
@ -1290,8 +1293,8 @@ snowballstemmer = [
|
|||
{file = "snowballstemmer-2.0.0.tar.gz", hash = "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"},
|
||||
]
|
||||
sphinx = [
|
||||
{file = "Sphinx-3.0.4-py3-none-any.whl", hash = "sha256:779a519adbd3a70fc7c468af08c5e74829868b0a5b34587b33340e010291856c"},
|
||||
{file = "Sphinx-3.0.4.tar.gz", hash = "sha256:ea64df287958ee5aac46be7ac2b7277305b0381d213728c3a49d8bb9b8415807"},
|
||||
{file = "Sphinx-3.1.1-py3-none-any.whl", hash = "sha256:97c9e3bcce2f61d9f5edf131299ee9d1219630598d9f9a8791459a4d9e815be5"},
|
||||
{file = "Sphinx-3.1.1.tar.gz", hash = "sha256:74fbead182a611ce1444f50218a1c5fc70b6cc547f64948f5182fb30a2a20258"},
|
||||
]
|
||||
sphinxcontrib-applehelp = [
|
||||
{file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"},
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ build-backend = "poetry.masonry.api"
|
|||
|
||||
[tool.poetry]
|
||||
name = "openflexure-microscope-server"
|
||||
version = "2.1.3"
|
||||
version = "2.2.0-beta.0"
|
||||
description = "Python module, and Flask-based web API, to run the OpenFlexure Microscope."
|
||||
|
||||
authors = [
|
||||
|
|
@ -42,10 +42,11 @@ opencv-python-headless = [
|
|||
{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
|
||||
]
|
||||
labthings = "0.6.4"
|
||||
labthings = "0.6.5"
|
||||
pynpm = "^0.1.2"
|
||||
camera-stage-mapping = {git = "https://gitlab.com/openflexure/microscope-extensions/camera-stage-mapping.git"}
|
||||
sangaboard = "^0.2.0"
|
||||
sangaboard = "^0.2"
|
||||
gevent = "20.6.0" # Pinned version to guarantee piwheels wheel
|
||||
|
||||
[tool.poetry.extras]
|
||||
rpi = ["RPi.GPIO"]
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class TestCaptureMethods(unittest.TestCase):
|
|||
with camera.new_image() as output:
|
||||
|
||||
camera.capture(
|
||||
output.file, use_video_port=use_video_port, resize=resize
|
||||
output, use_video_port=use_video_port, resize=resize
|
||||
)
|
||||
|
||||
# Ensure file deletion fails and returns False
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue