shepherd-core 2025.2.2__py3-none-any.whl → 2025.4.2__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 (56) hide show
  1. shepherd_core/calibration_hw_def.py +11 -11
  2. shepherd_core/commons.py +4 -4
  3. shepherd_core/data_models/base/cal_measurement.py +10 -11
  4. shepherd_core/data_models/base/calibration.py +11 -8
  5. shepherd_core/data_models/base/content.py +1 -1
  6. shepherd_core/data_models/base/shepherd.py +6 -7
  7. shepherd_core/data_models/base/wrapper.py +2 -2
  8. shepherd_core/data_models/content/energy_environment.py +4 -3
  9. shepherd_core/data_models/content/firmware.py +9 -7
  10. shepherd_core/data_models/content/virtual_harvester.py +30 -22
  11. shepherd_core/data_models/content/virtual_source.py +17 -16
  12. shepherd_core/data_models/experiment/experiment.py +15 -14
  13. shepherd_core/data_models/experiment/observer_features.py +7 -8
  14. shepherd_core/data_models/experiment/target_config.py +12 -12
  15. shepherd_core/data_models/readme.md +2 -1
  16. shepherd_core/data_models/task/__init__.py +5 -5
  17. shepherd_core/data_models/task/emulation.py +13 -14
  18. shepherd_core/data_models/task/firmware_mod.py +11 -11
  19. shepherd_core/data_models/task/harvest.py +7 -6
  20. shepherd_core/data_models/task/observer_tasks.py +7 -7
  21. shepherd_core/data_models/task/programming.py +11 -11
  22. shepherd_core/data_models/task/testbed_tasks.py +8 -8
  23. shepherd_core/data_models/testbed/cape.py +7 -6
  24. shepherd_core/data_models/testbed/gpio.py +8 -7
  25. shepherd_core/data_models/testbed/mcu.py +8 -7
  26. shepherd_core/data_models/testbed/observer.py +9 -7
  27. shepherd_core/data_models/testbed/target.py +9 -7
  28. shepherd_core/data_models/testbed/testbed.py +11 -10
  29. shepherd_core/decoder_waveform/uart.py +5 -5
  30. shepherd_core/fw_tools/converter.py +4 -3
  31. shepherd_core/fw_tools/patcher.py +14 -15
  32. shepherd_core/fw_tools/validation.py +3 -2
  33. shepherd_core/inventory/__init__.py +4 -4
  34. shepherd_core/inventory/python.py +1 -1
  35. shepherd_core/inventory/system.py +11 -8
  36. shepherd_core/inventory/target.py +3 -3
  37. shepherd_core/logger.py +2 -2
  38. shepherd_core/reader.py +43 -43
  39. shepherd_core/testbed_client/client_abc_fix.py +20 -13
  40. shepherd_core/testbed_client/client_web.py +18 -11
  41. shepherd_core/testbed_client/fixtures.py +24 -44
  42. shepherd_core/testbed_client/user_model.py +6 -5
  43. shepherd_core/version.py +1 -1
  44. shepherd_core/vsource/target_model.py +3 -3
  45. shepherd_core/vsource/virtual_converter_model.py +3 -3
  46. shepherd_core/vsource/virtual_harvester_model.py +7 -9
  47. shepherd_core/vsource/virtual_harvester_simulation.py +6 -5
  48. shepherd_core/vsource/virtual_source_model.py +6 -5
  49. shepherd_core/vsource/virtual_source_simulation.py +7 -6
  50. shepherd_core/writer.py +33 -34
  51. {shepherd_core-2025.2.2.dist-info → shepherd_core-2025.4.2.dist-info}/METADATA +3 -4
  52. shepherd_core-2025.4.2.dist-info/RECORD +81 -0
  53. {shepherd_core-2025.2.2.dist-info → shepherd_core-2025.4.2.dist-info}/WHEEL +1 -1
  54. shepherd_core-2025.2.2.dist-info/RECORD +0 -81
  55. {shepherd_core-2025.2.2.dist-info → shepherd_core-2025.4.2.dist-info}/top_level.txt +0 -0
  56. {shepherd_core-2025.2.2.dist-info → shepherd_core-2025.4.2.dist-info}/zip-safe +0 -0
@@ -1,14 +1,12 @@
1
1
  """Current implementation of a file-based database."""
2
2
 
3
3
  import copy
4
- import os
5
4
  import pickle
5
+ from collections.abc import Mapping
6
6
  from datetime import datetime
7
7
  from datetime import timedelta
8
8
  from pathlib import Path
9
9
  from typing import Any
10
- from typing import Dict
11
- from typing import List
12
10
  from typing import Optional
13
11
  from typing import Union
14
12
 
@@ -16,10 +14,11 @@ import yaml
16
14
  from pydantic import validate_call
17
15
  from typing_extensions import Self
18
16
 
19
- from ..data_models.base.timezone import local_now
20
- from ..data_models.base.timezone import local_tz
21
- from ..data_models.base.wrapper import Wrapper
22
- from ..logger import logger
17
+ from shepherd_core.data_models.base.timezone import local_now
18
+ from shepherd_core.data_models.base.timezone import local_tz
19
+ from shepherd_core.data_models.base.wrapper import Wrapper
20
+ from shepherd_core.logger import logger
21
+
23
22
  from .cache_path import cache_user_path
24
23
 
25
24
  # Proposed field-name:
@@ -35,8 +34,8 @@ class Fixture:
35
34
 
36
35
  def __init__(self, model_type: str) -> None:
37
36
  self.model_type: str = model_type.lower()
38
- self.elements_by_name: Dict[str, dict] = {}
39
- self.elements_by_id: Dict[int, dict] = {}
37
+ self.elements_by_name: dict[str, dict] = {}
38
+ self.elements_by_id: dict[int, dict] = {}
40
39
  # Iterator reset
