shepherd-core 2025.6.1__tar.gz → 2025.6.3__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 (147) hide show
  1. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/PKG-INFO +1 -1
  2. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/examples/eenv_generator.py +2 -2
  3. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/examples/uart_decode_waveform.py +2 -2
  4. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/__init__.py +2 -2
  5. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/__init__.py +1 -1
  6. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/base/shepherd.py +48 -13
  7. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/content/firmware.py +4 -4
  8. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/content/virtual_harvester.py +3 -3
  9. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/content/virtual_source.py +5 -5
  10. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/experiment/observer_features.py +2 -2
  11. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/task/__init__.py +9 -4
  12. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/task/emulation.py +14 -5
  13. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/task/firmware_mod.py +14 -5
  14. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/task/harvest.py +5 -0
  15. shepherd_core-2025.6.3/shepherd_core/data_models/task/helper_paths.py +15 -0
  16. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/task/observer_tasks.py +16 -15
  17. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/task/programming.py +8 -2
  18. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/task/testbed_tasks.py +12 -0
  19. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/decoder_waveform/uart.py +7 -7
  20. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/fw_tools/patcher.py +6 -6
  21. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/fw_tools/validation.py +2 -2
  22. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/inventory/system.py +2 -2
  23. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/logger.py +3 -3
  24. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/testbed_client/fixtures.py +5 -5
  25. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/version.py +1 -1
  26. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/vsource/virtual_harvester_model.py +2 -2
  27. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/vsource/virtual_source_simulation.py +2 -2
  28. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core.egg-info/PKG-INFO +1 -1
  29. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core.egg-info/SOURCES.txt +1 -0
  30. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/README.md +0 -0
  31. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/examples/experiment_from_yaml.yaml +0 -0
  32. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/examples/experiment_generic_var1.py +0 -0
  33. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/examples/experiment_generic_var2.py +0 -0
  34. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/examples/experiment_models.py +0 -0
  35. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/examples/firmware_model.py +0 -0
  36. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/examples/firmware_modification.py +0 -0
  37. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/examples/inventory.py +0 -0
  38. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/examples/simulate_vharvester.py +0 -0
  39. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/examples/simulate_vsource.py +0 -0
  40. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/examples/uart_raw2.csv +0 -0
  41. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/examples/vsource_debug_sim.py +0 -0
  42. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/pyproject.toml +0 -0
  43. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/setup.cfg +0 -0
  44. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/calibration_hw_def.py +0 -0
  45. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/commons.py +0 -0
  46. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/config.py +0 -0
  47. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/base/__init__.py +0 -0
  48. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/base/cal_measurement.py +0 -0
  49. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/base/calibration.py +0 -0
  50. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/base/content.py +0 -0
  51. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/base/timezone.py +0 -0
  52. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/base/wrapper.py +0 -0
  53. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/content/__init__.py +0 -0
  54. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/content/_external_fixtures.yaml +0 -0
  55. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/content/energy_environment.py +0 -0
  56. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/content/energy_environment_fixture.yaml +0 -0
  57. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/content/firmware_datatype.py +0 -0
  58. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/content/virtual_harvester_fixture.yaml +0 -0
  59. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/content/virtual_source_fixture.yaml +0 -0
  60. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/experiment/__init__.py +0 -0
  61. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/experiment/experiment.py +0 -0
  62. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/experiment/target_config.py +0 -0
  63. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/readme.md +0 -0
  64. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/testbed/__init__.py +0 -0
  65. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/testbed/cape.py +0 -0
  66. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/testbed/cape_fixture.yaml +0 -0
  67. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/testbed/gpio.py +0 -0
  68. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/testbed/gpio_fixture.yaml +0 -0
  69. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/testbed/mcu.py +0 -0
  70. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/testbed/mcu_fixture.yaml +0 -0
  71. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/testbed/observer.py +0 -0
  72. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/testbed/observer_fixture.yaml +0 -0
  73. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/testbed/target.py +0 -0
  74. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/testbed/target_fixture.old1 +0 -0
  75. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/testbed/target_fixture.yaml +0 -0
  76. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/testbed/testbed.py +0 -0
  77. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/testbed/testbed_fixture.yaml +0 -0
  78. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/data_models/virtual_source_doc.txt +0 -0
  79. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/decoder_waveform/__init__.py +0 -0
  80. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/fw_tools/__init__.py +0 -0
  81. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/fw_tools/converter.py +0 -0
  82. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/fw_tools/converter_elf.py +0 -0
  83. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/inventory/__init__.py +0 -0
  84. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/inventory/python.py +0 -0
  85. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/inventory/target.py +0 -0
  86. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/reader.py +0 -0
  87. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/testbed_client/__init__.py +0 -0
  88. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/testbed_client/cache_path.py +0 -0
  89. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/testbed_client/client_abc_fix.py +0 -0
  90. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/testbed_client/client_web.py +0 -0
  91. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/testbed_client/user_model.py +0 -0
  92. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/vsource/__init__.py +0 -0
  93. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/vsource/target_model.py +0 -0
  94. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/vsource/virtual_converter_model.py +0 -0
  95. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/vsource/virtual_harvester_simulation.py +0 -0
  96. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/vsource/virtual_source_model.py +0 -0
  97. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core/writer.py +0 -0
  98. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core.egg-info/dependency_links.txt +0 -0
  99. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core.egg-info/requires.txt +0 -0
  100. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core.egg-info/top_level.txt +0 -0
  101. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/shepherd_core.egg-info/zip-safe +0 -0
  102. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/__init__.py +0 -0
  103. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/conftest.py +0 -0
  104. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/__init__.py +0 -0
  105. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/conftest.py +0 -0
  106. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/example_cal_data.yaml +0 -0
  107. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/example_cal_data_faulty.yaml +0 -0
  108. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/example_cal_meas.yaml +0 -0
  109. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/example_cal_meas_faulty1.yaml +0 -0
  110. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/example_cal_meas_faulty2.yaml +0 -0
  111. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/example_config_emulator.yaml +0 -0
  112. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/example_config_experiment.yaml +0 -0
  113. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/example_config_experiment_alternative.yaml +0 -0
  114. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/example_config_harvester.yaml +0 -0
  115. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/example_config_testbed.yaml +0 -0
  116. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/example_config_virtsource.yaml +0 -0
  117. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/test_base_models.py +0 -0
  118. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/test_content_fixtures.py +0 -0
  119. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/test_content_models.py +0 -0
  120. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/test_examples.py +0 -0
  121. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/test_experiment_models.py +0 -0
  122. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/test_task_generation.py +0 -0
  123. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/test_task_models.py +0 -0
  124. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/test_testbed_fixtures.py +0 -0
  125. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/data_models/test_testbed_models.py +0 -0
  126. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/decoder_waveform/__init__.py +0 -0
  127. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/decoder_waveform/test_decoder.py +0 -0
  128. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/fw_tools/__init__.py +0 -0
  129. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/fw_tools/build_msp.elf +0 -0
  130. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/fw_tools/build_nrf.elf +0 -0
  131. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/fw_tools/conftest.py +0 -0
  132. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/fw_tools/test_converter.py +0 -0
  133. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/fw_tools/test_patcher.py +0 -0
  134. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/fw_tools/test_validation.py +0 -0
  135. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/inventory/__init__.py +0 -0
  136. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/inventory/test_inventory.py +0 -0
  137. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/test_cal_hw.py +0 -0
  138. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/test_examples.py +0 -0
  139. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/test_logger.py +0 -0
  140. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/test_reader.py +0 -0
  141. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/test_writer.py +0 -0
  142. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/testbed_client/__init__.py +0 -0
  143. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/vsource/__init__.py +0 -0
  144. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/vsource/conftest.py +0 -0
  145. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/vsource/test_converter.py +0 -0
  146. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/vsource/test_harvester.py +0 -0
  147. {shepherd_core-2025.6.1 → shepherd_core-2025.6.3}/tests/vsource/test_z.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shepherd_core
