mx-bluesky 1.5.11__py3-none-any.whl → 1.5.14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. mx_bluesky/Getting started.ipynb +170 -0
  2. mx_bluesky/_version.py +2 -2
  3. mx_bluesky/beamlines/aithre_lasershaping/experiment_plans/__init__.py +0 -0
  4. mx_bluesky/beamlines/aithre_lasershaping/experiment_plans/robot_load_plan.py +198 -0
  5. mx_bluesky/beamlines/aithre_lasershaping/parameters/__init__.py +0 -0
  6. mx_bluesky/beamlines/aithre_lasershaping/parameters/constants.py +17 -0
  7. mx_bluesky/beamlines/aithre_lasershaping/parameters/robot_load_parameters.py +13 -0
  8. mx_bluesky/beamlines/aithre_lasershaping/pin_tip_centring.py +31 -0
  9. mx_bluesky/beamlines/aithre_lasershaping/robot_load.py +74 -0
  10. mx_bluesky/beamlines/i04/__init__.py +6 -2
  11. mx_bluesky/beamlines/i04/callbacks/murko_callback.py +27 -12
  12. mx_bluesky/beamlines/i04/experiment_plans/i04_grid_detect_then_xray_centre_plan.py +88 -13
  13. mx_bluesky/beamlines/i04/external_interaction/__init__.py +0 -0
  14. mx_bluesky/beamlines/i04/external_interaction/config_server.py +15 -0
  15. mx_bluesky/beamlines/i04/oav_centering_plans/__init__.py +0 -0
  16. mx_bluesky/beamlines/i04/oav_centering_plans/oav_imaging.py +115 -0
  17. mx_bluesky/beamlines/i04/parameters/__init__.py +0 -0
  18. mx_bluesky/beamlines/i04/parameters/constants.py +21 -0
  19. mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +24 -1
  20. mx_bluesky/beamlines/i04/thawing_plan.py +147 -152
  21. mx_bluesky/beamlines/i24/serial/dcid.py +4 -5
  22. mx_bluesky/beamlines/i24/serial/extruder/i24ssx_extruder_collect_py3v2.py +5 -2
  23. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/CustomChip_py3v1.edl +11 -11
  24. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DetStage.edl +3 -3
  25. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +142 -142
  26. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/MappingLite-oxford_py3v1.edl +135 -135
  27. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/PMAC_Command.edl +8 -8
  28. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/pumpprobe-py3v1.edl +13 -13
  29. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_chip_collect_py3v1.py +7 -4
  30. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_chip_manager_py3v1.py +35 -32
  31. mx_bluesky/beamlines/i24/serial/parameters/utils.py +5 -5
  32. mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +113 -306
  33. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +8 -2
  34. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +1 -1
  35. mx_bluesky/beamlines/i24/serial/web_gui_plans/general_plans.py +6 -6
  36. mx_bluesky/beamlines/i24/serial/web_gui_plans/oav_plans.py +64 -0
  37. mx_bluesky/{hyperion/device_setup_plans/smargon.py → common/device_setup_plans/gonio.py} +9 -6
  38. mx_bluesky/common/device_setup_plans/manipulate_sample.py +8 -1
  39. mx_bluesky/common/device_setup_plans/robot_load_unload.py +1 -1
  40. mx_bluesky/common/device_setup_plans/setup_oav.py +8 -0
  41. mx_bluesky/common/device_setup_plans/setup_zebra_and_shutter.py +0 -5
  42. mx_bluesky/common/device_setup_plans/xbpm_feedback.py +8 -1
  43. mx_bluesky/common/experiment_plans/beamstop_check.py +229 -0
  44. mx_bluesky/common/experiment_plans/common_flyscan_xray_centre_plan.py +2 -0
  45. mx_bluesky/common/experiment_plans/inner_plans/read_hardware.py +5 -2
  46. mx_bluesky/common/experiment_plans/oav_snapshot_plan.py +0 -1
  47. mx_bluesky/{hyperion → common}/experiment_plans/pin_tip_centring_plan.py +20 -21
  48. mx_bluesky/common/external_interaction/callbacks/common/grid_detection_callback.py +5 -0
  49. mx_bluesky/common/external_interaction/callbacks/common/ispyb_callback_base.py +10 -12
  50. mx_bluesky/common/external_interaction/callbacks/common/ispyb_mapping.py +3 -5
  51. mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py +5 -5
  52. mx_bluesky/common/external_interaction/config_server.py +2 -2
  53. mx_bluesky/common/external_interaction/ispyb/data_model.py +11 -4
  54. mx_bluesky/common/external_interaction/ispyb/exp_eye_store.py +159 -2
  55. mx_bluesky/common/external_interaction/ispyb/ispyb_store.py +76 -166
  56. mx_bluesky/common/external_interaction/ispyb/ispyb_utils.py +0 -14
  57. mx_bluesky/common/parameters/components.py +1 -0
  58. mx_bluesky/common/parameters/constants.py +5 -2
  59. mx_bluesky/common/parameters/device_composites.py +4 -2
  60. mx_bluesky/common/utils/exceptions.py +15 -0
  61. mx_bluesky/common/utils/log.py +9 -0
  62. mx_bluesky/common/utils/utils.py +48 -0
  63. mx_bluesky/hyperion/__main__.py +3 -13
  64. mx_bluesky/hyperion/baton_handler.py +23 -6
  65. mx_bluesky/hyperion/experiment_plans/hyperion_flyscan_xray_centre_plan.py +1 -0
  66. mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +5 -6
  67. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +3 -10
  68. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +4 -2
  69. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +8 -2
  70. mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +2 -2
  71. mx_bluesky/hyperion/experiment_plans/udc_default_state.py +166 -0
  72. mx_bluesky/hyperion/external_interaction/agamemnon.py +1 -1
  73. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +48 -21
  74. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +2 -2
  75. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +1 -0
  76. mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +1 -4
  77. mx_bluesky/hyperion/external_interaction/config_server.py +5 -5
  78. mx_bluesky/hyperion/parameters/constants.py +10 -3
  79. mx_bluesky/hyperion/parameters/device_composites.py +4 -2
  80. mx_bluesky/hyperion/parameters/robot_load.py +1 -9
  81. mx_bluesky/hyperion/plan_runner.py +31 -0
  82. mx_bluesky/hyperion/plan_runner_api.py +14 -1
  83. mx_bluesky/hyperion/utils/context.py +2 -2
  84. mx_bluesky/jupyter_example.ipynb +9 -1
  85. {mx_bluesky-1.5.11.dist-info → mx_bluesky-1.5.14.dist-info}/METADATA +7 -6
  86. {mx_bluesky-1.5.11.dist-info → mx_bluesky-1.5.14.dist-info}/RECORD +90 -75
  87. mx_bluesky/common/experiment_plans/inner_plans/udc_default_state.py +0 -86
  88. mx_bluesky/common/external_interaction/callbacks/common/logging_callback.py +0 -29
  89. {mx_bluesky-1.5.11.dist-info → mx_bluesky-1.5.14.dist-info}/WHEEL +0 -0
  90. {mx_bluesky-1.5.11.dist-info → mx_bluesky-1.5.14.dist-info}/entry_points.txt +0 -0
  91. {mx_bluesky-1.5.11.dist-info → mx_bluesky-1.5.14.dist-info}/licenses/LICENSE +0 -0
  92. {mx_bluesky-1.5.11.dist-info → mx_bluesky-1.5.14.dist-info}/top_level.txt +0 -0