41
40
  self._iter_index: int = 0
42
41
  self._iter_list: list = list(self.elements_by_name.values())
@@ -44,25 +43,26 @@ class Fixture:
44
43
  def insert(self, data: Wrapper) -> None:
45
44
  # ⤷ TODO: could get easier
46
45
  # - when not model_name but class used
47
- # - use doubleref name->id->data (safes RAM)
46
+ # - use doubleref name->id->data (saves RAM)
48
47
  if data.datatype.lower() != self.model_type.lower():
49
48
  return
50
49
  if "name" not in data.parameters:
51
50
  return
52
51
  name = str(data.parameters["name"]).lower()
53
52
  _id = data.parameters["id"]
54
- data = data.parameters
55
- self.elements_by_name[name] = data
56
- self.elements_by_id[_id] = data
53
+ data_model = data.parameters
54
+ self.elements_by_name[name] = data_model
55
+ self.elements_by_id[_id] = data_model
57
56
  # update iterator
58
- self._iter_list: list = list(self.elements_by_name.values())
57
+ self._iter_list = list(self.elements_by_name.values())
59
58
 
60
59
  def __getitem__(self, key: Union[str, int]) -> dict:
61
60
  if isinstance(key, str):
62
61
  key = key.lower()
63
62
  if key in self.elements_by_name:
64
63
  return self.elements_by_name[key]
65
- key = int(key) if key.isdigit() else None
64
+ if key.isdigit():
65
+ key = int(key)
66
66
  if key in self.elements_by_id:
67
67
  return self.elements_by_id[int(key)]
68
68
  msg = f"{self.model_type} '{key}' not found!"
@@ -86,7 +86,9 @@ class Fixture:
86
86
  def refs(self) -> dict:
87
87
  return {_i["id"]: _i["name"] for _i in self.elements_by_id.values()}
88
88
 
89
- def inheritance(self, values: dict, chain: Optional[list] = None) -> (dict, list):
89
+ def inheritance(
90
+ self, values: dict[str, Any], chain: Optional[list[str]] = None
91
+ ) -> tuple[dict[str, Any], list[str]]:
90
92
  if chain is None:
91
93
  chain = []
92
94
  values = copy.copy(values)
@@ -144,7 +146,7 @@ class Fixture:
144
146
  return values, chain
145
147
 
146
148
  @staticmethod
147
- def fill_model(model: dict, base: dict) -> dict:
149
+ def fill_model(model: Mapping, base: dict) -> dict:
148
150
  base = copy.copy(base)
149
151
  for key, value in model.items():
150
152
  # keep previous entries
@@ -182,7 +184,7 @@ class Fixtures:
182
184
  self.file_path = Path(__file__).parent.parent.resolve() / "data_models"
183
185
  else:
184
186
  self.file_path = file_path
185
- self.components: Dict[str, Fixture] = {}
187
+ self.components: dict[str, Fixture] = {}
186
188
  cache_file = cache_user_path / "fixtures.pickle"
187
189
  sheep_detect = Path("/lib/firmware/am335x-pru0-fw").exists()
188
190
 
@@ -201,7 +203,10 @@ class Fixtures:
201
203
  if self.file_path.is_file():
202
204
  files = [self.file_path]
203
205
  elif self.file_path.is_dir():
204
- files = get_files(self.file_path, self.suffix)
206
+ files = list(
207
+ self.file_path.glob("**/*" + self.suffix)
208
+ ) # for py>=3.12: case_sensitive=False
209
+ logger.debug(" -> got %s %s-files", len(files), self.suffix)
205
210
  else:
206
211
  raise ValueError("Path must either be file or directory (or empty)")
207
212
 
@@ -245,28 +250,3 @@ class Fixtures:
245
250
  def to_file(file: Path) -> None:
246
251
  msg = f"TODO (val={file})"
247
252
  raise NotImplementedError(msg)
248
-
249
-
250
- def get_files(start_path: Path, suffix: str, recursion_depth: int = 0) -> List[Path]:
251
- """Generate a recursive list of all files in a directory."""
252
- if recursion_depth == 0:
253
- suffix = suffix.lower().split(".")[-1]
254
- dir_items = os.scandir(start_path)
255
- recursion_depth += 1
256
- files = []
257
-
258
- for item in dir_items:
259
- if item.is_dir():
260
- files += get_files(Path(item.path), suffix, recursion_depth)
261
- continue
262
-
263
- item_name = str(item.name).lower()
264
- item_ext = item_name.split(".")[-1]
265
- if item_ext == suffix and item_ext != item_name:
266
- files.append(Path(item.path))
267
- if not suffix and item_ext == item_name:
268
- files.append(Path(item.path))
269
-
270
- if recursion_depth == 1 and len(files) > 0:
271
- logger.debug(" -> got %s files with the suffix '%s'", len(files), suffix)
272
- return files
@@ -2,6 +2,8 @@
2
2
 
3
3
  import secrets
4
4
  from hashlib import pbkdf2_hmac
5
+ from typing import Annotated
6
+ from typing import Any
5
7
  from typing import Optional
6
8
  from typing import Union
7
9
  from uuid import uuid4
@@ -14,11 +16,10 @@ from pydantic import SecretStr
14
16
  from pydantic import StringConstraints
15
17
  from pydantic import model_validator
16
18
  from pydantic import validate_call
17
- from typing_extensions import Annotated
18
19
 
19
- from ..data_models.base.content import NameStr
20
- from ..data_models.base.content import SafeStr
21
- from ..data_models.base.shepherd import ShpModel
20
+ from shepherd_core.data_models.base.content import NameStr
21
+ from shepherd_core.data_models.base.content import SafeStr
22
+ from shepherd_core.data_models.base.shepherd import ShpModel
22
23
 
