shepherd-data 2025.2.2__tar.gz → 2025.4.2__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 (28) hide show
  1. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/PKG-INFO +4 -5
  2. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/pyproject.toml +8 -4
  3. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/shepherd_data/__init__.py +1 -1
  4. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/shepherd_data/cli.py +65 -23
  5. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/shepherd_data/ivonne.py +6 -6
  6. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/shepherd_data/mppt.py +5 -1
  7. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/shepherd_data/reader.py +18 -16
  8. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/shepherd_data.egg-info/PKG-INFO +4 -5
  9. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/shepherd_data.egg-info/requires.txt +1 -1
  10. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/README.md +0 -0
  11. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/setup.cfg +0 -0
  12. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/shepherd_data.egg-info/SOURCES.txt +0 -0
  13. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/shepherd_data.egg-info/dependency_links.txt +0 -0
  14. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/shepherd_data.egg-info/entry_points.txt +0 -0
  15. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/shepherd_data.egg-info/top_level.txt +0 -0
  16. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/shepherd_data.egg-info/zip-safe +0 -0
  17. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/tests/test_cli.py +0 -0
  18. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/tests/test_cli_downsample.py +0 -0
  19. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/tests/test_cli_extract.py +0 -0
  20. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/tests/test_cli_extract_gpio.py +0 -0
  21. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/tests/test_cli_extract_meta.py +0 -0
  22. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/tests/test_cli_extract_uart.py +0 -0
  23. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/tests/test_cli_plot.py +0 -0
  24. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/tests/test_cli_validate.py +0 -0
  25. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/tests/test_cli_version.py +0 -0
  26. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/tests/test_examples.py +0 -0
  27. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/tests/test_ivonne.py +0 -0
  28. {shepherd_data-2025.2.2 → shepherd_data-2025.4.2}/tests/test_reader.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: shepherd_data
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: click
33
32
  Requires-Dist: h5py
@@ -36,7 +35,7 @@ Requires-Dist: numpy
36
35
  Requires-Dist: pandas>=2.0.0
37
36
  Requires-Dist: pyYAML
38
37
  Requires-Dist: scipy
39
- Requires-Dist: shepherd-core[inventory]>=2025.02.2
38
+ Requires-Dist: shepherd-core[inventory]>=2025.04.2
40
39
  Requires-Dist: tqdm
41
40
  Provides-Extra: elf
42
41
  Requires-Dist: shepherd-core[elf]; extra == "elf"