3
- Version: 2025.6.1
3
+ Version: 2025.6.3
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>
@@ -8,7 +8,7 @@ from tqdm import trange
8
8
 
9
9
  from shepherd_core import Reader as ShpReader
10
10
  from shepherd_core import Writer as ShpWriter
11
- from shepherd_core import logger
11
+ from shepherd_core.logger import log
12
12
 
13
13
  # Config
14
14
  voltages_V = [4.0, 2.0]
@@ -26,7 +26,7 @@ for _v, _c in product(voltages_V, currents_A):
26
26
  file_path = path_here / f"{name}.h5"
27
27
 
28
28
  if file_path.exists():
29
- logger.info("File exists, will skip: %s", file_path.name)
29
+ log.info("File exists, will skip: %s", file_path.name)
30
30
  else:
31
31
  with ShpWriter(file_path) as file:
32
32
  file.store_hostname("artificial")
@@ -3,8 +3,8 @@
3
3
  from pathlib import Path
4
4
  from timeit import timeit
5
5
 
6
- from shepherd_core import logger
7
6
  from shepherd_core.decoder_waveform import Uart
7
+ from shepherd_core.logger import log
8
8
 
9
9
  # file captured with logic analyzer, 15.5k events (2700 symbols, 61 lines)
10
10
  trace = Path(__file__).parent / "uart_raw2.csv"
@@ -13,7 +13,7 @@ uwd = Uart(trace)
13
13
  sym = uwd.get_symbols()
14
14
  lne = uwd.get_lines()
