str-to-obj 2024.1__py3-none-any.whl → 2024.3__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.
- str_to_obj/__init__.py +2 -0
- str_to_obj/catalog/boolean.py +73 -0
- str_to_obj/catalog/callable.py +75 -0
- str_to_obj/catalog/choices.py +76 -0
- str_to_obj/catalog/collection.py +154 -0
- str_to_obj/catalog/number.py +120 -0
- str_to_obj/catalog/path.py +94 -0
- str_to_obj/interface/console.py +4 -4
- str_to_obj/main.py +5 -5
- str_to_obj/runtime/type.py +36 -0
- str_to_obj/runtime/value.py +34 -0
- str_to_obj/task/casting.py +26 -17
- str_to_obj/task/comparison.py +3 -3
- str_to_obj/task/inspection.py +5 -3
- str_to_obj/type/annotation.py +5 -9
- str_to_obj/type/hint.py +7 -7
- str_to_obj/type/hint_tree.py +17 -19
- str_to_obj/type/type.py +97 -0
- str_to_obj/type/value.py +47 -0
- str_to_obj/version.py +1 -1
- {str_to_obj-2024.1.dist-info → str_to_obj-2024.3.dist-info}/METADATA +1 -1
- str_to_obj-2024.3.dist-info/RECORD +24 -0
- {str_to_obj-2024.1.dist-info → str_to_obj-2024.3.dist-info}/WHEEL +1 -1
- str_to_obj-2024.1.dist-info/RECORD +0 -14
- {str_to_obj-2024.1.dist-info → str_to_obj-2024.3.dist-info}/top_level.txt +0 -0
str_to_obj/__init__.py
CHANGED
@@ -30,6 +30,8 @@
|
|
30
30
|
# knowledge of the CeCILL license and that you accept its terms.
|
31
31
|
|
32
32
|
from str_to_obj.main import ObjectFromStr
|
33
|
+
from str_to_obj.runtime.value import INVALID_VALUE
|
33
34
|
from str_to_obj.task.casting import CastValue
|
34
35
|
from str_to_obj.type.annotation import annotation_t
|
36
|
+
from str_to_obj.type.type import type_t
|
35
37
|
from str_to_obj.version import __version__
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# Copyright CNRS/Inria/UniCA
|
2
|
+
# Contributor(s): Eric Debreuve (since 2023)
|
3
|
+
#
|
4
|
+
# eric.debreuve@cnrs.fr
|
5
|
+
#
|
6
|
+
# This software is governed by the CeCILL license under French law and
|
7
|
+
# abiding by the rules of distribution of free software. You can use,
|
8
|
+
# modify and/ or redistribute the software under the terms of the CeCILL
|
9
|
+
# license as circulated by CEA, CNRS and INRIA at the following URL
|
10
|
+
# "http://www.cecill.info".
|
11
|
+
#
|
12
|
+
# As a counterpart to the access to the source code and rights to copy,
|
13
|
+
# modify and redistribute granted by the license, users are provided only
|
14
|
+
# with a limited warranty and the software's author, the holder of the
|
15
|
+
# economic rights, and the successive licensors have only limited
|
16
|
+
# liability.
|
17
|
+
#
|
18
|
+
# In this respect, the user's attention is drawn to the risks associated
|
19
|
+
# with loading, using, modifying and/or developing or reproducing the
|
20
|
+
# software by the user in light of its specific status of free software,
|
21
|
+
# that may mean that it is complicated to manipulate, and that also
|
22
|
+
# therefore means that it is reserved for developers and experienced
|
23
|
+
# professionals having in-depth computer knowledge. Users are therefore
|
24
|
+
# encouraged to load and test the software's suitability as regards their
|
25
|
+
# requirements in conditions enabling the security of their systems and/or
|
26
|
+
# data to be ensured and, more generally, to use and operate it in the
|
27
|
+
# same conditions as regards security.
|
28
|
+
#
|
29
|
+
# The fact that you are presently reading this means that you have had
|
30
|
+
# knowledge of the CeCILL license and that you accept its terms.
|
31
|
+
|
32
|
+
import dataclasses as dtcl
|
33
|
+
import typing as h
|
34
|
+
from enum import Enum as enum_t
|
35
|
+
|
36
|
+
from str_to_obj import annotation_t
|
37
|
+
from str_to_obj.type.hint import annotated_hint_t
|
38
|
+
|
39
|
+
|
40
|
+
class boolean_mode_e(enum_t):
|
41
|
+
# Always list true value first.
|
42
|
+
true_false = ("True", "False")
|
43
|
+
yes_no = ("Yes", "No")
|
44
|
+
on_off = ("On", "Off")
|
45
|
+
|
46
|
+
|
47
|
+
_BOOLEAN_MODES = tuple(boolean_mode_e.__members__.keys())
|
48
|
+
|
49
|
+
|
50
|
+
@dtcl.dataclass(slots=True, repr=False, eq=False)
|
51
|
+
class boolean_t(annotation_t):
|
52
|
+
|
53
|
+
ACCEPTED_TYPES: h.ClassVar[tuple[type, ...]] = (bool,)
|
54
|
+
|
55
|
+
mode: boolean_mode_e = boolean_mode_e.true_false
|
56
|
+
|
57
|
+
@classmethod
|
58
|
+
def NewAnnotatedType(
|
59
|
+
cls, /, *, mode: boolean_mode_e | str | None = None
|
60
|
+
) -> annotated_hint_t:
|
61
|
+
""""""
|
62
|
+
if mode is None:
|
63
|
+
mode = boolean_mode_e.true_false
|
64
|
+
elif isinstance(mode, str):
|
65
|
+
if mode in _BOOLEAN_MODES:
|
66
|
+
mode = boolean_mode_e[mode].value
|
67
|
+
else:
|
68
|
+
valid = " or ".join(_BOOLEAN_MODES)
|
69
|
+
raise ValueError(
|
70
|
+
f"Invalid boolean mode: Actual={mode}; Expected={valid}."
|
71
|
+
)
|
72
|
+
|
73
|
+
return h.Annotated[bool, cls(mode=mode)]
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Copyright CNRS/Inria/UniCA
|
2
|
+
# Contributor(s): Eric Debreuve (since 2023)
|
3
|
+
#
|
4
|
+
# eric.debreuve@cnrs.fr
|
5
|
+
#
|
6
|
+
# This software is governed by the CeCILL license under French law and
|
7
|
+
# abiding by the rules of distribution of free software. You can use,
|
8
|
+
# modify and/ or redistribute the software under the terms of the CeCILL
|
9
|
+
# license as circulated by CEA, CNRS and INRIA at the following URL
|
10
|
+
# "http://www.cecill.info".
|
11
|
+
#
|
12
|
+
# As a counterpart to the access to the source code and rights to copy,
|
13
|
+
# modify and redistribute granted by the license, users are provided only
|
14
|
+
# with a limited warranty and the software's author, the holder of the
|
15
|
+
# economic rights, and the successive licensors have only limited
|
16
|
+
# liability.
|
17
|
+
#
|
18
|
+
# In this respect, the user's attention is drawn to the risks associated
|
19
|
+
# with loading, using, modifying and/or developing or reproducing the
|
20
|
+
# software by the user in light of its specific status of free software,
|
21
|
+
# that may mean that it is complicated to manipulate, and that also
|
22
|
+
# therefore means that it is reserved for developers and experienced
|
23
|
+
# professionals having in-depth computer knowledge. Users are therefore
|
24
|
+
# encouraged to load and test the software's suitability as regards their
|
25
|
+
# requirements in conditions enabling the security of their systems and/or
|
26
|
+
# data to be ensured and, more generally, to use and operate it in the
|
27
|
+
# same conditions as regards security.
|
28
|
+
#
|
29
|
+
# The fact that you are presently reading this means that you have had
|
30
|
+
# knowledge of the CeCILL license and that you accept its terms.
|
31
|
+
|
32
|
+
import dataclasses as dtcl
|
33
|
+
import typing as h
|
34
|
+
|
35
|
+
from str_to_obj import annotation_t
|
36
|
+
from str_to_obj.catalog.choices import choices_t
|
37
|
+
from str_to_obj.type.hint import annotated_hint_t
|
38
|
+
|
39
|
+
|
40
|
+
@dtcl.dataclass(slots=True, repr=False, eq=False)
|
41
|
+
class callable_t(annotation_t):
|
42
|
+
ACCEPTED_TYPES: h.ClassVar[tuple[type, ...]] = (str,)
|
43
|
+
DUMMY_ENTRY: h.ClassVar[str] = "Dummy entry"
|
44
|
+
SEPARATOR: h.ClassVar[str] = ":"
|
45
|
+
kind: h.Literal["class", "function"]
|
46
|
+
choices: choices_t | None = None
|
47
|
+
allow_external: bool = False
|
48
|
+
|
49
|
+
@classmethod
|
50
|
+
def NewAnnotatedType(
|
51
|
+
cls,
|
52
|
+
/,
|
53
|
+
*,
|
54
|
+
kind: h.Literal["class", "function"] = "function",
|
55
|
+
catalog: h.Sequence[str] | bool | None = None,
|
56
|
+
allow_external: bool = False,
|
57
|
+
) -> annotated_hint_t:
|
58
|
+
""""""
|
59
|
+
if catalog is None:
|
60
|
+
annotation = cls(kind=kind, allow_external=allow_external)
|
61
|
+
elif isinstance(catalog, bool):
|
62
|
+
if catalog:
|
63
|
+
annotation = cls(
|
64
|
+
kind=kind,
|
65
|
+
choices=choices_t((cls.DUMMY_ENTRY,)),
|
66
|
+
allow_external=allow_external,
|
67
|
+
)
|
68
|
+
else:
|
69
|
+
annotation = cls(kind=kind, allow_external=allow_external)
|
70
|
+
else:
|
71
|
+
annotation = cls(
|
72
|
+
kind=kind, choices=choices_t(catalog), allow_external=allow_external
|
73
|
+
)
|
74
|
+
|
75
|
+
return h.Annotated[str, annotation]
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# Copyright CNRS/Inria/UniCA
|
2
|
+
# Contributor(s): Eric Debreuve (since 2023)
|
3
|
+
#
|
4
|
+
# eric.debreuve@cnrs.fr
|
5
|
+
#
|
6
|
+
# This software is governed by the CeCILL license under French law and
|
7
|
+
# abiding by the rules of distribution of free software. You can use,
|
8
|
+
# modify and/ or redistribute the software under the terms of the CeCILL
|
9
|
+
# license as circulated by CEA, CNRS and INRIA at the following URL
|
10
|
+
# "http://www.cecill.info".
|
11
|
+
#
|
12
|
+
# As a counterpart to the access to the source code and rights to copy,
|
13
|
+
# modify and redistribute granted by the license, users are provided only
|
14
|
+
# with a limited warranty and the software's author, the holder of the
|
15
|
+
# economic rights, and the successive licensors have only limited
|
16
|
+
# liability.
|
17
|
+
#
|
18
|
+
# In this respect, the user's attention is drawn to the risks associated
|
19
|
+
# with loading, using, modifying and/or developing or reproducing the
|
20
|
+
# software by the user in light of its specific status of free software,
|
21
|
+
# that may mean that it is complicated to manipulate, and that also
|
22
|
+
# therefore means that it is reserved for developers and experienced
|
23
|
+
# professionals having in-depth computer knowledge. Users are therefore
|
24
|
+
# encouraged to load and test the software's suitability as regards their
|
25
|
+
# requirements in conditions enabling the security of their systems and/or
|
26
|
+
# data to be ensured and, more generally, to use and operate it in the
|
27
|
+
# same conditions as regards security.
|
28
|
+
#
|
29
|
+
# The fact that you are presently reading this means that you have had
|
30
|
+
# knowledge of the CeCILL license and that you accept its terms.
|
31
|
+
|
32
|
+
import dataclasses as dtcl
|
33
|
+
import typing as h
|
34
|
+
|
35
|
+
from issue_manager import ISSUE_MANAGER
|
36
|
+
from str_to_obj import annotation_t
|
37
|
+
from str_to_obj.type.hint import annotated_hint_t
|
38
|
+
|
39
|
+
|
40
|
+
@dtcl.dataclass(slots=True, repr=False, eq=False)
|
41
|
+
class choices_t(annotation_t):
|
42
|
+
ACCEPTED_TYPES: h.ClassVar[tuple[type, ...]] = (str,)
|
43
|
+
options: h.Sequence[str]
|
44
|
+
|
45
|
+
def __post_init__(self) -> None:
|
46
|
+
""""""
|
47
|
+
with ISSUE_MANAGER.AddedContextLevel("Choices Annotation"):
|
48
|
+
for option in self.options:
|
49
|
+
if not isinstance(option, str):
|
50
|
+
ISSUE_MANAGER.Add(
|
51
|
+
f'Invalid type of option "{option}"',
|
52
|
+
actual=type(option).__name__,
|
53
|
+
expected="str",
|
54
|
+
)
|
55
|
+
|
56
|
+
@classmethod
|
57
|
+
def NewAnnotatedType(cls, options: h.Sequence[str], /) -> annotated_hint_t:
|
58
|
+
""""""
|
59
|
+
return h.Annotated[str, cls(tuple(options))]
|
60
|
+
|
61
|
+
def ValueIssues(self, value: str | h.Any, /) -> list[str]:
|
62
|
+
""""""
|
63
|
+
issues = annotation_t.ValueIssues(self, value)
|
64
|
+
if issues.__len__() > 0:
|
65
|
+
return issues
|
66
|
+
|
67
|
+
if (self.options.__len__() == 0) or (value in self.options):
|
68
|
+
# Options can be empty for a controlling parameter whose controlled section
|
69
|
+
# has not been specified. In a GUI context, such controlled section should
|
70
|
+
# have been populated programmatically.
|
71
|
+
return []
|
72
|
+
|
73
|
+
options = map(lambda _elm: f'"{_elm}"', self.options)
|
74
|
+
options = " or ".join(options)
|
75
|
+
|
76
|
+
return [f"Invalid choice: Actual={value}; Expected={options}."]
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# Copyright CNRS/Inria/UniCA
|
2
|
+
# Contributor(s): Eric Debreuve (since 2023)
|
3
|
+
#
|
4
|
+
# eric.debreuve@cnrs.fr
|
5
|
+
#
|
6
|
+
# This software is governed by the CeCILL license under French law and
|
7
|
+
# abiding by the rules of distribution of free software. You can use,
|
8
|
+
# modify and/ or redistribute the software under the terms of the CeCILL
|
9
|
+
# license as circulated by CEA, CNRS and INRIA at the following URL
|
10
|
+
# "http://www.cecill.info".
|
11
|
+
#
|
12
|
+
# As a counterpart to the access to the source code and rights to copy,
|
13
|
+
# modify and redistribute granted by the license, users are provided only
|
14
|
+
# with a limited warranty and the software's author, the holder of the
|
15
|
+
# economic rights, and the successive licensors have only limited
|
16
|
+
# liability.
|
17
|
+
#
|
18
|
+
# In this respect, the user's attention is drawn to the risks associated
|
19
|
+
# with loading, using, modifying and/or developing or reproducing the
|
20
|
+
# software by the user in light of its specific status of free software,
|
21
|
+
# that may mean that it is complicated to manipulate, and that also
|
22
|
+
# therefore means that it is reserved for developers and experienced
|
23
|
+
# professionals having in-depth computer knowledge. Users are therefore
|
24
|
+
# encouraged to load and test the software's suitability as regards their
|
25
|
+
# requirements in conditions enabling the security of their systems and/or
|
26
|
+
# data to be ensured and, more generally, to use and operate it in the
|
27
|
+
# same conditions as regards security.
|
28
|
+
#
|
29
|
+
# The fact that you are presently reading this means that you have had
|
30
|
+
# knowledge of the CeCILL license and that you accept its terms.
|
31
|
+
|
32
|
+
from __future__ import annotations
|
33
|
+
|
34
|
+
import dataclasses as dtcl
|
35
|
+
import types as t
|
36
|
+
import typing as h
|
37
|
+
|
38
|
+
from issue_manager import ISSUE_MANAGER
|
39
|
+
from str_to_obj import annotation_t
|
40
|
+
from str_to_obj.type.hint import any_hint_h
|
41
|
+
from str_to_obj.type.type import type_t
|
42
|
+
|
43
|
+
|
44
|
+
@dtcl.dataclass(slots=True, repr=False, eq=False)
|
45
|
+
class collection_t(annotation_t):
|
46
|
+
# Items of any type but None
|
47
|
+
ANY_ITEMS_TYPES: h.ClassVar[h.Any | tuple[h.Any, ...]] = h.Any
|
48
|
+
ANY_LENGTH: h.ClassVar[tuple[int, ...]] = (0,)
|
49
|
+
|
50
|
+
ACCEPTED_TYPES: h.ClassVar[tuple[type, ...]] = (list, set, tuple)
|
51
|
+
items_types: any_hint_h | tuple[any_hint_h, ...] | type_t | tuple[type_t, ...] = (
|
52
|
+
ANY_ITEMS_TYPES
|
53
|
+
)
|
54
|
+
lengths: int | tuple[int, ...] = ANY_LENGTH
|
55
|
+
|
56
|
+
def __post_init__(self) -> None:
|
57
|
+
""""""
|
58
|
+
original_item_types = self.items_types
|
59
|
+
original_lengths = self.lengths
|
60
|
+
|
61
|
+
if isinstance(self.items_types, h.Sequence):
|
62
|
+
items_types = []
|
63
|
+
for stripe in self.items_types:
|
64
|
+
if isinstance(stripe, type_t):
|
65
|
+
items_types.append(stripe)
|
66
|
+
else:
|
67
|
+
items_types.append(type_t.NewForHint(stripe))
|
68
|
+
self.items_types = items_types
|
69
|
+
elif not isinstance(self.items_types, type_t):
|
70
|
+
self.items_types = type_t.NewForHint(self.items_types)
|
71
|
+
|
72
|
+
if isinstance(self.lengths, int):
|
73
|
+
self.lengths = (self.lengths,)
|
74
|
+
else:
|
75
|
+
self.lengths = tuple(sorted(self.lengths))
|
76
|
+
|
77
|
+
with ISSUE_MANAGER.AddedContextLevel("Collection Annotation"):
|
78
|
+
if isinstance(self.items_types, h.Sequence):
|
79
|
+
if max(self.lengths) > self.items_types.__len__():
|
80
|
+
ISSUE_MANAGER.Add(
|
81
|
+
f'Allowed length(s) ("{original_lengths}") must not exceed '
|
82
|
+
f"the length of the item types sequence "
|
83
|
+
f'("{original_item_types}")'
|
84
|
+
)
|
85
|
+
if self.lengths != self.__class__.ANY_LENGTH:
|
86
|
+
for length in self.lengths:
|
87
|
+
if (not isinstance(length, int)) or (length < 0):
|
88
|
+
ISSUE_MANAGER.Add(
|
89
|
+
f"Invalid length in {self.lengths}",
|
90
|
+
actual=length,
|
91
|
+
expected="strictly positive integer",
|
92
|
+
)
|
93
|
+
|
94
|
+
@classmethod
|
95
|
+
def NewForType(
|
96
|
+
cls,
|
97
|
+
stripe: any_hint_h | tuple[any_hint_h, ...] | type_t | tuple[type_t, ...],
|
98
|
+
/,
|
99
|
+
) -> collection_t:
|
100
|
+
""""""
|
101
|
+
if not isinstance(stripe, (type_t | tuple[type_t, ...])):
|
102
|
+
stripe = type_t.NewForHint(stripe)
|
103
|
+
|
104
|
+
if stripe.type not in cls.ACCEPTED_TYPES:
|
105
|
+
with ISSUE_MANAGER.AddedContextLevel("Collection Annotation"):
|
106
|
+
ISSUE_MANAGER.Add(
|
107
|
+
f"Invalid type",
|
108
|
+
actual=stripe.type.__name__,
|
109
|
+
expected=", ".join(_elm.__name__ for _elm in cls.ACCEPTED_TYPES),
|
110
|
+
)
|
111
|
+
return collection_t()
|
112
|
+
|
113
|
+
if stripe.elements is None:
|
114
|
+
return collection_t()
|
115
|
+
|
116
|
+
if (n_elements := stripe.elements.__len__()) == 1:
|
117
|
+
return collection_t(items_types=stripe.elements[0], lengths=1)
|
118
|
+
|
119
|
+
if (n_elements == 2) and (stripe.elements[1].type is t.EllipsisType):
|
120
|
+
return collection_t(items_types=stripe.elements[0], lengths=cls.ANY_LENGTH)
|
121
|
+
|
122
|
+
return collection_t(items_types=stripe.elements, lengths=n_elements)
|
123
|
+
|
124
|
+
def ValueIssues(self, value: list | set | tuple | h.Any, /) -> list[str]:
|
125
|
+
""""""
|
126
|
+
issues = annotation_t.ValueIssues(self, value)
|
127
|
+
if issues.__len__() > 0:
|
128
|
+
return issues
|
129
|
+
|
130
|
+
if (self.lengths != self.__class__.ANY_LENGTH) and (
|
131
|
+
value.__len__() not in self.lengths
|
132
|
+
):
|
133
|
+
return [
|
134
|
+
f"{value}: Sequence of invalid length; "
|
135
|
+
f"Expected={' or '.join(map(str, self.lengths))}."
|
136
|
+
]
|
137
|
+
|
138
|
+
if isinstance(self.items_types, h.Sequence):
|
139
|
+
if value.__len__() > self.items_types.__len__():
|
140
|
+
return [
|
141
|
+
f"{value}: Sequence too long. "
|
142
|
+
f"Expected=At most {self.items_types.__len__()} element(s)."
|
143
|
+
]
|
144
|
+
|
145
|
+
output = []
|
146
|
+
for stripe, element in zip(self.items_types, value):
|
147
|
+
output.extend(stripe.ValueIssues(element))
|
148
|
+
return output
|
149
|
+
|
150
|
+
stripe: type_t = self.items_types
|
151
|
+
output = []
|
152
|
+
for element in value:
|
153
|
+
output.extend(stripe.ValueIssues(element))
|
154
|
+
return output
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# Copyright CNRS/Inria/UniCA
|
2
|
+
# Contributor(s): Eric Debreuve (since 2023)
|
3
|
+
#
|
4
|
+
# eric.debreuve@cnrs.fr
|
5
|
+
#
|
6
|
+
# This software is governed by the CeCILL license under French law and
|
7
|
+
# abiding by the rules of distribution of free software. You can use,
|
8
|
+
# modify and/ or redistribute the software under the terms of the CeCILL
|
9
|
+
# license as circulated by CEA, CNRS and INRIA at the following URL
|
10
|
+
# "http://www.cecill.info".
|
11
|
+
#
|
12
|
+
# As a counterpart to the access to the source code and rights to copy,
|
13
|
+
# modify and redistribute granted by the license, users are provided only
|
14
|
+
# with a limited warranty and the software's author, the holder of the
|
15
|
+
# economic rights, and the successive licensors have only limited
|
16
|
+
# liability.
|
17
|
+
#
|
18
|
+
# In this respect, the user's attention is drawn to the risks associated
|
19
|
+
# with loading, using, modifying and/or developing or reproducing the
|
20
|
+
# software by the user in light of its specific status of free software,
|
21
|
+
# that may mean that it is complicated to manipulate, and that also
|
22
|
+
# therefore means that it is reserved for developers and experienced
|
23
|
+
# professionals having in-depth computer knowledge. Users are therefore
|
24
|
+
# encouraged to load and test the software's suitability as regards their
|
25
|
+
# requirements in conditions enabling the security of their systems and/or
|
26
|
+
# data to be ensured and, more generally, to use and operate it in the
|
27
|
+
# same conditions as regards security.
|
28
|
+
#
|
29
|
+
# The fact that you are presently reading this means that you have had
|
30
|
+
# knowledge of the CeCILL license and that you accept its terms.
|
31
|
+
|
32
|
+
import dataclasses as dtcl
|
33
|
+
import typing as h
|
34
|
+
|
35
|
+
from issue_manager import ISSUE_MANAGER
|
36
|
+
from str_to_obj import annotation_t
|
37
|
+
|
38
|
+
number_h = int | float
|
39
|
+
|
40
|
+
|
41
|
+
@dtcl.dataclass(slots=True, repr=False, eq=False)
|
42
|
+
class number_t(annotation_t):
|
43
|
+
INFINITY_NEG: h.ClassVar[float] = -float("inf")
|
44
|
+
INFINITY_POS: h.ClassVar[float] = float("inf")
|
45
|
+
INFINITE_PRECISION: h.ClassVar[float] = 0.0
|
46
|
+
|
47
|
+
ACCEPTED_TYPES: h.ClassVar[tuple[type, ...]] = (int, float)
|
48
|
+
min: number_h = INFINITY_NEG
|
49
|
+
max: number_h = INFINITY_POS
|
50
|
+
min_inclusive: bool = True
|
51
|
+
max_inclusive: bool = True
|
52
|
+
precision: number_h = INFINITE_PRECISION
|
53
|
+
|
54
|
+
def __post_init__(self) -> None:
|
55
|
+
""""""
|
56
|
+
stripe = self.__class__
|
57
|
+
|
58
|
+
with ISSUE_MANAGER.AddedContextLevel("Number Annotation"):
|
59
|
+
if (self.min != stripe.INFINITY_NEG) and not isinstance(
|
60
|
+
self.min, number_h.__args__
|
61
|
+
):
|
62
|
+
ISSUE_MANAGER.Add(
|
63
|
+
f"Invalid type for min value {self.min}",
|
64
|
+
actual=type(self.min).__name__,
|
65
|
+
expected=number_h,
|
66
|
+
)
|
67
|
+
if (self.max != stripe.INFINITY_POS) and not isinstance(
|
68
|
+
self.max, number_h.__args__
|
69
|
+
):
|
70
|
+
ISSUE_MANAGER.Add(
|
71
|
+
f"Invalid type for max value {self.max}",
|
72
|
+
actual=type(self.max).__name__,
|
73
|
+
expected=number_h,
|
74
|
+
)
|
75
|
+
if (self.precision != stripe.INFINITE_PRECISION) and not isinstance(
|
76
|
+
self.precision, number_h.__args__
|
77
|
+
):
|
78
|
+
ISSUE_MANAGER.Add(
|
79
|
+
f"Invalid type for precision {self.precision}",
|
80
|
+
actual=type(self.precision).__name__,
|
81
|
+
expected=number_h,
|
82
|
+
)
|
83
|
+
if self.precision < 0:
|
84
|
+
ISSUE_MANAGER.Add(f"Invalid, negative precision {self.precision}")
|
85
|
+
if (self.min > self.max) or (
|
86
|
+
(self.min == self.max)
|
87
|
+
and not (self.min_inclusive and self.max_inclusive)
|
88
|
+
):
|
89
|
+
if self.min_inclusive:
|
90
|
+
opening = "["
|
91
|
+
else:
|
92
|
+
opening = "]"
|
93
|
+
if self.max_inclusive:
|
94
|
+
closing = "]"
|
95
|
+
else:
|
96
|
+
closing = "["
|
97
|
+
ISSUE_MANAGER.Add(
|
98
|
+
f"Empty value interval {opening}{self.min},{self.max}{closing}"
|
99
|
+
)
|
100
|
+
|
101
|
+
def ValueIssues(self, value: number_h, /) -> list[str]:
|
102
|
+
""""""
|
103
|
+
issues = annotation_t.ValueIssues(self, value)
|
104
|
+
if issues.__len__() > 0:
|
105
|
+
return issues
|
106
|
+
|
107
|
+
if self.min <= value <= self.max:
|
108
|
+
if ((value == self.min) and not self.min_inclusive) or (
|
109
|
+
(value == self.max) and not self.max_inclusive
|
110
|
+
):
|
111
|
+
return [f"{value}: Value outside prescribed interval."]
|
112
|
+
|
113
|
+
if (self.precision != self.__class__.INFINITE_PRECISION) and (
|
114
|
+
self.precision * int(value / self.precision) != value
|
115
|
+
):
|
116
|
+
return [f"{value}: Value of higher precision than the prescribed one."]
|
117
|
+
|
118
|
+
return []
|
119
|
+
else:
|
120
|
+
return [f"{value}: Value outside prescribed interval."]
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# Copyright CNRS/Inria/UniCA
|
2
|
+
# Contributor(s): Eric Debreuve (since 2023)
|
3
|
+
#
|
4
|
+
# eric.debreuve@cnrs.fr
|
5
|
+
#
|
6
|
+
# This software is governed by the CeCILL license under French law and
|
7
|
+
# abiding by the rules of distribution of free software. You can use,
|
8
|
+
# modify and/ or redistribute the software under the terms of the CeCILL
|
9
|
+
# license as circulated by CEA, CNRS and INRIA at the following URL
|
10
|
+
# "http://www.cecill.info".
|
11
|
+
#
|
12
|
+
# As a counterpart to the access to the source code and rights to copy,
|
13
|
+
# modify and redistribute granted by the license, users are provided only
|
14
|
+
# with a limited warranty and the software's author, the holder of the
|
15
|
+
# economic rights, and the successive licensors have only limited
|
16
|
+
# liability.
|
17
|
+
#
|
18
|
+
# In this respect, the user's attention is drawn to the risks associated
|
19
|
+
# with loading, using, modifying and/or developing or reproducing the
|
20
|
+
# software by the user in light of its specific status of free software,
|
21
|
+
# that may mean that it is complicated to manipulate, and that also
|
22
|
+
# therefore means that it is reserved for developers and experienced
|
23
|
+
# professionals having in-depth computer knowledge. Users are therefore
|
24
|
+
# encouraged to load and test the software's suitability as regards their
|
25
|
+
# requirements in conditions enabling the security of their systems and/or
|
26
|
+
# data to be ensured and, more generally, to use and operate it in the
|
27
|
+
# same conditions as regards security.
|
28
|
+
#
|
29
|
+
# The fact that you are presently reading this means that you have had
|
30
|
+
# knowledge of the CeCILL license and that you accept its terms.
|
31
|
+
|
32
|
+
import dataclasses as dtcl
|
33
|
+
import typing as h
|
34
|
+
from enum import Enum as enum_t
|
35
|
+
from pathlib import Path as pl_path_t
|
36
|
+
|
37
|
+
from str_to_obj import annotation_t
|
38
|
+
from str_to_obj.type.hint import annotated_hint_t
|
39
|
+
|
40
|
+
|
41
|
+
class path_type_e(enum_t):
|
42
|
+
document = 1
|
43
|
+
folder = 2
|
44
|
+
any = 3
|
45
|
+
|
46
|
+
|
47
|
+
class path_purpose_e(enum_t):
|
48
|
+
input = 1
|
49
|
+
output = 2
|
50
|
+
any = 3
|
51
|
+
|
52
|
+
|
53
|
+
@dtcl.dataclass(slots=True, repr=False, eq=False)
|
54
|
+
class path_t(annotation_t):
|
55
|
+
|
56
|
+
ACCEPTED_TYPES: h.ClassVar[tuple[type, ...]] = (pl_path_t,)
|
57
|
+
type_: path_type_e
|
58
|
+
purpose: path_purpose_e
|
59
|
+
|
60
|
+
@classmethod
|
61
|
+
def NewAnnotatedType(
|
62
|
+
cls, type_: path_type_e, purpose: path_purpose_e, /
|
63
|
+
) -> annotated_hint_t:
|
64
|
+
""""""
|
65
|
+
return h.Annotated[pl_path_t, cls(type_=type_, purpose=purpose)]
|
66
|
+
|
67
|
+
def ValueIssues(self, value: pl_path_t | h.Any, /) -> list[str]:
|
68
|
+
"""
|
69
|
+
None: Unspecified path.
|
70
|
+
"""
|
71
|
+
issues = annotation_t.ValueIssues(self, value)
|
72
|
+
if issues.__len__() > 0:
|
73
|
+
return issues
|
74
|
+
|
75
|
+
if self.purpose is not path_purpose_e.input:
|
76
|
+
return []
|
77
|
+
|
78
|
+
if value.exists():
|
79
|
+
if self.type_ is path_type_e.any:
|
80
|
+
if value.is_file() or value.is_dir():
|
81
|
+
return []
|
82
|
+
else:
|
83
|
+
return [f"{value}: Not a valid file or folder."]
|
84
|
+
|
85
|
+
if (self.type_ is path_type_e.document) and value.is_file():
|
86
|
+
return []
|
87
|
+
|
88
|
+
if (self.type_ is path_type_e.folder) and value.is_dir():
|
89
|
+
return []
|
90
|
+
|
91
|
+
return [
|
92
|
+
f"{value}: Non-existent file or folder, or file for folder, "
|
93
|
+
f"or folder for file."
|
94
|
+
]
|
str_to_obj/interface/console.py
CHANGED
@@ -29,10 +29,10 @@
|
|
29
29
|
# The fact that you are presently reading this means that you have had
|
30
30
|
# knowledge of the CeCILL license and that you accept its terms.
|
31
31
|
|
32
|
-
|
32
|
+
import typing as h
|
33
33
|
|
34
34
|
|
35
|
-
def TypeAsRichStr(value: Any, /, *, prefix: str = "") -> str:
|
35
|
+
def TypeAsRichStr(value: h.Any, /, *, prefix: str = "") -> str:
|
36
36
|
"""
|
37
37
|
Richer alternative (however, causes troubles with packages like TensorFlow):
|
38
38
|
Additional parameter: relative_to_home: bool = True
|
@@ -46,10 +46,10 @@ def TypeAsRichStr(value: Any, /, *, prefix: str = "") -> str:
|
|
46
46
|
return f"[yellow]{prefix}{type(value).__name__}[/]"
|
47
47
|
|
48
48
|
|
49
|
-
def NameValueTypeAsRichStr(name: str, value: Any, /, *, separator: str = "=") -> str:
|
49
|
+
def NameValueTypeAsRichStr(name: str, value: h.Any, /, *, separator: str = "=") -> str:
|
50
50
|
""""""
|
51
51
|
formatted_type = TypeAsRichStr(value, prefix=":")
|
52
|
-
if isinstance(value, Sequence) and (value.__len__() == 0):
|
52
|
+
if isinstance(value, h.Sequence) and (value.__len__() == 0):
|
53
53
|
value = "[cyan]<empty>[/]"
|
54
54
|
|
55
55
|
return f"[blue]{name}[/]{separator}{value}{formatted_type}"
|
str_to_obj/main.py
CHANGED
@@ -30,8 +30,8 @@
|
|
30
30
|
# knowledge of the CeCILL license and that you accept its terms.
|
31
31
|
|
32
32
|
import ast as bstr
|
33
|
-
|
34
|
-
|
33
|
+
import types as t
|
34
|
+
import typing as h
|
35
35
|
|
36
36
|
from str_to_obj.task.casting import CastValue
|
37
37
|
from str_to_obj.type.hint_tree import any_hint_h, hint_tree_t
|
@@ -41,13 +41,13 @@ def ObjectFromStr(
|
|
41
41
|
string: str,
|
42
42
|
/,
|
43
43
|
*,
|
44
|
-
expected_type: any_hint_h | hint_tree_t | type[NoneType] = None,
|
45
|
-
) -> tuple[Any, list[str]]:
|
44
|
+
expected_type: any_hint_h | hint_tree_t | type[t.NoneType] = None,
|
45
|
+
) -> tuple[h.Any, list[str]]:
|
46
46
|
"""
|
47
47
|
expected_type: Must not be passed explicitly as None since None is interpreted as
|
48
48
|
"no specific expected type". When expecting None, pass NoneType.
|
49
49
|
"""
|
50
|
-
if expected_type is NoneType:
|
50
|
+
if expected_type is t.NoneType:
|
51
51
|
if string.lower() == "none":
|
52
52
|
issues = []
|
53
53
|
else:
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Copyright CNRS/Inria/UNS
|
2
|
+
# Contributor(s): Eric Debreuve (since 2023)
|
3
|
+
#
|
4
|
+
# eric.debreuve@cnrs.fr
|
5
|
+
#
|
6
|
+
# This software is governed by the CeCILL license under French law and
|
7
|
+
# abiding by the rules of distribution of free software. You can use,
|
8
|
+
# modify and/ or redistribute the software under the terms of the CeCILL
|
9
|
+
# license as circulated by CEA, CNRS and INRIA at the following URL
|
10
|
+
# "http://www.cecill.info".
|
11
|
+
#
|
12
|
+
# As a counterpart to the access to the source code and rights to copy,
|
13
|
+
# modify and redistribute granted by the license, users are provided only
|
14
|
+
# with a limited warranty and the software's author, the holder of the
|
15
|
+
# economic rights, and the successive licensors have only limited
|
16
|
+
# liability.
|
17
|
+
#
|
18
|
+
# In this respect, the user's attention is drawn to the risks associated
|
19
|
+
# with loading, using, modifying and/or developing or reproducing the
|
20
|
+
# software by the user in light of its specific status of free software,
|
21
|
+
# that may mean that it is complicated to manipulate, and that also
|
22
|
+
# therefore means that it is reserved for developers and experienced
|
23
|
+
# professionals having in-depth computer knowledge. Users are therefore
|
24
|
+
# encouraged to load and test the software's suitability as regards their
|
25
|
+
# requirements in conditions enabling the security of their systems and/or
|
26
|
+
# data to be ensured and, more generally, to use and operate it in the
|
27
|
+
# same conditions as regards security.
|
28
|
+
#
|
29
|
+
# The fact that you are presently reading this means that you have had
|
30
|
+
# knowledge of the CeCILL license and that you accept its terms.
|
31
|
+
|
32
|
+
import typing as h
|
33
|
+
|
34
|
+
from str_to_obj.type.type import any_type_t
|
35
|
+
|
36
|
+
ANY_TYPE = any_type_t.NewForHint(h.Any)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Copyright CNRS/Inria/UNS
|
2
|
+
# Contributor(s): Eric Debreuve (since 2023)
|
3
|
+
#
|
4
|
+
# eric.debreuve@cnrs.fr
|
5
|
+
#
|
6
|
+
# This software is governed by the CeCILL license under French law and
|
7
|
+
# abiding by the rules of distribution of free software. You can use,
|
8
|
+
# modify and/ or redistribute the software under the terms of the CeCILL
|
9
|
+
# license as circulated by CEA, CNRS and INRIA at the following URL
|
10
|
+
# "http://www.cecill.info".
|
11
|
+
#
|
12
|
+
# As a counterpart to the access to the source code and rights to copy,
|
13
|
+
# modify and redistribute granted by the license, users are provided only
|
14
|
+
# with a limited warranty and the software's author, the holder of the
|
15
|
+
# economic rights, and the successive licensors have only limited
|
16
|
+
# liability.
|
17
|
+
#
|
18
|
+
# In this respect, the user's attention is drawn to the risks associated
|
19
|
+
# with loading, using, modifying and/or developing or reproducing the
|
20
|
+
# software by the user in light of its specific status of free software,
|
21
|
+
# that may mean that it is complicated to manipulate, and that also
|
22
|
+
# therefore means that it is reserved for developers and experienced
|
23
|
+
# professionals having in-depth computer knowledge. Users are therefore
|
24
|
+
# encouraged to load and test the software's suitability as regards their
|
25
|
+
# requirements in conditions enabling the security of their systems and/or
|
26
|
+
# data to be ensured and, more generally, to use and operate it in the
|
27
|
+
# same conditions as regards security.
|
28
|
+
#
|
29
|
+
# The fact that you are presently reading this means that you have had
|
30
|
+
# knowledge of the CeCILL license and that you accept its terms.
|
31
|
+
|
32
|
+
from str_to_obj.type.value import invalid_value_t
|
33
|
+
|
34
|
+
INVALID_VALUE = invalid_value_t()
|
str_to_obj/task/casting.py
CHANGED
@@ -32,9 +32,9 @@
|
|
32
32
|
from __future__ import annotations
|
33
33
|
|
34
34
|
import dataclasses as dtcl
|
35
|
+
import types as t
|
36
|
+
import typing as h
|
35
37
|
from pathlib import Path as path_t
|
36
|
-
from types import EllipsisType, NoneType, UnionType
|
37
|
-
from typing import Any, Iterable
|
38
38
|
|
39
39
|
from str_to_obj.type.hint import any_hint_h, simple_hint_h
|
40
40
|
from str_to_obj.type.hint_tree import hint_tree_t
|
@@ -55,7 +55,7 @@ class _value_node_t:
|
|
55
55
|
"""
|
56
56
|
|
57
57
|
type: simple_hint_h = None
|
58
|
-
leaf_value: Any | None = _NOT_A_LEAF_NODE
|
58
|
+
leaf_value: h.Any | None = _NOT_A_LEAF_NODE
|
59
59
|
|
60
60
|
|
61
61
|
@dtcl.dataclass(slots=True, repr=False, eq=False)
|
@@ -63,15 +63,15 @@ class _value_tree_t(_value_node_t):
|
|
63
63
|
elements: tuple[_value_tree_t, ...] = None
|
64
64
|
|
65
65
|
@classmethod
|
66
|
-
def NewFromValue(cls, value: Any, /) -> _value_tree_t:
|
66
|
+
def NewFromValue(cls, value: h.Any, /) -> _value_tree_t:
|
67
67
|
""""""
|
68
|
-
if isinstance(value, Iterable) and not isinstance(value, str):
|
68
|
+
if isinstance(value, h.Iterable) and not isinstance(value, str):
|
69
69
|
elements = tuple(cls.NewFromValue(_elm) for _elm in value)
|
70
70
|
return cls(type=type(value), elements=elements)
|
71
71
|
|
72
72
|
return cls(leaf_value=value)
|
73
73
|
|
74
|
-
def _RebuiltValue(self) -> Any:
|
74
|
+
def _RebuiltValue(self) -> h.Any:
|
75
75
|
""""""
|
76
76
|
if self.leaf_value is _NOT_A_LEAF_NODE:
|
77
77
|
return self.type(_elm._RebuiltValue() for _elm in self.elements)
|
@@ -80,7 +80,7 @@ class _value_tree_t(_value_node_t):
|
|
80
80
|
|
81
81
|
def CastValue(
|
82
82
|
self, hint_tree: hint_tree_t, /, *, only_check_validity: bool = False
|
83
|
-
) -> tuple[Any, list[str]] | list[str]:
|
83
|
+
) -> tuple[h.Any, list[str]] | list[str]:
|
84
84
|
""""""
|
85
85
|
issues = self._CastToHint(hint_tree)
|
86
86
|
if issues.__len__() > 0:
|
@@ -102,10 +102,10 @@ class _value_tree_t(_value_node_t):
|
|
102
102
|
hn_type = hint_node.type
|
103
103
|
hn_elements = hint_node.elements
|
104
104
|
|
105
|
-
if hn_type is Any:
|
105
|
+
if hn_type is h.Any:
|
106
106
|
return self._CompliesWithAnnotations(hint_node)
|
107
107
|
|
108
|
-
if hn_type is NoneType:
|
108
|
+
if hn_type is t.NoneType:
|
109
109
|
# None is not supposed to have annotations. They are ignored if it does.
|
110
110
|
if self.leaf_value is None:
|
111
111
|
output = []
|
@@ -113,7 +113,7 @@ class _value_tree_t(_value_node_t):
|
|
113
113
|
output = [f"{self.leaf_value}: Invalid value; Expected=None."]
|
114
114
|
return output
|
115
115
|
|
116
|
-
if hn_type is UnionType:
|
116
|
+
if hn_type is t.UnionType:
|
117
117
|
issues = []
|
118
118
|
for element in hn_elements:
|
119
119
|
local_issues = self._CastToHint(element)
|
@@ -167,8 +167,11 @@ class _value_tree_t(_value_node_t):
|
|
167
167
|
rebuilt = self._RebuiltValue()
|
168
168
|
try:
|
169
169
|
_ = hn_type(rebuilt)
|
170
|
-
except:
|
171
|
-
return [
|
170
|
+
except Exception as exception:
|
171
|
+
return [
|
172
|
+
f"{rebuilt}: Cannot be cast to type {hn_type.__name__} "
|
173
|
+
f"({exception.__class__.__name__})."
|
174
|
+
]
|
172
175
|
else:
|
173
176
|
self.type = hn_type
|
174
177
|
|
@@ -176,7 +179,9 @@ class _value_tree_t(_value_node_t):
|
|
176
179
|
|
177
180
|
n_value_elements = self.elements.__len__()
|
178
181
|
n_hint_elements = hn_elements.__len__()
|
179
|
-
has_ellipsis = (n_hint_elements == 2) and (
|
182
|
+
has_ellipsis = (n_hint_elements == 2) and (
|
183
|
+
hn_elements[1].type is t.EllipsisType
|
184
|
+
)
|
180
185
|
should_fake_ellipsis = (n_hint_elements == 1) and issubclass(
|
181
186
|
hn_type, (list, set)
|
182
187
|
)
|
@@ -208,17 +213,21 @@ class _value_tree_t(_value_node_t):
|
|
208
213
|
output = []
|
209
214
|
|
210
215
|
for annotation in hint_node.annotations:
|
211
|
-
output.extend(annotation.
|
216
|
+
output.extend(annotation.ValueIssues(self._RebuiltValue()))
|
212
217
|
|
213
218
|
return output
|
214
219
|
|
215
220
|
|
216
221
|
def CastValue(
|
217
|
-
value: Any,
|
218
|
-
|
222
|
+
value: h.Any,
|
223
|
+
hint: any_hint_h | hint_tree_t,
|
224
|
+
/,
|
225
|
+
*,
|
226
|
+
only_check_validity: bool = False,
|
227
|
+
) -> tuple[h.Any, list[str]] | list[str]:
|
219
228
|
""""""
|
220
229
|
value_tree = _value_tree_t.NewFromValue(value)
|
221
230
|
if not isinstance(hint, hint_tree_t):
|
222
|
-
hint = hint_tree_t.
|
231
|
+
hint = hint_tree_t.NewForHint(hint)
|
223
232
|
|
224
233
|
return value_tree.CastValue(hint, only_check_validity=only_check_validity)
|
str_to_obj/task/comparison.py
CHANGED
@@ -29,8 +29,8 @@
|
|
29
29
|
# The fact that you are presently reading this means that you have had
|
30
30
|
# knowledge of the CeCILL license and that you accept its terms.
|
31
31
|
|
32
|
+
import types as t
|
32
33
|
import typing as h
|
33
|
-
from types import UnionType
|
34
34
|
|
35
35
|
from str_to_obj.type.hint_tree import hint_tree_t
|
36
36
|
|
@@ -47,12 +47,12 @@ def TypesAreCompatible(
|
|
47
47
|
if h.Any in (one.type, another.type):
|
48
48
|
return True
|
49
49
|
|
50
|
-
if one.type is UnionType:
|
50
|
+
if one.type is t.UnionType:
|
51
51
|
return any(
|
52
52
|
TypesAreCompatible(_elm, another, strict_mode=strict_mode)
|
53
53
|
for _elm in one.elements
|
54
54
|
)
|
55
|
-
if another.type is UnionType:
|
55
|
+
if another.type is t.UnionType:
|
56
56
|
return any(
|
57
57
|
TypesAreCompatible(one, _elm, strict_mode=strict_mode)
|
58
58
|
for _elm in another.elements
|
str_to_obj/task/inspection.py
CHANGED
@@ -29,7 +29,7 @@
|
|
29
29
|
# The fact that you are presently reading this means that you have had
|
30
30
|
# knowledge of the CeCILL license and that you accept its terms.
|
31
31
|
|
32
|
-
|
32
|
+
import typing as h
|
33
33
|
|
34
34
|
from str_to_obj.type.annotation import annotation_t
|
35
35
|
from str_to_obj.type.hint import annotated_hint_t, any_hint_h, raw_hint_h
|
@@ -45,7 +45,9 @@ def TypeOfAnnotatedHint(annotated_hint: annotated_hint_t, /) -> raw_hint_h:
|
|
45
45
|
return annotated_hint.__args__[0]
|
46
46
|
|
47
47
|
|
48
|
-
def AnnotationsOfAnnotatedHint(
|
48
|
+
def AnnotationsOfAnnotatedHint(
|
49
|
+
annotated_hint: annotated_hint_t, /
|
50
|
+
) -> tuple[h.Any, ...]:
|
49
51
|
""""""
|
50
52
|
output = tuple(annotated_hint.__metadata__)
|
51
53
|
if all(isinstance(_elm, annotation_t) for _elm in output):
|
@@ -56,7 +58,7 @@ def AnnotationsOfAnnotatedHint(annotated_hint: annotated_hint_t, /) -> tuple[Any
|
|
56
58
|
)
|
57
59
|
|
58
60
|
|
59
|
-
def HintComponents(hint: any_hint_h, /) -> tuple[raw_hint_h, tuple[Any, ...]]:
|
61
|
+
def HintComponents(hint: any_hint_h, /) -> tuple[raw_hint_h, tuple[h.Any, ...]]:
|
60
62
|
""""""
|
61
63
|
if IsAnnotated(hint):
|
62
64
|
return TypeOfAnnotatedHint(hint), AnnotationsOfAnnotatedHint(hint)
|
str_to_obj/type/annotation.py
CHANGED
@@ -30,20 +30,16 @@
|
|
30
30
|
# knowledge of the CeCILL license and that you accept its terms.
|
31
31
|
|
32
32
|
import dataclasses as dtcl
|
33
|
-
|
33
|
+
import typing as h
|
34
34
|
|
35
35
|
from rich.text import Text as text_t
|
36
|
-
|
37
|
-
from str_to_obj.interface.console import (
|
38
|
-
NameValueTypeAsRichStr,
|
39
|
-
TypeAsRichStr,
|
40
|
-
)
|
36
|
+
from str_to_obj.interface.console import NameValueTypeAsRichStr, TypeAsRichStr
|
41
37
|
from str_to_obj.type.hint import annotated_hint_t, non_complex_hint_h
|
42
38
|
|
43
39
|
|
44
40
|
@dtcl.dataclass(slots=True, repr=False, eq=False)
|
45
41
|
class annotation_t:
|
46
|
-
ACCEPTED_TYPES: ClassVar[tuple[non_complex_hint_h, ...]] = (Any,)
|
42
|
+
ACCEPTED_TYPES: h.ClassVar[tuple[non_complex_hint_h, ...]] = (h.Any,)
|
47
43
|
|
48
44
|
@classmethod
|
49
45
|
def NewAnnotatedType(cls, *args, **kwargs) -> annotated_hint_t:
|
@@ -57,11 +53,11 @@ class annotation_t:
|
|
57
53
|
"""
|
58
54
|
raise NotImplementedError
|
59
55
|
|
60
|
-
def
|
56
|
+
def ValueIssues(self, value: h.Any, /) -> list[str]:
|
61
57
|
""""""
|
62
58
|
types = self.__class__.ACCEPTED_TYPES
|
63
59
|
stripe = type(value)
|
64
|
-
if (Any in types) or (stripe in types) or issubclass(stripe, types):
|
60
|
+
if (h.Any in types) or (stripe in types) or issubclass(stripe, types):
|
65
61
|
return []
|
66
62
|
|
67
63
|
expected = " or ".join(map(str, types))
|
str_to_obj/type/hint.py
CHANGED
@@ -29,14 +29,14 @@
|
|
29
29
|
# The fact that you are presently reading this means that you have had
|
30
30
|
# knowledge of the CeCILL license and that you accept its terms.
|
31
31
|
|
32
|
-
|
33
|
-
|
32
|
+
import types as t
|
33
|
+
import typing as h
|
34
34
|
|
35
35
|
# --- Unannotated hints
|
36
36
|
# Atoms
|
37
|
-
generic_hint_h = Any
|
38
|
-
simple_hint_h = type[Any]
|
39
|
-
complex_hint_h = GenericAlias | UnionType
|
37
|
+
generic_hint_h = h.Any
|
38
|
+
simple_hint_h = type[h.Any]
|
39
|
+
complex_hint_h = t.GenericAlias | t.UnionType
|
40
40
|
|
41
41
|
# Grouping
|
42
42
|
non_complex_hint_h = generic_hint_h | simple_hint_h
|
@@ -44,7 +44,7 @@ raw_hint_h = non_complex_hint_h | complex_hint_h
|
|
44
44
|
|
45
45
|
|
46
46
|
# --- Annotated hints
|
47
|
-
annotated_hint_t = type(Annotated[object, None])
|
47
|
+
annotated_hint_t = type(h.Annotated[object, None])
|
48
48
|
|
49
49
|
|
50
50
|
# --- [Un]Annotated hints
|
@@ -54,4 +54,4 @@ any_hint_h = raw_hint_h | annotated_hint_t
|
|
54
54
|
# --- Complex hints additional components:
|
55
55
|
# - EllipsisType for GenericAlias, as in: tuple[int, ...]
|
56
56
|
# - None for UnionType, as in: int | None
|
57
|
-
complex_hint_additions_h = EllipsisType | None
|
57
|
+
complex_hint_additions_h = t.EllipsisType | None
|
str_to_obj/type/hint_tree.py
CHANGED
@@ -33,8 +33,8 @@ from __future__ import annotations
|
|
33
33
|
|
34
34
|
import dataclasses as dtcl
|
35
35
|
import re as regx
|
36
|
-
|
37
|
-
|
36
|
+
import types as t
|
37
|
+
import typing as h
|
38
38
|
|
39
39
|
from str_to_obj.task.inspection import HintComponents
|
40
40
|
from str_to_obj.type.annotation import annotation_t
|
@@ -52,7 +52,7 @@ class _hint_node_t:
|
|
52
52
|
Leave elements to the tree.
|
53
53
|
"""
|
54
54
|
|
55
|
-
type: non_complex_hint_h | UnionType | EllipsisType | type[NoneType]
|
55
|
+
type: non_complex_hint_h | t.UnionType | t.EllipsisType | type[t.NoneType]
|
56
56
|
annotations: tuple[annotation_t, ...] = dtcl.field(default_factory=tuple)
|
57
57
|
|
58
58
|
|
@@ -61,9 +61,7 @@ class hint_tree_t(_hint_node_t):
|
|
61
61
|
elements: tuple[hint_tree_t, ...] = None
|
62
62
|
|
63
63
|
@classmethod
|
64
|
-
def
|
65
|
-
cls, hint: any_hint_h | complex_hint_additions_h, /
|
66
|
-
) -> hint_tree_t:
|
64
|
+
def NewForHint(cls, hint: any_hint_h | complex_hint_additions_h, /) -> hint_tree_t:
|
67
65
|
"""
|
68
66
|
Note that type hints cannot translate into hint trees with an OR-node having a child
|
69
67
|
OR-node. For example: str | (int | float) is interpreted as str | int | float. This
|
@@ -80,24 +78,24 @@ class hint_tree_t(_hint_node_t):
|
|
80
78
|
|
81
79
|
# Dealing with complex_hint_additions_h first
|
82
80
|
if hint is Ellipsis:
|
83
|
-
return cls(type=EllipsisType)
|
84
|
-
if (hint is None) or (hint is NoneType):
|
85
|
-
return cls(type=NoneType)
|
81
|
+
return cls(type=t.EllipsisType)
|
82
|
+
if (hint is None) or (hint is t.NoneType):
|
83
|
+
return cls(type=t.NoneType)
|
86
84
|
|
87
85
|
# nnts: Do not use "annotations" since it shadows __future__.annotations.
|
88
86
|
raw_hint, nnts = HintComponents(hint)
|
89
87
|
|
90
|
-
if (origin := get_origin(raw_hint)) is None:
|
88
|
+
if (origin := h.get_origin(raw_hint)) is None:
|
91
89
|
return cls(type=raw_hint, annotations=nnts)
|
92
90
|
|
93
91
|
# Handled types: list, set, tuple, with sets using the dict delimiters { and }.
|
94
92
|
if origin is dict:
|
95
93
|
raise TypeError(f"{origin.__name__}: Unhandled type.")
|
96
94
|
|
97
|
-
if origin is Union:
|
98
|
-
origin = UnionType
|
95
|
+
if origin is h.Union:
|
96
|
+
origin = t.UnionType
|
99
97
|
# get_args returns NoneType for None. This must be taken into account above.
|
100
|
-
elements = tuple(cls.
|
98
|
+
elements = tuple(cls.NewForHint(_elm) for _elm in h.get_args(raw_hint))
|
101
99
|
return cls(type=origin, annotations=nnts, elements=elements)
|
102
100
|
|
103
101
|
@property
|
@@ -112,12 +110,12 @@ class hint_tree_t(_hint_node_t):
|
|
112
110
|
return output
|
113
111
|
|
114
112
|
@property
|
115
|
-
def template(self) -> simple_hint_h | dict[int, Any] | None:
|
113
|
+
def template(self) -> simple_hint_h | dict[int, h.Any] | None:
|
116
114
|
""""""
|
117
|
-
if self.type is NoneType:
|
115
|
+
if self.type is t.NoneType:
|
118
116
|
return None
|
119
117
|
|
120
|
-
if self.type is UnionType:
|
118
|
+
if self.type is t.UnionType:
|
121
119
|
return {_key: _elm.template for _key, _elm in enumerate(self.elements)}
|
122
120
|
|
123
121
|
if self.elements is None:
|
@@ -143,12 +141,12 @@ class hint_tree_t(_hint_node_t):
|
|
143
141
|
""""""
|
144
142
|
output = (
|
145
143
|
str(self.template)
|
146
|
-
.replace(str(EllipsisType), "...")
|
144
|
+
.replace(str(t.EllipsisType), "...")
|
147
145
|
.replace("<class '", "")
|
148
146
|
.replace("'>", "")
|
149
147
|
)
|
150
|
-
output = regx.sub("{\d: ", "{", output, flags=regx.ASCII)
|
151
|
-
output = regx.sub(", \d:", " |", output, flags=regx.ASCII)
|
148
|
+
output = regx.sub(r"{\d: ", "{", output, flags=regx.ASCII)
|
149
|
+
output = regx.sub(r", \d:", " |", output, flags=regx.ASCII)
|
152
150
|
|
153
151
|
return output
|
154
152
|
|
str_to_obj/type/type.py
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# Copyright CNRS/Inria/UniCA
|
2
|
+
# Contributor(s): Eric Debreuve (since 2021)
|
3
|
+
#
|
4
|
+
# eric.debreuve@cnrs.fr
|
5
|
+
#
|
6
|
+
# This software is governed by the CeCILL license under French law and
|
7
|
+
# abiding by the rules of distribution of free software. You can use,
|
8
|
+
# modify and/ or redistribute the software under the terms of the CeCILL
|
9
|
+
# license as circulated by CEA, CNRS and INRIA at the following URL
|
10
|
+
# "http://www.cecill.info".
|
11
|
+
#
|
12
|
+
# As a counterpart to the access to the source code and rights to copy,
|
13
|
+
# modify and redistribute granted by the license, users are provided only
|
14
|
+
# with a limited warranty and the software's author, the holder of the
|
15
|
+
# economic rights, and the successive licensors have only limited
|
16
|
+
# liability.
|
17
|
+
#
|
18
|
+
# In this respect, the user's attention is drawn to the risks associated
|
19
|
+
# with loading, using, modifying and/or developing or reproducing the
|
20
|
+
# software by the user in light of its specific status of free software,
|
21
|
+
# that may mean that it is complicated to manipulate, and that also
|
22
|
+
# therefore means that it is reserved for developers and experienced
|
23
|
+
# professionals having in-depth computer knowledge. Users are therefore
|
24
|
+
# encouraged to load and test the software's suitability as regards their
|
25
|
+
# requirements in conditions enabling the security of their systems and/or
|
26
|
+
# data to be ensured and, more generally, to use and operate it in the
|
27
|
+
# same conditions as regards security.
|
28
|
+
#
|
29
|
+
# The fact that you are presently reading this means that you have had
|
30
|
+
# knowledge of the CeCILL license and that you accept its terms.
|
31
|
+
|
32
|
+
import dataclasses as dtcl
|
33
|
+
import typing as h
|
34
|
+
|
35
|
+
from rich.text import Text as text_t
|
36
|
+
from str_to_obj import CastValue, ObjectFromStr, annotation_t
|
37
|
+
from str_to_obj.runtime.value import INVALID_VALUE
|
38
|
+
from str_to_obj.type.hint_tree import hint_tree_t
|
39
|
+
|
40
|
+
|
41
|
+
@dtcl.dataclass(slots=True, repr=False, eq=False)
|
42
|
+
class type_t(hint_tree_t):
|
43
|
+
def FirstAnnotationWithType(
|
44
|
+
self, stripe: type[annotation_t], /
|
45
|
+
) -> annotation_t | None:
|
46
|
+
""""""
|
47
|
+
for annotation in self.all_annotations:
|
48
|
+
if isinstance(annotation, stripe):
|
49
|
+
return annotation
|
50
|
+
|
51
|
+
return None
|
52
|
+
|
53
|
+
def ValueIssues(self, value: h.Any, /) -> list[str]:
|
54
|
+
""""""
|
55
|
+
return CastValue(value, self, only_check_validity=True)
|
56
|
+
|
57
|
+
def InterpretedValueOf(self, value: h.Any, /) -> tuple[h.Any, list[str]]:
|
58
|
+
""""""
|
59
|
+
if isinstance(value, str):
|
60
|
+
typed_value, issues = ObjectFromStr(value, expected_type=self)
|
61
|
+
else:
|
62
|
+
typed_value, issues = CastValue(value, self)
|
63
|
+
|
64
|
+
if issues.__len__() > 0:
|
65
|
+
return INVALID_VALUE, issues
|
66
|
+
|
67
|
+
return typed_value, []
|
68
|
+
|
69
|
+
def __str__(self) -> str:
|
70
|
+
""""""
|
71
|
+
return text_t.from_markup(self.__rich__()).plain
|
72
|
+
|
73
|
+
def __rich__(self) -> str:
|
74
|
+
""""""
|
75
|
+
output = [f"[white]{self.template_as_str}[/]"]
|
76
|
+
|
77
|
+
for annotation in self.all_annotations:
|
78
|
+
output.append(type(annotation).__name__)
|
79
|
+
|
80
|
+
return "[yellow]" + "::".join(output) + "[/]"
|
81
|
+
|
82
|
+
|
83
|
+
@dtcl.dataclass(slots=True, repr=False, eq=False)
|
84
|
+
class any_type_t(type_t):
|
85
|
+
def FirstAnnotationWithType(
|
86
|
+
self, stripe: type[annotation_t], /
|
87
|
+
) -> annotation_t | None:
|
88
|
+
""""""
|
89
|
+
return None
|
90
|
+
|
91
|
+
def ValueIssues(self, _: h.Any, /) -> list[str]:
|
92
|
+
""""""
|
93
|
+
return []
|
94
|
+
|
95
|
+
def InterpretedValueOf(self, value: h.Any, /) -> tuple[h.Any, list[str]]:
|
96
|
+
""""""
|
97
|
+
return value, []
|
str_to_obj/type/value.py
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# Copyright CNRS/Inria/UniCA
|
2
|
+
# Contributor(s): Eric Debreuve (since 2021)
|
3
|
+
#
|
4
|
+
# eric.debreuve@cnrs.fr
|
5
|
+
#
|
6
|
+
# This software is governed by the CeCILL license under French law and
|
7
|
+
# abiding by the rules of distribution of free software. You can use,
|
8
|
+
# modify and/ or redistribute the software under the terms of the CeCILL
|
9
|
+
# license as circulated by CEA, CNRS and INRIA at the following URL
|
10
|
+
# "http://www.cecill.info".
|
11
|
+
#
|
12
|
+
# As a counterpart to the access to the source code and rights to copy,
|
13
|
+
# modify and redistribute granted by the license, users are provided only
|
14
|
+
# with a limited warranty and the software's author, the holder of the
|
15
|
+
# economic rights, and the successive licensors have only limited
|
16
|
+
# liability.
|
17
|
+
#
|
18
|
+
# In this respect, the user's attention is drawn to the risks associated
|
19
|
+
# with loading, using, modifying and/or developing or reproducing the
|
20
|
+
# software by the user in light of its specific status of free software,
|
21
|
+
# that may mean that it is complicated to manipulate, and that also
|
22
|
+
# therefore means that it is reserved for developers and experienced
|
23
|
+
# professionals having in-depth computer knowledge. Users are therefore
|
24
|
+
# encouraged to load and test the software's suitability as regards their
|
25
|
+
# requirements in conditions enabling the security of their systems and/or
|
26
|
+
# data to be ensured and, more generally, to use and operate it in the
|
27
|
+
# same conditions as regards security.
|
28
|
+
#
|
29
|
+
# The fact that you are presently reading this means that you have had
|
30
|
+
# knowledge of the CeCILL license and that you accept its terms.
|
31
|
+
|
32
|
+
|
33
|
+
class invalid_value_t:
|
34
|
+
"""
|
35
|
+
Do not use invalid_value_t = object, for example, otherwise isinstance returns True
|
36
|
+
for anything.
|
37
|
+
"""
|
38
|
+
|
39
|
+
TEXT = "INVALID VALUE"
|
40
|
+
|
41
|
+
def __str__(self) -> str:
|
42
|
+
""""""
|
43
|
+
return self.__class__.TEXT
|
44
|
+
|
45
|
+
def __rich__(self) -> str:
|
46
|
+
""""""
|
47
|
+
return f"[red]{self.__class__.TEXT}[/]"
|
str_to_obj/version.py
CHANGED
@@ -0,0 +1,24 @@
|
|
1
|
+
str_to_obj/__init__.py,sha256=Lmbi2FLFg5Tma54APGsz7NUPCLA3Zslq13SdluTqRyk,1826
|
2
|
+
str_to_obj/main.py,sha256=ZcH2KpO9AO6Ozoroin_KkY78P5kV625sALf-ffbIOkE,2481
|
3
|
+
str_to_obj/version.py,sha256=RHtzTU05mIGO6twdijPeFO7ZnW72tfwbHKpkbUhztEw,1609
|
4
|
+
str_to_obj/catalog/boolean.py,sha256=-1k39odWGgm42cotKxdV00i_M4UbKByK_V8gawgYmjY,2724
|
5
|
+
str_to_obj/catalog/callable.py,sha256=mYj-qNGY0ncvFBNzzbphb0hOF1YMqPaXX1iRr6rev1c,2985
|
6
|
+
str_to_obj/catalog/choices.py,sha256=CxBngQYiC7Pct-sLQHmtDaPr93gt_H_DreO9-s3Znv0,3176
|
7
|
+
str_to_obj/catalog/collection.py,sha256=rOUtBkfL6KV05HtkHaMOrTls0bXRIq1lCECLvN_B37Q,6193
|
8
|
+
str_to_obj/catalog/number.py,sha256=UmUkOwnwNZyNxDeXzm_rZHFHpnzc5ovg3Us47Uord5c,4840
|
9
|
+
str_to_obj/catalog/path.py,sha256=rri5nZbZClINC__VzS4a6CpRvaLvkSVqcattuWGP9Ys,3217
|
10
|
+
str_to_obj/interface/console.py,sha256=YF95b31qPqzeG4lnYtoJ7awQM3QDSOhIjZUeadGd7L4,2406
|
11
|
+
str_to_obj/runtime/type.py,sha256=diT38JyuZHJb5-uzIf97s9iDzp_ADyLto5LGg0H3tyc,1657
|
12
|
+
str_to_obj/runtime/value.py,sha256=cj7RvdAEy9Xc6INaGGq0CirjZ9gv-moFFUlXuFbzvP8,1637
|
13
|
+
str_to_obj/task/casting.py,sha256=0CEVkXXLR9-8ilL3IbnrTTWx-q8Wvkx1VLPVEuCXgx8,8601
|
14
|
+
str_to_obj/task/comparison.py,sha256=j6YHHRrkR2oucQnwshl0fMQm7ReuaJFVpwqMHgld5X0,2562
|
15
|
+
str_to_obj/task/inspection.py,sha256=9wllLu9WmkB3LfduBnT4Wa47M4UzGy5bJK9OTnPfOgI,2494
|
16
|
+
str_to_obj/type/annotation.py,sha256=dmLMtOnbP4NP7pPYEy9r7YhHF21EoTyz2BaO4vkB7qk,3216
|
17
|
+
str_to_obj/type/hint.py,sha256=jlQgGosqoszkreJYFS_pga5RpqrUiLYALoxtzrB5rPU,2180
|
18
|
+
str_to_obj/type/hint_tree.py,sha256=-24_WCPX94K5ryY6DZKV-_4vnTyaqPV3vJ-OVX-iuxQ,6052
|
19
|
+
str_to_obj/type/type.py,sha256=6KZgHRKZi1zzJ9KiW2LLvUXVr0EALUM4Yz8cwvgFeBg,3459
|
20
|
+
str_to_obj/type/value.py,sha256=9sXtYf0kVHVpcczS_ZtVZa9_M17pIlYSZjYYE33sS1Y,1904
|
21
|
+
str_to_obj-2024.3.dist-info/METADATA,sha256=0Tz93wp4VmDSrNNiHUTIK6C0jdtTPX-794C9--Sbzyg,5389
|
22
|
+
str_to_obj-2024.3.dist-info/WHEEL,sha256=cpQTJ5IWu9CdaPViMhC9YzF8gZuS5-vlfoFihTBC86A,91
|
23
|
+
str_to_obj-2024.3.dist-info/top_level.txt,sha256=h-6bR_TAnXRGVSLTeNPlivlaG64xYM3E__Xg7sCgeug,11
|
24
|
+
str_to_obj-2024.3.dist-info/RECORD,,
|
@@ -1,14 +0,0 @@
|
|
1
|
-
str_to_obj/__init__.py,sha256=odgBU079fpQMu9GXhY0Lc19Rris-8Oa60Dd4F5SxGpQ,1735
|
2
|
-
str_to_obj/main.py,sha256=YxjO1kyeZxGf_31KRZVZ39F7gr0gYM1OyOa7Y8m4iG8,2488
|
3
|
-
str_to_obj/version.py,sha256=rlojaXevssZP7Crmyq2gSWwZ5xM-WJW9n3iVSVFSO18,1609
|
4
|
-
str_to_obj/interface/console.py,sha256=PHcDGFmscXYSrPOJRjmlBPglQUBBGUhe1Bov3pthGT0,2414
|
5
|
-
str_to_obj/task/casting.py,sha256=74eIRKpokqq-JqEK0UwRSQs1F4Zdj8pRDQXAPkqg3Ug,8468
|
6
|
-
str_to_obj/task/comparison.py,sha256=-1xreDu25SrWyTEGwcZMctURj2A47zAh2QJdMf4EuTg,2568
|
7
|
-
str_to_obj/task/inspection.py,sha256=dwaK4uMN5iPgcWWVnFu86eSkuPy1G_phBAGTqEa_414,2488
|
8
|
-
str_to_obj/type/annotation.py,sha256=bR7tQVfYmAohizn4APx_D0aNoPo6gMjyFRoa-QpDNKM,3241
|
9
|
-
str_to_obj/type/hint.py,sha256=Jv3m1VbXICsUwPTCFidW6A1bX-xgNU2twRP_H1DX5v4,2221
|
10
|
-
str_to_obj/type/hint_tree.py,sha256=1Gp4I6kgRGshKQJBurJF0wbG02xv-d5Spe2f-4h19qk,6113
|
11
|
-
str_to_obj-2024.1.dist-info/METADATA,sha256=_AkIgj6_TGFJvuo0wvmP5Hdnuk9Z8jtbxbo_OaqmuQU,5389
|
12
|
-
str_to_obj-2024.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
13
|
-
str_to_obj-2024.1.dist-info/top_level.txt,sha256=h-6bR_TAnXRGVSLTeNPlivlaG64xYM3E__Xg7sCgeug,11
|
14
|
-
str_to_obj-2024.1.dist-info/RECORD,,
|
File without changes
|