diff --git a/mx3_beamline_library/plans/optical_centering.py b/mx3_beamline_library/plans/optical_centering.py index 466810a..6fdee21 100644 --- a/mx3_beamline_library/plans/optical_centering.py +++ b/mx3_beamline_library/plans/optical_centering.py @@ -1,7 +1,7 @@ import pickle from io import BytesIO -from os import getcwd, mkdir, path -from typing import Generator, Union +from os import getcwd, makedirs, path +from typing import Generator import matplotlib.pyplot as plt import numpy as np @@ -57,11 +57,11 @@ def __init__( self, sample_id: str, beam_position: tuple[int, int], - grid_step: Union[tuple[float, float], None] = None, + grid_step: tuple[float, float] | None = None, calibrated_alignment_z: float = 0.634, plot: bool = False, top_camera_background_img_array: npt.NDArray = None, - output_directory: Union[str, None] = None, + output_directory: str | None = None, use_top_camera_camera: bool = True, manual_mode: bool = False, extra_config: OpticalCenteringExtraConfig | None = None, @@ -73,7 +73,7 @@ def __init__( Sample id beam_position : tuple[int, int] Position of the beam - grid_step : Union[tuple[float, float], None] + grid_step : tuple[float, float] | None The step of the grid (x,y) in micrometers. Can also be None only if manual_mode=True calibrated_alignment_z : float, optional. @@ -87,7 +87,7 @@ def __init__( Top camera background image array used to determine if there is a pin. If top_camera_background_img_array is None, we use the default background image from the mx3-beamline-library - output_directory : Union[str, None], optional + output_directory : str | None, optional The directory where all diagnostic plots are saved if self.plot=True. If output_directory=None, we use the current working directory, by default None @@ -139,16 +139,15 @@ def __init__( self.sample_path = path.join(self.output_directory, self.sample_id) if self.plot: try: - mkdir(self.sample_path) + makedirs(self.sample_path) except FileExistsError: pass self.use_top_camera_camera = use_top_camera_camera self.manual_mode = manual_mode if not self.manual_mode: - assert ( - self.grid_step is not None - ), "grid_step can only be None if manual_mode=True" + if grid_step is None: + raise ValueError("grid_step can only be None if manual_mode=True") def _check_top_camera_config(self) -> None: """ @@ -628,7 +627,7 @@ def find_loop_edge_coordinates(self) -> tuple[float, float]: Returns ------- tuple[float, float] - The x and y pixel coordinates of the edge of the loop, + The x and y pixel coordinates of the edge of the loop """ x_coord_list = [] y_coord_list = [] @@ -787,12 +786,12 @@ def find_edge_and_flat_angles(self) -> Generator[Msg, None, None]: plt.figure() plt.plot(x_new, y_new, label="Curve fit") plt.scatter(np.radians(omega_list), np.array(area_list), label="Data") - plt.xlabel("$\omega$ [radians]", fontsize=18) - plt.ylabel("Area [pixels$^2$]", fontsize=18) + plt.xlabel("omega [radians]") + plt.ylabel("Area [pixels^2]") plt.legend(fontsize=15) - plt.tight_layout() + # plt.tight_layout() filename = path.join(self.sample_path, f"{self.sample_id}_area_curve_fit") - plt.savefig(filename) + plt.savefig(filename, dpi=70) plt.close() return successful_centering @@ -1006,8 +1005,8 @@ def save_image( c="r", marker="x", ) - plt.title(f"$\omega={round(md3.omega.position)}^\circ$", fontsize=18) - plt.savefig(filename) + plt.title(f"omega={round(md3.omega.position)}", fontsize=18) + plt.savefig(filename, dpi=70) plt.close() def prepare_raster_grid( diff --git a/mx3_beamline_library/science/optical_and_loop_centering/loop_edge_detection.py b/mx3_beamline_library/science/optical_and_loop_centering/loop_edge_detection.py index ec86630..94540a1 100644 --- a/mx3_beamline_library/science/optical_and_loop_centering/loop_edge_detection.py +++ b/mx3_beamline_library/science/optical_and_loop_centering/loop_edge_detection.py @@ -236,5 +236,5 @@ def plot_raster_grid( ) x = rectangle_coordinates.top_left[0] * np.ones(len(x)) plt.plot(x, z, color="red", linestyle="--") - plt.savefig(filename) + plt.savefig(filename, dpi=70) plt.close() diff --git a/poetry.lock b/poetry.lock index f5d989b..b4e607f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. [[package]] name = "aiofiles" @@ -6,6 +6,8 @@ version = "24.1.0" description = "File support for asyncio." optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"}, {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"}, @@ -22,6 +24,8 @@ version = "0.7.12" description = "A configurable sidebar-enabled Sphinx theme" optional = false python-versions = "*" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, @@ -38,6 +42,8 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -54,6 +60,8 @@ version = "4.6.2.post1" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, @@ -81,6 +89,8 @@ version = "1.4.4" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = "*" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, @@ -97,6 +107,8 @@ version = "1.0.6" description = "Library for common devices, plans, and tools for beamline control and acquisition." optional = false python-versions = "^3.10" +groups = ["main"] +markers = "python_version <= \"3.11\" and extra == \"as-acquisition-library\"" files = [] develop = false @@ -123,6 +135,8 @@ version = "1.0.3" description = "An ophyd signal backed by redis." optional = false python-versions = ">=3.9,<3.13" +groups = ["main"] +markers = "python_version <= \"3.11\" and extra == \"as-redis-signal\"" files = [] develop = false @@ -143,6 +157,8 @@ version = "5.0.1" description = "Timeout context manager for asyncio programs" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] +markers = "python_full_version < \"3.11.3\"" files = [ {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, @@ -159,6 +175,8 @@ version = "24.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, @@ -183,6 +201,8 @@ version = "2.16.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, @@ -202,6 +222,8 @@ version = "0.5.1" description = "Bitshuffle filter for improving typed data compression." optional = false python-versions = "*" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "bitshuffle-0.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:19c9c1efe5d8bf593326fc41abb432c3fe07140e37fd15cec3d3b42ac0e1fae2"}, {file = "bitshuffle-0.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c15973d96941a99ac6d6a62648000dd3dc59ba37c9b12723fdfaac1036db355"}, @@ -234,6 +256,8 @@ version = "1.12.0" description = "Experiment specification & orchestration." optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "bluesky-1.12.0-py3-none-any.whl", hash = "sha256:1bea7d67679d1dd15f94764a317cf26d84f42ef14a51aef88e55af0e1e496665"}, {file = "bluesky-1.12.0.tar.gz", hash = "sha256:63579d05c8d3bdec6a644c900de5e0d55c95b7bbbede03c0003f2a7ed61f9274"}, @@ -274,6 +298,8 @@ version = "5.5.0" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, @@ -290,6 +316,8 @@ version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main", "dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, @@ -306,6 +334,8 @@ version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -322,6 +352,8 @@ version = "3.4.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" +groups = ["main", "dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, @@ -441,10 +473,12 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +markers = {main = "python_version <= \"3.11\" and platform_system == \"Windows\"", dev = "python_version <= \"3.11\" and sys_platform == \"win32\""} [package.source] type = "legacy" @@ -457,6 +491,8 @@ version = "15.0.1" description = "Colored terminal output for Python's logging module" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934"}, {file = "coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0"}, @@ -479,6 +515,8 @@ version = "1.3.0" description = "Python library for calculating contours of 2D quadrilateral grids" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "contourpy-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:880ea32e5c774634f9fcd46504bf9f080a41ad855f4fef54f5380f5133d343c7"}, {file = "contourpy-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:76c905ef940a4474a6289c71d53122a4f77766eef23c03cd57016ce19d0f7b42"}, @@ -568,6 +606,8 @@ version = "7.6.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07"}, {file = "coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0"}, @@ -650,6 +690,8 @@ version = "0.12.1" description = "Composable style cycles" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, @@ -670,6 +712,8 @@ version = "3.0.11" description = "The Cython compiler for writing C extensions in the Python language." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "Cython-3.0.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:44292aae17524abb4b70a25111fe7dec1a0ad718711d47e3786a211d5408fdaa"}, {file = "Cython-3.0.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a75d45fbc20651c1b72e4111149fed3b33d270b0a4fb78328c54d965f28d55e1"}, @@ -750,6 +794,8 @@ version = "0.3.9" description = "Distribution utilities" optional = false python-versions = "*" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, @@ -766,6 +812,8 @@ version = "0.16" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"}, @@ -782,6 +830,8 @@ version = "1.1.10" description = "Python 3.4 Enum backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4" optional = false python-versions = "*" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "enum34-1.1.10-py2-none-any.whl", hash = "sha256:a98a201d6de3f2ab3db284e70a33b0f896fbf35f8086594e8c9e74b909058d53"}, {file = "enum34-1.1.10-py3-none-any.whl", hash = "sha256:c3858660960c984d6ab0ebad691265180da2b43f07e061c0f8dca9ef3cffd328"}, @@ -799,6 +849,8 @@ version = "1.22.1" description = "Data model used by the bluesky ecosystem." optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "event_model-1.22.1-py3-none-any.whl", hash = "sha256:f9dd1a41ab4f09bda0de182c9d30c5c464357c6b5a699d4cd0b52e4e88cf8cff"}, {file = "event_model-1.22.1.tar.gz", hash = "sha256:9861c24aafb5f06a9295d43bcf9b7e071eacbf92c214b409de221f1b6470e059"}, @@ -824,6 +876,8 @@ version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] +markers = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, @@ -837,12 +891,44 @@ type = "legacy" url = "https://pypi.asci.synchrotron.org.au/root/pypi/+simple" reference = "cachedpypi" +[[package]] +name = "fakeredis" +version = "2.26.2" +description = "Python implementation of redis API, can be used for testing purposes." +optional = false +python-versions = ">=3.7,<4.0" +groups = ["dev"] +markers = "python_version <= \"3.11\"" +files = [ + {file = "fakeredis-2.26.2-py3-none-any.whl", hash = "sha256:86d4129df001efc25793cb334008160fccc98425d9f94de47884a92b63988c14"}, + {file = "fakeredis-2.26.2.tar.gz", hash = "sha256:3ee5003a314954032b96b1365290541346c9cc24aab071b52cc983bb99ecafbf"}, +] + +[package.dependencies] +redis = {version = ">=4.3", markers = "python_full_version > \"3.8.0\""} +sortedcontainers = ">=2,<3" +typing-extensions = {version = ">=4.7,<5.0", markers = "python_version < \"3.11\""} + +[package.extras] +bf = ["pyprobables (>=0.6,<0.7)"] +cf = ["pyprobables (>=0.6,<0.7)"] +json = ["jsonpath-ng (>=1.6,<2.0)"] +lua = ["lupa (>=2.1,<3.0)"] +probabilistic = ["pyprobables (>=0.6,<0.7)"] + +[package.source] +type = "legacy" +url = "https://pypi.asci.synchrotron.org.au/root/pypi/+simple" +reference = "cachedpypi" + [[package]] name = "filelock" version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, @@ -864,6 +950,8 @@ version = "0.3" description = "Saves and loads to the cache a transformed versions of a source object." optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "flexcache-0.3-py3-none-any.whl", hash = "sha256:d43c9fea82336af6e0115e308d9d33a185390b8346a017564611f1466dcd2e32"}, {file = "flexcache-0.3.tar.gz", hash = "sha256:18743bd5a0621bfe2cf8d519e4c3bfdf57a269c15d1ced3fb4b64e0ff4600656"}, @@ -886,6 +974,8 @@ version = "0.4" description = "Parsing made fun ... using typing." optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "flexparser-0.4-py3-none-any.whl", hash = "sha256:3738b456192dcb3e15620f324c447721023c0293f6af9955b481e91d00179846"}, {file = "flexparser-0.4.tar.gz", hash = "sha256:266d98905595be2ccc5da964fe0a2c3526fbbffdc45b65b3146d75db992ef6b2"}, @@ -908,6 +998,8 @@ version = "4.54.1" description = "Tools to manipulate font files" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "fonttools-4.54.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ed7ee041ff7b34cc62f07545e55e1468808691dddfd315d51dd82a6b37ddef2"}, {file = "fonttools-4.54.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41bb0b250c8132b2fcac148e2e9198e62ff06f3cc472065dff839327945c5882"}, @@ -984,6 +1076,8 @@ version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.7" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -1000,6 +1094,8 @@ version = "3.12.1" description = "Read and write HDF5 files from Python" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "h5py-3.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f0f1a382cbf494679c07b4371f90c70391dedb027d517ac94fa2c05299dacda"}, {file = "h5py-3.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cb65f619dfbdd15e662423e8d257780f9a66677eae5b4b3fc9dca70b5fd2d2a3"}, @@ -1043,6 +1139,8 @@ version = "4.4.0" description = "HDF5 Plugins for Windows, MacOS, and Linux" optional = false python-versions = ">=3.7" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "hdf5plugin-4.4.0-py3-none-macosx_10_9_universal2.whl", hash = "sha256:4bb9c0d65d3b76de0079242cd4cc5f94260e7eee80e22e09c4e6bcf0d91a6392"}, {file = "hdf5plugin-4.4.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c75f91e3a80941e547e6510504a00b39482106c77d03e73ebe85dc7e88fb07ea"}, @@ -1070,6 +1168,8 @@ version = "1.0.1" description = "a heap with decrease-key and increase-key operations" optional = false python-versions = "*" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "HeapDict-1.0.1-py3-none-any.whl", hash = "sha256:6065f90933ab1bb7e50db403b90cab653c853690c5992e69294c2de2b253fc92"}, {file = "HeapDict-1.0.1.tar.gz", hash = "sha256:8495f57b3e03d8e46d5f1b2cc62ca881aca392fd5cc048dc0aa2e1a6d23ecdb6"}, @@ -1086,6 +1186,8 @@ version = "1.2.6" description = "A persistent dictionary with history backed by sqlite" optional = false python-versions = "*" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "historydict-1.2.6-py3-none-any.whl", hash = "sha256:b4b00a170f05502aa682caba62435da5fe1f73037e884707581fe84f8d7b43f5"}, {file = "historydict-1.2.6.tar.gz", hash = "sha256:a800ae05d28b618fe0c913ff0d64e4aebe05d76934fa610539f70ead37dc6fb5"}, @@ -1102,6 +1204,8 @@ version = "1.0.6" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"}, {file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"}, @@ -1128,6 +1232,8 @@ version = "0.27.2" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, @@ -1158,6 +1264,8 @@ version = "0.2.0" description = "File transport adapter for httpx." optional = false python-versions = ">=3.6.1" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "httpx-file-0.2.0.tar.gz", hash = "sha256:a00f1dd02c9ffb5e7e072205c30f7ae0d867c397318b045a40b3268f2cdfa932"}, {file = "httpx_file-0.2.0-py3-none-any.whl", hash = "sha256:9a425b351bf65aa394c02096204dc3fa8b647573a289079f927d3e3abfa3c7c8"}, @@ -1181,6 +1289,8 @@ version = "10.0" description = "Human friendly output for text interfaces using Python" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477"}, {file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"}, @@ -1200,6 +1310,8 @@ version = "2.6.1" description = "File identification library for Python" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, @@ -1219,6 +1331,8 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main", "dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -1238,6 +1352,8 @@ version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, @@ -1254,6 +1370,8 @@ version = "6.4.5" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717"}, {file = "importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065"}, @@ -1278,6 +1396,8 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -1294,6 +1414,8 @@ version = "3.1.4" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, @@ -1316,6 +1438,8 @@ version = "4.23.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, @@ -1342,6 +1466,8 @@ version = "2024.10.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"}, {file = "jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272"}, @@ -1361,6 +1487,8 @@ version = "1.4.7" description = "A fast implementation of the Cassowary constraint solver" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8a9c83f75223d5e48b0bc9cb1bf2776cf01563e00ade8775ffe13b0b6e1af3a6"}, {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:58370b1ffbd35407444d57057b57da5d6549d2d854fa30249771775c63b5fe17"}, @@ -1489,6 +1617,8 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -1564,6 +1694,8 @@ version = "3.9.2" description = "Python plotting package" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "matplotlib-3.9.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9d78bbc0cbc891ad55b4f39a48c22182e9bdaea7fc0e5dbd364f49f729ca1bbb"}, {file = "matplotlib-3.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c375cc72229614632c87355366bdf2570c2dac01ac66b8ad048d2dabadf2d0d4"}, @@ -1632,6 +1764,8 @@ version = "1.1.0" description = "MessagePack serializer" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"}, {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"}, @@ -1710,6 +1844,8 @@ version = "0.4.8" description = "Numpy data serialization using msgpack" optional = false python-versions = "*" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "msgpack-numpy-0.4.8.tar.gz", hash = "sha256:c667d3180513422f9c7545be5eec5d296dcbb357e06f72ed39cc683797556e69"}, {file = "msgpack_numpy-0.4.8-py2.py3-none-any.whl", hash = "sha256:773c19d4dfbae1b3c7b791083e2caf66983bb19b40901646f61d8731554ae3da"}, @@ -1730,6 +1866,8 @@ version = "0.2.2" description = "MX Robot Library" optional = false python-versions = ">=3.9,<3.13" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [] develop = false @@ -1751,6 +1889,8 @@ version = "3.4.2" description = "Python package for creating and manipulating graphs and networks" optional = false python-versions = ">=3.10" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"}, {file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"}, @@ -1775,6 +1915,8 @@ version = "1.9.1" description = "Node.js virtual environment builder" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -1791,6 +1933,8 @@ version = "1.26.4" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, @@ -1841,6 +1985,8 @@ version = "4.10.0.84" description = "Wrapper package for OpenCV python bindings." optional = false python-versions = ">=3.6" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "opencv-python-4.10.0.84.tar.gz", hash = "sha256:72d234e4582e9658ffea8e9cae5b63d488ad06994ef12d81dc303b17472f3526"}, {file = "opencv_python-4.10.0.84-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:fc182f8f4cda51b45f01c64e4cbedfc2f00aff799debebc305d8d0210c43f251"}, @@ -1869,6 +2015,8 @@ version = "1.9.0" description = "Bluesky hardware abstraction with an emphasis on EPICS" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "ophyd-1.9.0-py3-none-any.whl", hash = "sha256:f8e109dd2f57eb0e4711795de19646878a5c7f1f35e9abbece924af60b3df280"}, {file = "ophyd-1.9.0.tar.gz", hash = "sha256:a7ab41c5be9e131f880b664a50e13bfc432631ab581df4788429ee59e0fe68a6"}, @@ -1896,6 +2044,8 @@ version = "24.1" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, @@ -1912,6 +2062,8 @@ version = "2.2.3" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, @@ -2002,6 +2154,8 @@ version = "10.4.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, @@ -2104,6 +2258,8 @@ version = "0.24.3" description = "Physical quantities module" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "Pint-0.24.3-py3-none-any.whl", hash = "sha256:d98667e46fd03a1b94694fbfa104ec30858684d8ab26952e2a348b48059089bb"}, {file = "pint-0.24.3.tar.gz", hash = "sha256:d54771093e8b94c4e0a35ac638c2444ddf3ef685652bab7675ffecfa0c5c5cdf"}, @@ -2138,6 +2294,8 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -2159,6 +2317,8 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -2179,6 +2339,8 @@ version = "2.21.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, @@ -2202,6 +2364,8 @@ version = "2.8.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"}, {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"}, @@ -2226,6 +2390,8 @@ version = "2.20.1" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"}, {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"}, @@ -2332,6 +2498,8 @@ version = "2.6.1" description = "Settings management using Pydantic" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "pydantic_settings-2.6.1-py3-none-any.whl", hash = "sha256:7fb0637c786a558d3103436278a7c4f1cfd29ba8973238a50c5bb9a55387da87"}, {file = "pydantic_settings-2.6.1.tar.gz", hash = "sha256:e0f92546d8a9923cb8941689abf85d6601a8c19a23e97a34b2964a2e3f813ca0"}, @@ -2357,6 +2525,8 @@ version = "3.5.1" description = "Epics Channel Access for Python" optional = false python-versions = "*" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "pyepics-3.5.1.tar.gz", hash = "sha256:a4d0f2d0d163aa34a53f560519f5664a42ba96aeb19bbf92e46228f22fa87ff6"}, ] @@ -2375,6 +2545,8 @@ version = "2.18.0" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, @@ -2394,6 +2566,8 @@ version = "3.2.0" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "pyparsing-3.2.0-py3-none-any.whl", hash = "sha256:93d9577b88da0bbea8cc8334ee8b918ed014968fd2ec383e868fb8afb1ccef84"}, {file = "pyparsing-3.2.0.tar.gz", hash = "sha256:cbf74e27246d595d9a74b186b810f6fbb86726dbf3b9532efb343f6d7294fe9c"}, @@ -2413,6 +2587,8 @@ version = "3.5.4" description = "A python implementation of GNU readline." optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "sys_platform == \"win32\" and python_version <= \"3.11\"" files = [ {file = "pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6"}, {file = "pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7"}, @@ -2432,6 +2608,8 @@ version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, @@ -2459,6 +2637,8 @@ version = "6.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, @@ -2476,12 +2656,38 @@ type = "legacy" url = "https://pypi.asci.synchrotron.org.au/root/pypi/+simple" reference = "cachedpypi" +[[package]] +name = "pytest-mock" +version = "3.14.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\"" +files = [ + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, +] + +[package.dependencies] +pytest = ">=6.2.5" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[package.source] +type = "legacy" +url = "https://pypi.asci.synchrotron.org.au/root/pypi/+simple" +reference = "cachedpypi" + [[package]] name = "python-dateutil" version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -2501,6 +2707,8 @@ version = "1.0.1" description = "Read key-value pairs from a .env file and set them as environment variables" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, @@ -2520,6 +2728,8 @@ version = "2024.2" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, @@ -2536,6 +2746,8 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -2603,6 +2815,8 @@ version = "5.2.0" description = "Python client for Redis database and key-value store" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "redis-5.2.0-py3-none-any.whl", hash = "sha256:ae174f2bb3b1bf2b09d54bf3e51fbc1469cf6c10aa03e21141f51969801a7897"}, {file = "redis-5.2.0.tar.gz", hash = "sha256:0b1087665a771b1ff2e003aa5bdd354f15a70c9e25d5a7dbf9c722c16528a7b0"}, @@ -2626,6 +2840,8 @@ version = "0.35.1" description = "JSON Referencing + Python" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, @@ -2646,6 +2862,8 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -2672,6 +2890,8 @@ version = "0.21.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "rpds_py-0.21.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a017f813f24b9df929674d0332a374d40d7f0162b326562daae8066b502d0590"}, {file = "rpds_py-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:20cc1ed0bcc86d8e1a7e968cce15be45178fd16e2ff656a243145e0b439bd250"}, @@ -2776,6 +2996,8 @@ version = "1.14.1" description = "Fundamental algorithms for scientific computing in Python" optional = false python-versions = ">=3.10" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "scipy-1.14.1-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:b28d2ca4add7ac16ae8bb6632a3c86e4b9e4d52d3e34267f6e1b0c1f8d87e389"}, {file = "scipy-1.14.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d0d2821003174de06b69e58cef2316a6622b60ee613121199cb2852a873f8cf3"}, @@ -2831,6 +3053,8 @@ version = "75.3.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "setuptools-75.3.0-py3-none-any.whl", hash = "sha256:f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd"}, {file = "setuptools-75.3.0.tar.gz", hash = "sha256:fba5dd4d766e97be1b1681d98712680ae8f2f26d7881245f2ce9e40714f1a686"}, @@ -2856,6 +3080,8 @@ version = "1.16.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -2872,6 +3098,8 @@ version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -2888,6 +3116,8 @@ version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." optional = false python-versions = "*" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, @@ -2898,12 +3128,32 @@ type = "legacy" url = "https://pypi.asci.synchrotron.org.au/root/pypi/+simple" reference = "cachedpypi" +[[package]] +name = "sortedcontainers" +version = "2.4.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +optional = false +python-versions = "*" +groups = ["dev"] +markers = "python_version <= \"3.11\"" +files = [ + {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, + {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, +] + +[package.source] +type = "legacy" +url = "https://pypi.asci.synchrotron.org.au/root/pypi/+simple" +reference = "cachedpypi" + [[package]] name = "sphinx" version = "5.3.0" description = "Python documentation generator" optional = false python-versions = ">=3.6" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"}, {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, @@ -2943,6 +3193,8 @@ version = "0.5.2" description = "Read the Docs theme for Sphinx" optional = false python-versions = "*" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "sphinx_rtd_theme-0.5.2-py2.py3-none-any.whl", hash = "sha256:4a05bdbe8b1446d77a01e20a23ebc6777c74f43237035e76be89699308987d6f"}, {file = "sphinx_rtd_theme-0.5.2.tar.gz", hash = "sha256:32bd3b5d13dc8186d7a42fc816a23d32e83a4827d7d9882948e7b837c232da5a"}, @@ -2966,6 +3218,8 @@ version = "2.0.0" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}, {file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"}, @@ -2987,6 +3241,8 @@ version = "2.0.0" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}, {file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"}, @@ -3008,6 +3264,8 @@ version = "2.1.0" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}, {file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"}, @@ -3029,6 +3287,8 @@ version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" optional = false python-versions = ">=3.5" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {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"}, @@ -3048,6 +3308,8 @@ version = "2.0.0" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}, {file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"}, @@ -3069,6 +3331,8 @@ version = "2.0.0" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}, {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}, @@ -3090,6 +3354,8 @@ version = "2.0.2" description = "Super State Machine gives you utilities to build finite state machines." optional = false python-versions = "*" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "super_state_machine-2.0.2-py2.py3-none-any.whl", hash = "sha256:6f615d55970be4ab57f5121a15b60568145effa49e9316a2eaaf51b0b81f3456"}, {file = "super_state_machine-2.0.2.tar.gz", hash = "sha256:e038a4c573ab80f157bf726c3036367919704f62cd166eb46837143165035958"}, @@ -3110,6 +3376,8 @@ version = "0.1.0a105" description = "Structured Scientific Data Access Service" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "tiled-0.1.0a105-py3-none-any.whl", hash = "sha256:1772df1715b513fc19220d041eed5d2cc401096afbf8a7a3c31023520c3ee261"}, {file = "tiled-0.1.0a105.tar.gz", hash = "sha256:81abe6b92ea80a2794da72f6346db586e981d1c115ea5b1633e28f0f2c75b198"}, @@ -3140,10 +3408,12 @@ version = "2.0.2" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, ] +markers = {main = "python_full_version == \"3.10.0\" and extra == \"as-redis-signal\"", dev = "python_version < \"3.11\""} [package.source] type = "legacy" @@ -3156,6 +3426,8 @@ version = "1.0.0" description = "List processing tools and functional utilities" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "toolz-1.0.0-py3-none-any.whl", hash = "sha256:292c8f1c4e7516bf9086f8850935c799a874039c8bcf959d47b600e4c44a6236"}, {file = "toolz-1.0.0.tar.gz", hash = "sha256:2c86e3d9a04798ac556793bced838816296a2f085017664e4995cb40a1047a02"}, @@ -3172,6 +3444,8 @@ version = "4.67.0" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "tqdm-4.67.0-py3-none-any.whl", hash = "sha256:0cd8af9d56911acab92182e88d763100d4788bdf421d251616040cc4d44863be"}, {file = "tqdm-4.67.0.tar.gz", hash = "sha256:fe5a6f95e6fe0b9755e9469b77b9c3cf850048224ecaa8293d7d2d31f97d869a"}, @@ -3198,10 +3472,12 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] +markers = {main = "python_version <= \"3.11\"", dev = "python_version < \"3.11\""} [package.source] type = "legacy" @@ -3214,6 +3490,8 @@ version = "2024.2" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, @@ -3230,6 +3508,8 @@ version = "2.2.3" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, @@ -3252,6 +3532,8 @@ version = "20.27.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\"" files = [ {file = "virtualenv-20.27.1-py3-none-any.whl", hash = "sha256:f11f1b8a29525562925f745563bfd48b189450f61fb34c4f9cc79dd5aa32a1f4"}, {file = "virtualenv-20.27.1.tar.gz", hash = "sha256:142c6be10212543b32c6c45d3d3893dff89112cc588b7d0879ae5a1ec03a47ba"}, @@ -3277,6 +3559,8 @@ version = "2.2.0" description = "Mutable mapping tools" optional = false python-versions = ">=3.7" +groups = ["main"] +markers = "python_version <= \"3.11\"" files = [ {file = "zict-2.2.0-py2.py3-none-any.whl", hash = "sha256:dabcc8c8b6833aa3b6602daad50f03da068322c1a90999ff78aed9eecc8fa92c"}, {file = "zict-2.2.0.tar.gz", hash = "sha256:d7366c2e2293314112dcf2432108428a67b927b00005619feefc310d12d833f3"}, @@ -3295,6 +3579,6 @@ as-acquisition-library = ["as-acquisition-library"] as-redis-signal = ["as-redis-signal"] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = ">=3.10,<3.12" -content-hash = "bd979b3e2735ce3aea95f8e7f96e0ae631390bd25b71d49666d73be3268ba4b0" +content-hash = "b812f73bfc91b361325a8692ad9165df33a8c4c42d90c6211c06dbe7c3beceac" diff --git a/pyproject.toml b/pyproject.toml index b346626..86f54d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "mx3-beamline-library" -version = "1.2.6" +version = "1.2.7" description = "Ophyd devices and bluesky plans." authors = ["Stephen Mudie "] @@ -68,6 +68,8 @@ alabaster = "0.7.12" pytest-cov = "6.0.0" Sphinx = "^5.3.0" sphinx-rtd-theme = "0.5.2" +pytest-mock="^3.14.0" +fakeredis="2.26.2" [tool.black] line-length = 88 diff --git a/tests/conftest.py b/tests/conftest.py index 864ac23..48a9fb3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,9 +1,29 @@ from os import environ import pytest +from bluesky import RunEngine +from bluesky.callbacks.best_effort import BestEffortCallback @pytest.fixture(scope="session") def set_bl_active_env(): environ["BL_ACTIVE"] = "False" yield + + +@pytest.fixture(scope="session") +def sample_id(): + return "test_sample" + + +@pytest.fixture(scope="session") +def run_engine(): + RE = RunEngine({}) + bec = BestEffortCallback() + RE.subscribe(bec) + return RE + + +@pytest.fixture(scope="session") +def session_tmpdir(tmpdir_factory): + return tmpdir_factory.mktemp("session_test_directory") diff --git a/tests/test_beam.py b/tests/test_beam.py index 46e7f5a..13e123b 100644 --- a/tests/test_beam.py +++ b/tests/test_beam.py @@ -1,3 +1,7 @@ +import pytest +from bluesky.plan_stubs import mv +from bluesky.run_engine import RunEngine + from mx3_beamline_library.devices.beam import ( attenuation, energy_dmm, @@ -7,29 +11,34 @@ ) -def test_energy_master(): +@pytest.mark.order("first") +def test_energy_master(run_engine: RunEngine): assert energy_master.get() == 13.0 - energy_master.set(13.2) + run_engine(mv(energy_master, 13.2)) assert energy_master.get() == 13.2 -def test_transmission(): +@pytest.mark.order(after="test_energy_master") +def test_transmission(run_engine: RunEngine): assert transmission.get() == 0.1 - transmission.set(0.2) + run_engine(mv(transmission, 0.2)) assert transmission.get() == 0.2 -def test_attenuation(): +@pytest.mark.order(after="test_transmission") +def test_attenuation(run_engine: RunEngine): assert attenuation.get() == 0.9 - attenuation.set(0.8) + run_engine(mv(attenuation, 0.8)) assert attenuation.get() == 0.8 -def test_energy_dmm(): +@pytest.mark.order(after="test_attenuation") +def test_energy_dmm(run_engine: RunEngine): assert energy_dmm.get() == 13.0 - energy_dmm.set(13.2) + run_engine(mv(energy_dmm, 13.2)) assert energy_dmm.get() == 13.2 +@pytest.mark.order(after="test_energy_dmm") def test_ring_current(): assert ring_current.get() == 200.0 diff --git a/tests/test_optical_centering.py b/tests/test_optical_centering.py new file mode 100644 index 0000000..36ddde2 --- /dev/null +++ b/tests/test_optical_centering.py @@ -0,0 +1,443 @@ +from os import path + +import fakeredis +import numpy as np +import pytest +from bluesky import RunEngine +from pytest_mock.plugin import MockerFixture + +from mx3_beamline_library.plans.image_analysis import ( + get_image_from_md3_camera, + get_image_from_top_camera, +) +from mx3_beamline_library.plans.optical_centering import OpticalCentering, md3 +from mx3_beamline_library.schemas.optical_centering import ( + OpticalCenteringExtraConfig, + TopCamera, +) +from mx3_beamline_library.schemas.xray_centering import RasterGridCoordinates + + +@pytest.fixture +def fake_redis() -> fakeredis.FakeStrictRedis: + return fakeredis.FakeStrictRedis() + + +@pytest.fixture +def optical_centering_instance(sample_id: str): + optical_centering = OpticalCentering( + sample_id=sample_id, + beam_position=(612, 512), + grid_step=(100, 100), + plot=False, + calibrated_alignment_z=0.47, + manual_mode=False, + use_top_camera_camera=True, + extra_config=OpticalCenteringExtraConfig( + top_camera=TopCamera(x_pixel_target=804, y_pixel_target=437) + ), + ) + return optical_centering + + +@pytest.mark.order("first") +def test_three_click_centering( + optical_centering_instance: OpticalCentering, run_engine: RunEngine +): + + # Exercise + run_engine( + optical_centering_instance.three_click_centering( + x_coords=[1, 1.4, 1.2], + y_coords=[1, 1, 1], + omega_positions=[0, np.pi / 2, np.pi], + ) + ) + + # Verify + assert round(md3.sample_x.position, 2) == 0.3 + assert round(md3.sample_y.position, 2) == -0.1 + assert round(md3.omega.position, 2) == 0 + assert round(md3.alignment_x.position, 2) == 0.43 + assert round(md3.alignment_y.position, 2) == 0.66 + assert round(md3.alignment_z.position, 2) == -0.69 + + +@pytest.mark.order(after="test_three_click_centering") +def test_two_click_centering( + optical_centering_instance: OpticalCentering, run_engine: RunEngine +): + # This test has to be executed after the three_click_centering test + # because the md3 values change dynamically + + # Exercise + run_engine( + optical_centering_instance.two_click_centering( + x_coords=[1, 1.4], y_coords=[1, 1], omega_positions=[0, np.pi / 2] + ) + ) + + # Verify + assert round(md3.sample_x.position, 2) == 1.29 + assert round(md3.sample_y.position, 2) == 0.49 + assert round(md3.omega.position, 2) == 0 + assert round(md3.alignment_x.position, 2) == 0.43 + assert round(md3.alignment_y.position, 2) == 1.32 + assert round(md3.alignment_z.position, 2) == 0.47 + + +def test_find_edge_and_flat_angles( + optical_centering_instance: OpticalCentering, + run_engine: RunEngine, + mocker: MockerFixture, +): + # Setup + three_click_centering = mocker.patch( + "mx3_beamline_library.plans.optical_centering.OpticalCentering.three_click_centering" + ) + # Exercise + run_engine(optical_centering_instance.find_edge_and_flat_angles()) + + # Verify + assert three_click_centering.call_count == 1 + + +def test_center_loop( + optical_centering_instance: OpticalCentering, + mocker: MockerFixture, + sample_id: str, + fake_redis: fakeredis.FakeStrictRedis, + run_engine: RunEngine, +): + # Setup + mocker.patch( + "mx3_beamline_library.plans.optical_centering.redis_connection", new=fake_redis + ) + # RE = RunEngine({}) + # bec = BestEffortCallback() + # RE.subscribe(bec) + + # Exercise + run_engine(optical_centering_instance.center_loop()) + + # Verify + assert fake_redis.get(f"optical_centering_results:{sample_id}") is not None + + +def test_center_loop_manual_mode( + mocker: MockerFixture, + sample_id: str, + run_engine: RunEngine, + fake_redis: fakeredis.FakeStrictRedis, +): + # Setup + mocker.patch( + "mx3_beamline_library.plans.optical_centering.redis_connection", new=fake_redis + ) + optical_centering = OpticalCentering( + sample_id=sample_id, + beam_position=(612, 512), + grid_step=(100, 100), + plot=False, + calibrated_alignment_z=0.47, + manual_mode=True, + use_top_camera_camera=True, + extra_config=OpticalCenteringExtraConfig( + top_camera=TopCamera(x_pixel_target=804, y_pixel_target=437) + ), + ) + + # Exercise + run_engine(optical_centering.center_loop()) + + # Verify + assert fake_redis.get(f"optical_centering_results:{sample_id}") is None + + +def test_set_optical_centering_params(optical_centering_instance: OpticalCentering): + # Setup + config = OpticalCenteringExtraConfig() + + # Exercise + optical_centering_instance._set_optical_centering_config_parameters(config) + + # Verify + assert ( + optical_centering_instance.top_cam_pixels_per_mm_x + == config.top_camera.pixels_per_mm_x + ) + assert ( + optical_centering_instance.top_cam_pixels_per_mm_y + == config.top_camera.pixels_per_mm_y + ) + assert optical_centering_instance.x_pixel_target == config.top_camera.x_pixel_target + assert optical_centering_instance.y_pixel_target == config.top_camera.y_pixel_target + + +def test_multipoint_centre_three_clicks(optical_centering_instance: OpticalCentering): + # Exercise + result = optical_centering_instance.multi_point_centre( + x_coords=[1, 2, 3], omega_list=[0, 90, 180], two_clicks=False + ) + + # Verify + assert round(result[0], 2) == -1.12 + assert round(result[1], 2) == 1.11 + + +def test_multipoint_centre_two_clicks(optical_centering_instance: OpticalCentering): + # Exercise + result = optical_centering_instance.multi_point_centre( + x_coords=[1, 2], omega_list=[0, 90], two_clicks=True + ) + + # Verify + assert round(result[0], 2) == 2.16 + assert round(result[1], 2) == 0.28 + + +def test_find_loop_edge_coordinates(optical_centering_instance: OpticalCentering): + # Exercise + result = optical_centering_instance.find_loop_edge_coordinates() + + # Verify + assert result == (666, 168) + + +def test_prepare_raster_grid(optical_centering_instance: OpticalCentering): + # Exercise + result = optical_centering_instance.prepare_raster_grid(omega=0) + + # Verify + assert isinstance(result, RasterGridCoordinates) + + +def test_get_md3_camera_jpeg_image(optical_centering_instance: OpticalCentering): + # Exercise + result = optical_centering_instance._get_md3_camera_jpeg_image() + + # Verify + assert result is not None + assert isinstance(result, bytes) + assert len(result) > 0 + + +def test_sine_function(optical_centering_instance: OpticalCentering): + # Setup and Exercise + result = optical_centering_instance._sine_function(np.pi / 2, 1, -np.pi / 2, 0) + + # Verify + assert result == 1.0 + + +def test_three_click_centering_function(optical_centering_instance: OpticalCentering): + # Setup and Exercise + result = optical_centering_instance.three_click_centering_function( + np.pi / 2, 1, 0, 0 + ) + + # Verify + assert result == 1.0 + + +def test_two_click_centering_function(optical_centering_instance: OpticalCentering): + # Setup and Exercise + result = optical_centering_instance.two_click_centering_function(np.pi / 2, 1, 0) + + # Verify + assert optical_centering_instance.beam_position == (612, 512) + assert round(result, 2) == 1.41 + + +def test_init_udc_error(): + # Execute and verify + with pytest.raises( + ValueError, match="grid_step can only be None if manual_mode=True" + ): + OpticalCentering( + sample_id="sample_tmp", + beam_position=(612, 512), + grid_step=None, + plot=False, + calibrated_alignment_z=0.47, + manual_mode=False, + use_top_camera_camera=True, + extra_config=OpticalCenteringExtraConfig( + top_camera=TopCamera(x_pixel_target=804, y_pixel_target=437) + ), + ) + + +@pytest.mark.order(after="test_two_click_centering") +def test_init_create_folder(sample_id: str, session_tmpdir): + # Setup + OpticalCentering( + sample_id=sample_id, + beam_position=(612, 512), + grid_step=(100, 100), + plot=True, + calibrated_alignment_z=0.47, + manual_mode=False, + use_top_camera_camera=True, + output_directory=session_tmpdir, + ) + + # Verify + output = path.join(session_tmpdir, sample_id) + assert path.exists(output) + + +@pytest.mark.order(after="test_init_create_folder") +def test_find_loop_edge_coordinates_with_plot(sample_id: str, session_tmpdir): + # Setup + optical_centering = OpticalCentering( + sample_id=sample_id, + beam_position=(612, 512), + grid_step=(100, 100), + plot=True, + calibrated_alignment_z=0.47, + manual_mode=False, + use_top_camera_camera=True, + output_directory=session_tmpdir, + ) + + # Exercise + result = optical_centering.find_loop_edge_coordinates() + + # Verify + assert result == (666, 168) + assert path.exists( + path.join( + session_tmpdir, sample_id, "test_sample_loop_centering_180_zoom_1.png" + ) + ) + + +@pytest.mark.order(after="test_find_loop_edge_coordinates_with_plot") +def test_find_zoom_0_maximum_area_with_plot( + sample_id: str, session_tmpdir, run_engine: RunEngine +): + # Setup + optical_centering = OpticalCentering( + sample_id=sample_id, + beam_position=(612, 512), + grid_step=(100, 100), + plot=True, + calibrated_alignment_z=0.47, + manual_mode=False, + use_top_camera_camera=True, + output_directory=session_tmpdir, + ) + + # Exercise + run_engine(optical_centering._find_zoom_0_maximum_area()) + + # Verify + assert path.exists( + path.join(session_tmpdir, sample_id, "test_sample_270_top_camera.png") + ) + assert path.exists( + path.join(session_tmpdir, sample_id, "test_sample_180_top_camera.png") + ) + + +@pytest.mark.order(after="test_find_zoom_0_maximum_area_with_plot") +def test_find_edge_and_flat_angles_with_plot( + sample_id: str, session_tmpdir, run_engine: RunEngine +): + # Setup + optical_centering = OpticalCentering( + sample_id=sample_id, + beam_position=(612, 512), + grid_step=(100, 100), + plot=True, + calibrated_alignment_z=0.47, + manual_mode=False, + use_top_camera_camera=True, + output_directory=session_tmpdir, + ) + + # Exercise + run_engine(optical_centering.find_edge_and_flat_angles()) + + # Verify + assert path.exists( + path.join(session_tmpdir, sample_id, f"{sample_id}_area_estimation_0.png") + ) + assert path.exists( + path.join(session_tmpdir, sample_id, f"{sample_id}_area_estimation_180.png") + ) + assert path.exists( + path.join(session_tmpdir, sample_id, f"{sample_id}_area_estimation_270.png") + ) + + +def test_prepare_raster_grid_with_plot(sample_id: str, session_tmpdir): + # Setup + optical_centering = OpticalCentering( + sample_id=sample_id, + beam_position=(612, 512), + grid_step=(100, 100), + plot=True, + calibrated_alignment_z=0.47, + manual_mode=False, + use_top_camera_camera=True, + output_directory=session_tmpdir, + ) + filename = path.join(optical_centering.sample_path, f"{sample_id}_raster_grid_flat") + + # Exercise + result = optical_centering.prepare_raster_grid(omega=0, filename=filename) + + # Verify + assert isinstance(result, RasterGridCoordinates) + assert path.exists(f"{filename}.png") + + +@pytest.mark.order(after="test_prepare_raster_grid_with_plot") +def test_save_gray_scale_image(sample_id: str, session_tmpdir): + # Setup + optical_centering = OpticalCentering( + sample_id=sample_id, + beam_position=(612, 512), + grid_step=(100, 100), + plot=True, + calibrated_alignment_z=0.47, + manual_mode=False, + use_top_camera_camera=True, + output_directory=session_tmpdir, + ) + filename = path.join(optical_centering.sample_path, "top_camera_plot") + data, x_size, y_size = get_image_from_top_camera() + new_data = data.reshape((y_size, x_size)) + # Exercise + optical_centering.save_image( + data=new_data, x_coord=1, y_coord=1, filename=filename, grayscale_img=True + ) + + # Verify + assert path.exists(f"{filename}.png") + + +@pytest.mark.order(after="test_save_gray_scale_image") +def test_save_rgb_image(sample_id: str, session_tmpdir): + # Setup + optical_centering = OpticalCentering( + sample_id=sample_id, + beam_position=(612, 512), + grid_step=(100, 100), + plot=True, + calibrated_alignment_z=0.47, + manual_mode=False, + use_top_camera_camera=True, + output_directory=session_tmpdir, + ) + filename = path.join(optical_centering.sample_path, "md3_plot") + data = get_image_from_md3_camera() + # Exercise + optical_centering.save_image( + data=data, x_coord=1, y_coord=1, filename=filename, grayscale_img=False + ) + + # Verify + assert path.exists(f"{filename}.png")