15
15
  txt = uwd.get_text()
16
- logger.info(txt)
16
+ log.info(txt)
17
17
 
18
18
  do_analysis = False
19
19
  if do_analysis:
@@ -17,7 +17,7 @@ from .data_models.task.emulation import Compression
17
17
  from .inventory import Inventory
18
18
  from .logger import get_verbose_level
19
19
  from .logger import increase_verbose_level
20
- from .logger import logger
20
+ from .logger import log
21
21
  from .reader import Reader
22
22
  from .testbed_client.client_web import WebClient
23
23
  from .version import version
@@ -41,5 +41,5 @@ __all__ = [
41
41
  "increase_verbose_level",
42
42
  "local_now",
43
43
  "local_tz",
44
- "logger",
44
+ "log",
45
45
  ]
@@ -3,7 +3,7 @@
3
3
  Public models are directly referenced here and are usable like:
4
4
 
5
5
  '''python
6
- from shepherd-core import data_models
6
+ from shepherd_core import data_models
7
7
 
8
8
  cdata = data_models.CapeData(serial_number="A123")
9
9
  '''
@@ -2,6 +2,7 @@
2
2
 
3
3
  import hashlib
4
4
  import pathlib
5
+ import pickle
5
6
  from collections.abc import Generator
6
7
  from datetime import timedelta
7
8
  from ipaddress import IPv4Address
@@ -47,6 +48,24 @@ yaml.add_representer(IPv4Address, generic2str, SafeDumper)
47
48
  yaml.add_representer(UUID, generic2str, SafeDumper)
48
49
 
49
50
 
51
+ def path_to_str(old: dict) -> dict:
52
+ r"""Allow platform-independent pickling of ShpModel.
53
+
54
+ Helper Fn
55
+ Posix-Paths (/xyz/abc) in WindowsPath gets converted to \\xyz\\abc when exported
56
+ intended usage: pickle.dump(path_to_str(model.model_dump()))
57
+ """
58
+ new: dict = {}
59
+ for key, value in old.items():
60
+ if isinstance(value, Path):
61
+ new[key] = value.as_posix().replace("\\", "/")
62
+ elif isinstance(value, dict):
63
+ new[key] = path_to_str(value)
64
+ else:
65
+ new[key] = value
66
+ return new
67
+
68
+
50
69
  class ShpModel(BaseModel):
51
70
  """Pre-configured Pydantic Base-Model (specifically for shepherd).
52
71
 
@@ -126,40 +145,56 @@ class ShpModel(BaseModel):
126
145
  comment: Optional[str] = None,
127
146
  *,
128
147
  minimal: bool = True,
148
+ use_pickle: bool = False,
129
149
  ) -> Path:
130
150
  """Store data to yaml in a wrapper.
131
151
 
132
152
  minimal: stores minimal set (filters out unset & default parameters)
153
+ pickle: uses pickle to serialize data, on BBB >100x faster for large files
133
154
  comment: documentation.
134
155
  """
135
- model_dict = self.model_dump(exclude_unset=minimal)
136
156
  model_wrap = Wrapper(
137
157
  datatype=type(self).__name__,
138
158
  comment=comment,
139
159
  created=local_now(),
140
- parameters=model_dict,
141
- )
142
- model_yaml = yaml.safe_dump(
143
- model_wrap.model_dump(exclude_unset=minimal, exclude_defaults=minimal),
144
- default_flow_style=False,
145
- sort_keys=False,
160
+ parameters=self.model_dump(exclude_unset=minimal),
146
161
  )
162
+ model_dict = model_wrap.model_dump(exclude_unset=minimal, exclude_defaults=minimal)
163
+ if use_pickle:
164
+ model_serial = pickle.dumps(path_to_str(model_dict))
165
+ model_path = Path(path).resolve().with_suffix(".pickle")
166
+ else:
167
+ # TODO: x64 windows supports CSafeLoader/dumper,
168
+ # there are examples that replace load if avail
169
+ model_serial = yaml.safe_dump(
170
+ model_dict,
171
+ default_flow_style=False,
172
+ sort_keys=False,
173
+ )
174
+ model_path = Path(path).resolve().with_suffix(".yaml")
147
175
  # TODO: handle directory
148
- model_path = Path(path).resolve().with_suffix(".yaml")
176
+
149
177
  if not model_path.parent.exists():
150
178
  model_path.parent.mkdir(parents=True)
151
179
  with model_path.open("w") as f:
152
- f.write(model_yaml)
180
+ f.write(model_serial)
153
181
  return model_path
154
182
 
155
183
  @classmethod
156
184
  def from_file(cls, path: Union[str, Path]) -> Self:
157
- """Load from yaml."""
158
- with Path(path).open() as shp_file:
159
- shp_dict = yaml.safe_load(shp_file)
185
+ """Load from YAML or pickle file."""
186
+ path: Path = Path(path)
187
+ if not Path(path).exists():
188
+ raise FileNotFoundError
189
+ if path.suffix.lower() == ".pickle":
190
+ with Path(path).open("rb") as shp_file:
191
+ shp_dict = pickle.load(shp_file) # noqa: S301
192
+ else:
193
+ with Path(path).open() as shp_file:
194
+ shp_dict = yaml.safe_load(shp_file)
160
195
  shp_wrap = Wrapper(**shp_dict)
161
196
  if shp_wrap.datatype != cls.__name__:
162
- raise ValueError("Model in file does not match the requirement")
197
+ raise ValueError("Model in file does not match the actual Class")
163
198
  return cls(**shp_wrap.parameters)
164
199
 
165
200
  def get_hash(self) -> str:
@@ -19,7 +19,7 @@ from typing_extensions import Unpack
19
19
  from shepherd_core import fw_tools
20
20
  from shepherd_core.data_models.base.content import ContentModel
21
21
  from shepherd_core.data_models.testbed.mcu import MCU
22
- from shepherd_core.logger import logger
22
+ from shepherd_core.logger import log
23
23
  from shepherd_core.testbed_client import tb_client
24
24
 
25
25
  from .firmware_datatype import FirmwareDType
@@ -135,8 +135,8 @@ class Firmware(ContentModel, title="Firmware of Target"):
135
135
  if ("nrf52" in arch or "arm" in arch) and not fw_tools.is_elf_nrf52(file):
136
136
  raise ValueError("File is not a ELF for nRF52")
137
137
  except RuntimeError:
138
- logger.warning("ObjCopy not found -> Arch of Firmware can't be verified")
139
- logger.debug("ELF-File '%s' has arch: %s", file.name, arch)
138
+ log.warning("ObjCopy not found -> Arch of Firmware can't be verified")
139
+ log.debug("ELF-File '%s' has arch: %s", file.name, arch)
140
140
  if "mcu" not in kwargs:
141
141
  kwargs["mcu"] = arch_to_mcu[arch]
142
142
 
@@ -159,7 +159,7 @@ class Firmware(ContentModel, title="Firmware of Target"):
159
159
  match = self.data_hash == hash_new
160
160
 
161
161
  if not match:
162
- logger.warning("FW-Hash does not match with stored value!")
162
+ log.warning("FW-Hash does not match with stored value!")
163
163
  # TODO: it might be more appropriate to raise here
164
164
  return match
165
165
 
@@ -14,7 +14,7 @@ from shepherd_core.config import config
14
14
  from shepherd_core.data_models.base.calibration import CalibrationHarvester
15
15
  from shepherd_core.data_models.base.content import ContentModel
16
16
  from shepherd_core.data_models.base.shepherd import ShpModel
17
- from shepherd_core.logger import logger
17
+ from shepherd_core.logger import log
18
18
  from shepherd_core.testbed_client import tb_client
19
19
 
20
20
  from .energy_environment import EnergyDType
@@ -310,7 +310,7 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
310
310
  if values["name"] == "neutral":
311
311
  # TODO: same test is later done in calc_algorithm_num() again
312
312
  raise ValueError("Resulting Harvester can't be neutral")
313
- logger.debug("VHrv-Inheritances: %s", chain)
313
+ log.debug("VHrv-Inheritances: %s", chain)
314
314
 
315
315
  # post corrections -> should be in separate validator
316
316
  cal = CalibrationHarvester() # TODO: as argument?
@@ -372,7 +372,7 @@ class VirtualHarvesterConfig(ContentModel, title="Config for the Harvester"):
372
372
  duration_ms = min(max(self.duration_ms, time_min_ms), interval_ms)
373
373
  _ratio = (duration_ms / interval_ms) / (self.duration_ms / self.interval_ms)
374
374
  if (_ratio - 1) > 0.1:
375
- logger.debug(
375
+ log.debug(
376
376
  "Ratio between interval & duration has changed "
377
377
  "more than 10%% due to constraints (%.4f)",
378
378
  _ratio,
@@ -10,7 +10,7 @@ from typing_extensions import Self
10
10
  from shepherd_core.config import config
11
11
  from shepherd_core.data_models.base.content import ContentModel
12
12
  from shepherd_core.data_models.base.shepherd import ShpModel
13
- from shepherd_core.logger import logger
13
+ from shepherd_core.logger import log
14
14
  from shepherd_core.testbed_client import tb_client
15
15
 
16
16
  from .energy_environment import EnergyDType
@@ -128,7 +128,7 @@ class VirtualSourceConfig(ContentModel, title="Config for the virtual Source"):
128
128
  def query_database(cls, values: dict[str, Any]) -> dict[str, Any]:
129
129
  values, chain = tb_client.try_completing_model(cls.__name__, values)
130
130
  values = tb_client.fill_in_user_data(values)
131
- logger.debug("VSrc-Inheritances: %s", chain)
131
+ log.debug("VSrc-Inheritances: %s", chain)
132
132
  return values
133
133
 
134
134
  @model_validator(mode="after")
@@ -187,7 +187,7 @@ class VirtualSourceConfig(ContentModel, title="Config for the virtual Source"):
187
187
  if not (isinstance(dV_output_en_thrs_mV, (int, float)) and (dV_output_en_thrs_mV >= 0)):
188
188
  dV_output_en_thrs_mV = 0
189
189
  if not (isinstance(dV_output_imed_low_mV, (int, float)) and (dV_output_imed_low_mV >= 0)):
190
- logger.warning("VSrc: C_output shouldn't be larger than C_intermediate")
190
+ log.warning("VSrc: C_output shouldn't be larger than C_intermediate")
191
191
  dV_output_imed_low_mV = 0
192
192
 
193
193
  # decide which hysteresis-thresholds to use for buck-converter
@@ -224,7 +224,7 @@ class VirtualSourceConfig(ContentModel, title="Config for the virtual Source"):
224
224
  enable_storage = self.C_intermediate_uF > 0
225
225
  enable_boost = self.enable_boost and enable_storage
226
226
  if enable_boost != self.enable_boost:
227
- logger.warning("VSrc - boost was disabled due to missing storage capacitor!")
227
+ log.warning("VSrc - boost was disabled due to missing storage capacitor!")
228
228
  enable_feedback = (
229
229
  self.enable_feedback_to_hrv
230
230
  and enable_storage
@@ -235,7 +235,7 @@ class VirtualSourceConfig(ContentModel, title="Config for the virtual Source"):
235
235
  reason = "enabled boost, " if enable_boost else ""
236
236
  reason += "" if dtype_in == EnergyDType.ivcurve else "input not ivcurve, "
237
237
  reason += "" if enable_storage else "no storage capacitor"
238
- logger.warning("VSRC - feedback to harvester was disabled! Reasons: %s", reason)
238
+ log.warning("VSRC - feedback to harvester was disabled! Reasons: %s", reason)
239
239
  return (
240
240
  1 * int(enable_storage)
241
241
  + 2 * int(enable_boost)
@@ -13,9 +13,9 @@ from pydantic import model_validator
13
13
  from typing_extensions import Self
14
14
  from typing_extensions import deprecated
15
15
 
16
- from shepherd_core import logger
17
16
  from shepherd_core.data_models.base.shepherd import ShpModel
18
17
  from shepherd_core.data_models.testbed.gpio import GPIO
18
+ from shepherd_core.logger import log
19
19
 
20
20
  # defaults (pre-init complex types)
21
21
  zero_duration = timedelta(seconds=0)
@@ -190,7 +190,7 @@ class GpioTracing(ShpModel, title="Config for GPIO-Tracing"):
190
190
  if self.duration and self.duration.total_seconds() < 0:
191
191
  raise ValueError("Duration can't be negative.")
192
192
  if self.uart_decode:
193
- logger.error(
193
+ log.error(
194
194
  "Feature GpioTracing.uart_decode reserved for future use. "
195
195
  "Use UartLogging or manually decode serial with the provided waveform decoder."
196
196
  )
@@ -3,6 +3,7 @@
3
3
  These models import externally from all other model-modules!
4
4
  """
5
5
 
6
+ import pickle
6
7
  from pathlib import Path
7
8
  from typing import Optional
8
9
  from typing import Union
@@ -11,7 +12,7 @@ import yaml
11
12
 
12
13
  from shepherd_core.data_models.base.shepherd import ShpModel
13
14
  from shepherd_core.data_models.base.wrapper import Wrapper
14
- from shepherd_core.logger import logger
15
+ from shepherd_core.logger import log
15
16
 
16
17
  from .emulation import Compression
17
18
  from .emulation import EmulationTask
@@ -44,7 +45,11 @@ def prepare_task(config: Union[ShpModel, Path, str], observer: Optional[str] = N
44
45
  if isinstance(config, str):
45
46
  config = Path(config)
46
47
 
47
- if isinstance(config, Path):
48
+ if isinstance(config, Path) and config.exists() and config.suffix.lower() == ".pickle":
49
+ with config.resolve().open("rb") as shp_file:
50
+ shp_dict = pickle.load(shp_file) # noqa: S301
51
+ shp_wrap = Wrapper(**shp_dict)
52
+ elif isinstance(config, Path) and config.exists() and config.suffix.lower() == ".yaml":
48
53
  with config.resolve().open() as shp_file:
49
54
  shp_dict = yaml.safe_load(shp_file)
50
55
  shp_wrap = Wrapper(**shp_dict)
@@ -59,12 +64,12 @@ def prepare_task(config: Union[ShpModel, Path, str], observer: Optional[str] = N
59
64
 
60
65
  if shp_wrap.datatype == TestbedTasks.__name__:
61
66
  if observer is None:
62
- logger.debug(
67
+ log.debug(
63
68
  "Task-Set contained TestbedTasks & no observer was provided -> will return TB-Tasks"
64
69
  )
65
70
  return shp_wrap
66
71
  tbt = TestbedTasks(**shp_wrap.parameters)
67
- logger.debug("Loading Testbed-Tasks %s for %s", tbt.name, observer)
72
+ log.debug("Loading Testbed-Tasks %s for %s", tbt.name, observer)
68
73
  obt = tbt.get_observer_tasks(observer)
69
74
  if obt is None:
70
75
  msg = f"Observer '{observer}' is not in TestbedTask-Set"
@@ -1,10 +1,12 @@
1
1
  """Configuration for the Observer in Emulation-Mode."""
2
2
 
3
3
  import copy
4
+ from collections.abc import Set as AbstractSet
4
5
  from datetime import datetime
5
6
  from datetime import timedelta
6
7
  from enum import Enum
7
8
  from pathlib import Path
9
+ from pathlib import PurePosixPath
8
10
  from typing import Annotated
9
11
  from typing import Optional
10
12
  from typing import Union
@@ -28,7 +30,9 @@ from shepherd_core.data_models.experiment.observer_features import UartLogging
28
30
  from shepherd_core.data_models.experiment.target_config import vsrc_neutral
29
31
  from shepherd_core.data_models.testbed import Testbed
30
32
  from shepherd_core.data_models.testbed.cape import TargetPort
31
- from shepherd_core.logger import logger
33
+ from shepherd_core.logger import log
34
+
35
+ from .helper_paths import path_posix
32
36
 
33
37
 
34
38
  class Compression(str, Enum):
@@ -149,9 +153,9 @@ class EmulationTask(ShpModel):
149
153
  _io is not None for _io in (self.gpio_actuation, self.gpio_tracing, self.uart_logging)
150
154
  )
151
155
  if self.enable_io and not io_requested:
152
- logger.warning("Target IO enabled, but no feature requested IO")
156
+ log.warning("Target IO enabled, but no feature requested IO")
153
157
  if not self.enable_io and io_requested:
154
- logger.warning("Target IO not enabled, but a feature requested IO")
158
+ log.warning("Target IO not enabled, but a feature requested IO")
155
159
  return self
156
160
 
157
161
  @classmethod
@@ -165,8 +169,8 @@ class EmulationTask(ShpModel):
165
169
  )
166
170
 
167
171
  return cls(
168
- input_path=tgt_cfg.energy_env.data_path,
169
- output_path=root_path / f"emu_{obs.name}.h5",
172
+ input_path=path_posix(tgt_cfg.energy_env.data_path),
173
+ output_path=path_posix(root_path / f"emu_{obs.name}.h5"),
170
174
  time_start=copy.copy(xp.time_start),
171
175
  duration=xp.duration,
172
176
  enable_io=io_requested,
@@ -180,6 +184,11 @@ class EmulationTask(ShpModel):
180
184
  sys_logging=xp.sys_logging,
181
185
  )
182
186
 
187
+ def is_contained(self, paths: AbstractSet[PurePosixPath]) -> bool:
188
+ all_ok = any(self.input_path.is_relative_to(path) for path in paths)
189
+ all_ok &= any(self.output_path.is_relative_to(path) for path in paths)
190
+ return all_ok
191
+
183
192
 
184
193
  # TODO: herdConfig
185
194
  # - store if path is remote (read & write)
@@ -1,7 +1,8 @@
1
1
  """Config for Task that adds the custom ID to the firmware & stores it into a file."""
2
2
 
3
- import copy
3
+ from collections.abc import Set as AbstractSet
4
4
  from pathlib import Path
5
+ from pathlib import PurePosixPath
5
6
  from typing import Annotated
6
7
  from typing import Optional
7
8
  from typing import TypedDict
@@ -22,7 +23,9 @@ from shepherd_core.data_models.experiment.experiment import Experiment
22
23
  from shepherd_core.data_models.testbed import Testbed
23
24
  from shepherd_core.data_models.testbed.target import IdInt16
24
25
  from shepherd_core.data_models.testbed.target import MCUPort
25
- from shepherd_core.logger import logger
26
+ from shepherd_core.logger import log
27
+
28
+ from .helper_paths import path_posix
26
29
 
27
30
 
28
31
  class FirmwareModTask(ShpModel):
@@ -42,7 +45,7 @@ class FirmwareModTask(ShpModel):
42
45
  FirmwareDType.base64_hex,
43
46
  FirmwareDType.path_hex,
44
47
  }:
45
- logger.warning("Firmware is scheduled to get custom-ID but is not in elf-format")
48
+ log.warning("Firmware is scheduled to get custom-ID but is not in elf-format")
46
49
  return self
47
50
 
48
51
  @classmethod
@@ -68,10 +71,10 @@ class FirmwareModTask(ShpModel):
68
71
  fw_id = obs.get_target(tgt_id).testbed_id
69
72
 
70
73
  return cls(
71
- data=fw.data,
74
+ data=path_posix(fw.data) if isinstance(fw.data, Path) else fw.data,
72
75
  data_type=fw.data_type,
73
76
  custom_id=fw_id,
74
- firmware_file=copy.copy(fw_path),
77
+ firmware_file=path_posix(fw_path),
75
78
  )
76
79
 
77
80
  @classmethod
@@ -90,3 +93,9 @@ class FirmwareModTask(ShpModel):
90
93
  path_new: Path = path / fw.name
91
94
  kwargs["firmware_file"] = path_new.with_suffix(".hex")
92
95
  return cls(**kwargs)
96
+
97
+ def is_contained(self, paths: AbstractSet[PurePosixPath]) -> bool:
98
+ all_ok = any(self.firmware_file.is_relative_to(path) for path in paths)
99
+ if isinstance(self.data, Path):
100
+ all_ok = any(self.data.is_relative_to(path) for path in paths)
101
+ return all_ok
@@ -1,8 +1,10 @@
1
1
  """Config for the Observer in Harvest-Mode to record IV data from a harvesting-source."""
