PySerials 0.0.0.dev1__tar.gz → 0.0.0.dev3__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.
- {PySerials-0.0.0.dev1 → pyserials-0.0.0.dev3}/PKG-INFO +2 -2
- {PySerials-0.0.0.dev1 → pyserials-0.0.0.dev3}/pyproject.toml +2 -2
- {PySerials-0.0.0.dev1 → pyserials-0.0.0.dev3}/src/PySerials.egg-info/PKG-INFO +2 -2
- {PySerials-0.0.0.dev1 → pyserials-0.0.0.dev3}/src/PySerials.egg-info/SOURCES.txt +6 -2
- {PySerials-0.0.0.dev1 → pyserials-0.0.0.dev3}/src/PySerials.egg-info/requires.txt +1 -1
- pyserials-0.0.0.dev3/src/pyserials/exception/__init__.py +4 -0
- pyserials-0.0.0.dev3/src/pyserials/exception/_base.py +10 -0
- pyserials-0.0.0.dev3/src/pyserials/exception/read.py +119 -0
- pyserials-0.0.0.dev3/src/pyserials/exception/update.py +131 -0
- pyserials-0.0.0.dev3/src/pyserials/exception/validate.py +73 -0
- {PySerials-0.0.0.dev1 → pyserials-0.0.0.dev3}/src/pyserials/read.py +7 -16
- {PySerials-0.0.0.dev1 → pyserials-0.0.0.dev3}/src/pyserials/update.py +15 -7
- {PySerials-0.0.0.dev1 → pyserials-0.0.0.dev3}/src/pyserials/validate.py +39 -13
- {PySerials-0.0.0.dev1 → pyserials-0.0.0.dev3}/src/pyserials/write.py +9 -1
- PySerials-0.0.0.dev1/src/pyserials/exception.py +0 -140
- {PySerials-0.0.0.dev1 → pyserials-0.0.0.dev3}/README.md +0 -0
- {PySerials-0.0.0.dev1 → pyserials-0.0.0.dev3}/setup.cfg +0 -0
- {PySerials-0.0.0.dev1 → pyserials-0.0.0.dev3}/src/PySerials.egg-info/dependency_links.txt +0 -0
- {PySerials-0.0.0.dev1 → pyserials-0.0.0.dev3}/src/PySerials.egg-info/not-zip-safe +0 -0
- {PySerials-0.0.0.dev1 → pyserials-0.0.0.dev3}/src/PySerials.egg-info/top_level.txt +0 -0
- {PySerials-0.0.0.dev1 → pyserials-0.0.0.dev3}/src/pyserials/__init__.py +0 -0
- {PySerials-0.0.0.dev1 → pyserials-0.0.0.dev3}/src/pyserials/format.py +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PySerials
|
|
3
|
-
Version: 0.0.0.
|
|
3
|
+
Version: 0.0.0.dev3
|
|
4
4
|
Requires-Python: >=3.10
|
|
5
|
-
Requires-Dist: jsonschema<5,>=4.
|
|
5
|
+
Requires-Dist: jsonschema<5,>=4.21.0
|
|
6
6
|
Requires-Dist: ruamel.yaml<0.18,>=0.17.32
|
|
7
7
|
Requires-Dist: ruamel.yaml.string<1,>=0.1.1
|
|
8
8
|
Requires-Dist: tomlkit<0.12,>=0.11.8
|
|
@@ -17,10 +17,10 @@ namespaces = true
|
|
|
17
17
|
# ----------------------------------------- Project Metadata -------------------------------------
|
|
18
18
|
#
|
|
19
19
|
[project]
|
|
20
|
-
version = "0.0.0.
|
|
20
|
+
version = "0.0.0.dev3"
|
|
21
21
|
name = "PySerials"
|
|
22
22
|
dependencies = [
|
|
23
|
-
"jsonschema >= 4.
|
|
23
|
+
"jsonschema >= 4.21.0, < 5",
|
|
24
24
|
"ruamel.yaml >= 0.17.32, < 0.18", # https://yaml.readthedocs.io/en/stable/
|
|
25
25
|
"ruamel.yaml.string >= 0.1.1, < 1",
|
|
26
26
|
"tomlkit >= 0.11.8, < 0.12", # https://tomlkit.readthedocs.io/en/stable/,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PySerials
|
|
3
|
-
Version: 0.0.0.
|
|
3
|
+
Version: 0.0.0.dev3
|
|
4
4
|
Requires-Python: >=3.10
|
|
5
|
-
Requires-Dist: jsonschema<5,>=4.
|
|
5
|
+
Requires-Dist: jsonschema<5,>=4.21.0
|
|
6
6
|
Requires-Dist: ruamel.yaml<0.18,>=0.17.32
|
|
7
7
|
Requires-Dist: ruamel.yaml.string<1,>=0.1.1
|
|
8
8
|
Requires-Dist: tomlkit<0.12,>=0.11.8
|
|
@@ -7,9 +7,13 @@ src/PySerials.egg-info/not-zip-safe
|
|
|
7
7
|
src/PySerials.egg-info/requires.txt
|
|
8
8
|
src/PySerials.egg-info/top_level.txt
|
|
9
9
|
src/pyserials/__init__.py
|
|
10
|
-
src/pyserials/exception.py
|
|
11
10
|
src/pyserials/format.py
|
|
12
11
|
src/pyserials/read.py
|
|
13
12
|
src/pyserials/update.py
|
|
14
13
|
src/pyserials/validate.py
|
|
15
|
-
src/pyserials/write.py
|
|
14
|
+
src/pyserials/write.py
|
|
15
|
+
src/pyserials/exception/__init__.py
|
|
16
|
+
src/pyserials/exception/_base.py
|
|
17
|
+
src/pyserials/exception/read.py
|
|
18
|
+
src/pyserials/exception/update.py
|
|
19
|
+
src/pyserials/exception/validate.py
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Exceptions raised by `pyserials.read` module."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
from typing import Literal as _Literal
|
|
5
|
+
from pathlib import Path as _Path
|
|
6
|
+
|
|
7
|
+
from pyserials.exception._base import PySerialsException as _PySerialsException
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PySerialsReadException(_PySerialsException):
|
|
11
|
+
"""Base class for all exceptions raised by `pyserials.read` module."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, message: str, source_type: _Literal["file", "string"]):
|
|
14
|
+
super().__init__(message=f"Failed to read data from {source_type}. {message}.")
|
|
15
|
+
return
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class PySerialsReadFromFileException(PySerialsReadException):
|
|
19
|
+
"""Base class for all exceptions raised when reading data from a file.
|
|
20
|
+
|
|
21
|
+
Attributes
|
|
22
|
+
----------
|
|
23
|
+
filepath : pathlib.Path
|
|
24
|
+
The path of the input datafile.
|
|
25
|
+
"""
|
|
26
|
+
def __init__(self, message: str, filepath: _Path):
|
|
27
|
+
super().__init__(message=message, source_type="file")
|
|
28
|
+
self.filepath: _Path = filepath
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class PySerialsReadFromStringException(PySerialsReadException):
|
|
33
|
+
"""Base class for all exceptions raised when reading data from a string.
|
|
34
|
+
|
|
35
|
+
Attributes
|
|
36
|
+
----------
|
|
37
|
+
data_type : {"json", "yaml", "toml"}
|
|
38
|
+
The type of data.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(self, message: str, data_type: _Literal["json", "yaml", "toml"]):
|
|
42
|
+
super().__init__(message=message, source_type="string")
|
|
43
|
+
self.data_type = data_type
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class PySerialsInvalidFileExtensionError(PySerialsReadFromFileException):
|
|
48
|
+
"""Exception raised when a file to be read has an unrecognized extension."""
|
|
49
|
+
|
|
50
|
+
def __init__(self, filepath: _Path):
|
|
51
|
+
message = (
|
|
52
|
+
f"The extension of the file at '{filepath}' is invalid; "
|
|
53
|
+
f"expected one of 'json', 'yaml', or 'toml', but got '{filepath.suffix.removeprefix('.')}'. "
|
|
54
|
+
"Please provide the extension explicitly, or rename the file to have a valid extension."
|
|
55
|
+
)
|
|
56
|
+
super().__init__(message=message, filepath=filepath)
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class PySerialsMissingFileError(PySerialsReadFromFileException):
|
|
61
|
+
"""Exception raised when a file to be read does not exist."""
|
|
62
|
+
|
|
63
|
+
def __init__(self, filepath: _Path):
|
|
64
|
+
message = f"The file at '{filepath}' does not exist."
|
|
65
|
+
super().__init__(message=message, filepath=filepath)
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class PySerialsEmptyFileError(PySerialsReadFromFileException):
|
|
70
|
+
"""Exception raised when a file to be read is empty."""
|
|
71
|
+
|
|
72
|
+
def __init__(self, filepath: _Path):
|
|
73
|
+
message = f"The file at '{filepath}' is empty."
|
|
74
|
+
super().__init__(message=message, filepath=filepath)
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class PySerialsInvalidDataFileError(PySerialsReadFromFileException):
|
|
79
|
+
"""Exception raised when a file to be read is invalid.
|
|
80
|
+
|
|
81
|
+
Attributes
|
|
82
|
+
----------
|
|
83
|
+
data : str
|
|
84
|
+
The input data that was supposed to be read.
|
|
85
|
+
data_type : {"json", "yaml", "toml"}
|
|
86
|
+
The type of data.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
def __init__(self, filepath: _Path, data_type: _Literal["json", "yaml", "toml"], data: str):
|
|
90
|
+
message = f"The {data_type} file at '{filepath}' is invalid."
|
|
91
|
+
super().__init__(message=message, filepath=filepath)
|
|
92
|
+
self.data = data
|
|
93
|
+
self.data_type = data_type
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class PySerialsEmptyStringError(PySerialsReadFromStringException):
|
|
98
|
+
"""Exception raised when a string to be read is empty."""
|
|
99
|
+
|
|
100
|
+
def __init__(self, data_type: _Literal["json", "yaml", "toml"]):
|
|
101
|
+
message = f"The {data_type} string is empty."
|
|
102
|
+
super().__init__(message=message, data_type=data_type)
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class PySerialsInvalidDataStringError(PySerialsReadFromStringException):
|
|
107
|
+
"""Exception raised when a string to be read is invalid.
|
|
108
|
+
|
|
109
|
+
Attributes
|
|
110
|
+
----------
|
|
111
|
+
data : str
|
|
112
|
+
The input data that was supposed to be read.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
def __init__(self, data_type: _Literal["json", "yaml", "toml"], data: str):
|
|
116
|
+
message = f"The {data_type} string is invalid."
|
|
117
|
+
super().__init__(message=message, data_type=data_type)
|
|
118
|
+
self.data = data
|
|
119
|
+
return
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Exceptions raised by `pyserials.update` module."""
|
|
2
|
+
|
|
3
|
+
from typing import Any as _Any
|
|
4
|
+
|
|
5
|
+
from pyserials.exception._base import PySerialsException as _PySerialsException
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PySerialsUpdateException(_PySerialsException):
|
|
9
|
+
"""Base class for all exceptions raised by `pyserials.update` module."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, message: str):
|
|
12
|
+
super().__init__(f"Failed to update data. {message}.")
|
|
13
|
+
return
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PySerialsUpdateDictFromAddonException(PySerialsUpdateException):
|
|
17
|
+
"""Base class for all exceptions raised by `pyserials.update.dict_from_addon`.
|
|
18
|
+
|
|
19
|
+
Attributes
|
|
20
|
+
----------
|
|
21
|
+
address : str
|
|
22
|
+
The address of the data that failed to update.
|
|
23
|
+
value_data : Any
|
|
24
|
+
The value of the data in the source dictionary.
|
|
25
|
+
value_addon : Any
|
|
26
|
+
The value of the data in the addon dictionary.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
message: str,
|
|
32
|
+
address: str,
|
|
33
|
+
value_data: _Any,
|
|
34
|
+
value_addon: _Any,
|
|
35
|
+
):
|
|
36
|
+
super().__init__(message=message)
|
|
37
|
+
self.address = address
|
|
38
|
+
self.value_data = value_data
|
|
39
|
+
self.value_addon = value_addon
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class PySerialsUpdateTemplatedDataException(PySerialsUpdateException):
|
|
44
|
+
"""Base class for all exceptions raised by `pyserials.update.templated_data_from_source`.
|
|
45
|
+
|
|
46
|
+
Attributes
|
|
47
|
+
----------
|
|
48
|
+
templated_data : str
|
|
49
|
+
The templated data that failed to update.
|
|
50
|
+
source_data : dict
|
|
51
|
+
The data that was used to update the template.
|
|
52
|
+
template_start : str
|
|
53
|
+
The start marker of the template.
|
|
54
|
+
template_end : str
|
|
55
|
+
The end marker of the template.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
message: str,
|
|
61
|
+
templated_data: str,
|
|
62
|
+
source_data: dict,
|
|
63
|
+
template_start: str,
|
|
64
|
+
template_end: str
|
|
65
|
+
):
|
|
66
|
+
super().__init__(message=message)
|
|
67
|
+
self.templated_data = templated_data
|
|
68
|
+
self.source_data = source_data
|
|
69
|
+
self.template_start = template_start
|
|
70
|
+
self.template_end = template_end
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class PySerialsDictUpdateTypeMismatchError(PySerialsUpdateDictFromAddonException):
|
|
75
|
+
"""Exception raised when a dict update fails due to a type mismatch."""
|
|
76
|
+
|
|
77
|
+
def __init__(self, address: str, value_data: _Any, value_addon: _Any):
|
|
78
|
+
message = (
|
|
79
|
+
f"There was a type mismatch between the source and addon dictionary values at '{address}'; "
|
|
80
|
+
f"the value is of type '{type(value_data).__name__}' in the source data, "
|
|
81
|
+
f"but of type '{type(value_addon).__name__}' in the addon data"
|
|
82
|
+
)
|
|
83
|
+
super().__init__(message=message, address=address, value_data=value_data, value_addon=value_addon)
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class PySerialsDictUpdateDuplicationError(PySerialsUpdateDictFromAddonException):
|
|
88
|
+
"""Exception raised when a dict update fails due to a duplication."""
|
|
89
|
+
|
|
90
|
+
def __init__(self, address: str, value_data: _Any, value_addon: _Any):
|
|
91
|
+
message = (
|
|
92
|
+
f"There was a duplication in the addon dictionary at '{address}'; "
|
|
93
|
+
f"the value of type '{type(value_addon).__name__}' already exists in the source data"
|
|
94
|
+
)
|
|
95
|
+
super().__init__(message=message, address=address, value_data=value_data, value_addon=value_addon)
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class PySerialsTemplateUpdateMissingSourceError(PySerialsUpdateTemplatedDataException):
|
|
100
|
+
"""Exception raised when a templated data update fails due to a missing key in source data.
|
|
101
|
+
|
|
102
|
+
Attributes
|
|
103
|
+
----------
|
|
104
|
+
address_full : str
|
|
105
|
+
The full address in the source data where the key/index is missing.
|
|
106
|
+
address_missing : str
|
|
107
|
+
The key/index that is missing in the source data at `address_full`.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
def __init__(
|
|
111
|
+
self,
|
|
112
|
+
address_full: str,
|
|
113
|
+
address_missing: str,
|
|
114
|
+
templated_data: str,
|
|
115
|
+
source_data: dict,
|
|
116
|
+
template_start: str,
|
|
117
|
+
template_end: str
|
|
118
|
+
):
|
|
119
|
+
message = (
|
|
120
|
+
f"The key/index '{address_missing}' is missing in the source data at '{address_full}'"
|
|
121
|
+
)
|
|
122
|
+
super().__init__(
|
|
123
|
+
message=message,
|
|
124
|
+
templated_data=templated_data,
|
|
125
|
+
source_data=source_data,
|
|
126
|
+
template_start=template_start,
|
|
127
|
+
template_end=template_end
|
|
128
|
+
)
|
|
129
|
+
self.address_full = address_full
|
|
130
|
+
self.address_missing = address_missing
|
|
131
|
+
return
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Exceptions raised by `pyserials.validate` module."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
from typing import Any as _Any
|
|
5
|
+
|
|
6
|
+
from pyserials.exception._base import PySerialsException as _PySerialsException
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PySerialsValidateException(_PySerialsException):
|
|
10
|
+
"""Base class for all exceptions raised by `pyserials.validate` module.
|
|
11
|
+
|
|
12
|
+
Attributes
|
|
13
|
+
----------
|
|
14
|
+
data : dict | list | str | int | float | bool
|
|
15
|
+
The data that failed validation.
|
|
16
|
+
schema : dict
|
|
17
|
+
The schema that the data failed to validate against.
|
|
18
|
+
validator : Any
|
|
19
|
+
The validator that was used to validate the data against the schema.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
message: str,
|
|
25
|
+
data: dict | list | str | int | float | bool,
|
|
26
|
+
schema: dict,
|
|
27
|
+
validator: _Any,
|
|
28
|
+
):
|
|
29
|
+
message = (
|
|
30
|
+
"Failed to validate data against schema "
|
|
31
|
+
f"using validator '{validator.__class__.__name__}'; {message}."
|
|
32
|
+
)
|
|
33
|
+
super().__init__(message=message)
|
|
34
|
+
self.data = data
|
|
35
|
+
self.schema = schema
|
|
36
|
+
self.validator = validator
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class PySerialsSchemaValidationError(PySerialsValidateException):
|
|
41
|
+
"""Exception raised when data validation fails due to the data being invalid against the schema."""
|
|
42
|
+
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
data: dict | list | str | int | float | bool,
|
|
46
|
+
schema: dict,
|
|
47
|
+
validator: _Any,
|
|
48
|
+
):
|
|
49
|
+
super().__init__(
|
|
50
|
+
message="the data is invalid",
|
|
51
|
+
data=data,
|
|
52
|
+
schema=schema,
|
|
53
|
+
validator=validator,
|
|
54
|
+
)
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class PySerialsInvalidSchemaError(PySerialsValidateException):
|
|
59
|
+
"""Exception raised when data validation fails due to the schema being invalid."""
|
|
60
|
+
|
|
61
|
+
def __init__(
|
|
62
|
+
self,
|
|
63
|
+
data: dict | list | str | int | float | bool,
|
|
64
|
+
schema: dict,
|
|
65
|
+
validator: _Any,
|
|
66
|
+
):
|
|
67
|
+
super().__init__(
|
|
68
|
+
message="the schema is invalid",
|
|
69
|
+
data=data,
|
|
70
|
+
schema=schema,
|
|
71
|
+
validator=validator,
|
|
72
|
+
)
|
|
73
|
+
return
|
|
@@ -21,11 +21,7 @@ def from_file(
|
|
|
21
21
|
if data_type == "yml":
|
|
22
22
|
data_type = "yaml"
|
|
23
23
|
if data_type not in ("json", "yaml", "toml"):
|
|
24
|
-
raise _exception.
|
|
25
|
-
error_type=_exception.ReadErrorType.EXTENSION_INVALID,
|
|
26
|
-
source_type="file",
|
|
27
|
-
filepath=path,
|
|
28
|
-
)
|
|
24
|
+
raise _exception.read.PySerialsInvalidFileExtensionError(filepath=path)
|
|
29
25
|
if data_type == "json":
|
|
30
26
|
return json_from_file(path=path, strict=json_strict)
|
|
31
27
|
if data_type == "yaml":
|
|
@@ -139,18 +135,16 @@ def _read_from_file(
|
|
|
139
135
|
exception,
|
|
140
136
|
):
|
|
141
137
|
path = _Path(path).resolve()
|
|
142
|
-
common_error_args = {"source_type": "file", "data_type": data_type, "filepath": path}
|
|
143
138
|
if not path.is_file():
|
|
144
|
-
raise _exception.
|
|
139
|
+
raise _exception.read.PySerialsMissingFileError(filepath=path)
|
|
145
140
|
data = path.read_text()
|
|
146
|
-
common_error_args["data"] = data
|
|
147
141
|
if data.strip() == "":
|
|
148
|
-
raise _exception.
|
|
142
|
+
raise _exception.read.PySerialsEmptyFileError(filepath=path)
|
|
149
143
|
try:
|
|
150
144
|
content = loader(data)
|
|
151
145
|
except exception as e:
|
|
152
|
-
raise _exception.
|
|
153
|
-
|
|
146
|
+
raise _exception.read.PySerialsInvalidDataFileError(
|
|
147
|
+
filepath=path, data_type=data_type, data=data
|
|
154
148
|
) from e
|
|
155
149
|
return content
|
|
156
150
|
|
|
@@ -161,13 +155,10 @@ def _read_from_string(
|
|
|
161
155
|
loader,
|
|
162
156
|
exception,
|
|
163
157
|
):
|
|
164
|
-
common_error_args = {"source_type": "string", "data": data, "data_type": data_type}
|
|
165
158
|
if not data.strip():
|
|
166
|
-
raise _exception.
|
|
159
|
+
raise _exception.read.PySerialsEmptyStringError(data_type=data_type)
|
|
167
160
|
try:
|
|
168
161
|
content = loader(data)
|
|
169
162
|
except exception as e:
|
|
170
|
-
raise _exception.
|
|
171
|
-
error_type=_exception.ReadErrorType.DATA_INVALID, **common_error_args
|
|
172
|
-
) from e
|
|
163
|
+
raise _exception.read.PySerialsInvalidDataStringError(data_type=data_type, data=data) from e
|
|
173
164
|
return content
|
|
@@ -14,8 +14,7 @@ def dict_from_addon(
|
|
|
14
14
|
def recursive(source: dict, add: dict, path: str, log: dict):
|
|
15
15
|
|
|
16
16
|
def raise_duplication_error():
|
|
17
|
-
raise _exception.
|
|
18
|
-
error_type=_exception.UpdateErrorType.DUPLICATION,
|
|
17
|
+
raise _exception.update.PySerialsDictUpdateDuplicationError(
|
|
19
18
|
address=fullpath,
|
|
20
19
|
value_data=source[key],
|
|
21
20
|
value_addon=value,
|
|
@@ -28,8 +27,7 @@ def dict_from_addon(
|
|
|
28
27
|
source[key] = value
|
|
29
28
|
continue
|
|
30
29
|
if type(source[key]) is not type(value):
|
|
31
|
-
raise _exception.
|
|
32
|
-
error_type=_exception.UpdateErrorType.TYPE_MISMATCH,
|
|
30
|
+
raise _exception.update.PySerialsDictUpdateTypeMismatchError(
|
|
33
31
|
address=fullpath,
|
|
34
32
|
value_data=source[key],
|
|
35
33
|
value_addon=value,
|
|
@@ -91,6 +89,8 @@ class _TemplateFiller:
|
|
|
91
89
|
):
|
|
92
90
|
self._data = templated_data
|
|
93
91
|
self._source = source_data
|
|
92
|
+
self._template_start = template_start
|
|
93
|
+
self._template_end = template_end
|
|
94
94
|
marker_start = _re.escape(template_start)
|
|
95
95
|
marker_end = _re.escape(template_end)
|
|
96
96
|
self._pattern_template_whole = _re.compile(rf"^{marker_start}([\w\.\:\-\[\] ]+){marker_end}$")
|
|
@@ -128,12 +128,20 @@ class _TemplateFiller:
|
|
|
128
128
|
except (TypeError, KeyError, IndexError) as e:
|
|
129
129
|
try:
|
|
130
130
|
next_layer = self._recursive_subst(obj)[curr_add]
|
|
131
|
-
except (TypeError, KeyError, IndexError)
|
|
132
|
-
raise
|
|
131
|
+
except (TypeError, KeyError, IndexError):
|
|
132
|
+
raise _exception.update.PySerialsTemplateUpdateMissingSourceError(
|
|
133
|
+
address_full=address_full,
|
|
134
|
+
address_missing=curr_add,
|
|
135
|
+
templated_data=self._data,
|
|
136
|
+
source_data=self._source,
|
|
137
|
+
template_start=self._template_start,
|
|
138
|
+
template_end=self._template_end,
|
|
139
|
+
) from e
|
|
133
140
|
return recursive_retrieve(next_layer, address)
|
|
134
141
|
|
|
142
|
+
address_full = match.strip()
|
|
135
143
|
parsed_address = []
|
|
136
|
-
for add in
|
|
144
|
+
for add in address_full.split("."):
|
|
137
145
|
name = self._pattern_address_name.match(add).group()
|
|
138
146
|
indices = self._pattern_address_indices.findall(add)
|
|
139
147
|
parsed_address.append(name)
|
|
@@ -6,9 +6,39 @@ import pyserials.exception as _exception
|
|
|
6
6
|
def jsonschema(
|
|
7
7
|
data: dict | list | str | int | float | bool,
|
|
8
8
|
schema: dict,
|
|
9
|
-
validator: _jsonschema.
|
|
9
|
+
validator: _jsonschema.protocols.Validator = _jsonschema.Draft202012Validator,
|
|
10
10
|
fill_defaults: bool = True,
|
|
11
|
-
|
|
11
|
+
raise_invalid_data: bool = True,
|
|
12
|
+
) -> bool:
|
|
13
|
+
"""
|
|
14
|
+
Validate data against a JSON schema.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
data : dict | list | str | int | float | bool
|
|
19
|
+
The data to validate.
|
|
20
|
+
schema : dict
|
|
21
|
+
The schema to validate the data against.
|
|
22
|
+
validator : jsonschema.protocols.Validator, default: jsonschema.Draft202012Validator
|
|
23
|
+
The JSON schema validator to use.
|
|
24
|
+
fill_defaults : bool, default: True
|
|
25
|
+
Whether to fill in the data with default values from the schema,
|
|
26
|
+
when they are not present.
|
|
27
|
+
raise_invalid_data : bool, default: True
|
|
28
|
+
Whether to raise an exception when the data is invalid against the schema.
|
|
29
|
+
|
|
30
|
+
Returns
|
|
31
|
+
-------
|
|
32
|
+
valid : bool
|
|
33
|
+
Whether the data is valid against the schema.
|
|
34
|
+
|
|
35
|
+
Raises
|
|
36
|
+
------
|
|
37
|
+
pyserials.exception.PySerialsInvalidSchemaError
|
|
38
|
+
If the schema is invalid.
|
|
39
|
+
pyserials.exception.PySerialsSchemaValidationError
|
|
40
|
+
If the data is invalid against the schema and `raise_invalid_data` is `True`.
|
|
41
|
+
"""
|
|
12
42
|
def _extend_with_default(validator_class):
|
|
13
43
|
# https://python-jsonschema.readthedocs.io/en/stable/faq/#why-doesn-t-my-schema-s-default-property-set-the-default-on-my-instance
|
|
14
44
|
|
|
@@ -30,22 +60,18 @@ def jsonschema(
|
|
|
30
60
|
return _jsonschema.validators.extend(validator_class, {"properties": set_defaults})
|
|
31
61
|
|
|
32
62
|
validator = _extend_with_default(validator) if fill_defaults else validator
|
|
33
|
-
|
|
63
|
+
error_args = {"data": data, "schema": schema, "validator": validator}
|
|
34
64
|
try:
|
|
35
65
|
validator(schema).validate(data)
|
|
36
|
-
except (_jsonschema.exceptions.ValidationError, _jsonschema.exceptions.FormatError) as e:
|
|
37
|
-
raise _exception.ValidationError(
|
|
38
|
-
error_type=_exception.ValidationErrorType.DATA_INVALID,
|
|
39
|
-
**common_error_args,
|
|
40
|
-
) from e
|
|
41
66
|
except (
|
|
42
67
|
_jsonschema.exceptions.SchemaError,
|
|
43
68
|
_jsonschema.exceptions.UndefinedTypeCheck,
|
|
44
69
|
_jsonschema.exceptions.UnknownType,
|
|
45
70
|
_UnresolvableReferencingError,
|
|
46
71
|
) as e:
|
|
47
|
-
raise _exception.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
72
|
+
raise _exception.validate.PySerialsInvalidSchemaError(**error_args) from e
|
|
73
|
+
except (_jsonschema.exceptions.ValidationError, _jsonschema.exceptions.FormatError) as e:
|
|
74
|
+
if raise_invalid_data:
|
|
75
|
+
raise _exception.validate.PySerialsSchemaValidationError(**error_args) from e
|
|
76
|
+
return False
|
|
77
|
+
return True
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from pathlib import Path as _Path
|
|
2
|
-
|
|
2
|
+
import json as _json
|
|
3
3
|
import ruamel.yaml as _yaml
|
|
4
4
|
import tomlkit as _tomlkit
|
|
5
5
|
|
|
@@ -28,3 +28,11 @@ def to_toml_string(
|
|
|
28
28
|
sort_keys: bool = True,
|
|
29
29
|
) -> str:
|
|
30
30
|
return _tomlkit.dumps(data, sort_keys=sort_keys)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def to_json_string(
|
|
34
|
+
data: dict | list | str | int | float | bool | _yaml.CommentedMap | _yaml.CommentedSeq,
|
|
35
|
+
sort_keys: bool = True,
|
|
36
|
+
indent: int | None = None,
|
|
37
|
+
) -> str:
|
|
38
|
+
return _json.dumps(data, indent=indent, sort_keys=sort_keys)
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
from typing import Literal as _Literal, Any as _Any
|
|
2
|
-
from pathlib import Path as _Path
|
|
3
|
-
from enum import Enum as _Enum
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class ReadErrorType(_Enum):
|
|
7
|
-
"""Enumeration of all possible read error types."""
|
|
8
|
-
|
|
9
|
-
FILE_MISSING = "the file does not exist"
|
|
10
|
-
FILE_EMPTY = "the file is empty"
|
|
11
|
-
STRING_EMPTY = "the string is empty"
|
|
12
|
-
DATA_INVALID = "the data is invalid"
|
|
13
|
-
EXTENSION_INVALID = "the file extension is invalid"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class ValidationErrorType(_Enum):
|
|
17
|
-
"""Enumeration of all possible validation error types."""
|
|
18
|
-
|
|
19
|
-
SCHEMA_INVALID = "the schema is invalid"
|
|
20
|
-
DATA_INVALID = "the data is invalid"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class UpdateErrorType(_Enum):
|
|
24
|
-
"""Enumeration of all possible update error types."""
|
|
25
|
-
|
|
26
|
-
TYPE_MISMATCH = (
|
|
27
|
-
"type mismatch; the value of '{address}' is of type '{type_source}' in the source data, "
|
|
28
|
-
"but of type '{type_addon}' in the addon data"
|
|
29
|
-
)
|
|
30
|
-
DUPLICATION = (
|
|
31
|
-
"duplication; the value of '{address}' with type {type_source} already exists in the source data"
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class PySerialsError(Exception):
|
|
36
|
-
"""Base class for all PySerials errors."""
|
|
37
|
-
|
|
38
|
-
def __init__(self, message: str):
|
|
39
|
-
self.message = message
|
|
40
|
-
super().__init__(message)
|
|
41
|
-
return
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class ReadError(PySerialsError):
|
|
45
|
-
"""Base class for all PySerials read errors."""
|
|
46
|
-
|
|
47
|
-
def __init__(
|
|
48
|
-
self,
|
|
49
|
-
error_type: ReadErrorType,
|
|
50
|
-
source_type: _Literal["file", "string"],
|
|
51
|
-
data_type: _Literal["json", "yaml", "toml"] | None = None,
|
|
52
|
-
filepath: _Path | None = None,
|
|
53
|
-
data: str | None = None,
|
|
54
|
-
):
|
|
55
|
-
self._type = error_type
|
|
56
|
-
self._source_type = source_type
|
|
57
|
-
self._data_type = data_type
|
|
58
|
-
self._filepath = filepath
|
|
59
|
-
self._data = data
|
|
60
|
-
|
|
61
|
-
message = f"Failed to read {data_type} data from {source_type}; {error_type.value}."
|
|
62
|
-
super().__init__(message)
|
|
63
|
-
return
|
|
64
|
-
|
|
65
|
-
@property
|
|
66
|
-
def type(self) -> ReadErrorType:
|
|
67
|
-
"""The type of error."""
|
|
68
|
-
return self._type
|
|
69
|
-
|
|
70
|
-
@property
|
|
71
|
-
def source_type(self) -> _Literal["file", "string"]:
|
|
72
|
-
"""Source of the data; either 'string' or 'file'."""
|
|
73
|
-
return self._source_type
|
|
74
|
-
|
|
75
|
-
@property
|
|
76
|
-
def data_type(self) -> _Literal["json", "yaml", "toml"] | None:
|
|
77
|
-
"""The type of data; either 'json', 'yaml', 'toml', or None, in case of an invalid extension error."""
|
|
78
|
-
return self._data_type
|
|
79
|
-
|
|
80
|
-
@property
|
|
81
|
-
def filepath(self) -> _Path | None:
|
|
82
|
-
"""The path of the input datafile; available only when source-type is file."""
|
|
83
|
-
return self._filepath
|
|
84
|
-
|
|
85
|
-
@property
|
|
86
|
-
def data(self) -> str | None:
|
|
87
|
-
"""The input data;
|
|
88
|
-
available only when source-type is string, or when the input file was read successfully."""
|
|
89
|
-
return self._data
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
class ValidationError(PySerialsError):
|
|
93
|
-
"""Base class for all PySerials validation errors."""
|
|
94
|
-
|
|
95
|
-
def __init__(
|
|
96
|
-
self,
|
|
97
|
-
error_type: ValidationErrorType,
|
|
98
|
-
data: dict | list | str | int | float | bool,
|
|
99
|
-
schema: dict,
|
|
100
|
-
validator: _Any,
|
|
101
|
-
):
|
|
102
|
-
self._type = error_type
|
|
103
|
-
self._data = data
|
|
104
|
-
self._schema = schema
|
|
105
|
-
self._validator = validator
|
|
106
|
-
|
|
107
|
-
message = f"Failed to validate data against schema using validator '{validator}'; {error_type.value}."
|
|
108
|
-
super().__init__(message)
|
|
109
|
-
return
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
class UpdateError(PySerialsError):
|
|
113
|
-
"""Base class for all PySerials update errors."""
|
|
114
|
-
|
|
115
|
-
def __init__(self, message: str):
|
|
116
|
-
super().__init__(message)
|
|
117
|
-
return
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
class DictUpdateError(UpdateError):
|
|
121
|
-
"""Base class for all PySerials dict update errors."""
|
|
122
|
-
|
|
123
|
-
def __init__(
|
|
124
|
-
self,
|
|
125
|
-
error_type: UpdateErrorType,
|
|
126
|
-
address: str,
|
|
127
|
-
value_data: _Any,
|
|
128
|
-
value_addon: _Any,
|
|
129
|
-
):
|
|
130
|
-
self._type = error_type
|
|
131
|
-
self._address = address
|
|
132
|
-
self._value_data = value_data
|
|
133
|
-
self._value_addon = value_addon
|
|
134
|
-
|
|
135
|
-
error_details = error_type.value.format(
|
|
136
|
-
address=address, type_source=type(value_data), type_addon=type(value_addon)
|
|
137
|
-
)
|
|
138
|
-
message = f"Failed to update dictionary due to {error_details}."
|
|
139
|
-
super().__init__(message)
|
|
140
|
-
return
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|