23
24
 
24
25
  @validate_call
@@ -63,7 +64,7 @@ class User(ShpModel):
63
64
 
64
65
  @model_validator(mode="before")
65
66
  @classmethod
66
- def query_database(cls, values: dict) -> dict:
67
+ def query_database(cls, values: dict[str, Any]) -> dict[str, Any]:
67
68
  # TODO:
68
69
 
69
70
  # post correction
shepherd_core/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Separated string avoids circular imports."""
2
2
 
3
- version: str = "2025.02.2"
3
+ version: str = "2025.04.2"
@@ -79,9 +79,9 @@ class DiodeTarget(TargetABC):
79
79
 
80
80
  def step(self, voltage_uV: int, *, pwr_good: bool) -> float:
81
81
  if pwr_good or not self.ctrl:
82
- V_CC = voltage_uV * 1e-6
83
- V_D = V_CC / 2
84
- I_R = I_D = 0
82
+ V_CC: float = voltage_uV * 1e-6
83
+ V_D: float = V_CC / 2
84
+ I_R = I_D = 0.0
85
85
  # there is no direct formular, but this iteration converges fast
86
86
  for _ in range(10):
87
87
  # low voltages tend to produce log(x<0)=err
@@ -17,9 +17,9 @@ Compromises:
17
17
  import math
18
18
  from typing import Optional
19
19
 
20
- from ..data_models import CalibrationEmulator
21
- from ..data_models.content.virtual_source import LUT_SIZE
22
- from ..data_models.content.virtual_source import ConverterPRUConfig
20
+ from shepherd_core.data_models import CalibrationEmulator
21
+ from shepherd_core.data_models.content.virtual_source import LUT_SIZE
22
+ from shepherd_core.data_models.content.virtual_source import ConverterPRUConfig
23
23
 
24
24
 
25
25
  class PruCalibration:
@@ -16,10 +16,8 @@ Compromises:
16
16
 
17
17
  """
18
18
 
19
- from typing import Tuple
20
-
21
- from ..data_models.content.virtual_harvester import HarvesterPRUConfig
22
- from ..logger import logger
19
+ from shepherd_core.data_models.content.virtual_harvester import HarvesterPRUConfig
20
+ from shepherd_core.logger import logger
23
21
 
24
22
 
25
23
  class VirtualHarvesterModel:
@@ -94,7 +92,7 @@ class VirtualHarvesterModel:
94
92
  self.voltage_nxt: int = 0
95
93
  self.current_nxt: int = 0
96
94
 
97
- def ivcurve_sample(self, _voltage_uV: int, _current_nA: int) -> Tuple[int, int]:
95
+ def ivcurve_sample(self, _voltage_uV: int, _current_nA: int) -> tuple[int, int]:
98
96
  if self._cfg.window_size <= 1:
99
97
  return _voltage_uV, _current_nA
100
98
  if self._cfg.algorithm >= self.HRV_MPPT_OPT:
@@ -108,7 +106,7 @@ class VirtualHarvesterModel:
108
106
  # next line is only implied in C
109
107
  return _voltage_uV, _current_nA
110
108
 
111
- def ivcurve_2_cv(self, _voltage_uV: int, _current_nA: int) -> Tuple[int, int]:
109
+ def ivcurve_2_cv(self, _voltage_uV: int, _current_nA: int) -> tuple[int, int]:
112
110
  compare_now = _voltage_uV < self.voltage_set_uV
113
111
  step_size_now = abs(_voltage_uV - self.voltage_last)
114
112
  distance_now = abs(_voltage_uV - self.voltage_set_uV)
@@ -146,7 +144,7 @@ class VirtualHarvesterModel:
146
144
  self.compare_last = compare_now
147
145
  return self.voltage_hold, self.current_hold
148
146
 
149
- def ivcurve_2_mppt_voc(self, _voltage_uV: int, _current_nA: int) -> Tuple[int, int]:
147
+ def ivcurve_2_mppt_voc(self, _voltage_uV: int, _current_nA: int) -> tuple[int, int]:
150
148
  self.interval_step = self.interval_step + 1
151
149
  if self.interval_step >= self._cfg.interval_n:
152
150
  self.interval_step = 0
@@ -176,7 +174,7 @@ class VirtualHarvesterModel:
176
174
 
177
175
  return _voltage_uV, _current_nA
178
176
 
179
- def ivcurve_2_mppt_po(self, _voltage_uV: int, _current_nA: int) -> Tuple[int, int]:
177
+ def ivcurve_2_mppt_po(self, _voltage_uV: int, _current_nA: int) -> tuple[int, int]:
180
178
  self.interval_step = self.interval_step + 1
181
179
  if self.interval_step >= self._cfg.interval_n:
182
180
  self.interval_step = 0
@@ -220,7 +218,7 @@ class VirtualHarvesterModel:
220
218
 
221
219
  return _voltage_uV, _current_nA
222
220
 
223
- def ivcurve_2_mppt_opt(self, _voltage_uV: int, _current_nA: int) -> Tuple[int, int]:
221
+ def ivcurve_2_mppt_opt(self, _voltage_uV: int, _current_nA: int) -> tuple[int, int]:
224
222
  self.age_now += 1
225
223
  self.age_nxt += 1
226
224
 
@@ -13,11 +13,12 @@ from typing import Optional
13
13
 
14
14
  from tqdm import tqdm
15
15
 
