pyglaze 0.2.0__py3-none-any.whl → 0.2.1__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.
pyglaze/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.2.0"
1
+ __version__ = "0.2.1"
pyglaze/device/ampcom.py CHANGED
@@ -32,6 +32,13 @@ if TYPE_CHECKING:
32
32
  from pyglaze.helpers.types import FloatArray
33
33
 
34
34
 
35
+ class DeviceComError(Exception):
36
+ """Raised when an error occurs in the communication with the device."""
37
+
38
+ def __init__(self: DeviceComError, message: str) -> None:
39
+ super().__init__(message)
40
+
41
+
35
42
  @dataclass
36
43
  class _ForceAmpCom:
37
44
  config: ForceDeviceConfiguration
@@ -272,12 +279,12 @@ class _LeAmpCom:
272
279
  self._raw_byte_send_ints(
273
280
  [self.scanning_points, self.config.integration_periods, self.config.use_ema]
274
281
  )
275
- return self._get_response()
282
+ return self._get_response(self.SEND_SETTINGS_COMMAND)
276
283
 
277
284
  def write_list(self: _LeAmpCom) -> str:
278
285
  self._encode_send_response(self.SEND_LIST_COMMAND)
279
286
  self._raw_byte_send_floats(self.scanning_list)
280
- return self._get_response()
287
+ return self._get_response(self.SEND_LIST_COMMAND)
281
288
 
282
289
  def start_scan(self: _LeAmpCom) -> tuple[str, np.ndarray, np.ndarray, np.ndarray]:
283
290
  self._encode_send_response(self.START_COMMAND)
@@ -301,7 +308,7 @@ class _LeAmpCom:
301
308
 
302
309
  def _encode_send_response(self: _LeAmpCom, command: str) -> str:
303
310
  self._encode_and_send(command)
304
- return self._get_response()
311
+ return self._get_response(command)
305
312
 
306
313
  def _encode_and_send(self: _LeAmpCom, command: str) -> None:
307
314
  self.__ser.write(command.encode(self.ENCODING))
@@ -326,9 +333,19 @@ class _LeAmpCom:
326
333
  time.sleep(self.config._sweep_length_ms * 1e-3 * 0.01) # noqa: SLF001, access to private attribute for backwards compatibility
327
334
  status = self._get_status()
328
335
 
329
- @_BackoffRetry(backoff_base=0.05, logger=logging.getLogger(LOGGER_NAME))
330
- def _get_response(self: _LeAmpCom) -> str:
331
- return self.__ser.read_until().decode(self.ENCODING).strip()
336
+ @_BackoffRetry(
337
+ backoff_base=1e-2, max_tries=3, logger=logging.getLogger(LOGGER_NAME)
338
+ )
339
+ def _get_response(self: _LeAmpCom, command: str) -> str:
340
+ response = self.__ser.read_until().decode(self.ENCODING).strip()
341
+
342
+ if len(response) == 0:
343
+ msg = f"Command: '{command}'. Empty response received"
344
+ raise serialutil.SerialException(msg)
345
+ if response[: len(self.OK_RESPONSE)] != self.OK_RESPONSE:
346
+ msg = f"Command: '{command}'. Expected response '{self.OK_RESPONSE}', received: '{response}'"
347
+ raise DeviceComError(msg)
348
+ return response
332
349
 
333
350
  @_BackoffRetry(
334
351
  backoff_base=1e-2, max_tries=5, logger=logging.getLogger(LOGGER_NAME)
@@ -26,6 +26,8 @@ class MockDevice(ABC):
26
26
  self: MockDevice,
27
27
  fail_after: float = np.inf,
28
28
  n_fails: float = np.inf,
29
+ *,
30
+ empty_responses: bool = False,
29
31
  ) -> None:
30
32
  pass
31
33
 
@@ -183,7 +185,11 @@ class LeMockDevice(MockDevice):
183
185
  DAC_BITWIDTH = 2**12
184
186
 
185
187
  def __init__(
186
- self: LeMockDevice, fail_after: float = np.inf, n_fails: float = np.inf
188
+ self: LeMockDevice,
189
+ fail_after: float = np.inf,
190
+ n_fails: float = np.inf,
191
+ *,
192
+ empty_responses: bool = False,
187
193
  ) -> None:
188
194
  self.fail_after = fail_after
189
195
  self.fails_wanted = n_fails
@@ -197,6 +203,7 @@ class LeMockDevice(MockDevice):
197
203
  self.use_ema: bool | None = None
198
204
  self.scanning_list: list[float] | None = None
199
205
  self._scan_start_time: float | None = None
206
+ self.empty_responses = empty_responses
200
207
 
201
208
  def write(self: LeMockDevice, input_bytes: bytes) -> None:
202
209
  """Mock-write to the serial connection."""
@@ -217,12 +224,16 @@ class LeMockDevice(MockDevice):
217
224
 
218
225
  def read(self: LeMockDevice, size: int) -> bytes:
219
226
  """Mock-read from the serial connection."""
227
+ if self.empty_responses:
228
+ return self._create_scan_bytes(n_bytes=0)
220
229
  if self.state == _LeMockState.IDLE:
221
230
  return self._create_scan_bytes(n_bytes=size)
222
231
  raise NotImplementedError
223
232
 
224
233
  def read_until(self: LeMockDevice, _: bytes = b"\r") -> bytes: # noqa: PLR0911
225
234
  """Mock-read_until from the serial connection."""
235
+ if self.empty_responses:
236
+ return "".encode(self.ENCODING)
226
237
  if self.state == _LeMockState.WAITING_FOR_SETTINGS:
227
238
  return "ACK: Ready to receive settings.".encode(self.ENCODING)
228
239
  if self.state == _LeMockState.RECEIVED_SETTINGS:
@@ -353,6 +364,7 @@ def list_mock_devices() -> list[str]:
353
364
  "mock_device",
354
365
  "mock_device_scan_should_fail",
355
366
  "mock_device_fail_first_scan",
367
+ "mock_device_empty_responses",
356
368
  ]
357
369
 
358
370
 
@@ -364,6 +376,8 @@ def _mock_device_factory(config: DeviceConfiguration) -> MockDevice:
364
376
  return mock_class()
365
377
  if config.amp_port == "mock_device_fail_first_scan":
366
378
  return mock_class(fail_after=0, n_fails=1)
379
+ if config.amp_port == "mock_device_empty_responses":
380
+ return mock_class(empty_responses=True)
367
381
 
368
382
  msg = f"Unknown mock device requested: {config.amp_port}. Valid options are: {list_mock_devices()}"
369
383
  raise ValueError(msg)
@@ -95,13 +95,15 @@ class _AsyncScanner:
95
95
  def _get_scan(self: _AsyncScanner) -> _TimestampedWaveform:
96
96
  try:
97
97
  return self._shared_mem.get(timeout=self._SCAN_TIMEOUT)
98
- except Empty as err:
98
+ except Exception as err:
99
+ scanner_err: Exception | None = None
99
100
  if self._scanner_conn.poll(timeout=self.startup_timeout):
100
101
  msg: _ScannerHealth = self._scanner_conn.recv()
101
- if not msg.is_alive:
102
- self.is_scanning = False
103
102
  if msg.error:
104
- raise msg.error from err
103
+ scanner_err = msg.error
104
+ self.stop_scan()
105
+ if scanner_err:
106
+ raise scanner_err from err
105
107
  raise
106
108
 
107
109
  @staticmethod
@@ -121,7 +123,7 @@ class _AsyncScanner:
121
123
  while not stop_signal.is_set():
122
124
  try:
123
125
  waveform = _TimestampedWaveform(datetime.now(), scanner.scan()) # noqa: DTZ005
124
- except (serialutil.SerialException, TimeoutError) as e:
126
+ except Exception as e: # noqa: BLE001
125
127
  parent_conn.send(
126
128
  _ScannerHealth(is_alive=False, is_healthy=False, error=e)
127
129
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyglaze
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Pyglaze is a library used to operate the devices of Glaze Technologies
5
5
  Author: GLAZE Technologies ApS
6
6
  License: BSD 3-Clause License
@@ -1,13 +1,13 @@
1
- pyglaze/__init__.py,sha256=Zn1KFblwuFHiDRdRAiRnDBRkbPttWh44jKa5zG2ov0E,22
1
+ pyglaze/__init__.py,sha256=HfjVOrpTnmZ-xVFCYSVmX50EXaBQeJteUHG-PD6iQs8,22
2
2
  pyglaze/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  pyglaze/datamodels/__init__.py,sha256=DJLByl2C7pC4RM4Uh6PW-McM5RIGBjcopzGywCKhSlI,111
4
4
  pyglaze/datamodels/pulse.py,sha256=BoW_GDvkwEpn_UYUFPoUfi5E_oCnvv8cU4nZKG7Ph2U,20911
5
5
  pyglaze/datamodels/waveform.py,sha256=n31DhJHFeBNZ3hHqQUiCGXssm5Dc8wV6tGPkhmFYB4Q,5809
6
6
  pyglaze/device/__init__.py,sha256=5RjCHuFKMi9g2KLUkxixO9hNpAgkUBcOURNTuhAdoUk,177
7
- pyglaze/device/ampcom.py,sha256=mtsprV51UeRwqkvbbLRbO2WeRd2XdFcU4_Q8AMul28g,16523
7
+ pyglaze/device/ampcom.py,sha256=6JXl7PojYr4F1_pdVItO-XbGCp8mpy4wpwaaUn8Loog,17214
8
8
  pyglaze/device/configuration.py,sha256=gh_eerX8TdXx3LnFxHieJqOpfDfE9cV6Xgm5WYVnvO0,7994
9
9
  pyglaze/devtools/__init__.py,sha256=9EW20idoaZv_5GuSgDmfpTPjfCZ-Rl27EV3oJebmwnQ,90
10
- pyglaze/devtools/mock_device.py,sha256=XJ1jwOOYPDqynX3QDYfEJk9n3sTMmefNPAe9N3M1T5Q,13522
10
+ pyglaze/devtools/mock_device.py,sha256=3RMa-JAehbbRCmOO74aZzGPdYp2qsP-XE8ulzLXDP6Q,13994
11
11
  pyglaze/devtools/thz_pulse.py,sha256=xp-T9psdOrUMtSUFu8HEwQJVu_aMixJdZHtg_BCVu_k,923
12
12
  pyglaze/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  pyglaze/helpers/types.py,sha256=p9xSAP5Trr1FcCWl7ynCWqDOUZKgMQYzMUXSwDpAKHg,599
@@ -15,11 +15,11 @@ pyglaze/helpers/utilities.py,sha256=n_x9Tqm305MUorS29O6CoJM8Mi4apo2bsN_odrRaVAw,
15
15
  pyglaze/interpolation/__init__.py,sha256=WCxHPsiI7zvJykp-jfytoEbO4Tla-YIF6A7fjDfcDvU,72
16
16
  pyglaze/interpolation/interpolation.py,sha256=rQWzPD7W8TXETps7VZI0gcfAOCWO8pGL1HhhBnyxaMw,735
17
17
  pyglaze/scanning/__init__.py,sha256=uCBaeDTufOrC9KWf30ICqcmvFg_YT85olb3M9jkvZRg,99
18
- pyglaze/scanning/_asyncscanner.py,sha256=n4PhRBSZLFSSeGgBkgVwFsdY4rnduXJnQo6TQ0ujVkw,5224
18
+ pyglaze/scanning/_asyncscanner.py,sha256=SldM7XCavfugsDstxMVI_WLL6GUJutLYN0iU__mfY5A,5277
19
19
  pyglaze/scanning/client.py,sha256=3qrQStkeLQzCeu4yMHJ_ENLGQ7E5GMc4CP9J55rk-ug,1817
20
20
  pyglaze/scanning/scanner.py,sha256=PSjXVpSpHpYIl-sW34pIThocbM9GSHJ_E4gGcsePeTw,8139
21
- pyglaze-0.2.0.dist-info/LICENSE,sha256=LCP3sGBX7LxuQopcjeug1fW4tngWCHF4zB7QCgB28xM,1504
22
- pyglaze-0.2.0.dist-info/METADATA,sha256=ZWjcLSzB9CD-_MmxsXdaX2tEcQbia3zLTH2kp5cnp1E,3498
23
- pyglaze-0.2.0.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
24
- pyglaze-0.2.0.dist-info/top_level.txt,sha256=X7d5rqVVuWNmtK4-Uh4sgOLlqye8vaHZOr5RYba0REo,8
25
- pyglaze-0.2.0.dist-info/RECORD,,
21
+ pyglaze-0.2.1.dist-info/LICENSE,sha256=LCP3sGBX7LxuQopcjeug1fW4tngWCHF4zB7QCgB28xM,1504
22
+ pyglaze-0.2.1.dist-info/METADATA,sha256=WETmnm6CF1_eyojd-ahfIFLJ_VCxV2jshcrbSoPLg3Y,3498
23
+ pyglaze-0.2.1.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
24
+ pyglaze-0.2.1.dist-info/top_level.txt,sha256=X7d5rqVVuWNmtK4-Uh4sgOLlqye8vaHZOr5RYba0REo,8
25
+ pyglaze-0.2.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (72.2.0)
2
+ Generator: setuptools (75.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5