2
2
 
3
+ from collections.abc import Set as AbstractSet
3
4
  from datetime import datetime
4
5
  from datetime import timedelta
5
6
  from pathlib import Path
7
+ from pathlib import PurePosixPath
6
8
  from typing import Annotated
7
9
  from typing import Optional
8
10
 
@@ -84,3 +86,6 @@ class HarvestTask(ShpModel):
84
86
  if self.duration and self.duration.total_seconds() < 0:
85
87
  raise ValueError("Task-Duration can't be negative.")
86
88
  return self
89
+
90
+ def is_contained(self, paths: AbstractSet[PurePosixPath]) -> bool:
91
+ return any(self.output_path.is_relative_to(path) for path in paths)
@@ -0,0 +1,15 @@
1
+ r"""Helper FN to avoid unwanted behavior.
2
+
3
+ On windows Path("\xyz") gets transformed to "/xyz", but not on linux.
4
+ When sending an experiment via fastapi, this bug gets triggered.
5
+ """
6
+
7
+ from pathlib import Path
8
+
9
+
10
+ def path_posix(path: Path) -> Path:
11
+ r"""Help Linux to get from "\xyz" to "/xyz".
12
+
13
+ This isn't a problem on windows and gets triggered when sending XP via fastapi.
14
+ """
15
+ return Path(path.as_posix().replace("\\", "/"))
@@ -1,8 +1,9 @@
1
1
  """Collection of tasks for selected observer included in experiment."""
2
2
 
3
+ from collections.abc import Set as AbstractSet
3
4
  from datetime import datetime
4
- from datetime import timedelta
5
5
  from pathlib import Path
6
+ from pathlib import PurePosixPath
6
7
  from typing import Annotated
7
8
  from typing import Optional
8
9
 
@@ -18,6 +19,7 @@ from shepherd_core.data_models.testbed.testbed import Testbed
18
19
 
19
20
  from .emulation import EmulationTask
20
21
  from .firmware_mod import FirmwareModTask
22
+ from .helper_paths import path_posix
21
23
  from .programming import ProgrammingTask
22
24
 
23
25
 
@@ -27,7 +29,7 @@ class ObserverTasks(ShpModel):
27
29
  observer: NameStr
28
30
 
29
31
  # PRE PROCESS
30
- time_prep: datetime # TODO: should be optional
32
+ time_prep: Optional[datetime] = None # TODO: currently not used
31
33
  root_path: Path
32
34
 
33
35
  # fw mod, store as hex-file and program
@@ -55,24 +57,14 @@ class ObserverTasks(ShpModel):
55
57
  if not tb.shared_storage:
56
58
  raise ValueError("Implementation currently relies on shared storage!")
57
59
 
58
- t_start = (
59
- xp.time_start
60
- if isinstance(xp.time_start, datetime)
61
- else datetime.now().astimezone() + timedelta(minutes=3)
62
- ) # TODO: this is messed up, just a hotfix
63
-
64
60
  obs = tb.get_observer(tgt_id)
65
- xp_dir = "experiments/" + xp.name + "_" + t_start.strftime("%Y-%m-%d_%H-%M-%S")
66
- root_path = tb.data_on_observer / xp_dir
67
- # TODO: Paths should be "friendlier"
68
- # - replace whitespace with "_" and remove non-alphanum?
69
-
61
+ root_path = tb.data_on_observer / "experiments" / xp.folder_name()
70
62
  fw_paths = [root_path / f"fw{_i}_{obs.name}.hex" for _i in [1, 2]]
71
63
 
72
64
  return cls(
73
65
  observer=obs.name,
74
- time_prep=t_start - tb.prep_duration,
75
- root_path=root_path,
66
+ # time_prep=
67
+ root_path=path_posix(root_path),
76
68
  fw1_mod=FirmwareModTask.from_xp(xp, tb, tgt_id, 1, fw_paths[0]),
77
69
  fw2_mod=FirmwareModTask.from_xp(xp, tb, tgt_id, 2, fw_paths[1]),
78
70
  fw1_prog=ProgrammingTask.from_xp(xp, tb, tgt_id, 1, fw_paths[0]),
@@ -100,3 +92,12 @@ class ObserverTasks(ShpModel):
100
92
  raise ValueError("Emu-Task should have a valid output-path")
101
93
  values[self.observer] = self.emulation.output_path
102
94
  return values
95
+
96
+ def is_contained(self, paths: AbstractSet[PurePosixPath]) -> bool:
97
+ all_ok = any(self.root_path.is_relative_to(path) for path in paths)
98
+ all_ok &= self.fw1_mod.is_contained(paths)
99
+ all_ok &= self.fw2_mod.is_contained(paths)
100
+ all_ok &= self.fw1_prog.is_contained(paths)
101
+ all_ok &= self.fw2_prog.is_contained(paths)
102
+ all_ok &= self.emulation.is_contained(paths)
103
+ return all_ok
@@ -1,7 +1,8 @@
1
1
  """Config for a Task programming the selected target."""
2
2
 
3
- import copy
3
+ from collections.abc import Set as AbstractSet
4
4
  from pathlib import Path
5
+ from pathlib import PurePosixPath
5
6
  from typing import Annotated
6
7
  from typing import Optional
7
8
 
@@ -21,6 +22,8 @@ from shepherd_core.data_models.testbed.mcu import ProgrammerProtocol
21
22
  from shepherd_core.data_models.testbed.target import MCUPort
22
23
  from shepherd_core.data_models.testbed.testbed import Testbed
23
24
 
25
+ from .helper_paths import path_posix
26
+
24
27
 
25
28
  class ProgrammingTask(ShpModel):
26
29
  """Config for a Task programming the selected target."""
@@ -66,7 +69,7 @@ class ProgrammingTask(ShpModel):
66
69
  return None
67
70
 
68
71
  return cls(
69
- firmware_file=copy.copy(fw_path),
72
+ firmware_file=path_posix(fw_path),
70
73
  target_port=obs.get_target_port(tgt_id),
71
74
  mcu_port=mcu_port,
72
75
  mcu_type=fw.mcu.name,
@@ -74,3 +77,6 @@ class ProgrammingTask(ShpModel):
74
77
  datarate=fw.mcu.prog_datarate,
75
78
  protocol=fw.mcu.prog_protocol,
76
79
  )
80
+
81
+ def is_contained(self, paths: AbstractSet[PurePosixPath]) -> bool:
82
+ return any(self.firmware_file.is_relative_to(path) for path in paths)
@@ -1,6 +1,8 @@
1
1
  """Collection of tasks for all observers included in experiment."""
2
2
 
3
3
  from pathlib import Path
4
+ from pathlib import PurePosixPath
5
+ from typing import TYPE_CHECKING
4
6
  from typing import Annotated
5
7
  from typing import Optional
6
8
 
@@ -17,6 +19,9 @@ from shepherd_core.data_models.testbed.testbed import Testbed
17
19
 
18
20
  from .observer_tasks import ObserverTasks
19
21
 
22
+ if TYPE_CHECKING:
23
+ from collections.abc import Set as AbstractSet
24
+
20
25
 
21
26
  class TestbedTasks(ShpModel):
22
27
  """Collection of tasks for all observers included in experiment."""
@@ -56,3 +61,10 @@ class TestbedTasks(ShpModel):
56
61
  for obt in self.observer_tasks:
57
62
  values = {**values, **obt.get_output_paths()}
58
63
  return values
64
+
65
+ def is_contained(self) -> bool:
66
+ paths_allowed: AbstractSet[PurePosixPath] = {
67
+ PurePosixPath("/var/shepherd/"),
68
+ PurePosixPath("/tmp/"), # noqa: S108
69
+ }
70
+ return all(obt.is_contained(paths_allowed) for obt in self.observer_tasks)