@@ -2,9 +2,6 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING
4
4
 
5
- from mx_bluesky.common.external_interaction.callbacks.common.logging_callback import (
6
- format_doc_for_log,
7
- )
8
5
  from mx_bluesky.common.external_interaction.callbacks.common.plan_reactive_callback import (
9
6
  PlanReactiveCallback,
10
7
  )
@@ -14,7 +11,7 @@ from mx_bluesky.common.external_interaction.nexus.nexus_utils import (
14
11
  vds_type_based_on_bit_depth,
15
12
  )
16
13
  from mx_bluesky.common.external_interaction.nexus.write_nexus import NexusWriter
17
- from mx_bluesky.common.utils.log import NEXUS_LOGGER
14
+ from mx_bluesky.common.utils.log import NEXUS_LOGGER, format_doc_for_log
18
15
  from mx_bluesky.hyperion.parameters.constants import CONST, I03Constants
19
16
  from mx_bluesky.hyperion.parameters.rotation import SingleRotationScan
20
17
 
@@ -2,15 +2,15 @@ from functools import cache
2
2
 
3
3
  from mx_bluesky.common.external_interaction.config_server import MXConfigClient
4
4
  from mx_bluesky.hyperion.parameters.constants import (
5
- HyperionFeatureSetting,
6
- HyperionFeatureSettingSources,
5
+ HyperionFeatureSettings,
6
+ HyperionFeatureSettingsSources,
7
7
  )
