shepherd-core 2025.2.1__py3-none-any.whl → 2025.4.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.
@@ -3,7 +3,9 @@
3
3
  import struct
4
4
  from typing import Callable
5
5
  from typing import Generator
6
+ from typing import Mapping
6
7
  from typing import Optional
8
+ from typing import Sequence
7
9
  from typing import TypeVar
8
10
  from typing import Union
9
11
 
@@ -26,18 +28,18 @@ Calc_t = TypeVar("Calc_t", NDArray[np.float64], float)
26
28
 
27
29
 
28
30
  def dict_generator(
29
- in_dict: Union[dict, list], pre: Optional[list] = None
31
+ in_dict: Union[Mapping, Sequence], pre: Optional[list] = None
30
32
  ) -> Generator[list, None, None]:
31
33
  """Recursive helper-function to generate a 1D-List(or not?).
32
34
 
33
35
  TODO: isn't that a 1D-List generator?
34
36
  """
35
37
  pre = pre[:] if pre else []
36
- if isinstance(in_dict, dict):
38
+ if isinstance(in_dict, Mapping):
37
39
  for key, value in in_dict.items():
38
- if isinstance(value, dict):
40
+ if isinstance(value, Mapping):
39
41
  yield from dict_generator(value, [*pre, key])
40
- elif isinstance(value, (list, tuple)):
42
+ elif isinstance(value, Sequence):
41
43
  for v in value:
42
44
  yield from dict_generator(v, [*pre, key])
43
45
  else:
@@ -127,8 +127,9 @@ class Firmware(ContentModel, title="Firmware of Target"):
127
127
  arch = fw_tools.read_arch(file)
128
128
  if "msp430" in arch and not fw_tools.is_elf_msp430(file):
129
129
  raise ValueError("File is not a ELF for msp430")
130
- if "nrf52" in arch and not fw_tools.is_elf_nrf52(file):
130
+ if ("nrf52" in arch or "arm" in arch) and not fw_tools.is_elf_nrf52(file):
131
131
  raise ValueError("File is not a ELF for nRF52")
132
+ logger.debug("ELF-File '%s' has arch: %s", file.name, arch)
132
133
  if "mcu" not in kwargs:
133
134
  kwargs["mcu"] = arch_to_mcu[arch]
134
135
 
@@ -346,7 +346,7 @@ class ConverterPRUConfig(ShpModel):
346
346
  [min(255, round(256 * ival)) for ival in il] for il in data.LUT_input_efficiency
347
347
  ],
348
348
  LUT_out_inv_efficiency_n4=[
349
- min((2**14), round((2**4) / value)) if (value > 0) else int(2**14)
349
+ min((2**14), round((2**4) / value)) if (value > 0) else (2**14)
350
350
  for value in data.LUT_output_efficiency
351
351
  ],
352
352
  )
@@ -2,6 +2,7 @@
2
2
 
3
3
  from datetime import datetime
4
4
  from datetime import timedelta
5
+ from typing import Iterable
5
6
  from typing import List
6
7
  from typing import Optional
7
8
  from typing import Union
@@ -68,7 +69,7 @@ class Experiment(ShpModel, title="Config of an Experiment"):
68
69
  return self
69
70
 
70
71
  @staticmethod
71
- def _validate_targets(configs: List[TargetConfig]) -> None:
72
+ def _validate_targets(configs: Iterable[TargetConfig]) -> None:
72
73
  target_ids = []
73
74
  custom_ids = []
74
75
  for _config in configs:
@@ -86,7 +87,7 @@ class Experiment(ShpModel, title="Config of an Experiment"):
86
87
  raise ValueError("Custom Target-ID are faulty (some form of id-collisions)!")
87
88
 
88
89
  @staticmethod
89
- def _validate_observers(configs: List[TargetConfig], testbed: Testbed) -> None:
90
+ def _validate_observers(configs: Iterable[TargetConfig], testbed: Testbed) -> None:
90
91
  target_ids = [_id for _config in configs for _id in _config.target_IDs]
91
92
  obs_ids = [testbed.get_observer(_id).id for _id in target_ids]
92
93
  if len(target_ids) > len(set(obs_ids)):
@@ -46,6 +46,7 @@
46
46
  - these do not work
47
47
 
48
48
  ```Python
49
+ from typing import Dict
49
50
  from pydantic import Field
50
51
  from shepherd_core.data_models import ShpModel
51
52
 
@@ -54,7 +55,7 @@ class Experiment(ShpModel, title="Config of an Experiment"):
54
55
  super().__init__()
55
56
  self.Config.fields["output_path"].description = "test description"
56
57
  class Config:
57
- fields: dict[str, Field] = {}
58
+ fields: Dict[str, Field] = {}
58
59
  fields["output_path"] = Field(description="test description")
59
60
  ```
60
61
 
@@ -31,7 +31,7 @@ class ProgrammingTask(ShpModel):
31
31
  mcu_type: SafeStr
32
32
  # ⤷ must be either "nrf52" or "msp430" ATM, TODO: clean xp to tasks
33
33
  voltage: Annotated[float, Field(ge=1, lt=5)] = 3
34
- datarate: Annotated[int, Field(gt=0, le=1_000_000)] = 500_000
34
+ datarate: Annotated[int, Field(gt=0, le=1_000_000)] = 200_000
35
35
  protocol: ProgrammerProtocol
36
36
 
37
37
  simulate: bool = False
@@ -66,16 +66,14 @@ def is_hex_nrf52(file: Path) -> bool:
66
66
  """Try to detect specifics for that MCU.
67
67
 
68
68
  Observations:
69
- - addresses begin at 0x0
69
+ - addresses begin mostly at 0x0 (no must)
70
70
  - only one segment (.get_segments), todo
71
71
  """
72
72
  if is_hex(file):
73
73
  ih = IntelHex(file.as_posix())
74
- if ih.minaddr() != 0x0000:
75
- return False
76
-
77
- # conservative test for now - should be well below 1 MB + 256 kB
78
- return ih.get_memory_size() < 1310720
74
+ # conservative test for now - should be between 0 and 1 MB + 256 kB,
75
+ # but lately showed much higher values > 4 MB
76
+ return ih.get_memory_size() > 0
79
77
  return False
80
78
 
81
79
 
@@ -145,15 +143,15 @@ def determine_arch(file: Path) -> str:
145
143
  """Figure out arch (msp430 or nrf52)."""
146
144
  file_t = determine_type(file)
147
145
  if file_t == FirmwareDType.path_elf:
148
- if is_elf_nrf52(file):
149
- return "nrf52"
150
146
  if is_elf_msp430(file):
151
147
  return "msp430"
148
+ if is_elf_nrf52(file):
149
+ return "nrf52"
152
150
  raise ValueError("Arch of ELF '%s' could not be determined", file.name)
153
151
  if file_t == FirmwareDType.path_hex:
154
- if is_hex_nrf52(file):
155
- return "nrf52"
156
152
  if is_hex_msp430(file):
157
153
  return "msp430"
154
+ if is_hex_nrf52(file):
155
+ return "nrf52"
158
156
  raise ValueError("Arch of HEX '%s' could not be determined", file.name)
159
157
  raise ValueError("Arch of file '%s' could not be determined", file.name)
@@ -82,8 +82,8 @@ class InventoryList(ShpModel):
82
82
  if (_e.created.timestamp() - ts_earl) > 10:
83
83
  warnings["time_delta"] = f"[{self.hostname}] time-sync has failed"
84
84
 
