str-to-obj 2024.1__tar.gz → 2024.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. {str_to_obj-2024.1 → str_to_obj-2024.3}/PKG-INFO +1 -1
  2. {str_to_obj-2024.1 → str_to_obj-2024.3}/setup.py +2 -0
  3. {str_to_obj-2024.1 → str_to_obj-2024.3}/str_to_obj/__init__.py +2 -0
  4. str_to_obj-2024.3/str_to_obj/catalog/boolean.py +73 -0
  5. str_to_obj-2024.3/str_to_obj/catalog/callable.py +75 -0
  6. str_to_obj-2024.3/str_to_obj/catalog/choices.py +76 -0
  7. str_to_obj-2024.3/str_to_obj/catalog/collection.py +154 -0
  8. str_to_obj-2024.3/str_to_obj/catalog/number.py +120 -0
  9. str_to_obj-2024.3/str_to_obj/catalog/path.py +94 -0
  10. {str_to_obj-2024.1 → str_to_obj-2024.3}/str_to_obj/interface/console.py +4 -4
  11. {str_to_obj-2024.1 → str_to_obj-2024.3}/str_to_obj/main.py +5 -5
  12. str_to_obj-2024.3/str_to_obj/runtime/type.py +36 -0
  13. str_to_obj-2024.3/str_to_obj/runtime/value.py +34 -0
  14. {str_to_obj-2024.1 → str_to_obj-2024.3}/str_to_obj/task/casting.py +26 -17
  15. {str_to_obj-2024.1 → str_to_obj-2024.3}/str_to_obj/task/comparison.py +3 -3
  16. {str_to_obj-2024.1 → str_to_obj-2024.3}/str_to_obj/task/inspection.py +5 -3
  17. {str_to_obj-2024.1 → str_to_obj-2024.3}/str_to_obj/type/annotation.py +5 -9
  18. {str_to_obj-2024.1 → str_to_obj-2024.3}/str_to_obj/type/hint.py +7 -7
  19. {str_to_obj-2024.1 → str_to_obj-2024.3}/str_to_obj/type/hint_tree.py +17 -19
  20. str_to_obj-2024.3/str_to_obj/type/type.py +97 -0
  21. str_to_obj-2024.3/str_to_obj/type/value.py +47 -0
  22. {str_to_obj-2024.1 → str_to_obj-2024.3}/str_to_obj/version.py +1 -1
  23. {str_to_obj-2024.1 → str_to_obj-2024.3}/str_to_obj.egg-info/PKG-INFO +1 -1
  24. {str_to_obj-2024.1 → str_to_obj-2024.3}/str_to_obj.egg-info/SOURCES.txt +11 -1
  25. {str_to_obj-2024.1 → str_to_obj-2024.3}/MANIFEST.in +0 -0
  26. {str_to_obj-2024.1 → str_to_obj-2024.3}/README-COPYRIGHT-utf8.txt +0 -0
  27. {str_to_obj-2024.1 → str_to_obj-2024.3}/README-LICENCE-utf8.txt +0 -0
  28. {str_to_obj-2024.1 → str_to_obj-2024.3}/README.rst +0 -0
  29. {str_to_obj-2024.1 → str_to_obj-2024.3}/documentation/wiki/description.asciidoc +0 -0
  30. {str_to_obj-2024.1 → str_to_obj-2024.3}/pyproject.toml +0 -0
  31. {str_to_obj-2024.1 → str_to_obj-2024.3}/setup.cfg +0 -0
  32. {str_to_obj-2024.1 → str_to_obj-2024.3}/str_to_obj.egg-info/dependency_links.txt +0 -0
  33. {str_to_obj-2024.1 → str_to_obj-2024.3}/str_to_obj.egg-info/requires.txt +0 -0
  34. {str_to_obj-2024.1 → str_to_obj-2024.3}/str_to_obj.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: str-to-obj
3
- Version: 2024.1
3
+ Version: 2024.3
4
4
  Summary: Convert strings to Python objects guided by (potentially annotated) type hints
5
5
  Home-page: https://src.koda.cnrs.fr/eric.debreuve/str-to-obj/
6
6
  Author: Eric Debreuve
@@ -73,7 +73,9 @@ PYPI_STATUS = "4 - Beta" # https://pypi.org/classifiers/ Development Sta
73
73
  IMPORT_NAME = "str_to_obj"
74
74
  PACKAGES = [
75
75
  IMPORT_NAME,
76
+ f"{IMPORT_NAME}.catalog",
76
77
  f"{IMPORT_NAME}.interface",
78
+ f"{IMPORT_NAME}.runtime",
77
79
  f"{IMPORT_NAME}.task",
78
80
  f"{IMPORT_NAME}.type",
79
81
  ]
@@ -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
+ ]
@@ -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
- from typing import Any, Sequence
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}"
@@ -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
- from types import NoneType
34
- from typing import Any
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: