plesty-lib 0.2.4__tar.gz → 0.2.6__tar.gz

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. plesty_lib-0.2.6/.gitlab-ci.yml +24 -0
  2. plesty_lib-0.2.6/CHANGELOG.md +7 -0
  3. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/PKG-INFO +2 -1
  4. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/README.md +1 -0
  5. plesty_lib-0.2.6/gl-secret-detection-report.json +50 -0
  6. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/data/array.py +16 -25
  7. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/data/ctype_manager.py +6 -7
  8. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/data/table.py +2 -1
  9. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/data/types.py +6 -10
  10. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/data/units.py +5 -7
  11. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/device/async_wrapper.py +4 -3
  12. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/device/base_device_sync.py +91 -57
  13. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/device/base_tcp_scpi_device.py +32 -24
  14. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/device/base_visa_scpi_device.py +25 -18
  15. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/device/composite_device.py +7 -9
  16. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/device/device_utils.py +11 -15
  17. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/device/funcs.py +129 -131
  18. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/device/params.py +22 -47
  19. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/service/__init__.py +10 -7
  20. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/service/resource_manager.py +6 -12
  21. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/service/tcp_ip_client.py +12 -15
  22. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/service/tcp_ip_server.py +8 -18
  23. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/sim/data_generator.py +26 -30
  24. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/solver/iceblock.py +4 -3
  25. plesty_lib-0.2.6/plesty/lib/solver/scpi.py +158 -0
  26. plesty_lib-0.2.6/plesty/lib/test/__init__.py +7 -0
  27. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/test/device_func_system.py +5 -5
  28. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/test/device_param_system.py +7 -11
  29. plesty_lib-0.2.6/plesty/lib/test/device_pipeline.py +512 -0
  30. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/test/grouped_param_keys.py +3 -3
  31. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/test/resource_allocation.py +10 -12
  32. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/test/schema_params.py +5 -6
  33. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/traffic/__init__.py +5 -11
  34. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/traffic/serial.py +14 -28
  35. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/traffic/tcp_ip.py +6 -16
  36. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/traffic/usb_utils.py +3 -6
  37. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/traffic/utils.py +2 -3
  38. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/traffic/visa.py +4 -4
  39. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/utils/error_utils.py +7 -1
  40. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/utils/logger.py +4 -4
  41. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/utils/registry.py +4 -3
  42. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plestylib/__init__.py +1 -1
  43. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/pyproject.toml +9 -2
  44. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/tests/test_data_array.py +1 -3
  45. plesty_lib-0.2.6/tests/test_data_types.py +253 -0
  46. plesty_lib-0.2.6/tests/test_device_base.py +252 -0
  47. plesty_lib-0.2.6/tests/test_device_funcs.py +269 -0
  48. plesty_lib-0.2.6/tests/test_device_params.py +297 -0
  49. plesty_lib-0.2.6/tests/test_device_pipeline.py +781 -0
  50. plesty_lib-0.2.6/tests/test_device_scpi.py +278 -0
  51. plesty_lib-0.2.6/tests/test_misc.py +180 -0
  52. plesty_lib-0.2.6/tests/test_service_manager.py +120 -0
  53. plesty_lib-0.2.6/tests/test_sim.py +241 -0
  54. plesty_lib-0.2.6/tests/test_solver.py +177 -0
  55. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/tests/test_tcp_ip_resources.py +4 -12
  56. plesty_lib-0.2.6/tests/test_traffic_drivers.py +201 -0
  57. plesty_lib-0.2.6/tests/test_traffic_utils.py +173 -0
  58. plesty_lib-0.2.6/tests/test_utils.py +139 -0
  59. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/uv.lock +660 -125
  60. plesty_lib-0.2.4/.gitlab-ci.yml +0 -44
  61. plesty_lib-0.2.4/plesty/lib/solver/scpi.py +0 -108
  62. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/.gitignore +0 -0
  63. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/COPYING +0 -0
  64. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/LICENSE +0 -0
  65. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/LICENSES/GPL-3.0-or-later.txt +0 -0
  66. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/LICENSES/LGPL-3.0-or-later.txt +0 -0
  67. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/docs/api_reference.md +0 -0
  68. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/docs/contributing.md +0 -0
  69. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/docs/device/base_device.md +0 -0
  70. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/docs/device/cmd_solver.md +0 -0
  71. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/docs/device/error_handling.md +0 -0
  72. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/docs/device/func_system.md +0 -0
  73. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/docs/device/index.md +0 -0
  74. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/docs/device/logging_system.md +0 -0
  75. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/docs/device/param_system.md +0 -0
  76. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/docs/device/test_helper.md +0 -0
  77. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/docs/device/traffic_manager.md +0 -0
  78. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/docs/imgs/device_layers.png +0 -0
  79. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/docs/imgs/device_standard.png +0 -0
  80. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/docs/imgs/plesty_framework.png +0 -0
  81. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/docs/index.md +0 -0
  82. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/docs/quickstart.md +0 -0
  83. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/docs/toc.yaml +0 -0
  84. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/__init__.py +0 -0
  85. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/data/__init__.py +0 -0
  86. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/device/__init__.py +0 -0
  87. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/sim/__init__.py +0 -0
  88. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/solver/__init__.py +0 -0
  89. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/utils/__init__.py +0 -0
  90. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/plesty/lib/utils/dll_utils.py +0 -0
  91. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/spdx.tmpl +0 -0
  92. {plesty_lib-0.2.4 → plesty_lib-0.2.6}/tests/test_schema_params.py +0 -0
@@ -0,0 +1,24 @@
1
+ workflow:
2
+ rules:
3
+ # Create pipelines for merge requests
4
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event"
5
+ # Create branch pipelines for main and exp
6
+ - if: $CI_COMMIT_BRANCH == "main"
7
+ - if: $CI_COMMIT_BRANCH == "exp"
8
+ # Create pipelines for tags (dev and stable releases)
9
+ - if: $CI_COMMIT_TAG
10
+ # Otherwise do not create a pipeline
11
+ - when: never
12
+
13
+ stages:
14
+ - check
15
+ - security
16
+ - deploy
17
+ - release
18
+
19
+ include:
20
+ - component: $CI_SERVER_FQDN/plesty/plesty-ci/plesty-standard-ci@exp
21
+ inputs:
22
+ standard: quantum
23
+ extra_branch: exp
24
+ access_token: $CI_BOT_TOKEN
@@ -0,0 +1,7 @@
1
+ # Changelog
2
+
3
+ ## Unreleased — 2026-06-25
4
+
5
+ - Add `FuncMeta` and `SCPISolver.solve_func` for schema-driven operation dispatch
6
+ - Fix mypy type annotations across all public modules
7
+ - Migrate CI pipeline to `plesty-standard-ci@exp`
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plesty-lib
3
- Version: 0.2.4
3
+ Version: 0.2.6
4
4
  Summary: A library for the Plesty ecosystem.
5
5
  Author-email: Yunshuang Yuan <yunshuang.yuan@fkp.uni-hannover.de>
6
6
  Maintainer-email: Plesty Development Team <yunshuang.yuan@fkp.uni-hannover.de>
@@ -69,6 +69,7 @@ class PowermeterDevice(BaseVisaScpiDevice):
69
69
  ### 2. Use the device
70
70
 
71
71
  ```python
72
+ # replace the serial number P0000001 with that of your own device
72
73
  address = "USB0::0x1313::0x8078::P0000001::INSTR"
73
74
 
74
75
  with PowermeterDevice(address) as dev:
@@ -52,6 +52,7 @@ class PowermeterDevice(BaseVisaScpiDevice):
52
52
  ### 2. Use the device
53
53
 
54
54
  ```python
55
+ # replace the serial number P0000001 with that of your own device
55
56
  address = "USB0::0x1313::0x8078::P0000001::INSTR"
56
57
 
57
58
  with PowermeterDevice(address) as dev:
@@ -0,0 +1,50 @@
1
+ {
2
+ "scan": {
3
+ "analyzer": {
4
+ "id": "secrets",
5
+ "name": "secrets",
6
+ "url": "https://gitlab.com/gitlab-org/security-products/analyzers/secrets",
7
+ "vendor": {
8
+ "name": "GitLab"
9
+ },
10
+ "version": "7.37.0"
11
+ },
12
+ "scanner": {
13
+ "id": "gitleaks",
14
+ "name": "Gitleaks",
15
+ "url": "https://github.com/gitleaks/gitleaks",
16
+ "vendor": {
17
+ "name": "GitLab"
18
+ },
19
+ "version": "8.30.1"
20
+ },
21
+ "type": "secret_detection",
22
+ "start_time": "2026-06-26T14:45:00",
23
+ "end_time": "2026-06-26T14:45:01",
24
+ "status": "success",
25
+ "observability": {
26
+ "events": [
27
+ {
28
+ "event": "collect_secrets_analyzer_scan_metrics_from_pipeline",
29
+ "time_s": 0.483231567,
30
+ "exit_code": 0,
31
+ "git_strategy": "FetchShallow",
32
+ "repo_size_kb": 1260,
33
+ "commit_count": 1,
34
+ "bytes_scanned": 23,
35
+ "pipeline_type": "Tag"
36
+ },
37
+ {
38
+ "event": "collect_secrets_analyzer_ruleset_adoption_metrics_from_pipeline",
39
+ "custom_ruleset": false,
40
+ "passthrough_types": null,
41
+ "passthrough_count": 0,
42
+ "passthrough_auth_used": false,
43
+ "rules_version": "0.24.3"
44
+ }
45
+ ]
46
+ }
47
+ },
48
+ "version": "15.2.4",
49
+ "vulnerabilities": []
50
+ }
@@ -16,7 +16,7 @@ from typing import List, Dict, Any, Tuple, Optional
16
16
  from .units import Units
17
17
 
18
18
 
19
- class _PlestyArrayMeta(type(np.ndarray)):
19
+ class _PlestyArrayMeta(type(np.ndarray)): # type: ignore[misc]
20
20
  """Forward NumPy top-level callables as class methods for PlestyArray.
21
21
 
22
22
  Example:
@@ -81,7 +81,13 @@ class PlestyArray(np.ndarray, metaclass=_PlestyArrayMeta):
81
81
  _META_KEYS = {"name", "range", "options", "unit", "description"}
82
82
  __array_priority__ = 1000
83
83
 
84
- def update_meta(self, **kwargs):
84
+ name: Optional[str]
85
+ range: Optional[Tuple[float, float] | Tuple[int, int]]
86
+ options: Optional[List[Any]]
87
+ unit: Optional[str]
88
+ description: Optional[str]
89
+
90
+ def update_meta(self, **kwargs) -> None:
85
91
  """Update meta information for the PlestyArray.
86
92
 
87
93
  Example:
@@ -141,12 +147,8 @@ class PlestyArray(np.ndarray, metaclass=_PlestyArrayMeta):
141
147
  }
142
148
 
143
149
  @classmethod
144
- def _split_meta_kwargs(
145
- cls, kwargs: Dict[str, Any]
146
- ) -> Tuple[Dict[str, Any], Dict[str, Any]]:
147
- meta_kwargs = {
148
- k: kwargs.pop(k) for k in list(kwargs.keys()) if k in cls._META_KEYS
149
- }
150
+ def _split_meta_kwargs(cls, kwargs: Dict[str, Any]) -> Tuple[Dict[str, Any], Dict[str, Any]]:
151
+ meta_kwargs = {k: kwargs.pop(k) for k in list(kwargs.keys()) if k in cls._META_KEYS}
150
152
  return kwargs, meta_kwargs
151
153
 
152
154
  @classmethod
@@ -176,7 +178,7 @@ class PlestyArray(np.ndarray, metaclass=_PlestyArrayMeta):
176
178
  description: Optional[str] = None,
177
179
  ):
178
180
  """Create a new PlestyArray instance with optional metadata."""
179
- obj = np.asarray(input_array).view(cls)
181
+ obj: "PlestyArray" = np.asarray(input_array).view(cls) # type: ignore[assignment]
180
182
  obj.name = name
181
183
  obj.range = range
182
184
  obj.options = options
@@ -205,9 +207,7 @@ class PlestyArray(np.ndarray, metaclass=_PlestyArrayMeta):
205
207
  x.view(np.ndarray) if isinstance(x, PlestyArray) else x for x in out
206
208
  )
207
209
 
208
- np_inputs = tuple(
209
- x.view(np.ndarray) if isinstance(x, PlestyArray) else x for x in inputs
210
- )
210
+ np_inputs = tuple(x.view(np.ndarray) if isinstance(x, PlestyArray) else x for x in inputs)
211
211
 
212
212
  result = getattr(ufunc, method)(*np_inputs, **kwargs)
213
213
 
@@ -246,9 +246,7 @@ class PlestyArray(np.ndarray, metaclass=_PlestyArrayMeta):
246
246
  right_scale = right_expr.conversion_scale_to(left_expr)
247
247
  np_inputs_scaled = list(np_inputs)
248
248
  np_inputs_scaled[1] = np_inputs_scaled[1] * right_scale
249
- result = getattr(ufunc, method)(
250
- *tuple(np_inputs_scaled), **kwargs
251
- )
249
+ result = getattr(ufunc, method)(*tuple(np_inputs_scaled), **kwargs)
252
250
  meta["unit"] = Units.standardize(left.unit)
253
251
  else:
254
252
  lu = left.unit or "-"
@@ -286,11 +284,7 @@ class PlestyArray(np.ndarray, metaclass=_PlestyArrayMeta):
286
284
  meta["unit"] = f"({lu})^({ru})"
287
285
  else:
288
286
  lu = left.unit or "-"
289
- ru = (
290
- right.unit
291
- if isinstance(right, PlestyArray) and right.unit
292
- else "-"
293
- )
287
+ ru = right.unit if isinstance(right, PlestyArray) and right.unit else "-"
294
288
  meta["unit"] = f"({lu})^({ru})"
295
289
 
296
290
  elif isinstance(left, PlestyArray) and np.isscalar(right):
@@ -313,12 +307,9 @@ class PlestyArray(np.ndarray, metaclass=_PlestyArrayMeta):
313
307
  if source is None:
314
308
  source = self._find_meta_source(kwargs.values()) if kwargs else None
315
309
 
316
- np_args = tuple(
317
- a.view(np.ndarray) if isinstance(a, PlestyArray) else a for a in args
318
- )
310
+ np_args = tuple(a.view(np.ndarray) if isinstance(a, PlestyArray) else a for a in args)
319
311
  np_kwargs = {
320
- k: (v.view(np.ndarray) if isinstance(v, PlestyArray) else v)
321
- for k, v in kwargs.items()
312
+ k: (v.view(np.ndarray) if isinstance(v, PlestyArray) else v) for k, v in kwargs.items()
322
313
  }
323
314
 
324
315
  result = func(*np_args, **np_kwargs)
@@ -8,6 +8,7 @@
8
8
 
9
9
  import ctypes as ct
10
10
  from ctypes import POINTER
11
+ from typing import Any
11
12
 
12
13
 
13
14
  class CtypeParam:
@@ -20,7 +21,7 @@ class CtypeParam:
20
21
  self.ptype = self.get_pytype(ctype_str) if ptype is None else ptype
21
22
  self.ctype = self.get_ctype(ctype_str)
22
23
 
23
- def get_pytype(self, ctype_str):
24
+ def get_pytype(self, ctype_str: str) -> type:
24
25
  """Map a string representation of a C type to an actual ctypes type."""
25
26
  self.pytype_mapping = {
26
27
  "c_int": int,
@@ -35,15 +36,13 @@ class CtypeParam:
35
36
  "c_unsigned char": int,
36
37
  }
37
38
 
38
- clean_ctype_str = (
39
- ctype_str.replace("*", "").replace("ref", "").replace("ptr", "").strip()
40
- )
39
+ clean_ctype_str = ctype_str.replace("*", "").replace("ref", "").replace("ptr", "").strip()
41
40
  if clean_ctype_str in self.pytype_mapping:
42
41
  return self.pytype_mapping[clean_ctype_str]
43
42
  else:
44
43
  raise ValueError(f"Unsupported C type for Python mapping: {ctype_str}")
45
44
 
46
- def get_ctype(self, ctype_str):
45
+ def get_ctype(self, ctype_str: str) -> Any:
47
46
  """Map a string representation of a C type to an actual ctypes type."""
48
47
  ctype_mapping = {
49
48
  "c_int": ct.c_int,
@@ -70,13 +69,13 @@ class CtypeParam:
70
69
  else:
71
70
  raise ValueError(f"Unsupported C type: {ctype_str}")
72
71
 
73
- def to_pytype(self):
72
+ def to_pytype(self) -> Any:
74
73
  """Convert the C type value to its corresponding Python type."""
75
74
  if self.ptype is None:
76
75
  raise ValueError("Python type mapping is not defined for this C type.")
77
76
  return self.ptype(self.value)
78
77
 
79
- def to_ctype(self):
78
+ def to_ctype(self) -> Any:
80
79
  """Convert the Python value to its corresponding C type."""
81
80
  if self.ctype is None:
82
81
  raise ValueError("C type mapping is not defined for this value.")
@@ -10,6 +10,7 @@ in a structured way.
10
10
  """
11
11
 
12
12
  from dataclasses import dataclass
13
+ from typing import Any
13
14
  from .array import PlestyArray
14
15
 
15
16
 
@@ -18,7 +19,7 @@ class TableHeader:
18
19
  """Represents the header of a PlestyTable, defining column names and types."""
19
20
 
20
21
  name: str
21
- dtype: type | None = None
22
+ dtype: Any = None
22
23
  unit: str | None = None
23
24
  description: str | None = None
24
25
 
@@ -16,7 +16,7 @@ from .table import PlestyTable2D
16
16
  from .ctype_manager import CtypeParam
17
17
 
18
18
 
19
- def resolve_dtype(dtype: Any) -> type | None:
19
+ def resolve_dtype(dtype: Any) -> type[Any] | CtypeParam | None:
20
20
  """Resolve schema dtype strings into Python types.
21
21
 
22
22
  Supports scalar aliases and array types in the form array_<numpy_dtype>,
@@ -26,7 +26,7 @@ def resolve_dtype(dtype: Any) -> type | None:
26
26
  return dtype
27
27
 
28
28
  dtype_str = str(dtype).strip().lower()
29
- aliases = {
29
+ aliases: dict[str, type[Any]] = {
30
30
  "str": str,
31
31
  "string": str,
32
32
  "int": int,
@@ -58,9 +58,7 @@ def resolve_dtype(dtype: Any) -> type | None:
58
58
  np.dtype(item_token)
59
59
  return PlestyArray
60
60
  except TypeError as exc:
61
- raise ValueError(
62
- f"Unsupported numpy dtype token in array type: {dtype!r}"
63
- ) from exc
61
+ raise ValueError(f"Unsupported numpy dtype token in array type: {dtype!r}") from exc
64
62
 
65
63
  raise ValueError(f"Unsupported dtype in schema: {dtype!r}")
66
64
 
@@ -88,7 +86,7 @@ def normalize_shape(shape: Any) -> tuple[Any, ...] | None:
88
86
  return tuple(shape)
89
87
 
90
88
 
91
- def istype(value, dtype):
89
+ def istype(value: Any, dtype: Any) -> bool:
92
90
  """Check if a value is of a specified data type, including basic and iterable types."""
93
91
  if dtype in [int, float, str, bool]:
94
92
  return isinstance(value, dtype)
@@ -103,11 +101,9 @@ def istype(value, dtype):
103
101
  return False
104
102
 
105
103
 
106
- def cast_basic_type(value, dtype):
104
+ def cast_basic_type(value: Any, dtype: Any) -> Any:
107
105
  """Cast a value to the given basic type, raising ValueError on failure."""
108
106
  try:
109
107
  return dtype(value)
110
108
  except Exception as e:
111
- raise ValueError(
112
- f"Value {value} cannot be cast to expected type {dtype.__name__}: {e}"
113
- )
109
+ raise ValueError(f"Value {value} cannot be cast to expected type {dtype.__name__}: {e}")
@@ -7,7 +7,7 @@
7
7
  """Unit parsing, composition, and conversion utilities for Plesty."""
8
8
 
9
9
  import re
10
- from typing import Dict, Optional
10
+ from typing import Any, Dict, Optional
11
11
 
12
12
 
13
13
  class Units:
@@ -18,7 +18,7 @@ class Units:
18
18
  - ``factor``: numeric scaling factor to SI base units
19
19
  """
20
20
 
21
- SYMBOLS = {
21
+ SYMBOLS: dict[str, dict[str, Any]] = {
22
22
  # length
23
23
  "km": {"dim": "L", "factor": 1.0e3},
24
24
  "m": {"dim": "L", "factor": 1.0},
@@ -92,12 +92,12 @@ class Units:
92
92
  i += 2
93
93
 
94
94
  applied_exp = sign * exponent
95
- dim = entry["dim"]
95
+ dim: str = str(entry["dim"])
96
96
  dims[dim] = dims.get(dim, 0) + applied_exp
97
97
  if dims[dim] == 0:
98
98
  dims.pop(dim)
99
99
 
100
- factor *= entry["factor"] ** applied_exp
100
+ factor *= float(entry["factor"]) ** applied_exp
101
101
  sign = 1
102
102
  i += 1
103
103
 
@@ -167,9 +167,7 @@ class Units:
167
167
  numerator.append(symbol if power == 1 else f"{symbol}^{power}")
168
168
  else:
169
169
  pos_power = -power
170
- denominator.append(
171
- symbol if pos_power == 1 else f"{symbol}^{pos_power}"
172
- )
170
+ denominator.append(symbol if pos_power == 1 else f"{symbol}^{pos_power}")
173
171
 
174
172
  num_text = "*".join(numerator) if numerator else "1"
175
173
  if not denominator:
@@ -11,6 +11,7 @@ import threading
11
11
  import weakref
12
12
  from queue import Queue
13
13
  from functools import wraps
14
+ from typing import Any
14
15
  import inspect
15
16
 
16
17
 
@@ -28,7 +29,7 @@ class AsyncWrapperBase:
28
29
  ``async def _call(self, func, *args, **kwargs)``
29
30
  """
30
31
 
31
- _registry = weakref.WeakKeyDictionary()
32
+ _registry: weakref.WeakKeyDictionary[Any, Any] = weakref.WeakKeyDictionary()
32
33
 
33
34
  def __new__(cls, obj):
34
35
  """Return the existing wrapper for obj if one exists, otherwise create a new one."""
@@ -66,7 +67,7 @@ class AsyncWrapperBase:
66
67
  return attr # already async
67
68
 
68
69
  @wraps(attr)
69
- async def async_method(*args, timeout=None, **kwargs):
70
+ async def async_method(*args: Any, timeout: Any = None, **kwargs: Any) -> Any:
70
71
  coro = self._call(attr, *args, **kwargs)
71
72
  if timeout is not None:
72
73
  return await asyncio.wait_for(coro, timeout)
@@ -240,7 +241,7 @@ class AsyncDeviceThread:
240
241
  - When the underlying API is already fully asynchronous.
241
242
  """
242
243
 
243
- _registry = weakref.WeakKeyDictionary() # optional singleton per device
244
+ _registry: weakref.WeakKeyDictionary[Any, Any] = weakref.WeakKeyDictionary()
244
245
 
245
246
  def __new__(cls, device):
246
247
  """Return the existing wrapper for device if one exists, otherwise create a new one."""