shepherd-core 2023.8.6__py3-none-any.whl → 2023.8.8__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.
- shepherd_core/__init__.py +1 -1
- shepherd_core/data_models/__init__.py +3 -1
- shepherd_core/data_models/base/cal_measurement.py +17 -14
- shepherd_core/data_models/base/calibration.py +41 -8
- shepherd_core/data_models/base/content.py +17 -13
- shepherd_core/data_models/base/shepherd.py +29 -22
- shepherd_core/data_models/base/wrapper.py +5 -4
- shepherd_core/data_models/content/energy_environment.py +3 -2
- shepherd_core/data_models/content/firmware.py +10 -6
- shepherd_core/data_models/content/virtual_harvester.py +42 -39
- shepherd_core/data_models/content/virtual_source.py +83 -72
- shepherd_core/data_models/doc_virtual_source.py +7 -14
- shepherd_core/data_models/experiment/experiment.py +20 -15
- shepherd_core/data_models/experiment/observer_features.py +33 -31
- shepherd_core/data_models/experiment/target_config.py +24 -18
- shepherd_core/data_models/task/__init__.py +13 -5
- shepherd_core/data_models/task/emulation.py +35 -23
- shepherd_core/data_models/task/firmware_mod.py +14 -13
- shepherd_core/data_models/task/harvest.py +28 -13
- shepherd_core/data_models/task/observer_tasks.py +17 -7
- shepherd_core/data_models/task/programming.py +13 -13
- shepherd_core/data_models/task/testbed_tasks.py +16 -6
- shepherd_core/data_models/testbed/cape.py +3 -2
- shepherd_core/data_models/testbed/gpio.py +18 -15
- shepherd_core/data_models/testbed/mcu.py +7 -6
- shepherd_core/data_models/testbed/observer.py +23 -19
- shepherd_core/data_models/testbed/target.py +15 -14
- shepherd_core/data_models/testbed/testbed.py +14 -11
- shepherd_core/fw_tools/converter.py +7 -7
- shepherd_core/fw_tools/converter_elf.py +2 -2
- shepherd_core/fw_tools/patcher.py +7 -6
- shepherd_core/fw_tools/validation.py +3 -3
- shepherd_core/inventory/__init__.py +16 -8
- shepherd_core/inventory/python.py +4 -3
- shepherd_core/inventory/system.py +5 -5
- shepherd_core/inventory/target.py +4 -4
- shepherd_core/reader.py +3 -3
- shepherd_core/testbed_client/client.py +6 -4
- shepherd_core/testbed_client/user_model.py +14 -10
- shepherd_core/writer.py +2 -2
- {shepherd_core-2023.8.6.dist-info → shepherd_core-2023.8.8.dist-info}/METADATA +9 -2
- {shepherd_core-2023.8.6.dist-info → shepherd_core-2023.8.8.dist-info}/RECORD +49 -49
- {shepherd_core-2023.8.6.dist-info → shepherd_core-2023.8.8.dist-info}/WHEEL +1 -1
- tests/data_models/example_cal_data.yaml +2 -1
- tests/data_models/example_cal_meas.yaml +2 -1
- tests/data_models/test_base_models.py +19 -2
- tests/inventory/test_inventory.py +1 -1
- {shepherd_core-2023.8.6.dist-info → shepherd_core-2023.8.8.dist-info}/top_level.txt +0 -0
- {shepherd_core-2023.8.6.dist-info → shepherd_core-2023.8.8.dist-info}/zip-safe +0 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
from datetime import timedelta
|
|
2
2
|
from pathlib import Path
|
|
3
|
+
from typing import List
|
|
3
4
|
from typing import Optional
|
|
4
5
|
|
|
6
|
+
from pydantic import Field
|
|
5
7
|
from pydantic import HttpUrl
|
|
6
|
-
from pydantic import
|
|
7
|
-
from
|
|
8
|
+
from pydantic import model_validator
|
|
9
|
+
from typing_extensions import Annotated
|
|
8
10
|
|
|
9
11
|
from ...testbed_client import tb_client
|
|
10
12
|
from ..base.content import IdInt
|
|
@@ -22,9 +24,9 @@ class Testbed(ShpModel):
|
|
|
22
24
|
description: SafeStr
|
|
23
25
|
comment: Optional[SafeStr] = None
|
|
24
26
|
|
|
25
|
-
url: Optional[HttpUrl]
|
|
27
|
+
url: Optional[HttpUrl] = None
|
|
26
28
|
|
|
27
|
-
observers:
|
|
29
|
+
observers: Annotated[List[Observer], Field(min_length=1, max_length=64)]
|
|
28
30
|
|
|
29
31
|
shared_storage: bool = True
|
|
30
32
|
data_on_server: Path
|
|
@@ -34,20 +36,21 @@ class Testbed(ShpModel):
|
|
|
34
36
|
prep_duration: timedelta = timedelta(minutes=5)
|
|
35
37
|
# TODO: one BBone is currently time-keeper
|
|
36
38
|
|
|
37
|
-
@
|
|
39
|
+
@model_validator(mode="before")
|
|
40
|
+
@classmethod
|
|
38
41
|
def query_database(cls, values: dict) -> dict:
|
|
39
42
|
values, _ = tb_client.try_completing_model(cls.__name__, values)
|
|
40
43
|
return values
|
|
41
44
|
|
|
42
|
-
@
|
|
43
|
-
def post_validation(
|
|
45
|
+
@model_validator(mode="after")
|
|
46
|
+
def post_validation(self):
|
|
44
47
|
observers = []
|
|
45
48
|
ips = []
|
|
46
49
|
macs = []
|
|
47
50
|
capes = []
|
|
48
51
|
targets = []
|
|
49
52
|
eth_ports = []
|
|
50
|
-
for _obs in
|
|
53
|
+
for _obs in self.observers:
|
|
51
54
|
observers.append(_obs.id)
|
|
52
55
|
ips.append(_obs.ip)
|
|
53
56
|
macs.append(_obs.mac)
|
|
@@ -70,11 +73,11 @@ class Testbed(ShpModel):
|
|
|
70
73
|
raise ValueError("Target used more than once in Testbed")
|
|
71
74
|
if len(eth_ports) > len(set(eth_ports)):
|
|
72
75
|
raise ValueError("Observers-Ethernet-Port used more than once in Testbed")
|
|
73
|
-
if
|
|
76
|
+
if self.prep_duration.total_seconds() < 0:
|
|
74
77
|
raise ValueError("Task-Duration can't be negative.")
|
|
75
|
-
if not
|
|
78
|
+
if not self.shared_storage:
|
|
76
79
|
raise ValueError("Only shared-storage-option is implemented")
|
|
77
|
-
return
|
|
80
|
+
return self
|
|
78
81
|
|
|
79
82
|
def get_observer(self, target_id: int) -> Observer:
|
|
80
83
|
for _observer in self.observers:
|
|
@@ -5,7 +5,7 @@ from pathlib import Path
|
|
|
5
5
|
from typing import Union
|
|
6
6
|
|
|
7
7
|
import zstandard as zstd
|
|
8
|
-
from pydantic import
|
|
8
|
+
from pydantic import validate_call
|
|
9
9
|
|
|
10
10
|
from ..data_models.content.firmware_datatype import FirmwareDType
|
|
11
11
|
from .converter_elf import elf_to_hex
|
|
@@ -13,7 +13,7 @@ from .validation import is_elf
|
|
|
13
13
|
from .validation import is_hex
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
@
|
|
16
|
+
@validate_call
|
|
17
17
|
def firmware_to_hex(file_path: Path) -> Path:
|
|
18
18
|
"""Generic converter that handles ELF & HEX"""
|
|
19
19
|
if not file_path.is_file():
|
|
@@ -28,7 +28,7 @@ def firmware_to_hex(file_path: Path) -> Path:
|
|
|
28
28
|
)
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
@
|
|
31
|
+
@validate_call
|
|
32
32
|
def file_to_base64(file_path: Path) -> str:
|
|
33
33
|
"""Compress and encode content of file
|
|
34
34
|
- base64 adds ~33 % overhead
|
|
@@ -42,7 +42,7 @@ def file_to_base64(file_path: Path) -> str:
|
|
|
42
42
|
return base64.b64encode(file_cmpress).decode("ascii")
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
@
|
|
45
|
+
@validate_call
|
|
46
46
|
def base64_to_file(content: str, file_path: Path) -> None:
|
|
47
47
|
"""DeCompress and decode Content of file
|
|
48
48
|
- base64 adds ~33 % overhead
|
|
@@ -54,7 +54,7 @@ def base64_to_file(content: str, file_path: Path) -> None:
|
|
|
54
54
|
file.write(file_content)
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
@
|
|
57
|
+
@validate_call
|
|
58
58
|
def file_to_hash(file_path: Path) -> str:
|
|
59
59
|
if not file_path.is_file():
|
|
60
60
|
raise ValueError("Fn needs an existing file as input")
|
|
@@ -63,14 +63,14 @@ def file_to_hash(file_path: Path) -> str:
|
|
|
63
63
|
return hashlib.sha3_224(file_content).hexdigest()
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
@
|
|
66
|
+
@validate_call
|
|
67
67
|
def base64_to_hash(content: str) -> str:
|
|
68
68
|
file_cmpress = base64.b64decode(content)
|
|
69
69
|
file_content = zstd.ZstdDecompressor().decompress(file_cmpress)
|
|
70
70
|
return hashlib.sha3_224(file_content).hexdigest()
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
@
|
|
73
|
+
@validate_call
|
|
74
74
|
def extract_firmware(
|
|
75
75
|
data: Union[str, Path], data_type: FirmwareDType, file_path: Path
|
|
76
76
|
) -> Path:
|
|
@@ -2,12 +2,12 @@ import subprocess # noqa: S404
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
|
-
from pydantic import
|
|
5
|
+
from pydantic import validate_call
|
|
6
6
|
|
|
7
7
|
# extra src-file necessary to prevent circular import
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
@
|
|
10
|
+
@validate_call
|
|
11
11
|
def elf_to_hex(file_elf: Path, file_hex: Optional[Path] = None) -> Path:
|
|
12
12
|
if not file_elf.is_file():
|
|
13
13
|
raise ValueError("Fn needs an existing file as input")
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
from typing import Optional
|
|
3
3
|
|
|
4
|
-
from pydantic import
|
|
5
|
-
from pydantic import
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
from pydantic import validate_call
|
|
6
|
+
from typing_extensions import Annotated
|
|
6
7
|
|
|
7
8
|
from ..commons import uid_len_default
|
|
8
9
|
from ..commons import uid_str_default
|
|
@@ -21,7 +22,7 @@ except ImportError as e:
|
|
|
21
22
|
)
|
|
22
23
|
|
|
23
24
|
|
|
24
|
-
@
|
|
25
|
+
@validate_call
|
|
25
26
|
def find_symbol(file_elf: Path, symbol: str) -> bool:
|
|
26
27
|
if symbol is None or not is_elf(file_elf):
|
|
27
28
|
return False
|
|
@@ -44,7 +45,7 @@ def find_symbol(file_elf: Path, symbol: str) -> bool:
|
|
|
44
45
|
return True
|
|
45
46
|
|
|
46
47
|
|
|
47
|
-
@
|
|
48
|
+
@validate_call
|
|
48
49
|
def read_symbol(
|
|
49
50
|
file_elf: Path, symbol: str, length: int = uid_len_default
|
|
50
51
|
) -> Optional[int]:
|
|
@@ -76,11 +77,11 @@ def read_arch(file_elf: Path) -> Optional[str]:
|
|
|
76
77
|
return None
|
|
77
78
|
|
|
78
79
|
|
|
79
|
-
@
|
|
80
|
+
@validate_call
|
|
80
81
|
def modify_symbol_value(
|
|
81
82
|
file_elf: Path,
|
|
82
83
|
symbol: str,
|
|
83
|
-
value:
|
|
84
|
+
value: Annotated[int, Field(ge=0, lt=2 ** (8 * uid_len_default))],
|
|
84
85
|
overwrite: bool = False,
|
|
85
86
|
) -> Optional[Path]:
|
|
86
87
|
"""replaces value of symbol in ELF-File, hardcoded for uint16_t (2 byte)
|
|
@@ -7,7 +7,7 @@ from pathlib import Path
|
|
|
7
7
|
|
|
8
8
|
from intelhex import IntelHex
|
|
9
9
|
from intelhex import IntelHexError
|
|
10
|
-
from pydantic import
|
|
10
|
+
from pydantic import validate_call
|
|
11
11
|
|
|
12
12
|
from ..data_models.content.firmware_datatype import FirmwareDType
|
|
13
13
|
from ..logger import logger
|
|
@@ -26,7 +26,7 @@ except ImportError as e:
|
|
|
26
26
|
)
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
@
|
|
29
|
+
@validate_call
|
|
30
30
|
def is_hex(file: Path):
|
|
31
31
|
try:
|
|
32
32
|
_ = IntelHex(file.as_posix())
|
|
@@ -80,7 +80,7 @@ def is_hex_nrf52(file: Path) -> bool:
|
|
|
80
80
|
# https://github.com/eliben/pyelftools/wiki/User's-guide
|
|
81
81
|
|
|
82
82
|
|
|
83
|
-
@
|
|
83
|
+
@validate_call
|
|
84
84
|
def is_elf(file: Path) -> bool:
|
|
85
85
|
if not elf_support:
|
|
86
86
|
raise RuntimeError(elf_error_text)
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
- hardware-config
|
|
5
5
|
"""
|
|
6
6
|
from pathlib import Path
|
|
7
|
+
from typing import List
|
|
7
8
|
|
|
8
|
-
from pydantic
|
|
9
|
+
from pydantic import Field
|
|
10
|
+
from typing_extensions import Annotated
|
|
9
11
|
|
|
10
12
|
from ..data_models import ShpModel
|
|
11
13
|
from .python import PythonInventory
|
|
@@ -27,15 +29,21 @@ class Inventory(PythonInventory, SystemInventory, TargetInventory):
|
|
|
27
29
|
@classmethod
|
|
28
30
|
def collect(cls):
|
|
29
31
|
# one by one for more precise error messages
|
|
30
|
-
pid = PythonInventory.collect().
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
pid = PythonInventory.collect().model_dump(
|
|
33
|
+
exclude_unset=True, exclude_defaults=True
|
|
34
|
+
)
|
|
35
|
+
sid = SystemInventory.collect().model_dump(
|
|
36
|
+
exclude_unset=True, exclude_defaults=True
|
|
37
|
+
)
|
|
38
|
+
tid = TargetInventory.collect().model_dump(
|
|
39
|
+
exclude_unset=True, exclude_defaults=True
|
|
40
|
+
)
|
|
33
41
|
model = {**pid, **sid, **tid}
|
|
34
42
|
return cls(**model)
|
|
35
43
|
|
|
36
44
|
|
|
37
45
|
class InventoryList(ShpModel):
|
|
38
|
-
|
|
46
|
+
elements: Annotated[List[Inventory], Field(min_length=1)]
|
|
39
47
|
|
|
40
48
|
def to_csv(self, path: Path) -> None:
|
|
41
49
|
"""TODO: pretty messed up (raw lists and dicts for sub-elements)
|
|
@@ -45,8 +53,8 @@ class InventoryList(ShpModel):
|
|
|
45
53
|
if path.is_dir():
|
|
46
54
|
path = path / "inventory.yaml"
|
|
47
55
|
with open(path.as_posix(), "w") as fd:
|
|
48
|
-
fd.write(", ".join(self.
|
|
49
|
-
for item in self.
|
|
50
|
-
content = list(item.
|
|
56
|
+
fd.write(", ".join(self.elements[0].model_dump().keys()) + "\r\n")
|
|
57
|
+
for item in self.elements:
|
|
58
|
+
content = list(item.model_dump().values())
|
|
51
59
|
content = ["" if value is None else str(value) for value in content]
|
|
52
60
|
fd.write(", ".join(content) + "\r\n")
|
|
@@ -3,11 +3,13 @@ from contextlib import suppress
|
|
|
3
3
|
from importlib import import_module
|
|
4
4
|
from typing import Optional
|
|
5
5
|
|
|
6
|
+
from pydantic import ConfigDict
|
|
7
|
+
|
|
6
8
|
from ..data_models import ShpModel
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
class PythonInventory(ShpModel):
|
|
10
|
-
# versions
|
|
12
|
+
# program versions
|
|
11
13
|
python: Optional[str] = None
|
|
12
14
|
numpy: Optional[str] = None
|
|
13
15
|
h5py: Optional[str] = None
|
|
@@ -16,8 +18,7 @@ class PythonInventory(ShpModel):
|
|
|
16
18
|
shepherd_core: Optional[str] = None
|
|
17
19
|
shepherd_sheep: Optional[str] = None
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
min_anystr_length = 0
|
|
21
|
+
model_config = ConfigDict(str_min_length=0)
|
|
21
22
|
|
|
22
23
|
@classmethod
|
|
23
24
|
def collect(cls):
|
|
@@ -13,6 +13,7 @@ try:
|
|
|
13
13
|
except ImportError:
|
|
14
14
|
psutil_support = False
|
|
15
15
|
|
|
16
|
+
from pydantic import ConfigDict
|
|
16
17
|
from pydantic.types import PositiveInt
|
|
17
18
|
|
|
18
19
|
from ..data_models import ShpModel
|
|
@@ -20,7 +21,7 @@ from ..data_models import ShpModel
|
|
|
20
21
|
|
|
21
22
|
class SystemInventory(ShpModel):
|
|
22
23
|
uptime: PositiveInt
|
|
23
|
-
# seconds
|
|
24
|
+
# ⤷ seconds
|
|
24
25
|
|
|
25
26
|
system: str
|
|
26
27
|
release: str
|
|
@@ -34,12 +35,11 @@ class SystemInventory(ShpModel):
|
|
|
34
35
|
hostname: str
|
|
35
36
|
|
|
36
37
|
interfaces: dict = {}
|
|
37
|
-
# tuple with
|
|
38
|
+
# ⤷ tuple with
|
|
38
39
|
# ip IPvAnyAddress
|
|
39
40
|
# mac MACStr
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
min_anystr_length = 0
|
|
42
|
+
model_config = ConfigDict(str_min_length=0)
|
|
43
43
|
|
|
44
44
|
@classmethod
|
|
45
45
|
def collect(cls):
|
|
@@ -61,7 +61,7 @@ class SystemInventory(ShpModel):
|
|
|
61
61
|
)
|
|
62
62
|
|
|
63
63
|
model_dict = {
|
|
64
|
-
"uptime": uptime,
|
|
64
|
+
"uptime": round(uptime),
|
|
65
65
|
"system": platform.system(),
|
|
66
66
|
"release": platform.release(),
|
|
67
67
|
"version": platform.version(),
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
+
from typing import List
|
|
1
2
|
from typing import Optional
|
|
2
3
|
|
|
3
|
-
from pydantic import
|
|
4
|
+
from pydantic import ConfigDict
|
|
4
5
|
|
|
5
6
|
from ..data_models import ShpModel
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class TargetInventory(ShpModel):
|
|
9
10
|
cape: Optional[str] = None
|
|
10
|
-
targets:
|
|
11
|
+
targets: List[str] = []
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
min_anystr_length = 0
|
|
13
|
+
model_config = ConfigDict(str_min_length=0)
|
|
14
14
|
|
|
15
15
|
@classmethod
|
|
16
16
|
def collect(cls):
|
shepherd_core/reader.py
CHANGED
|
@@ -17,7 +17,7 @@ from typing import Union
|
|
|
17
17
|
import h5py
|
|
18
18
|
import numpy as np
|
|
19
19
|
import yaml
|
|
20
|
-
from pydantic import
|
|
20
|
+
from pydantic import validate_call
|
|
21
21
|
from tqdm import trange
|
|
22
22
|
|
|
23
23
|
from .commons import samplerate_sps_default
|
|
@@ -45,7 +45,7 @@ class BaseReader:
|
|
|
45
45
|
"emulator": [EnergyDType.ivsample],
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
@
|
|
48
|
+
@validate_call
|
|
49
49
|
def __init__(self, file_path: Optional[Path], verbose: Optional[bool] = True):
|
|
50
50
|
if not hasattr(self, "file_path"):
|
|
51
51
|
self.file_path: Optional[Path] = None
|
|
@@ -99,7 +99,7 @@ class BaseReader:
|
|
|
99
99
|
|
|
100
100
|
# retrieve cal-data
|
|
101
101
|
if not hasattr(self, "_cal"):
|
|
102
|
-
cal_dict = CalibrationSeries().
|
|
102
|
+
cal_dict = CalibrationSeries().model_dump()
|
|
103
103
|
for ds, param in product(
|
|
104
104
|
["current", "voltage", "time"], ["gain", "offset"]
|
|
105
105
|
):
|
|
@@ -3,7 +3,7 @@ from pathlib import Path
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
from typing import Union
|
|
5
5
|
|
|
6
|
-
from pydantic import
|
|
6
|
+
from pydantic import validate_call
|
|
7
7
|
|
|
8
8
|
from ..commons import testbed_server_default
|
|
9
9
|
from ..data_models.base.shepherd import ShpModel
|
|
@@ -35,7 +35,7 @@ class TestbedClient:
|
|
|
35
35
|
def __del__(self):
|
|
36
36
|
TestbedClient._instance = None
|
|
37
37
|
|
|
38
|
-
@
|
|
38
|
+
@validate_call
|
|
39
39
|
def connect(
|
|
40
40
|
self, server: Optional[str] = None, token: Union[str, Path, None] = None
|
|
41
41
|
) -> bool:
|
|
@@ -65,10 +65,12 @@ class TestbedClient:
|
|
|
65
65
|
def insert(self, data: ShpModel) -> bool:
|
|
66
66
|
wrap = Wrapper(
|
|
67
67
|
datatype=type(data).__name__,
|
|
68
|
-
parameters=data.
|
|
68
|
+
parameters=data.model_dump(),
|
|
69
69
|
)
|
|
70
70
|
if self._connected:
|
|
71
|
-
r = self._req.post(
|
|
71
|
+
r = self._req.post(
|
|
72
|
+
self._server + "/add", data=wrap.model_dump_json(), timeout=2
|
|
73
|
+
)
|
|
72
74
|
r.raise_for_status()
|
|
73
75
|
else:
|
|
74
76
|
self._fixtures.insert_model(wrap)
|
|
@@ -6,9 +6,10 @@ from pydantic import EmailStr
|
|
|
6
6
|
from pydantic import Field
|
|
7
7
|
from pydantic import SecretBytes
|
|
8
8
|
from pydantic import SecretStr
|
|
9
|
-
from pydantic import
|
|
10
|
-
from pydantic import
|
|
11
|
-
from pydantic import
|
|
9
|
+
from pydantic import StringConstraints
|
|
10
|
+
from pydantic import model_validator
|
|
11
|
+
from pydantic import validate_call
|
|
12
|
+
from typing_extensions import Annotated
|
|
12
13
|
|
|
13
14
|
from ..data_models.base.content import IdInt
|
|
14
15
|
from ..data_models.base.content import NameStr
|
|
@@ -17,8 +18,10 @@ from ..data_models.base.content import id_default
|
|
|
17
18
|
from ..data_models.base.shepherd import ShpModel
|
|
18
19
|
|
|
19
20
|
|
|
20
|
-
@
|
|
21
|
-
def hash_password(
|
|
21
|
+
@validate_call
|
|
22
|
+
def hash_password(
|
|
23
|
+
pw: Annotated[str, StringConstraints(min_length=20, max_length=100)]
|
|
24
|
+
) -> bytes:
|
|
22
25
|
# TODO: add salt of testbed -> this fn should be part of Testbed-Object
|
|
23
26
|
# NOTE: 1M Iterations need 25s on beaglebone
|
|
24
27
|
return pbkdf2_hmac(
|
|
@@ -46,19 +49,20 @@ class User(ShpModel):
|
|
|
46
49
|
email: EmailStr
|
|
47
50
|
|
|
48
51
|
pw_hash: Optional[SecretBytes] = None
|
|
49
|
-
# ⤷
|
|
52
|
+
# ⤷ was hash_password("this_will_become_a_salted_slow_hash") -> slowed BBB down
|
|
50
53
|
# ⤷ TODO (min_length=128, max_length=512)
|
|
51
54
|
|
|
52
55
|
token: SecretStr
|
|
53
56
|
# ⤷ TODO (min_length=128), request with: token.get_secret_value()
|
|
57
|
+
active: bool = False
|
|
54
58
|
|
|
55
|
-
@
|
|
59
|
+
@model_validator(mode="before")
|
|
60
|
+
@classmethod
|
|
56
61
|
def query_database(cls, values: dict) -> dict:
|
|
57
62
|
# TODO:
|
|
58
|
-
return values
|
|
59
63
|
|
|
60
|
-
|
|
61
|
-
def post_validation(cls, values: dict) -> dict:
|
|
64
|
+
# post correction
|
|
62
65
|
if values.get("token") is None:
|
|
63
66
|
values["token"] = "shepherd_token_" + secrets.token_urlsafe(nbytes=128)
|
|
67
|
+
|
|
64
68
|
return values
|
shepherd_core/writer.py
CHANGED
|
@@ -14,7 +14,7 @@ from typing import Union
|
|
|
14
14
|
import h5py
|
|
15
15
|
import numpy as np
|
|
16
16
|
import yaml
|
|
17
|
-
from pydantic import
|
|
17
|
+
from pydantic import validate_call
|
|
18
18
|
from yaml import SafeDumper
|
|
19
19
|
|
|
20
20
|
from .commons import samplerate_sps_default
|
|
@@ -90,7 +90,7 @@ class BaseWriter(BaseReader):
|
|
|
90
90
|
|
|
91
91
|
_chunk_shape: tuple = (BaseReader.samples_per_buffer,)
|
|
92
92
|
|
|
93
|
-
@
|
|
93
|
+
@validate_call
|
|
94
94
|
def __init__(
|
|
95
95
|
self,
|
|
96
96
|
file_path: Path,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: shepherd-core
|
|
3
|
-
Version: 2023.8.
|
|
3
|
+
Version: 2023.8.8
|
|
4
4
|
Summary: Programming- and CLI-Interface for the h5-dataformat of the Shepherd-Testbed
|
|
5
5
|
Home-page: https://pypi.org/project/shepherd-core/
|
|
6
6
|
Author: Ingmar Splitt, Kai Geissdoerfer
|
|
@@ -34,7 +34,7 @@ Requires-Dist: h5py
|
|
|
34
34
|
Requires-Dist: numpy
|
|
35
35
|
Requires-Dist: pyYAML
|
|
36
36
|
Requires-Dist: chromalog
|
|
37
|
-
Requires-Dist: pydantic[email]
|
|
37
|
+
Requires-Dist: pydantic[email] >2.0.0
|
|
38
38
|
Requires-Dist: tqdm
|
|
39
39
|
Requires-Dist: scipy
|
|
40
40
|
Requires-Dist: intelhex
|
|
@@ -100,6 +100,13 @@ The Library is available via PyPI and can be installed with
|
|
|
100
100
|
pip install shepherd-data
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
+
for bleeding-edge-features or dev-work it is possible to install directly from GitHub-Sources (here `dev`-branch):
|
|
104
|
+
|
|
105
|
+
```Shell
|
|
106
|
+
pip install git+https://github.com/orgua/shepherd-datalib.git@dev#subdirectory=shepherd_core -U
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
|
|
103
110
|
If you are working with ``.elf``-files (embedding into experiments) you make "objcopy" accessible to python. In Ubuntu, you can either install ``build-essential`` or ``binutils-$ARCH`` with arch being ``msp430`` or ``arm-none-eabi`` for the nRF52.
|
|
104
111
|
|
|
105
112
|
```shell
|