safeshield 1.2.2__py3-none-any.whl → 1.4.2__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.
- {safeshield-1.2.2.dist-info → safeshield-1.4.2.dist-info}/METADATA +9 -1
- safeshield-1.4.2.dist-info/RECORD +31 -0
- validator/core/validator.py +10 -10
- validator/factory.py +4 -4
- validator/rules/__init__.py +6 -8
- validator/rules/array.py +27 -34
- validator/rules/base.py +32 -8
- validator/rules/basic.py +105 -10
- validator/rules/boolean.py +157 -0
- validator/rules/comparison.py +130 -40
- validator/rules/date.py +36 -20
- validator/rules/files.py +179 -67
- validator/rules/format.py +122 -31
- validator/rules/numeric.py +188 -0
- validator/rules/string.py +71 -19
- validator/rules/utilities.py +233 -133
- validator/services/rule_conflict.py +2 -2
- validator/services/rule_error_handler.py +221 -24
- validator/services/rule_preparer.py +12 -25
- safeshield-1.2.2.dist-info/RECORD +0 -31
- validator/rules/conditional.py +0 -332
- validator/rules/type.py +0 -42
- {safeshield-1.2.2.dist-info → safeshield-1.4.2.dist-info}/LICENSE +0 -0
- {safeshield-1.2.2.dist-info → safeshield-1.4.2.dist-info}/WHEEL +0 -0
- {safeshield-1.2.2.dist-info → safeshield-1.4.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
from .base import Rule
|
|
2
|
+
from typing import Any, Dict, List, Optional, Set, Union, Tuple, Type
|
|
3
|
+
import decimal
|
|
4
|
+
|
|
5
|
+
class NumericRule(Rule):
|
|
6
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
7
|
+
if isinstance(value, (int, float)):
|
|
8
|
+
return True
|
|
9
|
+
if not isinstance(value, str):
|
|
10
|
+
return False
|
|
11
|
+
return value.replace('.', '', 1).isdigit()
|
|
12
|
+
|
|
13
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
14
|
+
return f"The :attribute must be a number."
|
|
15
|
+
|
|
16
|
+
class IntegerRule(Rule):
|
|
17
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
18
|
+
if isinstance(value, int):
|
|
19
|
+
return True
|
|
20
|
+
if not isinstance(value, str):
|
|
21
|
+
return False
|
|
22
|
+
return value.isdigit()
|
|
23
|
+
|
|
24
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
25
|
+
return f"The :attribute must be an integer."
|
|
26
|
+
|
|
27
|
+
class DigitsRule(Rule):
|
|
28
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
29
|
+
if not params or not isinstance(value, str):
|
|
30
|
+
return False
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
digits = int(params[0])
|
|
34
|
+
except ValueError:
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
return value.isdigit() and len(value) == digits
|
|
38
|
+
|
|
39
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
40
|
+
return f"The :attribute must be {params[0]} digits."
|
|
41
|
+
|
|
42
|
+
class DigitsBetweenRule(Rule):
|
|
43
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
44
|
+
if len(params) < 2 or not isinstance(value, str):
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
min_digits = int(params[0])
|
|
49
|
+
max_digits = int(params[1])
|
|
50
|
+
except ValueError:
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
return value.isdigit() and min_digits <= len(value) <= max_digits
|
|
54
|
+
|
|
55
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
56
|
+
return f"The :attribute must be between {params[0]} and {params[1]} digits."
|
|
57
|
+
|
|
58
|
+
class DecimalRule(Rule):
|
|
59
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
60
|
+
try:
|
|
61
|
+
decimal.Decimal(str(value))
|
|
62
|
+
return True
|
|
63
|
+
except (decimal.InvalidOperation, TypeError, ValueError):
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
67
|
+
return "The :attribute must be a decimal number."
|
|
68
|
+
|
|
69
|
+
class GreaterThanRule(Rule):
|
|
70
|
+
_name = 'gt'
|
|
71
|
+
|
|
72
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
73
|
+
if len(params) < 1:
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
threshold = decimal.Decimal(params[0])
|
|
78
|
+
numeric_value = decimal.Decimal(str(value))
|
|
79
|
+
return numeric_value > threshold
|
|
80
|
+
except (decimal.InvalidOperation, TypeError, ValueError):
|
|
81
|
+
return False
|
|
82
|
+
|
|
83
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
84
|
+
return f"The :attribute must be greater than {params[0]}."
|
|
85
|
+
|
|
86
|
+
class GreaterThanOrEqualRule(Rule):
|
|
87
|
+
_name = 'gte'
|
|
88
|
+
|
|
89
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
90
|
+
if len(params) < 1:
|
|
91
|
+
return False
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
threshold = decimal.Decimal(params[0])
|
|
95
|
+
numeric_value = decimal.Decimal(str(value))
|
|
96
|
+
return numeric_value >= threshold
|
|
97
|
+
except (decimal.InvalidOperation, TypeError, ValueError):
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
101
|
+
return f"The :attribute must be greater than or equal to {params[0]}."
|
|
102
|
+
|
|
103
|
+
class LessThanRule(Rule):
|
|
104
|
+
_name = 'lt'
|
|
105
|
+
|
|
106
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
107
|
+
if len(params) < 1:
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
threshold = decimal.Decimal(params[0])
|
|
112
|
+
numeric_value = decimal.Decimal(str(value))
|
|
113
|
+
return numeric_value < threshold
|
|
114
|
+
except (decimal.InvalidOperation, TypeError, ValueError):
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
118
|
+
return f"The :attribute must be less than {params[0]}."
|
|
119
|
+
|
|
120
|
+
class LessThanOrEqualRule(Rule):
|
|
121
|
+
_name = 'lte'
|
|
122
|
+
|
|
123
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
124
|
+
if len(params) < 1:
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
threshold = decimal.Decimal(params[0])
|
|
129
|
+
numeric_value = decimal.Decimal(str(value))
|
|
130
|
+
return numeric_value <= threshold
|
|
131
|
+
except (decimal.InvalidOperation, TypeError, ValueError):
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
135
|
+
return f"The :attribute must be less than or equal to {params[0]}."
|
|
136
|
+
|
|
137
|
+
class MaxDigitsRule(Rule):
|
|
138
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
139
|
+
if len(params) < 1:
|
|
140
|
+
return False
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
max_digits = int(params[0])
|
|
144
|
+
numeric_value = decimal.Decimal(str(value))
|
|
145
|
+
str_value = str(numeric_value).replace("-", "")
|
|
146
|
+
if '.' in str_value:
|
|
147
|
+
str_value = str_value.replace(".", "")
|
|
148
|
+
return len(str_value) <= max_digits
|
|
149
|
+
except (decimal.InvalidOperation, TypeError, ValueError):
|
|
150
|
+
return False
|
|
151
|
+
|
|
152
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
153
|
+
return f"The :attribute must not exceed {params[0]} digits."
|
|
154
|
+
|
|
155
|
+
class MinDigitsRule(Rule):
|
|
156
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
157
|
+
if len(params) < 1:
|
|
158
|
+
return False
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
min_digits = int(params[0])
|
|
162
|
+
numeric_value = decimal.Decimal(str(value))
|
|
163
|
+
str_value = str(numeric_value).replace("-", "")
|
|
164
|
+
if '.' in str_value:
|
|
165
|
+
str_value = str_value.replace(".", "")
|
|
166
|
+
return len(str_value) >= min_digits
|
|
167
|
+
except (decimal.InvalidOperation, TypeError, ValueError):
|
|
168
|
+
return False
|
|
169
|
+
|
|
170
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
171
|
+
return f"The :attribute must have at least {params[0]} digits."
|
|
172
|
+
|
|
173
|
+
class MultipleOfRule(Rule):
|
|
174
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
175
|
+
if not params:
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
divisor = float(params[0])
|
|
180
|
+
if divisor == 0:
|
|
181
|
+
return False
|
|
182
|
+
num = float(value)
|
|
183
|
+
return num % divisor == 0
|
|
184
|
+
except (ValueError, TypeError):
|
|
185
|
+
return False
|
|
186
|
+
|
|
187
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
188
|
+
return f"The :attribute must be a multiple of {params[0]}."
|
validator/rules/string.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from .base import
|
|
1
|
+
from .base import Rule
|
|
2
2
|
from typing import Any, Dict, List, Optional, Set, Union, Tuple, Type
|
|
3
3
|
import re
|
|
4
4
|
|
|
@@ -6,69 +6,121 @@ import re
|
|
|
6
6
|
# STRING VALIDATION RULES
|
|
7
7
|
# =============================================
|
|
8
8
|
|
|
9
|
-
class StringRule(
|
|
9
|
+
class StringRule(Rule):
|
|
10
10
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
11
11
|
return isinstance(value, str)
|
|
12
12
|
|
|
13
13
|
def message(self, field: str, params: List[str]) -> str:
|
|
14
|
-
return f"The :
|
|
14
|
+
return f"The :attribute must be a string."
|
|
15
15
|
|
|
16
|
-
class AlphaRule(
|
|
16
|
+
class AlphaRule(Rule):
|
|
17
17
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
18
18
|
return isinstance(value, str) and value.isalpha()
|
|
19
19
|
|
|
20
20
|
def message(self, field: str, params: List[str]) -> str:
|
|
21
|
-
return f"The :
|
|
21
|
+
return f"The :attribute may only contain letters."
|
|
22
22
|
|
|
23
|
-
class AlphaDashRule(
|
|
23
|
+
class AlphaDashRule(Rule):
|
|
24
24
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
25
25
|
return isinstance(value, str) and bool(re.match(r'^[a-zA-Z0-9_-]+$', value))
|
|
26
26
|
|
|
27
27
|
def message(self, field: str, params: List[str]) -> str:
|
|
28
|
-
return f"The :
|
|
28
|
+
return f"The :attribute may only contain letters, numbers, dashes and underscores."
|
|
29
29
|
|
|
30
|
-
class AlphaNumRule(
|
|
30
|
+
class AlphaNumRule(Rule):
|
|
31
31
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
32
32
|
return isinstance(value, str) and value.isalnum()
|
|
33
33
|
|
|
34
34
|
def message(self, field: str, params: List[str]) -> str:
|
|
35
|
-
return f"The :
|
|
35
|
+
return f"The :attribute may only contain letters and numbers."
|
|
36
36
|
|
|
37
|
-
class UppercaseRule(
|
|
37
|
+
class UppercaseRule(Rule):
|
|
38
38
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
39
39
|
return isinstance(value, str) and value == value.upper()
|
|
40
40
|
|
|
41
41
|
def message(self, field: str, params: List[str]) -> str:
|
|
42
|
-
return f"The :
|
|
42
|
+
return f"The :attribute must be uppercase."
|
|
43
43
|
|
|
44
|
-
class LowercaseRule(
|
|
44
|
+
class LowercaseRule(Rule):
|
|
45
45
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
46
46
|
return isinstance(value, str) and value == value.lower()
|
|
47
47
|
|
|
48
48
|
def message(self, field: str, params: List[str]) -> str:
|
|
49
|
-
return f"The :
|
|
49
|
+
return f"The :attribute must be lowercase."
|
|
50
50
|
|
|
51
|
-
class AsciiRule(
|
|
51
|
+
class AsciiRule(Rule):
|
|
52
52
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
53
53
|
return isinstance(value, str) and all(ord(c) < 128 for c in value)
|
|
54
54
|
|
|
55
55
|
def message(self, field: str, params: List[str]) -> str:
|
|
56
|
-
return f"The :
|
|
56
|
+
return f"The :attribute must only contain ASCII characters."
|
|
57
57
|
|
|
58
|
-
class StartsWithRule(
|
|
58
|
+
class StartsWithRule(Rule):
|
|
59
59
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
60
60
|
if not isinstance(value, str):
|
|
61
61
|
return False
|
|
62
62
|
return any(value.startswith(p) for p in params)
|
|
63
63
|
|
|
64
64
|
def message(self, field: str, params: List[str]) -> str:
|
|
65
|
-
return f"The :
|
|
65
|
+
return f"The :attribute must start with one of the following: {', '.join(params)}."
|
|
66
|
+
|
|
67
|
+
class DoesntStartWithRule(Rule):
|
|
68
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
69
|
+
if not isinstance(value, str) or not params:
|
|
70
|
+
return False
|
|
71
|
+
return all(not value.startswith(prefix) for prefix in params)
|
|
72
|
+
|
|
73
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
74
|
+
return f"The :attribute must not start with any of: {', '.join(params)}"
|
|
66
75
|
|
|
67
|
-
class EndsWithRule(
|
|
76
|
+
class EndsWithRule(Rule):
|
|
68
77
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
69
78
|
if not isinstance(value, str):
|
|
70
79
|
return False
|
|
71
80
|
return any(value.endswith(p) for p in params)
|
|
72
81
|
|
|
73
82
|
def message(self, field: str, params: List[str]) -> str:
|
|
74
|
-
return f"The :
|
|
83
|
+
return f"The :attribute must end with one of the following: {', '.join(params)}."
|
|
84
|
+
|
|
85
|
+
class DoesntEndWithRule(Rule):
|
|
86
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
87
|
+
if not isinstance(value, str) or not params:
|
|
88
|
+
return False
|
|
89
|
+
return all(not value.endswith(suffix) for suffix in params)
|
|
90
|
+
|
|
91
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
92
|
+
return f"The :attribute must not end with any of: {', '.join(params)}"
|
|
93
|
+
|
|
94
|
+
class ConfirmedRule(Rule):
|
|
95
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
96
|
+
confirmation_field = f"{field}_confirmation"
|
|
97
|
+
|
|
98
|
+
return value == self.get_field_value(confirmation_field, '')
|
|
99
|
+
|
|
100
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
101
|
+
return f"The :attribute confirmation does not match."
|
|
102
|
+
|
|
103
|
+
class RegexRule(Rule):
|
|
104
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
105
|
+
if not params or not isinstance(value, str):
|
|
106
|
+
return False
|
|
107
|
+
try:
|
|
108
|
+
return bool(re.fullmatch(params[0], value))
|
|
109
|
+
except re.error:
|
|
110
|
+
return False
|
|
111
|
+
|
|
112
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
113
|
+
return f"The :attribute format is invalid."
|
|
114
|
+
|
|
115
|
+
class NotRegexRule(Rule):
|
|
116
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
117
|
+
if not params or not isinstance(value, str):
|
|
118
|
+
return True
|
|
119
|
+
print(not bool(re.search(params[0], value)))
|
|
120
|
+
try:
|
|
121
|
+
return not bool(re.search(params[0], value))
|
|
122
|
+
except re.error:
|
|
123
|
+
return True
|
|
124
|
+
|
|
125
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
126
|
+
return f"The :attribute format is invalid."
|