8
8
 
9
9
 
10
10
  @cache
11
- def get_hyperion_config_client() -> MXConfigClient[HyperionFeatureSetting]:
11
+ def get_hyperion_config_client() -> MXConfigClient[HyperionFeatureSettings]:
12
12
  return MXConfigClient(
13
- feature_sources=HyperionFeatureSettingSources,
14
- feature_dc=HyperionFeatureSetting,
13
+ feature_sources=HyperionFeatureSettingsSources,
14
+ feature_dc=HyperionFeatureSettings,
15
15
  url="https://daq-config.diamond.ac.uk",
16
16
  )
@@ -8,7 +8,7 @@ from mx_bluesky.common.parameters.constants import (
8
8
  DocDescriptorNames,
9
9
  EnvironmentConstants,
10
10
  ExperimentParamConstants,
11
- FeatureSetting,
11
+ FeatureSettings,
12
12
  FeatureSettingSources,
13
13
  HardwareConstants,
14
14
  OavConstants,
@@ -32,20 +32,27 @@ class I03Constants:
32
32
 
33
33
 
34
34
  # These currently exist in GDA domain.properties
35
- class HyperionFeatureSettingSources(FeatureSettingSources):
35
+ class HyperionFeatureSettingsSources(FeatureSettingSources):
36
36
  USE_GPU_RESULTS = "gda.mx.hyperion.xrc.use_gpu_results"
37
37
  USE_PANDA_FOR_GRIDSCAN = "gda.mx.hyperion.use_panda_for_gridscans"
38
38
  SET_STUB_OFFSETS = "gda.mx.hyperion.do_stub_offsets"
39
39
  PANDA_RUNUP_DISTANCE_MM = "gda.mx.hyperion.panda_runup_distance_mm"
40
+ DETECTOR_DISTANCE_LIMIT_MAX_MM = "gda.detector.distance.limit.max"
41
+ DETECTOR_DISTANCE_LIMIT_MIN_MM = "gda.detector.distance.limit.min"
42
+ BEAMSTOP_DIODE_CHECK = "gda.mx.hyperion.enable_beamstop_diode_check"
40
43
 
41
44
 
42
45
  # Use these defaults if we can't read from the config server
43
46
  @dataclass
44
- class HyperionFeatureSetting(FeatureSetting):
47
+ class HyperionFeatureSettings(FeatureSettings):
45
48
  USE_GPU_RESULTS: bool = True
46
49
  USE_PANDA_FOR_GRIDSCAN: bool = False
47
50
  SET_STUB_OFFSETS: bool = False
48
51
  PANDA_RUNUP_DISTANCE_MM: float = 0.16
52
+ # From GDA mx-config hutch_utilities.py default values
53
+ DETECTOR_DISTANCE_LIMIT_MAX_MM: float = 700
54
+ DETECTOR_DISTANCE_LIMIT_MIN_MM: float = 250
55
+ BEAMSTOP_DIODE_CHECK: bool = False
49
56
 
50
57
 
51
58
  @dataclass(frozen=True)
@@ -13,10 +13,11 @@ from dodal.devices.fast_grid_scan import (
13
13
  ZebraFastGridScanThreeD,
14
14
  )
15
15
  from dodal.devices.flux import Flux
16
+ from dodal.devices.i03.beamsize import Beamsize
16
17
  from dodal.devices.robot import BartRobot
17
18
  from dodal.devices.s4_slit_gaps import S4SlitGaps
18
19
  from dodal.devices.synchrotron import Synchrotron
19
- from dodal.devices.undulator import Undulator
20
+ from dodal.devices.undulator import UndulatorInKeV
20
21
  from dodal.devices.xbpm_feedback import XBPMFeedback
21
22
  from dodal.devices.zebra.zebra import Zebra
22
23
  from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
@@ -41,7 +42,7 @@ class HyperionFlyScanXRayCentreComposite(FlyScanEssentialDevices):
41
42
  eiger: EigerDetector
42
43
  flux: Flux
43
44
  s4_slit_gaps: S4SlitGaps
44
- undulator: Undulator
45
+ undulator: UndulatorInKeV
45
46
  synchrotron: Synchrotron
46
47
  zebra: Zebra
47
48
  zocalo: ZocaloResults
@@ -52,6 +53,7 @@ class HyperionFlyScanXRayCentreComposite(FlyScanEssentialDevices):
52
53
  backlight: Backlight
53
54
  xbpm_feedback: XBPMFeedback
54
55
  zebra_fast_grid_scan: ZebraFastGridScanThreeD
56
+ beamsize: Beamsize
55
57
 
56
58
 
57
59
  @pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
@@ -1,5 +1,3 @@
1
- from pydantic import Field
2
-
3
1
  from mx_bluesky.common.parameters.components import (
4
2
  MxBlueskyParameters,
5
3
  WithOptionalEnergyChange,
@@ -7,9 +5,6 @@ from mx_bluesky.common.parameters.components import (
7
5
  WithSnapshot,
8
6
  WithVisit,
9
7
  )
10
- from mx_bluesky.common.parameters.constants import (
11
- HardwareConstants,
12
- )
13
8
  from mx_bluesky.hyperion.parameters.gridscan import (
14
9
  GridCommonWithHyperionDetectorParams,
15
10
  PinTipCentreThenXrayCentre,
@@ -19,12 +14,10 @@ from mx_bluesky.hyperion.parameters.gridscan import (
19
14
  class RobotLoadAndEnergyChange(
20
15
  MxBlueskyParameters, WithSample, WithSnapshot, WithOptionalEnergyChange, WithVisit
21
16
  ):
22
- thawing_time: float = Field(default=HardwareConstants.THAWING_TIME)
17
+ pass
23
18
 
24
19
 
25
20
  class RobotLoadThenCentre(GridCommonWithHyperionDetectorParams):
26
- thawing_time: float = Field(default=HardwareConstants.THAWING_TIME)
27
-
28
21
  @property
29
22
  def robot_load_params(self) -> RobotLoadAndEnergyChange:
30
23
  my_params = self.model_dump()
@@ -33,5 +26,4 @@ class RobotLoadThenCentre(GridCommonWithHyperionDetectorParams):
33
26
  @property
34
27
  def pin_centre_then_xray_centre_params(self) -> PinTipCentreThenXrayCentre:
35
28
  my_params = self.model_dump()
36
- del my_params["thawing_time"]
37
29
  return PinTipCentreThenXrayCentre(**my_params)
@@ -1,7 +1,9 @@
1
1
  import threading
2
+ import time
2
3
  from collections.abc import Callable
3
4
 
4
5
  from blueapi.core import BlueskyContext
6
+ from bluesky import plan_stubs as bps
5
7
  from bluesky.utils import MsgGenerator, RequestAbort
6
8
 
7
9
  from mx_bluesky.common.parameters.constants import Status
@@ -19,10 +21,15 @@ class PlanError(Exception):
19
21
  class PlanRunner(BaseRunner):
20
22
  """Runner that executes experiments from inside a running Bluesky plan"""
21
23
 
24
+ EXTERNAL_CALLBACK_WATCHDOG_TIMER_S = 60
25
+ EXTERNAL_CALLBACK_POLL_INTERVAL_S = 1
26
+
22
27
  def __init__(self, context: BlueskyContext, dev_mode: bool) -> None:
23
28
  super().__init__(context)
24
29
  self.current_status: Status = Status.IDLE
25
30
  self.is_dev_mode = dev_mode
31
+ self._callbacks_started = False
32
+ self._callback_watchdog_expiry = time.monotonic()
26
33
 
27
34
  def execute_plan(
28
35
  self,
@@ -38,6 +45,20 @@ class PlanRunner(BaseRunner):
38
45
  self.current_status = Status.BUSY
39
46
 
40
47
  try:
48
+ callback_expiry = time.monotonic() + self.EXTERNAL_CALLBACK_WATCHDOG_TIMER_S
49
+ while time.monotonic() < callback_expiry:
50
+ if self._callbacks_started:
51
+ break
52
+ # If on first launch the external callbacks aren't started yet, wait until they are
53
+ LOGGER.info("Waiting for external callbacks to start")
54
+ yield from bps.sleep(self.EXTERNAL_CALLBACK_POLL_INTERVAL_S)
55
+ else:
56
+ raise RuntimeError("External callbacks not running - try restarting")
57
+
58
+ if not self._external_callbacks_are_alive():
59
+ raise RuntimeError(
60
+ "External callback watchdog timer expired, check external callbacks are running."
61
+ )
41
62
  yield from experiment()
42
63
  self.current_status = Status.IDLE
43
64
  except WarningError as e:
@@ -74,3 +95,13 @@ class PlanRunner(BaseRunner):
74
95
  stopping_thread = threading.Thread(target=issue_abort)
75
96
  stopping_thread.start()
76
97
  return
98
+
99
+ def reset_callback_watchdog_timer(self):
100
+ """Called periodically to reset the watchdog timer when the external callbacks ping us."""
101
+ self._callbacks_started = True
102
+ self._callback_watchdog_expiry = (
103
+ time.monotonic() + self.EXTERNAL_CALLBACK_WATCHDOG_TIMER_S
104
+ )
105
+
106
+ def _external_callbacks_are_alive(self) -> bool:
107
+ return time.monotonic() < self._callback_watchdog_expiry
@@ -24,10 +24,11 @@ def create_server_for_udc(runner: PlanRunner) -> Thread: # pragma: no cover
24
24
  return flask_thread
25
25
 
26
26
 
27
- def create_app_for_udc(runner):
27
+ def create_app_for_udc(runner: PlanRunner):
28
28
  app = Flask(__name__)
29
29
  api = Api(app)
30
30
  api.add_resource(StatusResource, "/status", resource_class_args=[runner])
31
+ api.add_resource(CallbackLiveness, "/callbackPing", resource_class_args=[runner])
31
32
  return app
32
33
 
33
34
 
@@ -41,3 +42,15 @@ class StatusResource(Resource):
41
42
  def get(self):
42
43
  status = self._runner.current_status
43
44
  return {"status": status.value}
45
+
46
+
47
+ class CallbackLiveness(Resource):
48
+ """Called periodically by the external callbacks to indicate that they are still running"""
49
+
50
+ def __init__(self, runner: PlanRunner):
51
+ super().__init__()
52
+ self._runner = runner
53
+
54
+ def get(self):
55
+ LOGGER.debug("External callback ping received.")
56
+ self._runner.reset_callback_watchdog_timer()
@@ -27,8 +27,8 @@ def clear_all_device_caches(context: BlueskyContext):
27
27
 
28
28
 
29
29
  def setup_devices(context: BlueskyContext, dev_mode: bool):
30
- _, exceptions = context.with_dodal_module(
31
- get_beamline_based_on_environment_variable(),
30
+ _, exceptions = context.with_device_manager(
31
+ get_beamline_based_on_environment_variable().devices,
32
32
  mock=dev_mode,
33
33
  )
34
34
  if exceptions:
@@ -48,6 +48,14 @@
48
48
  "\n",
49
49
  "run_engine(scan(dets, motor, -1, 1, 10))"
50
50
  ]
51
+ },
52
+ {
53
+ "cell_type": "code",
54
+ "execution_count": null,
55
+ "id": "d117ffc8-882c-44fb-b65d-7e57544255d4",
56
+ "metadata": {},
57
+ "outputs": [],
58
+ "source": []
51
59
  }
52
60
  ],
53
61
  "metadata": {
@@ -66,7 +74,7 @@
66
74
  "name": "python",
67
75
  "nbconvert_exporter": "python",
68
76
  "pygments_lexer": "ipython3",
69
- "version": "3.10.4"
77
+ "version": "3.12.11"
70
78
  }
71
79
  },
72
80
  "nbformat": 4,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mx-bluesky
3
- Version: 1.5.11
3
+ Version: 1.5.14
4
4
  Summary: Bluesky tools for MX Beamlines at DLS
5
5
  Author-email: Dominic Oram <dominic.oram@diamond.ac.uk>
6
6
  License: Apache License
@@ -218,11 +218,10 @@ Requires-Dist: annotated_types
218
218
  Requires-Dist: caproto
219
219
  Requires-Dist: fastapi[all]
220
220
  Requires-Dist: flask-restful
221
- Requires-Dist: ispyb
222
221
  Requires-Dist: jupyterlab
223
222
  Requires-Dist: matplotlib
224
223
  Requires-Dist: nexgen>=0.11.0
225
- Requires-Dist: numpy==2.2.6
224
+ Requires-Dist: numpy>=2.3.5
226
225
  Requires-Dist: opencv-python
227
226
  Requires-Dist: opentelemetry-distro
228
227
  Requires-Dist: opentelemetry-exporter-otlp
@@ -238,17 +237,18 @@ Requires-Dist: deepdiff
238
237
  Requires-Dist: matplotlib
239
238
  Requires-Dist: cachetools
240
239
  Requires-Dist: daq-config-server>=v1.0.0-rc.2
241
- Requires-Dist: blueapi>=1.6.3
240
+ Requires-Dist: blueapi>=1.8.0
242
241
  Requires-Dist: ophyd>=1.10.5
243
- Requires-Dist: ophyd-async>=0.13.5
242
+ Requires-Dist: ophyd-async>=0.14.0
244
243
  Requires-Dist: bluesky>=1.14.6
245
- Requires-Dist: dls-dodal==1.65.0
244
+ Requires-Dist: dls-dodal==1.67.0
246
245
  Provides-Extra: dev
247
246
  Requires-Dist: black; extra == "dev"
248
247
  Requires-Dist: build; extra == "dev"
249
248
  Requires-Dist: diff-cover; extra == "dev"
250
249
  Requires-Dist: GitPython; extra == "dev"
251
250
  Requires-Dist: import-linter; extra == "dev"
251
+ Requires-Dist: ispyb; extra == "dev"
252
252
  Requires-Dist: ipython; extra == "dev"
253
253
  Requires-Dist: mypy; extra == "dev"
254
254
  Requires-Dist: myst-parser; extra == "dev"
@@ -262,6 +262,7 @@ Requires-Dist: pytest-cov; extra == "dev"
262
262
  Requires-Dist: pytest-random-order; extra == "dev"
263
263
  Requires-Dist: pytest-timeout; extra == "dev"
264
264
  Requires-Dist: pytest; extra == "dev"
265
+ Requires-Dist: responses; extra == "dev"
265
266
  Requires-Dist: ruff; extra == "dev"
266
267
  Requires-Dist: sphinx-autobuild; extra == "dev"
267
268
  Requires-Dist: sphinx-copybutton; extra == "dev"