16
- from .. import CalibrationHarvester
17
- from .. import Reader
18
- from .. import Writer
19
- from ..data_models.content.virtual_harvester import HarvesterPRUConfig
20
- from ..data_models.content.virtual_harvester import VirtualHarvesterConfig
16
+ from shepherd_core.data_models.base.calibration import CalibrationHarvester
17
+ from shepherd_core.data_models.content.virtual_harvester import HarvesterPRUConfig
18
+ from shepherd_core.data_models.content.virtual_harvester import VirtualHarvesterConfig
19
+ from shepherd_core.reader import Reader
20
+ from shepherd_core.writer import Writer
21
+
21
22
  from .virtual_harvester_model import VirtualHarvesterModel
22
23
 
23
24
 
@@ -12,11 +12,12 @@ NOTE: DO NOT OPTIMIZE -> stay close to original code-base
12
12
 
13
13
  from typing import Optional
14
14
 
15
- from ..data_models import CalibrationEmulator
16
- from ..data_models import EnergyDType
17
- from ..data_models import VirtualSourceConfig
18
- from ..data_models.content.virtual_harvester import HarvesterPRUConfig
19
- from ..data_models.content.virtual_source import ConverterPRUConfig
15
+ from shepherd_core.data_models.base.calibration import CalibrationEmulator
16
+ from shepherd_core.data_models.content.energy_environment import EnergyDType
17
+ from shepherd_core.data_models.content.virtual_harvester import HarvesterPRUConfig
18
+ from shepherd_core.data_models.content.virtual_source import ConverterPRUConfig
19
+ from shepherd_core.data_models.content.virtual_source import VirtualSourceConfig
20
+
20
21
  from .virtual_converter_model import PruCalibration
21
22
  from .virtual_converter_model import VirtualConverterModel
22
23
  from .virtual_harvester_model import VirtualHarvesterModel
@@ -14,13 +14,14 @@ from typing import Optional
14
14
  import numpy as np
15
15
  from tqdm import tqdm
16
16
 
17
- from .. import CalibrationEmulator
18
- from .. import Reader
19
- from .. import Writer
20
- from ..data_models import VirtualSourceConfig
21
- from ..logger import logger
22
- from . import VirtualSourceModel
17
+ from shepherd_core.data_models.base.calibration import CalibrationEmulator
18
+ from shepherd_core.data_models.content.virtual_source import VirtualSourceConfig
19
+ from shepherd_core.logger import logger
20
+ from shepherd_core.reader import Reader
21
+ from shepherd_core.writer import Writer
22
+
23
23
  from .target_model import TargetABC
24
+ from .virtual_source_model import VirtualSourceModel
24
25
 
25
26
 
26
27
  def simulate_source(
shepherd_core/writer.py CHANGED
@@ -3,13 +3,13 @@
3
3
  import logging
4
4
  import math
5
5
  import pathlib
6
+ from collections.abc import Mapping
6
7
  from datetime import timedelta
7
8
  from itertools import product
8
9
  from pathlib import Path
9
10
  from types import TracebackType
10
11
  from typing import Any
11
12
  from typing import Optional
12
- from typing import Type
13
13
  from typing import Union
14
14
 
15
15
  import h5py
@@ -17,11 +17,10 @@ import numpy as np
17
17
  import yaml
18
18
  from pydantic import validate_call
19
19
  from typing_extensions import Self
20
- from yaml import Dumper
20
+ from yaml import Node
21
21
  from yaml import SafeDumper
22
- from yaml import ScalarNode
23
22
 
24
- from .commons import samplerate_sps_default
23
+ from .commons import SAMPLERATE_SPS_DEFAULT
25
24
  from .data_models.base.calibration import CalibrationEmulator as CalEmu
26
25
  from .data_models.base.calibration import CalibrationHarvester as CalHrv
27
26
  from .data_models.base.calibration import CalibrationSeries as CalSeries
@@ -33,13 +32,13 @@ from .reader import Reader
33
32
 
34
33
  # copy of core/models/base/shepherd - needed also here
35
34
  def path2str(
36
- dumper: Dumper, data: Union[pathlib.Path, pathlib.WindowsPath, pathlib.PosixPath]
37
- ) -> ScalarNode:
35
+ dumper: SafeDumper, data: Union[pathlib.Path, pathlib.WindowsPath, pathlib.PosixPath]
36
+ ) -> Node:
38
37
  """Add a yaml-representation for a specific datatype."""
39
38
  return dumper.represent_scalar("tag:yaml.org,2002:str", str(data.as_posix()))
40
39
 
41
40
 
42
- def time2int(dumper: Dumper, data: timedelta) -> ScalarNode:
41
+ def time2int(dumper: SafeDumper, data: timedelta) -> Node:
43
42
  """Add a yaml-representation for a specific datatype."""
44
43
  return dumper.represent_scalar("tag:yaml.org,2002:int", str(int(data.total_seconds())))
45
44
 
@@ -92,11 +91,10 @@ class Writer(Reader):
92
91
 