@@ -18,7 +18,6 @@ classifiers = [
18
18
  "Intended Audience :: Developers",
19
19
  "Intended Audience :: Information Technology",
20
20
  "Intended Audience :: Science/Research",
21
- "Programming Language :: Python :: 3.8",
22
21
  "Programming Language :: Python :: 3.9",
23
22
  "Programming Language :: Python :: 3.10",
24
23
  "Programming Language :: Python :: 3.11",
@@ -29,7 +28,7 @@ classifiers = [
29
28
  "Natural Language :: English",
30
29
  ]
31
30
 
32
- requires-python = ">=3.8"
31
+ requires-python = ">=3.9"
33
32
  dependencies = [
34
33
  "click",
35
34
  "h5py",
@@ -38,7 +37,7 @@ dependencies = [
38
37
  "pandas>=2.0.0", # full-version, v2 is OK
39
38
  "pyYAML",
40
39
  "scipy", # full-version
41
- "shepherd-core[inventory]>=2025.02.2", # libs are strongly coupled
40
+ "shepherd-core[inventory]>=2025.04.2", # libs are strongly coupled
42
41
  "tqdm", # full-version
43
42
  ]
44
43
 
@@ -101,5 +100,10 @@ addopts = "-vvv --stepwise" # opts: verbose result for each tests
101
100
  source = ["shepherd_data"]
102
101
 
103
102
  [tool.mypy]
104
- python_version = 3.8
103
+ python_version = 3.9
105
104
  ignore_missing_imports = true
105
+ disable_error_code = ["call-arg", ]
106
+ exclude = [
107
+ "build/",
108
+ ".egg-info/",
109
+ ]
@@ -11,7 +11,7 @@ from shepherd_core import Writer
11
11
 
12
12
  from .reader import Reader
13
13
 
14
- __version__ = "2025.02.2"
14
+ __version__ = "2025.04.2"
15
15
 
16
16
  __all__ = [
17
17
  "Reader",
@@ -1,12 +1,10 @@
1
1
  """Command definitions for CLI."""
2
2
 
3
3
  import logging
4
- import os
5
4
  import sys
6
5
  from contextlib import suppress
7
6
  from datetime import datetime
8
7
  from pathlib import Path
9
- from typing import List
10
8
  from typing import Optional
11
9
 
12
10
  import click
@@ -22,7 +20,7 @@ from .reader import Reader
22
20
  logger = logging.getLogger("SHPData.cli")
23
21
 
24
22
 
25
- def path_to_flist(data_path: Path) -> List[Path]:
23
+ def path_to_flist(data_path: Path, *, recurse: bool = False) -> list[Path]:
26
24
  """Every path gets transformed to a list of paths.
27
25
 
28
26
  Transformations:
@@ -31,16 +29,14 @@ def path_to_flist(data_path: Path) -> List[Path]:
31
29
  - or else: empty list
32
30
  """
33
31
  data_path = Path(data_path).resolve()
34
- h5files = []
32
+ h5files: list = []
35
33
  if data_path.is_file() and data_path.suffix.lower() == ".h5":
36
34
  h5files.append(data_path)
37
35
  elif data_path.is_dir():
38
- flist = os.listdir(data_path)
39
- for file in flist:
40
- fpath = data_path / str(file)
41
- if not fpath.is_file() or fpath.suffix.lower() != ".h5":
42
- continue
43
- h5files.append(fpath)
36
+ files = data_path.glob(
37
+ "**/*.h5" if recurse else "*.h5"
38
+ ) # for py>=3.12: case_sensitive=False
39
+ h5files = [file for file in files if file.is_file()]
44
40
  return h5files
45
41
 
46
42
 
@@ -70,9 +66,15 @@ def version() -> None:
70
66
 
71
67
  @cli.command(short_help="Validates a file or directory containing shepherd-recordings")
72
68
  @click.argument("in_data", type=click.Path(exists=True, resolve_path=True))
73
- def validate(in_data: Path) -> None:
69
+ @click.option(
70
+ "--recurse",
71
+ "-a",
72
+ is_flag=True,
73
+ help="Also consider files in sub-folders",
74
+ )
75
+ def validate(in_data: Path, *, recurse: bool = False) -> None:
74
76
  """Validate a file or directory containing shepherd-recordings."""
75
- files = path_to_flist(in_data)
77
+ files = path_to_flist(in_data, recurse=recurse)
76
78
  verbose_level = get_verbose_level() # TODO: should be stored and passed in ctx
77
79
  valid_dir = True
78
80
  for file in files:
@@ -123,7 +125,13 @@ def validate(in_data: Path) -> None:
123
125
  "--raw",
124
126
  "-r",
125
127
  is_flag=True,
126
- help="Plot only power instead of voltage, current & power",
128
+ help="Don't convert data to si-units",
129
+ )
130
+ @click.option(
131
+ "--recurse",
132
+ "-a",
133
+ is_flag=True,
134
+ help="Also consider files in sub-folders",
127
135
  )
128
136
  def extract(
129
137
  in_data: Path,
@@ -133,9 +141,10 @@ def extract(
133
141
  separator: str,
134
142
  *,
135
143
  raw: bool = False,
144
+ recurse: bool = False,
136
145
  ) -> None:
137
146
  """Extract recorded IVSamples and store them to csv."""
138
- files = path_to_flist(in_data)
147
+ files = path_to_flist(in_data, recurse=recurse)
139
148
  verbose_level = get_verbose_level()
140
149
  if not isinstance(ds_factor, (float, int)) or ds_factor < 1:
141
150
  ds_factor = 1000
@@ -162,10 +171,15 @@ def extract(
162
171
  type=click.STRING,
163
172
  help="Set an individual csv-separator",
164
173
  )
165
- # TODO: a recursive option would help!
166
- def extract_meta(in_data: Path, separator: str) -> None:
174
+ @click.option(
175
+ "--recurse",
176
+ "-a",
177
+ is_flag=True,
178
+ help="Also consider files in sub-folders",
179
+ )
180
+ def extract_meta(in_data: Path, separator: str, *, recurse: bool = False) -> None:
167
181
  """Extract metadata and logs from file or directory containing shepherd-recordings."""
168
- files = path_to_flist(in_data)
182
+ files = path_to_flist(in_data, recurse=recurse)
169
183
  verbose_level = get_verbose_level()
170
184
  for file in files:
171
185
  logger.info("Extracting metadata & logs from '%s' ...", file.name)
@@ -195,9 +209,15 @@ def extract_meta(in_data: Path, separator: str) -> None:
195
209
  short_help="Extracts uart from gpio-trace in file or directory containing shepherd-recordings"
196
210
  )
197
211
  @click.argument("in_data", type=click.Path(exists=True, resolve_path=True))
198
- def extract_uart(in_data: Path) -> None:
212
+ @click.option(
213
+ "--recurse",
214
+ "-a",
215
+ is_flag=True,
216
+ help="Also consider files in sub-folders",
217
+ )
218
+ def extract_uart(in_data: Path, *, recurse: bool = False) -> None:
199
219
  """Extract UART from GPIO-trace in file or directory containing shepherd-recordings."""
200
- files = path_to_flist(in_data)
220
+ files = path_to_flist(in_data, recurse=recurse)
201
221
  verbose_level = get_verbose_level()
202
222
  for file in files:
203
223
  logger.info("Extracting uart from gpio-trace from from '%s' ...", file.name)
@@ -220,6 +240,7 @@ def extract_uart(in_data: Path) -> None:
220
240
  log_file.write(timestamp.strftime("%Y-%m-%d %H:%M:%S.%f") + ":")
221
241
  # TODO: allow to skip Timestamp and export raw text
222
242
  log_file.write(f"\t{str.encode(line[1])}")
243
+ # TODO: does this produce "\tb'abc'"?
223
244
  log_file.write("\n")
224
245
  except TypeError:
225
246
  logger.exception("ERROR: Will skip file. It caused an exception.")
@@ -233,10 +254,16 @@ def extract_uart(in_data: Path) -> None:
233
254
  default=";",
234
255
  type=click.STRING,
235
256
  help="Set an individual csv-separator",
257
+ ) # TODO: also configure decimal point
258
+ @click.option(
259
+ "--recurse",
260
+ "-a",
261
+ is_flag=True,
262
+ help="Also consider files in sub-folders",
236
263
  )
237
- def extract_gpio(in_data: Path, separator: str) -> None:
264
+ def extract_gpio(in_data: Path, separator: str, *, recurse: bool = False) -> None:
238
265
  """Extract UART from gpio-trace in file or directory containing shepherd-recordings."""
239
- files = path_to_flist(in_data)
266
+ files = path_to_flist(in_data, recurse=recurse)
240
267
  verbose_level = get_verbose_level()
241
268
  for file in files:
242
269
  logger.info("Extracting gpio-trace from from '%s' ...", file.name)
@@ -281,15 +308,23 @@ def extract_gpio(in_data: Path, separator: str) -> None:
281
308
  type=click.FLOAT,
282
309
  help="End-point in seconds, will be max if omitted",
283
310
  )