85
- # turn dict[hostname][type] = val
86
- # to dict[type][val] = list[hostnames]
85
+ # turn Dict[hostname][type] = val
86
+ # to Dict[type][val] = List[hostnames]
87
87
  _inp = {
88
88
  _e.hostname: _e.model_dump(exclude_unset=True, exclude_defaults=True)
89
89
  for _e in self.elements
shepherd_core/reader.py CHANGED
@@ -194,6 +194,7 @@ class Reader:
194
194
  self,
195
195
  start_n: int = 0,
196
196
  end_n: Optional[int] = None,
197
+ n_samples_per_buffer: Optional[int] = None,
197
198
  *,
198
199
  is_raw: bool = False,
199
200
  omit_ts: bool = False,
@@ -201,27 +202,28 @@ class Reader:
201
202
  """Read the specified range of buffers from the hdf5 file.
202
203
 
203
204
  Generator - can be configured on first call
204
- TODO: reconstruct - start/end mark samples &
205
- each call can request a certain number of samples.
206
205
 
207
206
  Args:
208
207
  ----
209
208
  :param start_n: (int) Index of first buffer to be read
210
209
  :param end_n: (int) Index of last buffer to be read
210
+ :param n_samples_per_buffer: (int) allows changing
211
211
  :param is_raw: (bool) output original data, not transformed to SI-Units
212
212
  :param omit_ts: (bool) optimize reading if timestamp is never used
213
213
  Yields: Buffers between start and end (tuple with time, voltage, current)
214
214
 
215
215
  """
216
- if end_n is None:
217
- end_n = int(self.ds_voltage.shape[0] // self.samples_per_buffer)
216
+ if n_samples_per_buffer is None:
217
+ n_samples_per_buffer = self.samples_per_buffer
218
+ end_max = int(self.ds_voltage.shape[0] // n_samples_per_buffer)
219
+ end_n = end_max if end_n is None else min(end_n, end_max)
218
220
  self._logger.debug("Reading blocks %d to %d from source-file", start_n, end_n)
219
221
  _raw = is_raw
220
222
  _wts = not omit_ts
221
223
 
222
224
  for i in range(start_n, end_n):
223
- idx_start = i * self.samples_per_buffer
224
- idx_end = idx_start + self.samples_per_buffer
225
+ idx_start = i * n_samples_per_buffer
226
+ idx_end = idx_start + n_samples_per_buffer
225
227
  if _raw:
226
228
  yield (
227
229
  self.ds_time[idx_start:idx_end] if _wts else None,
@@ -673,7 +675,7 @@ class Reader:
673
675
  return data != data_1
674
676
 
675
677
  def gpio_to_waveforms(self, name: Optional[str] = None) -> dict:
676
- waveforms: dict[str, np.ndarray] = {}
678
+ waveforms: Dict[str, np.ndarray] = {}
677
679
  if "gpio" not in self.h5file:
678
680
  return waveforms
679
681
 
@@ -1,14 +1,13 @@
1
1
  """Current implementation of a file-based database."""
2
2
 
3
3
  import copy
4
- import os
5
4
  import pickle
6
5
  from datetime import datetime
7
6
  from datetime import timedelta
8
7
  from pathlib import Path
9
8
  from typing import Any
10
9
  from typing import Dict
11
- from typing import List
10
+ from typing import Mapping
12
11
  from typing import Optional
13
12
  from typing import Union
14
13
 
@@ -144,7 +143,7 @@ class Fixture:
144
143
  return values, chain
145
144
 
146
145
  @staticmethod
147
- def fill_model(model: dict, base: dict) -> dict:
146
+ def fill_model(model: Mapping, base: dict) -> dict:
148
147
  base = copy.copy(base)
149
148
  for key, value in model.items():
150
149
  # keep previous entries
@@ -201,7 +200,10 @@ class Fixtures:
201
200
  if self.file_path.is_file():
202
201
  files = [self.file_path]
203
202
  elif self.file_path.is_dir():
204
- files = get_files(self.file_path, self.suffix)
203
+ files = list(
204
+ self.file_path.glob("**/*" + self.suffix)
205
+ ) # for py>=3.12: case_sensitive=False
206
+ logger.debug(" -> got %s %s-files", len(files), self.suffix)
205
207
  else:
206
208
  raise ValueError("Path must either be file or directory (or empty)")
207
209
 
@@ -245,28 +247,3 @@ class Fixtures:
245
247
  def to_file(file: Path) -> None:
246
248
  msg = f"TODO (val={file})"
247
249
  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
shepherd_core/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Separated string avoids circular imports."""
2
2
 
3
- version: str = "2025.02.1"
3
+ version: str = "2025.04.1"
shepherd_core/writer.py CHANGED
@@ -8,6 +8,7 @@ from itertools import product
8
8
  from pathlib import Path
9
9
  from types import TracebackType
10
10
  from typing import Any
11
+ from typing import Mapping
11
12
  from typing import Optional
12
13
  from typing import Type
13
14
  from typing import Union
@@ -364,7 +365,7 @@ class Writer(Reader):
364
365
  """Conveniently store relevant key-value data (attribute) in H5-structure."""
365
366
  self.h5file.attrs.__setitem__(key, item)
366
367
 
367
- def store_config(self, data: dict) -> None:
368
+ def store_config(self, data: Mapping) -> None:
368
369
  """Get a better self-describing Output-File.
369
370
 
370
371
  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.1
3
+ Version: 2025.4.1
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>
@@ -2,15 +2,15 @@ shepherd_core/__init__.py,sha256=fCld2mcl0y0h6kRyPal3DP-sWXnKl_0aYWYBdg4QuUk,127
2
2
  shepherd_core/calibration_hw_def.py,sha256=_nMzgNzSnYyqcLnVCGd4tfA2e0avUXbccjmNpFhiDgo,2830
3
3
  shepherd_core/commons.py,sha256=vymKXWcy_1bz7ChzzEATUkJ4p3czCzjIdsSehVjJOY8,218
4
4
  shepherd_core/logger.py,sha256=4Q4hTI-nccOZ1_A68fo4UEctfu3pJx3IeHfa9VuDDEo,1804
5
- shepherd_core/reader.py,sha256=BxVWE-4BfDUjVh03l0SZHT5PwVN0xPEy4aP8Kr858kI,28270
6
- shepherd_core/version.py,sha256=o-7_x2K2hYD5ZqmJzGFJI_9bnBF0jpY08HmDCMNTfMA,76
7
- shepherd_core/writer.py,sha256=GMR-7vkOgpTNPoknBWsRsC7-b7iGMtT60-KtSKunNe8,14636
5
+ shepherd_core/reader.py,sha256=tgETZPFSPzBk2BYHZ3NyWpY9j6j23FUWv1e3oCQhc6A,28401
6
+ shepherd_core/version.py,sha256=Tb2EjULatpKtVFMAniZ_AGEWDFfyFO0ffsW-t24FgFU,76
7
+ shepherd_core/writer.py,sha256=TD5oXuOFz_PUMWYeXfePz9x2O01DLRBVNMaLcngNjFc,14666
8
8
  shepherd_core/data_models/__init__.py,sha256=bnHSP_HBOYm4WuoiHs_vyiRsWlvkyDjsarMNfKedN-Q,1836
9
- shepherd_core/data_models/readme.md,sha256=1bdfEypY_0NMhXLxOPRnLAsFca0HuHdq7_01yEWxvUs,2470
9
+ shepherd_core/data_models/readme.md,sha256=DHPVmkWqDksWomRHRTVWVHy9wXF9oMJrITgKs4Pnz2g,2494
10
10
  shepherd_core/data_models/virtual_source_doc.txt,sha256=KizMcfGKj7BnHIbaJHT7KeTF01SV__UXv01qV_DGHSs,6057
11
11
  shepherd_core/data_models/base/__init__.py,sha256=PSJ6acWViqBm0Eiom8DIgKfFVrp5lzYr8OsDvP79vwI,94
12
12
  shepherd_core/data_models/base/cal_measurement.py,sha256=YScPG7QLynbUHdjcznYqU8O5KRh0XiROGxGSk9BETMk,3357
13
- shepherd_core/data_models/base/calibration.py,sha256=k5VwAK_cwr0a6QY82Lw-9uHth5KUReFOakuOTzghoso,10725
13
+ shepherd_core/data_models/base/calibration.py,sha256=-eiN9QbuZhw1CLzmyijeMgMVzzDE85p-Q5KryFoo5x8,10788
14
14
  shepherd_core/data_models/base/content.py,sha256=13j7GSgT73xn27jgDP508thUEJR4U-nCb5n7CJ50c9Y,2463
15
15
  shepherd_core/data_models/base/shepherd.py,sha256=DNrx59o1VBuy_liJuUzZRzmTTYB73D_pUWiNyMQyjYY,6112
16
16
  shepherd_core/data_models/base/timezone.py,sha256=2T6E46hJ1DAvmqKfu6uIgCK3RSoAKjGXRyzYNaqKyjY,665
@@ -19,14 +19,14 @@ shepherd_core/data_models/content/__init__.py,sha256=69aiNG0h5t1OF7HsLg_ke5eaQKs
19
19
  shepherd_core/data_models/content/_external_fixtures.yaml,sha256=0CH7YSWT_hzL-jcg4JjgN9ryQOzbS8S66_pd6GbMnHw,12259
20
20
  shepherd_core/data_models/content/energy_environment.py,sha256=WuXMkKqnibGzM2WeW1_m2DAsc0fDqE9CkBYYPSw-7eA,1540
21
21
  shepherd_core/data_models/content/energy_environment_fixture.yaml,sha256=UBXTdGT7MK98zx5w_RBCu-f9uNCKxRgiFBQFbmDUxPc,1301
22
- shepherd_core/data_models/content/firmware.py,sha256=MyEiaP6bkOm7i_oihDXTxHC7ajc5aqiIDLn7mhap6YY,5722
22
+ shepherd_core/data_models/content/firmware.py,sha256=rH74sJPAjUUheVv0y2Y93BeT63CeKqdSEo8HZgHGlMo,5813
23
23
  shepherd_core/data_models/content/firmware_datatype.py,sha256=XPU9LOoT3h5qFOlE8WU0vAkw-vymNxzor9kVFyEqsWg,255
24
24
  shepherd_core/data_models/content/virtual_harvester.py,sha256=cf-DrSacZsct9e8eGoR4PX7S7RXlbxQfJHVsLR9njlM,10749
25
25
  shepherd_core/data_models/content/virtual_harvester_fixture.yaml,sha256=LZe5ue1xYhXZwB3a32sva-L4uKhkQA5AtG9JzW4B2hQ,4564
26
- shepherd_core/data_models/content/virtual_source.py,sha256=PPAphxEXvgMM7OVZ2dBkYAvJQkmj5Kb2BYFogVUs7B8,15354
26
+ shepherd_core/data_models/content/virtual_source.py,sha256=EnkjLlh14YBUnoS4yILXraLN0qdnVoEzddJY0N1w3xk,15351
27
27
  shepherd_core/data_models/content/virtual_source_fixture.yaml,sha256=1o-31mGgn7eyCNidKoOUp9vZh3K4Al0kJgmz54Q2DAE,11191
28
28
  shepherd_core/data_models/experiment/__init__.py,sha256=lorsx0M-JWPIrt_UZfexsLwaITv5slFb3krBOt0idm8,618
29
- shepherd_core/data_models/experiment/experiment.py,sha256=wnn6T3czuh4rz6OSYtMltCTbRpPX55TLVAtQcKO7Uhg,4044
29
+ shepherd_core/data_models/experiment/experiment.py,sha256=79iHh2Dr_w4N4Bfq_g0KDr6wMpS_T7x-JIGHZCagEGo,4080
30
30
  shepherd_core/data_models/experiment/observer_features.py,sha256=qxnb7anuQz9ZW5IUlPdUXYPIl5U7O9uXkJqZtMnAb0Y,5156
31
31
  shepherd_core/data_models/experiment/target_config.py,sha256=XIsjbbo7yn_A4q3GMxWbiNzEGA0Kk5gH7-XfQQ7Kg0E,3674
32
32
  shepherd_core/data_models/task/__init__.py,sha256=vOsSeB5IBYYwzOLKom8-XuZyjF86m_svtCsw-agiV7o,3295
@@ -34,7 +34,7 @@ shepherd_core/data_models/task/emulation.py,sha256=tLb5auHOgdoG-e4hFljAYT49z7lME
34
34
  shepherd_core/data_models/task/firmware_mod.py,sha256=Rw_TA1ykQ7abUd_U0snqZlpZyrS8Nx6f4BEax1Xnji0,2818
35
35
  shepherd_core/data_models/task/harvest.py,sha256=HHnqWwRsJupaZJxuohs7NrK6VaDyoRzGOaG2h9y3s1Y,3360
36
36
  shepherd_core/data_models/task/observer_tasks.py,sha256=XlH_-EGRrdodTn0c2pjGvpcauc0a9NOnLhysKw8iRwk,3511
37
- shepherd_core/data_models/task/programming.py,sha256=Mg9_AZHIdG01FheEJAifIRPSB3iZ0UJITf8zeg2jyws,2323
37
+ shepherd_core/data_models/task/programming.py,sha256=hjzsxqqB0WXb0qY5gKk4iZ-Is3-OZNFnGehAxzA_0TI,2323
38
38
  shepherd_core/data_models/task/testbed_tasks.py,sha256=zvIitq0Ek1Ae7baWiBkSQN8nRugyw0N2P4SeVoj_QaY,2090
39
39
  shepherd_core/data_models/testbed/__init__.py,sha256=t9nwml5pbu7ZWghimOyZ8ujMIgnRgFkl23pNb5d_KdU,581
40
40
  shepherd_core/data_models/testbed/cape.py,sha256=D23ZKXpZRPIIOMn6LCoJrwHiRbSaYg-y7B6fAt1ap64,1246
@@ -56,8 +56,8 @@ shepherd_core/fw_tools/__init__.py,sha256=D9GGj9TzLWZfPjG_iV2BsF-Q1TGTYTgEzWTUI5
56
56
  shepherd_core/fw_tools/converter.py,sha256=3igRT33tghrBCao5njuPmePS-Y_lSa6EUHvwCakMo2s,3539
57
57
  shepherd_core/fw_tools/converter_elf.py,sha256=GQDVqIqMW4twNMvZIV3sowFMezhs2TN-IYREjRP7Xt4,1089
58
58
  shepherd_core/fw_tools/patcher.py,sha256=D6MHaCvKRRVQYSZozODAp_l7UnqxVsvnulPzpkfXWW8,4108
59
- shepherd_core/fw_tools/validation.py,sha256=hBLCKIUumPTA6iuXMVbMYph2jamaxeSTxRqsvl3C4-I,4699
60
- shepherd_core/inventory/__init__.py,sha256=nRO11HG4eJ_FaXebSkE0dd1H6qvjrX5n3OQHOzKXVvk,3841
59
+ shepherd_core/fw_tools/validation.py,sha256=dOweyWwVMq0b4Liha39mGUOiblD7Nz5kSFVrlxUx41o,4707
60
+ shepherd_core/inventory/__init__.py,sha256=nyDBAE7kdDeh1McyavMWH1cY0WqphjFS1NL4XRAyzUw,3841
61
61
  shepherd_core/inventory/python.py,sha256=OWNnyEt0IDPW9XGW-WloU0FExwgZzYNA05VpRj4cZGc,1250
62
62
  shepherd_core/inventory/system.py,sha256=Q_mtM2zhUsTvzELgpEVxSxL9aFS1RSGcCX6RFpyZ-r8,3155
63
63
  shepherd_core/inventory/target.py,sha256=Lq11j25tWieXheOxIDaQb-lc-2omxYVex5P6uGiLUyk,507
@@ -65,7 +65,7 @@ shepherd_core/testbed_client/__init__.py,sha256=QtbsBUzHwOoM6rk0qa21ywuz63YV7af1
65
65
  shepherd_core/testbed_client/cache_path.py,sha256=tS0er9on5fw8wddMCt1jkc2uyYOdSTvX_UmfmYJf6tY,445
66
66
  shepherd_core/testbed_client/client_abc_fix.py,sha256=BsSkpvJHURRejlS-YPF1f6QRPC_X0fYEsJpinzsx6Jc,4079
67
67
  shepherd_core/testbed_client/client_web.py,sha256=rOg0Gf-s3S8U0z2ssh9_bNnq4aRQINfuuVHionPf38M,5788
68
- shepherd_core/testbed_client/fixtures.py,sha256=4Uk583R4r6I5IB78HxOn-9UNH3sbFha7OPEdcSXvMCU,9939
68
+ shepherd_core/testbed_client/fixtures.py,sha256=W8trHZXyuD0xzb28ZW-2BQ3ZDU2tI3LC4mjrbOUX57E,9215
69
69
  shepherd_core/testbed_client/user_model.py,sha256=5M3vWkAGBwdGDUYAanAjrZwpzMBlh3XLOVvNYWiLmms,2107
70
70
  shepherd_core/vsource/__init__.py,sha256=vTvFWuJn4eurPNzEiMd15c1Rd6o3DTWzCfbhOomflZU,771
71
71
  shepherd_core/vsource/target_model.py,sha256=LaB5ppi2-IIpIepDqDvOliR-BupzccJl44yRxjlF-ms,5113
@@ -74,8 +74,8 @@ shepherd_core/vsource/virtual_harvester_model.py,sha256=GyA0uGl3r42t5c4roYtEaj22
74
74
  shepherd_core/vsource/virtual_harvester_simulation.py,sha256=MFT583s73BJZYyhcqgnDUGTPr9s_lN_lKafzJG6kueE,2457
75
75
  shepherd_core/vsource/virtual_source_model.py,sha256=9tiOzgrEgdEH5Uuhd8hjmmIkquNDuaNjLlblvInIlzg,3036
76
76
  shepherd_core/vsource/virtual_source_simulation.py,sha256=ZOnFd2uPawY2G1salCiLGx9o1OBG99_-EHBtDjx4ZUo,5140
77
- shepherd_core-2025.2.1.dist-info/METADATA,sha256=rYFb9DPtDYpbwy5xpIMWv7OtfQ87J12-pMQbvToPRpk,7806
78
- shepherd_core-2025.2.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
79
- shepherd_core-2025.2.1.dist-info/top_level.txt,sha256=wy-t7HRBrKARZxa-Y8_j8d49oVHnulh-95K9ikxVhew,14
80
- shepherd_core-2025.2.1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
81
- shepherd_core-2025.2.1.dist-info/RECORD,,
77
+ shepherd_core-2025.4.1.dist-info/METADATA,sha256=3IZbuK5skDrSs1QRlY9uA8MJLsUx0NSYALft4GwVfeQ,7806
78
+ shepherd_core-2025.4.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
79
+ shepherd_core-2025.4.1.dist-info/top_level.txt,sha256=wy-t7HRBrKARZxa-Y8_j8d49oVHnulh-95K9ikxVhew,14
80
+ shepherd_core-2025.4.1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
81
+ shepherd_core-2025.4.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5