93
92
  """
94
93
 
95
- comp_default: int = 1
96
- mode_default: str = "harvester"
97
- datatype_default: EnergyDType = EnergyDType.ivsample
94
+ MODE_DEFAULT: str = "harvester"
95
+ DATATYPE_DEFAULT: EnergyDType = EnergyDType.ivsample
98
96
 
99
- _chunk_shape: tuple = (Reader.samples_per_buffer,)
97
+ _CHUNK_SHAPE: tuple = (Reader.BUFFER_SAMPLES_N,)
100
98
 
101
99
  @validate_call
102
100
  def __init__(
@@ -110,7 +108,7 @@ class Writer(Reader):
110
108
  *,
111
109
  modify_existing: bool = False,
112
110
  force_overwrite: bool = False,
113
- verbose: Optional[bool] = True,
111
+ verbose: bool = True,
114
112
  ) -> None:
115
113
  self._modify = modify_existing
116
114
  if compression is not None:
@@ -123,41 +121,42 @@ class Writer(Reader):
123
121
  # -> logger gets configured in reader()
124
122
 
125
123
  if self._modify or force_overwrite or not file_path.exists():
126
- self.file_path: Path = file_path.resolve()
127
- self._logger.info("Storing data to '%s'", self.file_path)
124
+ file_path = file_path.resolve()
125
+ self._logger.info("Storing data to '%s'", file_path)
128
126
  elif file_path.exists() and not file_path.is_file():
129
127
  msg = f"Path is not a file ({file_path})"
130
128
  raise TypeError(msg)
131
129
  else:
132
130
  base_dir = file_path.resolve().parents[0]
133
- self.file_path = unique_path(base_dir / file_path.stem, file_path.suffix)
131
+ file_path_new = unique_path(base_dir / file_path.stem, file_path.suffix)
134
132
  self._logger.warning(
135
133
  "File '%s' already exists -> storing under '%s' instead",
136
134
  file_path,
137
- self.file_path.name,
135
+ file_path_new.name,
138
136
  )
137
+ file_path = file_path_new
139
138
 
140
139
  # open file
141
140
  if self._modify:
142
- self.h5file = h5py.File(self.file_path, "r+") # = rw
141
+ self.h5file = h5py.File(file_path, "r+") # = rw
143
142
  else:
144
- if not self.file_path.parent.exists():
145
- self.file_path.parent.mkdir(parents=True)
146
- self.h5file = h5py.File(self.file_path, "w")
143
+ if not file_path.parent.exists():
144
+ file_path.parent.mkdir(parents=True)
145
+ self.h5file = h5py.File(file_path, "w")
147
146
  # ⤷ write, truncate if exist
148
147
  self._create_skeleton()
149
148
 
150
149
  # Handle Mode
151
- if isinstance(mode, str) and mode not in self.mode_dtype_dict:
152
- msg = f"Can't handle mode '{mode}' (choose one of {self.mode_dtype_dict})"
150
+ if isinstance(mode, str) and mode not in self.MODE_TO_DTYPE:
151
+ msg = f"Can't handle mode '{mode}' (choose one of {self.MODE_TO_DTYPE})"
153
152
  raise ValueError(msg)
154
153
 
155
154
  if mode is not None:
156
155
  self.h5file.attrs["mode"] = mode
157
156
  if "mode" not in self.h5file.attrs:
158
- self.h5file.attrs["mode"] = self.mode_default
157
+ self.h5file.attrs["mode"] = self.MODE_DEFAULT
159
158
 
160
- _dtypes = self.mode_dtype_dict[self.get_mode()]
159
+ _dtypes = self.MODE_TO_DTYPE[self.get_mode()]
161
160
 
162
161
  # Handle Datatype
163
162
  if isinstance(datatype, str):
@@ -169,7 +168,7 @@ class Writer(Reader):
169
168
  if isinstance(datatype, EnergyDType):
170
169
  self.h5file["data"].attrs["datatype"] = datatype.name
171
170
  if "datatype" not in self.h5file["data"].attrs:
172
- self.h5file["data"].attrs["datatype"] = self.datatype_default.name
171
+ self.h5file["data"].attrs["datatype"] = self.DATATYPE_DEFAULT.name
173
172
  if self.get_datatype() not in _dtypes:
174
173
  msg = (
175
174
  f"Can't handle value '{self.get_datatype()}' of datatype (choose one of {_dtypes})"
@@ -203,7 +202,7 @@ class Writer(Reader):
203
202
  settings = list(self.h5file.id.get_access_plist().get_cache())
204
203
  self._logger.debug("H5Py Cache_setting=%s (_mdc, _nslots, _nbytes, _w0)", settings)
205
204
 
206
- super().__init__(file_path=None, verbose=verbose)
205
+ super().__init__(file_path=file_path, verbose=verbose)
207
206
 
208
207
  def __enter__(self) -> Self:
209
208
  super().__enter__()
@@ -211,7 +210,7 @@ class Writer(Reader):
211
210
 
212
211
  def __exit__(
213
212
  self,
214
- typ: Optional[Type[BaseException]] = None,
213
+ typ: Optional[type[BaseException]] = None,
215
214
  exc: Optional[BaseException] = None,
216
215
  tb: Optional[TracebackType] = None,
217
216
  extra_arg: int = 0,
@@ -250,7 +249,7 @@ class Writer(Reader):
250
249
  (0,),
251
250
  dtype="u8",
252
251
  maxshape=(None,),
253
- chunks=self._chunk_shape,
252
+ chunks=self._CHUNK_SHAPE,
254
253
  compression=self._compression,
255
254
  )
256
255
  grp_data["time"].attrs["unit"] = "s"
@@ -261,7 +260,7 @@ class Writer(Reader):
261
260
  (0,),
262
261
  dtype="u4",
263
262
  maxshape=(None,),
264
- chunks=self._chunk_shape,
263
+ chunks=self._CHUNK_SHAPE,
265
264
  compression=self._compression,
266
265
  )
267
266
  grp_data["current"].attrs["unit"] = "A"
@@ -272,7 +271,7 @@ class Writer(Reader):
272
271
  (0,),
273
272
  dtype="u4",
274
273
  maxshape=(None,),
275
- chunks=self._chunk_shape,
274
+ chunks=self._CHUNK_SHAPE,
276
275
  compression=self._compression,
277
276
  )
278
277
  grp_data["voltage"].attrs["unit"] = "V"
@@ -346,10 +345,10 @@ class Writer(Reader):
346
345
  def _align(self) -> None:
347
346
  """Align datasets with buffer-size of shepherd."""
348
347
  self._refresh_file_stats()
349
- n_buff = self.ds_voltage.size / self.samples_per_buffer
350
- size_new = int(math.floor(n_buff) * self.samples_per_buffer)
348
+ n_buff = self.ds_voltage.size / self.BUFFER_SAMPLES_N
349
+ size_new = int(math.floor(n_buff) * self.BUFFER_SAMPLES_N)
351
350
  if size_new < self.ds_voltage.size:
352
- if self.samplerate_sps != samplerate_sps_default:
351
+ if self.samplerate_sps != SAMPLERATE_SPS_DEFAULT:
353
352
  self._logger.debug("skipped alignment due to altered samplerate")
354
353
  return
355
354
  self._logger.info(
@@ -364,7 +363,7 @@ class Writer(Reader):
364
363
  """Conveniently store relevant key-value data (attribute) in H5-structure."""
365
364
  self.h5file.attrs.__setitem__(key, item)
366
365
 
367
- def store_config(self, data: dict) -> None:
366
+ def store_config(self, data: Mapping) -> None:
368
367
  """Get a better self-describing Output-File.
369
368
 
370
369
  TODO: use data-model?
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: shepherd_core
3
- Version: 2025.2.2
3
+ Version: 2025.4.2
4
4
  Summary: Programming- and CLI-Interface for the h5-dataformat of the Shepherd-Testbed
5
5
  Author-email: Ingmar Splitt <ingmar.splitt@tu-dresden.de>
6
6
  Maintainer-email: Ingmar Splitt <ingmar.splitt@tu-dresden.de>
@@ -18,7 +18,6 @@ Classifier: Development Status :: 5 - Production/Stable
18
18
  Classifier: Intended Audience :: Developers
19
19
  Classifier: Intended Audience :: Information Technology
20
20
  Classifier: Intended Audience :: Science/Research
21
- Classifier: Programming Language :: Python :: 3.8
22
21
  Classifier: Programming Language :: Python :: 3.9
23
22
  Classifier: Programming Language :: Python :: 3.10
24
23
  Classifier: Programming Language :: Python :: 3.11
@@ -27,7 +26,7 @@ Classifier: Programming Language :: Python :: 3.13
27
26
  Classifier: License :: OSI Approved :: MIT License
28
27
  Classifier: Operating System :: OS Independent
29
28
  Classifier: Natural Language :: English
30
- Requires-Python: >=3.8
29
+ Requires-Python: >=3.9
31
30
  Description-Content-Type: text/markdown
32
31
  Requires-Dist: h5py
33
32
  Requires-Dist: numpy