311
+ @click.option(
312
+ "--recurse",
313
+ "-a",
314
+ is_flag=True,
315
+ help="Also consider files in sub-folders",
316
+ )
284
317
  def downsample(
285
318
  in_data: Path,
286
319
  ds_factor: Optional[float],
287
320
  sample_rate: Optional[int],
288
321
  start: Optional[float],
289
322
  end: Optional[float],
323
+ *,
324
+ recurse: bool = False,
290
325
  ) -> None:
291
326
  """Create an array of down-sampled files from file or dir containing shepherd-recordings."""
292
- files = path_to_flist(in_data)
327
+ files = path_to_flist(in_data, recurse=recurse)
293
328
  verbose_level = get_verbose_level()
294
329
  for file in files:
295
330
  try:
@@ -351,6 +386,12 @@ def downsample(
351
386
  is_flag=True,
352
387
  help="Plot only power instead of voltage, current & power",
353
388
  )
389
+ @click.option(
390
+ "--recurse",
391
+ "-a",
392
+ is_flag=True,
393
+ help="Also consider files in sub-folders",
394
+ )
354
395
  def plot(
355
396
  in_data: Path,
356
397
  start: Optional[float],
@@ -360,9 +401,10 @@ def plot(
360
401
  *,
361
402
  multiplot: bool,
362
403
  only_power: bool,
404
+ recurse: bool = False,
363
405
  ) -> None:
364
406
  """Plot IV-trace from file or directory containing shepherd-recordings."""
365
- files = path_to_flist(in_data)
407
+ files = path_to_flist(in_data, recurse=recurse)
366
408
  verbose_level = get_verbose_level()
367
409
  multiplot = multiplot and len(files) > 1
368
410
  data = []
@@ -17,14 +17,14 @@ import pickle
17
17
  from pathlib import Path
18
18
  from types import TracebackType
19
19
  from typing import Optional
20
- from typing import Type
21
20
 
22
21
  import numpy as np
23
22
  import pandas as pd
24
23
  from tqdm import trange
25
24
  from typing_extensions import Self
26
25
 
27
- from . import Writer
26
+ from shepherd_core.writer import Writer as CoreWriter
27
+
28
28
  from .mppt import MPPTracker
29
29
  from .mppt import OptimalTracker
30
30
  from .mppt import iv_model
@@ -88,7 +88,7 @@ class Reader:
88
88
 
89
89
  def __exit__(
90
90
  self,
91
- typ: Optional[Type[BaseException]] = None,
91
+ typ: Optional[type[BaseException]] = None,
92
92
  exc: Optional[BaseException] = None,
93
93
  tb: Optional[TracebackType] = None,
94
94
  extra_arg: int = 0,
@@ -132,7 +132,7 @@ class Reader:
132
132
 
133
133
  v_proto = np.linspace(0, v_max, pts_per_curve)
134
134
 
135
- with Writer(shp_output, datatype="ivcurve", window_samples=pts_per_curve) as sfw:
135
+ with CoreWriter(shp_output, datatype="ivcurve", window_samples=pts_per_curve) as sfw:
136
136
  sfw.store_hostname("IVonne")
137
137
  curve_interval_us = round(sfw.sample_interval_ns * pts_per_curve / 1000)
138
138
  up_factor = self.sample_interval_ns // sfw.sample_interval_ns
@@ -207,7 +207,7 @@ class Reader:
207
207
  v_max,
208
208
  )
209
209
 
210
- with Writer(shp_output, datatype="ivsample") as sfw:
210
+ with CoreWriter(shp_output, datatype="ivsample") as sfw:
211
211
  sfw.store_hostname("IVonne")
212
212
  interval_us = round(sfw.sample_interval_ns / 1000)
213
213
  up_factor = self.sample_interval_ns // sfw.sample_interval_ns
@@ -260,7 +260,7 @@ class Reader:
260
260
  self._logger.info("File already exists, will skip '%s'", shp_output.name)
261
261
  return
262
262
 
263
- with Writer(shp_output, datatype="isc_voc") as sfw:
263
+ with CoreWriter(shp_output, datatype="isc_voc") as sfw:
264
264
  sfw.store_hostname("IVonne")
265
265
  interval_us = round(sfw.sample_interval_ns / 1000)
266
266
  up_factor = self.sample_interval_ns // sfw.sample_interval_ns
@@ -3,6 +3,9 @@
3
3
  Might be exchanged by shepherds py-model of pru-harvesters.
4
4
  """
5
5
 
6
+ from abc import ABC
7
+ from abc import abstractmethod
8
+
6
9
  import numpy as np
7
10
  import pandas as pd
8
11
 
@@ -36,7 +39,7 @@ def find_oc(v_arr: np.ndarray, i_arr: np.ndarray, ratio: float = 0.05) -> np.nda
36
39
  return v_arr[np.argmax(i_arr < i_arr[0] * ratio)]
37
40
 
38
41
 
39
- class MPPTracker:
42
+ class MPPTracker(ABC):
40
43
  """Prototype for a MPPT-class.
41
44
 
42
45
  :param v_max: Maximum voltage supported by shepherd
@@ -48,6 +51,7 @@ class MPPTracker:
48
51
  self.v_max: float = v_max
49
52
  self.v_proto: np.ndarray = np.linspace(0, v_max, pts_per_curve)
50
53
 
54
+ @abstractmethod
51
55
  def process(self, coeffs: pd.DataFrame) -> pd.DataFrame:
52
56
  """Apply harvesting model to input data.
53
57
 
@@ -1,9 +1,10 @@
1
1
  """Reader-Baseclass for opening shepherds hdf5-files."""
2
2
 
3
3
  import math
4
+ from collections.abc import Mapping
5
+ from collections.abc import Sequence
4
6
  from datetime import datetime
5
7
  from pathlib import Path
6
- from typing import Dict
7
8
  from typing import Optional
8
9
  from typing import Union
9
10
 
@@ -33,9 +34,9 @@ class Reader(CoreReader):
33
34
 
34
35
  def __init__(
35
36
  self,
36
- file_path: Optional[Path],
37
+ file_path: Path,
37
38
  *,
38
- verbose: Optional[bool] = True,
39
+ verbose: bool = True,
39
40
  ) -> None:
40
41
  super().__init__(file_path, verbose=verbose)
41
42
 
@@ -56,12 +57,16 @@ class Reader(CoreReader):
56
57
  if csv_path.exists():
57
58
  self._logger.info("File already exists, will skip '%s'", csv_path.name)
58
59
  return 0
59
- datasets = [key if isinstance(h5_group[key], h5py.Dataset) else [] for key in h5_group]
60
+ datasets: list[str] = [
61
+ str(key) for key in h5_group if isinstance(h5_group[key], h5py.Dataset)
62
+ ]
60
63
  datasets.remove("time")
61
64
  datasets = ["time", *datasets]
62
65
  separator = separator.strip().ljust(2)
63
- header = [h5_group[key].attrs["description"].replace(", ", separator) for key in datasets]
64
- header = separator.join(header)
66
+ header_elements: list[str] = [
67
+ str(h5_group[key].attrs["description"]).replace(", ", separator) for key in datasets
68
+ ]
69
+ header: str = separator.join(header_elements)
65
70
  with csv_path.open("w", encoding="utf-8-sig") as csv_file:
66
71
  self._logger.info("CSV-Generator will save '%s' to '%s'", h5_group.name, csv_path.name)
67
72
  csv_file.write(header + "\n")
@@ -222,7 +227,7 @@ class Reader(CoreReader):
222
227
  for _iter in trange(
223
228
  0,
224
229
  iterations,
225
- desc=f"downsampling {data_src.name}",
230
+ desc=f"downsampling {data_src.name if isinstance(data_src, h5py.Dataset) else ''}",
226
231
  leave=False,
227
232
  disable=iterations < 8,
228
233
  ):
@@ -242,7 +247,7 @@ class Reader(CoreReader):
242
247
  self,
243
248
  start_s: Optional[float],
244
249
  end_s: Optional[float],
245
- ds_factor: Optional[float],
250
+ ds_factor: float,
246
251
  ) -> Path:
247
252
  """Cut source to given limits, downsample by factor and store result in separate file.
248
253
 
@@ -290,10 +295,7 @@ class Reader(CoreReader):
290
295
  else:
291
296
  cut_str = ""
292
297
 
293
- if ds_factor > 1: # noqa: SIM108
294
- ds_str = f".downsample_x{round(ds_factor)}"
295
- else:
296
- ds_str = ""
298
+ ds_str = f".downsample_x{round(ds_factor)}" if ds_factor > 1 else ""
297
299
 
298
300
  dst_file = self.file_path.resolve().with_suffix(cut_str + ds_str + ".h5")
299
301
  if dst_file.exists():
@@ -403,7 +405,7 @@ class Reader(CoreReader):
403
405
  for _ in trange(
404
406
  0,
405
407
  iterations,
406
- desc=f"resampling {data_src.name}",
408
+ desc=f"resampling {data_src.name if isinstance(data_src, h5py.Dataset) else ''}",
407
409
  leave=False,
408
410
  disable=iterations < 8,
409
411
  ):
@@ -456,7 +458,7 @@ class Reader(CoreReader):
456
458
  end_s: Optional[float] = None,
457
459
  *,
458
460
  relative_timestamp: bool = True,
459
- ) -> Optional[Dict]:
461
+ ) -> Optional[dict]:
460
462
  """Provide down-sampled iv-data that can be fed into plot_to_file().
461
463
 
462
464
  :param start_s: time in seconds, relative to start of recording
@@ -506,7 +508,7 @@ class Reader(CoreReader):
506
508
 
507
509
  @staticmethod
508
510
  def assemble_plot(
509
- data: Union[dict, list], width: int = 20, height: int = 10, *, only_pwr: bool = False
511
+ data: Union[Mapping, Sequence], width: int = 20, height: int = 10, *, only_pwr: bool = False
510
512
  ) -> plt.Figure:
511
513
  """Create the actual figure.
512
514
 
@@ -520,7 +522,7 @@ class Reader(CoreReader):
520
522
  :return: figure
521
523
  """
522
524
  # TODO: allow choosing freely from I, V, P, GPIO
523
- if isinstance(data, dict):
525
+ if isinstance(data, Mapping):
524
526
  data = [data]
525
527
  if only_pwr:
526
528
  fig, ax = plt.subplots(1, 1, figsize=(width, height), layout="tight")
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: shepherd_data
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: click
33
32
  Requires-Dist: h5py
@@ -36,7 +35,7 @@ Requires-Dist: numpy
36
35
  Requires-Dist: pandas>=2.0.0
37
36
  Requires-Dist: pyYAML
38
37
  Requires-Dist: scipy
39
- Requires-Dist: shepherd-core[inventory]>=2025.02.2
38
+ Requires-Dist: shepherd-core[inventory]>=2025.04.2
40
39
  Requires-Dist: tqdm
41
40
  Provides-Extra: elf
42
41
  Requires-Dist: shepherd-core[elf]; extra == "elf"
@@ -5,7 +5,7 @@ numpy
5
5
  pandas>=2.0.0
6
6
  pyYAML
7
7
  scipy
8
- shepherd-core[inventory]>=2025.02.2
8
+ shepherd-core[inventory]>=2025.04.2
9
9
  tqdm
10
10
 
11
11
  [dev]