@@ -0,0 +1,81 @@
1
+ shepherd_core/__init__.py,sha256=fCld2mcl0y0h6kRyPal3DP-sWXnKl_0aYWYBdg4QuUk,1270
2
+ shepherd_core/calibration_hw_def.py,sha256=aL94bA1Sf14L5A3PLdVvQVYtGi28S4NUWA65wbim8bw,2895
3
+ shepherd_core/commons.py,sha256=rTxtndtiJ4cOHYRPRbdZdqp6T90CKFFN-I-YAFzhm4Q,200
4
+ shepherd_core/logger.py,sha256=i8j3alm8icAx_h1_IZ6SgVgC9W5J7S-bDc31mPDWl-w,1812
5
+ shepherd_core/reader.py,sha256=Xu_MSADsR4Xzh8YH-N50mKIHZlhPyFigv8umQSsYsJ0,28320
6
+ shepherd_core/version.py,sha256=dd_uzGz0uQwh1pttFJSjXzIKovnBzMAXJGgpRa5QR34,76
7
+ shepherd_core/writer.py,sha256=HRiCnJRM4hIBT6UBZJZ5ZOZ79OyBpBN4a2kX-XmOYRE,14574
8
+ shepherd_core/data_models/__init__.py,sha256=bnHSP_HBOYm4WuoiHs_vyiRsWlvkyDjsarMNfKedN-Q,1836
9
+ shepherd_core/data_models/readme.md,sha256=DHPVmkWqDksWomRHRTVWVHy9wXF9oMJrITgKs4Pnz2g,2494
10
+ shepherd_core/data_models/virtual_source_doc.txt,sha256=KizMcfGKj7BnHIbaJHT7KeTF01SV__UXv01qV_DGHSs,6057
11
+ shepherd_core/data_models/base/__init__.py,sha256=PSJ6acWViqBm0Eiom8DIgKfFVrp5lzYr8OsDvP79vwI,94
12
+ shepherd_core/data_models/base/cal_measurement.py,sha256=c-vjACNxsQY8LU2Msw0COrsTY-8pToJ5ZWkOztJ9tVY,3380
13
+ shepherd_core/data_models/base/calibration.py,sha256=oUTfY6iUWUbBbOw5aMCRkdEfHzIV8aUrhwqek0QzPJM,10849
14
+ shepherd_core/data_models/base/content.py,sha256=t3hw5Aes4-tVrWGOMSBGpcD642VM1RAWIo9jtbXifd0,2452
15
+ shepherd_core/data_models/base/shepherd.py,sha256=P1xW10_vIbGNyQ3Ary67vyovw9kmDtXhN-yPFYR-0Is,6085
16
+ shepherd_core/data_models/base/timezone.py,sha256=2T6E46hJ1DAvmqKfu6uIgCK3RSoAKjGXRyzYNaqKyjY,665
17
+ shepherd_core/data_models/base/wrapper.py,sha256=7QwvI30GuORH7WmyGLnRMsZ3xkRRkXIAvZ-pYRAL-WI,755
18
+ shepherd_core/data_models/content/__init__.py,sha256=69aiNG0h5t1OF7HsLg_ke5eaQKsKyMK8o6Kfaby5vlY,525
19
+ shepherd_core/data_models/content/_external_fixtures.yaml,sha256=0CH7YSWT_hzL-jcg4JjgN9ryQOzbS8S66_pd6GbMnHw,12259
20
+ shepherd_core/data_models/content/energy_environment.py,sha256=emlVFbcPA0-_jgpYI0Xwebm81_gWLYD8Tex0XVtJr4c,1618
21
+ shepherd_core/data_models/content/energy_environment_fixture.yaml,sha256=UBXTdGT7MK98zx5w_RBCu-f9uNCKxRgiFBQFbmDUxPc,1301
22
+ shepherd_core/data_models/content/firmware.py,sha256=KLByo1GuUMJ8ZbM6WccB8IJArorNetBxqOpuUb2_QIE,5926
23
+ shepherd_core/data_models/content/firmware_datatype.py,sha256=XPU9LOoT3h5qFOlE8WU0vAkw-vymNxzor9kVFyEqsWg,255
24
+ shepherd_core/data_models/content/virtual_harvester.py,sha256=PQ3CykoMgMoklmb_vypRdU512BuVhV-KjPTmBPKTzCM,11227
25
+ shepherd_core/data_models/content/virtual_harvester_fixture.yaml,sha256=LZe5ue1xYhXZwB3a32sva-L4uKhkQA5AtG9JzW4B2hQ,4564
26
+ shepherd_core/data_models/content/virtual_source.py,sha256=ftCcWCYKHw854lGwuxLf_ZIOY6O9vLixHHC77BqcSQA,15441
27
+ shepherd_core/data_models/content/virtual_source_fixture.yaml,sha256=1o-31mGgn7eyCNidKoOUp9vZh3K4Al0kJgmz54Q2DAE,11191
28
+ shepherd_core/data_models/experiment/__init__.py,sha256=lorsx0M-JWPIrt_UZfexsLwaITv5slFb3krBOt0idm8,618
29
+ shepherd_core/data_models/experiment/experiment.py,sha256=h24HaZltat0mKbOT1h9O58B5noTIUcGQbBIzmPi4oo8,4232
30
+ shepherd_core/data_models/experiment/observer_features.py,sha256=u6nJthpluZrxw9ym_rvkTrlOQTKlc1TY5b1HPqbO4Jg,5185
31
+ shepherd_core/data_models/experiment/target_config.py,sha256=ZODI4_MwBLxKTQ1DwFrLZAuKprxDiLyg5-JYOQxXp_w,3808
32
+ shepherd_core/data_models/task/__init__.py,sha256=3wTIJ1L5wcRDLR6JRaT0bNh7vABckSoOd5erPwhhCGI,3331
33
+ shepherd_core/data_models/task/emulation.py,sha256=PfSWci9V47EWpJRTKyHBxQZodHs3yQXMce0Mslm_-Ns,6665
34
+ shepherd_core/data_models/task/firmware_mod.py,sha256=WLmevU9-Q5QnyANbkrSCdFbnVuSi8N8fXdQ2l_Um9e8,3034
35
+ shepherd_core/data_models/task/harvest.py,sha256=rQ5Aq40m4WHpEFfiwn309GjLIPu4aKzF4Fvbo4j7PLs,3470
36
+ shepherd_core/data_models/task/observer_tasks.py,sha256=icUPtoIqic8IpE0U9Fc7btMj4CpYx1PoAu_wAqqZkU8,3608
37
+ shepherd_core/data_models/task/programming.py,sha256=6UHb8rT1M3bglt_3fHql6MsHE8sTvi9ii7Uhug5O4PM,2552
38
+ shepherd_core/data_models/task/testbed_tasks.py,sha256=J3aOSVVG1KvEI_83j0TK0gc5rN_v_T-URFRhrEs8czY,2176
39
+ shepherd_core/data_models/testbed/__init__.py,sha256=t9nwml5pbu7ZWghimOyZ8ujMIgnRgFkl23pNb5d_KdU,581
40
+ shepherd_core/data_models/testbed/cape.py,sha256=vfS05D0rC1-_wMiHeLw69VE9PxXC6PHl9ndtrv219_k,1396
41
+ shepherd_core/data_models/testbed/cape_fixture.yaml,sha256=uwZxe6hsqvofn5tzg4sffjbVtTVUkextL1GCri_z2A4,2197
42
+ shepherd_core/data_models/testbed/gpio.py,sha256=pOY_7Zs32sRGuqJZrZDrjvr69QkFXC8iXgav7kk2heY,2334
43
+ shepherd_core/data_models/testbed/gpio_fixture.yaml,sha256=yXvoXAau2hancKi2yg1xIkErPWQa6gIxNUG3y8JuF9Y,3076
44
+ shepherd_core/data_models/testbed/mcu.py,sha256=fuq2AWbVFbbzPRPCgIeMNFhJhVNCIsmpjFagnOXkjbY,1514
45
+ shepherd_core/data_models/testbed/mcu_fixture.yaml,sha256=lRZMLs27cTeERSFGkbMt5xgxbn11Gh9G1mQqOZK136I,522
46
+ shepherd_core/data_models/testbed/observer.py,sha256=RO7i9TmHcxx69P-EHN59M9MAJetoHZg5qJUD5SEtrcg,3386
47
+ shepherd_core/data_models/testbed/observer_fixture.yaml,sha256=jqAatTebWrShXBlhqkCUQIrtVqEjl7RVDR9mosS2LJQ,4807
48
+ shepherd_core/data_models/testbed/target.py,sha256=W7U9nCz_xWMRCCgaGuqOV4dGi4ZeQ1SAp7W2FqCkanU,1978
49
+ shepherd_core/data_models/testbed/target_fixture.old1,sha256=ivH9uTgC2Z4L_J4KDHAyIHZnB7iy9EUu1yM3M0s1lQQ,3675
50
+ shepherd_core/data_models/testbed/target_fixture.yaml,sha256=LyOJa7yH17tHIGC25jlkLJ_DKnbFSoGhD-6Uh6q_HqQ,4132
51
+ shepherd_core/data_models/testbed/testbed.py,sha256=K6llqMTn-AU18XXRXdhzibIx-n4CQjtrXnLwqxOQesI,3828
52
+ shepherd_core/data_models/testbed/testbed_fixture.yaml,sha256=LaaU8mXLQboYWYNPpA3CWmMPy2w6T6cve6gLgDaA3l0,717
53
+ shepherd_core/decoder_waveform/__init__.py,sha256=-ohGz0fA2tKxUJk4FAQXKtI93d6YGdy0CrkdhOod1QU,120
54
+ shepherd_core/decoder_waveform/uart.py,sha256=vuw9fKwZb_1mMtQ5fdiZN8Cr1YWSgYKar9FIMK8Bogo,11084
55
+ shepherd_core/fw_tools/__init__.py,sha256=D9GGj9TzLWZfPjG_iV2BsF-Q1TGTYTgEzWTUI5ReVAA,2090
56
+ shepherd_core/fw_tools/converter.py,sha256=F06iJlYYG2w-OCKayrgeO870lOX87xWVTYTAi1X3UPU,3604
57
+ shepherd_core/fw_tools/converter_elf.py,sha256=GQDVqIqMW4twNMvZIV3sowFMezhs2TN-IYREjRP7Xt4,1089
58
+ shepherd_core/fw_tools/patcher.py,sha256=ISbnA2ZLTIV2JZh_b3GorNDGNrLaBN0fFnFnyh-smNU,3946
59
+ shepherd_core/fw_tools/validation.py,sha256=GWm_T3xmRde-jQ8cf27kPosBTFTK3E2M2LSb24pT2g8,4732
60
+ shepherd_core/inventory/__init__.py,sha256=yQxP55yV61xXWfZSSzekQQYopPZCspFpHSyG7VTqtpg,3819
61
+ shepherd_core/inventory/python.py,sha256=pvugNgLZaDllIXX_KiuvpcWUWlJtD2IUKYDRjcTGQss,1262
62
+ shepherd_core/inventory/system.py,sha256=qFTRVXClQZoCeV2KM8RAbst8XL7IaJwwV0pGHPZSJs4,3325
63
+ shepherd_core/inventory/target.py,sha256=zLUNQs2FE7jMDsiRtbeAwqRVcit4e2F1UUOF04xw-XY,520
64
+ shepherd_core/testbed_client/__init__.py,sha256=QtbsBUzHwOoM6rk0qa21ywuz63YV7af1fwUtWW8Vg_4,234
65
+ shepherd_core/testbed_client/cache_path.py,sha256=tS0er9on5fw8wddMCt1jkc2uyYOdSTvX_UmfmYJf6tY,445
66
+ shepherd_core/testbed_client/client_abc_fix.py,sha256=3oDg2RSEP88mygtX820Y-685kYKvqQK_hwp2iFylLUU,4265
67
+ shepherd_core/testbed_client/client_web.py,sha256=zqY4MGMWfTl2_0T1qrQl5Vz9SPjl-wMj8O5yMMQyo9I,6044
68
+ shepherd_core/testbed_client/fixtures.py,sha256=OyL50wEuJ8336RPtinxOFbUsIVtLrgyO2_ROOyiBTEE,9317
69
+ shepherd_core/testbed_client/user_model.py,sha256=f4WZ8IvSCt3s1RG_Bhi43ojiJQsZYyolJ3Ft8HNRYas,2175
70
+ shepherd_core/vsource/__init__.py,sha256=vTvFWuJn4eurPNzEiMd15c1Rd6o3DTWzCfbhOomflZU,771
71
+ shepherd_core/vsource/target_model.py,sha256=BjOlwX_gIOJ91e4OOLB4_OsCpuhq9vm57ERjM-iBhAM,5129
72
+ shepherd_core/vsource/virtual_converter_model.py,sha256=jUnJwP-FFDMtXm1NCLUJfZTvImYH4_A9rc_lXVAZ33I,11628
73
+ shepherd_core/vsource/virtual_harvester_model.py,sha256=8uPTMDth4KI5XarkX9EJtMRZ_8eWUeWCZxgmx4be3Fs,9763
74
+ shepherd_core/vsource/virtual_harvester_simulation.py,sha256=H4m9lB4Hk_sskGi3LoFYARBpq1uY3WsJj-ktF3nk2Ck,2558
75
+ shepherd_core/vsource/virtual_source_model.py,sha256=-8RwBrkIdO0g4zpo7XHnqv8F_qNh_qf5hxEUJoIuAmg,3164
76
+ shepherd_core/vsource/virtual_source_simulation.py,sha256=6FKbaO-zaODp0nf-ksnl-nRAVpFKGQrpxpIBrGIhD0I,5284
77
+ shepherd_core-2025.4.2.dist-info/METADATA,sha256=SD_1RcpaF_Pb-IbG2uAh7xkWHJv8TOaqy8c0zQI7Q1o,7756
78
+ shepherd_core-2025.4.2.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91
79
+ shepherd_core-2025.4.2.dist-info/top_level.txt,sha256=wy-t7HRBrKARZxa-Y8_j8d49oVHnulh-95K9ikxVhew,14
80
+ shepherd_core-2025.4.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
81
+ shepherd_core-2025.4.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (78.1.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5