safeshield 1.3.2__py3-none-any.whl → 1.4.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.
- {safeshield-1.3.2.dist-info → safeshield-1.4.3.dist-info}/METADATA +9 -1
- safeshield-1.4.3.dist-info/RECORD +31 -0
- validator/core/validator.py +7 -7
- validator/factory.py +4 -4
- validator/rules/__init__.py +6 -8
- validator/rules/array.py +23 -30
- validator/rules/base.py +32 -8
- validator/rules/basic.py +64 -165
- validator/rules/boolean.py +157 -0
- validator/rules/comparison.py +121 -31
- validator/rules/date.py +30 -14
- validator/rules/files.py +176 -64
- validator/rules/format.py +116 -25
- validator/rules/numeric.py +188 -0
- validator/rules/string.py +68 -9
- validator/rules/utilities.py +209 -34
- validator/services/rule_conflict.py +2 -2
- validator/services/rule_error_handler.py +4 -2
- validator/services/rule_preparer.py +12 -25
- safeshield-1.3.2.dist-info/RECORD +0 -31
- validator/rules/conditional.py +0 -165
- validator/rules/type.py +0 -49
- {safeshield-1.3.2.dist-info → safeshield-1.4.3.dist-info}/LICENSE +0 -0
- {safeshield-1.3.2.dist-info → safeshield-1.4.3.dist-info}/WHEEL +0 -0
- {safeshield-1.3.2.dist-info → safeshield-1.4.3.dist-info}/top_level.txt +0 -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,49 +6,56 @@ import re
|
|
|
6
6
|
# STRING VALIDATION RULES
|
|
7
7
|
# =============================================
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class StringRule(Rule):
|
|
10
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
11
|
+
return isinstance(value, str)
|
|
12
|
+
|
|
13
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
14
|
+
return f"The :attribute must be a string."
|
|
15
|
+
|
|
16
|
+
class AlphaRule(Rule):
|
|
10
17
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
11
18
|
return isinstance(value, str) and value.isalpha()
|
|
12
19
|
|
|
13
20
|
def message(self, field: str, params: List[str]) -> str:
|
|
14
21
|
return f"The :attribute may only contain letters."
|
|
15
22
|
|
|
16
|
-
class AlphaDashRule(
|
|
23
|
+
class AlphaDashRule(Rule):
|
|
17
24
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
18
25
|
return isinstance(value, str) and bool(re.match(r'^[a-zA-Z0-9_-]+$', value))
|
|
19
26
|
|
|
20
27
|
def message(self, field: str, params: List[str]) -> str:
|
|
21
28
|
return f"The :attribute may only contain letters, numbers, dashes and underscores."
|
|
22
29
|
|
|
23
|
-
class AlphaNumRule(
|
|
30
|
+
class AlphaNumRule(Rule):
|
|
24
31
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
25
32
|
return isinstance(value, str) and value.isalnum()
|
|
26
33
|
|
|
27
34
|
def message(self, field: str, params: List[str]) -> str:
|
|
28
35
|
return f"The :attribute may only contain letters and numbers."
|
|
29
36
|
|
|
30
|
-
class UppercaseRule(
|
|
37
|
+
class UppercaseRule(Rule):
|
|
31
38
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
32
39
|
return isinstance(value, str) and value == value.upper()
|
|
33
40
|
|
|
34
41
|
def message(self, field: str, params: List[str]) -> str:
|
|
35
42
|
return f"The :attribute must be uppercase."
|
|
36
43
|
|
|
37
|
-
class LowercaseRule(
|
|
44
|
+
class LowercaseRule(Rule):
|
|
38
45
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
39
46
|
return isinstance(value, str) and value == value.lower()
|
|
40
47
|
|
|
41
48
|
def message(self, field: str, params: List[str]) -> str:
|
|
42
49
|
return f"The :attribute must be lowercase."
|
|
43
50
|
|
|
44
|
-
class AsciiRule(
|
|
51
|
+
class AsciiRule(Rule):
|
|
45
52
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
46
53
|
return isinstance(value, str) and all(ord(c) < 128 for c in value)
|
|
47
54
|
|
|
48
55
|
def message(self, field: str, params: List[str]) -> str:
|
|
49
56
|
return f"The :attribute must only contain ASCII characters."
|
|
50
57
|
|
|
51
|
-
class StartsWithRule(
|
|
58
|
+
class StartsWithRule(Rule):
|
|
52
59
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
53
60
|
if not isinstance(value, str):
|
|
54
61
|
return False
|
|
@@ -56,8 +63,17 @@ class StartsWithRule(ValidationRule):
|
|
|
56
63
|
|
|
57
64
|
def message(self, field: str, params: List[str]) -> str:
|
|
58
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)}"
|
|
59
75
|
|
|
60
|
-
class EndsWithRule(
|
|
76
|
+
class EndsWithRule(Rule):
|
|
61
77
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
62
78
|
if not isinstance(value, str):
|
|
63
79
|
return False
|
|
@@ -65,3 +81,46 @@ class EndsWithRule(ValidationRule):
|
|
|
65
81
|
|
|
66
82
|
def message(self, field: str, params: List[str]) -> str:
|
|
67
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."
|
validator/rules/utilities.py
CHANGED
|
@@ -1,7 +1,58 @@
|
|
|
1
|
-
from .base import
|
|
1
|
+
from .base import Rule
|
|
2
2
|
from typing import Any, Dict, List, Optional, Set, Union, Tuple, Type
|
|
3
|
-
from .basic import
|
|
3
|
+
from .basic import *
|
|
4
|
+
from .boolean import AcceptedRule, DeclinedRule
|
|
4
5
|
|
|
6
|
+
class RequiredIfRule(RequiredRule):
|
|
7
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
8
|
+
if len(params) == 1 and callable(params[0]):
|
|
9
|
+
condition_met = params[0](self.validator.data)
|
|
10
|
+
elif len(params) == 1 and isinstance(params[0], bool):
|
|
11
|
+
condition_met = params[0]
|
|
12
|
+
else:
|
|
13
|
+
conditions = list(zip(params[::2], params[1::2]))
|
|
14
|
+
|
|
15
|
+
condition_met = any(
|
|
16
|
+
str(self.get_field_value(field, '')) == str(expected_value)
|
|
17
|
+
for field, expected_value in conditions
|
|
18
|
+
if field and expected_value is not None
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if condition_met:
|
|
22
|
+
return super().validate(field, value, params)
|
|
23
|
+
|
|
24
|
+
return True
|
|
25
|
+
|
|
26
|
+
class RequiredUnlessRule(RequiredRule):
|
|
27
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
28
|
+
if len(params) < 2:
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
other_field, other_value = params[0], params[1]
|
|
32
|
+
actual_value = self.get_field_value(other_field, '')
|
|
33
|
+
|
|
34
|
+
if actual_value == other_value:
|
|
35
|
+
return True
|
|
36
|
+
|
|
37
|
+
return super().validate(field, value, params)
|
|
38
|
+
|
|
39
|
+
class RequiredAllIfRule(RequiredRule):
|
|
40
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
41
|
+
if len(params) < 2:
|
|
42
|
+
return False
|
|
43
|
+
|
|
44
|
+
conditions = [(f.strip(), v.strip()) for f, v in zip(params[::2], params[1::2])]
|
|
45
|
+
|
|
46
|
+
all_conditions_met = all(
|
|
47
|
+
self.get_field_value(f) == v
|
|
48
|
+
for f, v in conditions
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if not all_conditions_met:
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
return super().validate(field, value, params)
|
|
55
|
+
|
|
5
56
|
class RequiredWithRule(RequiredRule):
|
|
6
57
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
7
58
|
if not params:
|
|
@@ -38,101 +89,225 @@ class RequiredWithoutAllRule(RequiredRule):
|
|
|
38
89
|
return super().validate(field, value, params)
|
|
39
90
|
return True
|
|
40
91
|
|
|
41
|
-
class
|
|
92
|
+
class RequiredIfAcceptedRule(RequiredRule, AcceptedRule):
|
|
42
93
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
43
94
|
if not params:
|
|
44
95
|
return False
|
|
45
|
-
|
|
96
|
+
|
|
97
|
+
if AcceptedRule.validate(self, field, params[0], params):
|
|
46
98
|
return super().validate(field, value, params)
|
|
47
99
|
|
|
48
100
|
return True
|
|
49
101
|
|
|
50
|
-
class
|
|
102
|
+
class RequiredIfDeclinedRule(RequiredRule, DeclinedRule):
|
|
51
103
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
52
104
|
if not params:
|
|
53
105
|
return False
|
|
54
106
|
|
|
55
|
-
if
|
|
107
|
+
if DeclinedRule.validate(self, field, params[0], params):
|
|
56
108
|
return super().validate(field, value, params)
|
|
57
109
|
|
|
58
|
-
|
|
110
|
+
return True
|
|
111
|
+
|
|
112
|
+
class RequiredArrayKeysRule(Rule):
|
|
59
113
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
60
|
-
if not params:
|
|
114
|
+
if not isinstance(value, dict) or not params:
|
|
61
115
|
return False
|
|
116
|
+
return all(key in value for key in params)
|
|
117
|
+
|
|
118
|
+
def message(self, field: str, params: List[str]) -> str:
|
|
119
|
+
return f"The :attribute must contain all required keys: {', '.join(params)}"
|
|
120
|
+
|
|
121
|
+
class ProhibitedIfRule(ProhibitedRule):
|
|
122
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
123
|
+
if len(params) == 1 and callable(params[0]):
|
|
124
|
+
condition_met = params[0](self.validator.data)
|
|
125
|
+
elif len(params) == 1 and isinstance(params[0], bool):
|
|
126
|
+
condition_met = params[0]
|
|
127
|
+
else:
|
|
128
|
+
other_field, other_value = params[0], params[1]
|
|
129
|
+
actual_value = self.get_field_value(other_field, '')
|
|
62
130
|
|
|
63
|
-
|
|
131
|
+
condition_met = actual_value == other_value
|
|
132
|
+
|
|
133
|
+
if condition_met:
|
|
64
134
|
return super().validate(field, value, params)
|
|
135
|
+
return True
|
|
136
|
+
|
|
137
|
+
class ProhibitedUnlessRule(ProhibitedRule):
|
|
138
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
139
|
+
if len(params) < 2:
|
|
140
|
+
return False
|
|
141
|
+
|
|
142
|
+
other_field, other_value = params[0], params[1]
|
|
143
|
+
actual_value = self.get_field_value(other_field, '')
|
|
65
144
|
|
|
66
|
-
|
|
145
|
+
if actual_value == other_value:
|
|
146
|
+
return True
|
|
147
|
+
|
|
148
|
+
return super().validate(field, value, params)
|
|
67
149
|
|
|
68
|
-
class
|
|
150
|
+
class ProhibitedIfAcceptedRule(ProhibitedRule, AcceptedRule):
|
|
69
151
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
70
152
|
if not params:
|
|
71
153
|
return False
|
|
72
154
|
|
|
73
|
-
if
|
|
155
|
+
if AcceptedRule.validate(self, field, params[0], params):
|
|
74
156
|
return super().validate(field, value, params)
|
|
75
|
-
|
|
76
157
|
return True
|
|
77
158
|
|
|
78
|
-
class
|
|
159
|
+
class ProhibitedIfDeclinedRule(ProhibitedRule, DeclinedRule):
|
|
79
160
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
80
|
-
if
|
|
161
|
+
if not params:
|
|
162
|
+
return False
|
|
163
|
+
|
|
164
|
+
if DeclinedRule.validate(self, field, params[0], params):
|
|
81
165
|
return super().validate(field, value, params)
|
|
82
|
-
|
|
166
|
+
|
|
83
167
|
return True
|
|
84
168
|
|
|
85
|
-
class
|
|
169
|
+
class PresentIfRule(PresentRule):
|
|
86
170
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
87
|
-
|
|
88
|
-
|
|
171
|
+
condition_met = False
|
|
172
|
+
|
|
173
|
+
if len(params) == 1 and callable(params[0]):
|
|
174
|
+
condition_met = params[0](self.validator.data)
|
|
175
|
+
elif len(params) == 1 and isinstance(params[0], bool):
|
|
176
|
+
condition_met = params[0]
|
|
177
|
+
else:
|
|
178
|
+
conditions = list(zip(params[::2], params[1::2]))
|
|
179
|
+
for other_field, expected_value in conditions:
|
|
180
|
+
if self.get_field_value(other_field, None) == expected_value:
|
|
181
|
+
condition_met = True
|
|
182
|
+
break
|
|
89
183
|
|
|
184
|
+
if condition_met:
|
|
185
|
+
return super().validate(field, value, params)
|
|
186
|
+
|
|
90
187
|
return True
|
|
91
188
|
|
|
92
|
-
class
|
|
189
|
+
class PresentUnlessRule(PresentRule):
|
|
93
190
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
94
|
-
if
|
|
191
|
+
if len(params) < 2:
|
|
95
192
|
return False
|
|
193
|
+
other_field, other_value = params[0], params[1]
|
|
96
194
|
|
|
97
|
-
if
|
|
195
|
+
if self.get_field_value(other_field, None) != other_value:
|
|
98
196
|
return super().validate(field, value, params)
|
|
197
|
+
|
|
99
198
|
return True
|
|
100
|
-
|
|
101
|
-
class
|
|
199
|
+
|
|
200
|
+
class PresentWithRule(PresentRule):
|
|
102
201
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
103
202
|
if not params:
|
|
104
203
|
return False
|
|
105
|
-
|
|
106
|
-
if DeclinedRule.validate(self, field, params[0], params):
|
|
204
|
+
if any(self.get_field_value(param, None) is not None for param in params):
|
|
107
205
|
return super().validate(field, value, params)
|
|
108
206
|
|
|
109
207
|
return True
|
|
110
208
|
|
|
111
|
-
class
|
|
209
|
+
class PresentWithAllRule(PresentRule):
|
|
112
210
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
113
211
|
if not params:
|
|
114
212
|
return False
|
|
115
213
|
|
|
116
|
-
if
|
|
214
|
+
if all(self.get_field_value(param, None) is not None for param in params):
|
|
215
|
+
return super().validate(field, value, params)
|
|
216
|
+
|
|
217
|
+
class MissingIfRule(MissingRule):
|
|
218
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
219
|
+
condition_met = False
|
|
220
|
+
|
|
221
|
+
if len(params) == 1 and callable(params[0]):
|
|
222
|
+
condition_met = params[0](self.validator.data)
|
|
223
|
+
elif len(params) == 1 and isinstance(params[0], bool):
|
|
224
|
+
condition_met = params[0]
|
|
225
|
+
else:
|
|
226
|
+
conditions = list(zip(params[::2], params[1::2]))
|
|
227
|
+
for other_field, expected_value in conditions:
|
|
228
|
+
if self.get_field_value(other_field, None) == expected_value:
|
|
229
|
+
condition_met = True
|
|
230
|
+
break
|
|
231
|
+
|
|
232
|
+
if condition_met:
|
|
117
233
|
return super().validate(field, value, params)
|
|
118
234
|
|
|
119
235
|
return True
|
|
120
236
|
|
|
121
|
-
class
|
|
237
|
+
class MissingUnlessRule(MissingRule):
|
|
238
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
239
|
+
if len(params) < 2:
|
|
240
|
+
return False
|
|
241
|
+
other_field, other_value = params[0], params[1]
|
|
242
|
+
if self.get_field_value(other_field, None) != other_value:
|
|
243
|
+
return super().validate(field, value, params)
|
|
244
|
+
|
|
245
|
+
return True
|
|
246
|
+
|
|
247
|
+
class MissingWithRule(MissingRule):
|
|
122
248
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
123
249
|
if not params:
|
|
124
250
|
return False
|
|
125
251
|
|
|
126
|
-
if
|
|
252
|
+
if any(self.get_field_value(param, None) is not None for param in params):
|
|
127
253
|
return super().validate(field, value, params)
|
|
128
254
|
|
|
129
255
|
return True
|
|
130
256
|
|
|
131
|
-
class
|
|
257
|
+
class MissingWithAllRule(MissingRule):
|
|
132
258
|
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
133
|
-
if not
|
|
259
|
+
if not params:
|
|
134
260
|
return False
|
|
135
|
-
|
|
261
|
+
|
|
262
|
+
if all(self.get_field_value(param, None) is not None for param in params):
|
|
263
|
+
return super().validate(field, value, params)
|
|
264
|
+
|
|
265
|
+
return True
|
|
136
266
|
|
|
267
|
+
class ExcludeIfRule(ExcludeRule):
|
|
268
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
269
|
+
if len(params) == 1 and callable(params[0]):
|
|
270
|
+
condition_met = params[0](self.validator.data)
|
|
271
|
+
elif len(params) == 1 and isinstance(params[0], bool):
|
|
272
|
+
condition_met = params[0]
|
|
273
|
+
else:
|
|
274
|
+
conditions = list(zip(params[::2], params[1::2]))
|
|
275
|
+
condition_met = all(
|
|
276
|
+
str(self.get_field_value(f)) == str(v)
|
|
277
|
+
for f, v in conditions
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
if condition_met:
|
|
281
|
+
return super().validate(field, value, params)
|
|
282
|
+
return True
|
|
283
|
+
|
|
284
|
+
class ExcludeUnlessRule(ExcludeRule):
|
|
285
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
286
|
+
conditions = [(f.strip(), v.strip()) for f, v in zip(params[::2], params[1::2])]
|
|
287
|
+
|
|
288
|
+
all_conditions_met = all(
|
|
289
|
+
self.get_field_value(f) == v
|
|
290
|
+
for f, v in conditions
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
if not all_conditions_met:
|
|
294
|
+
return super().validate(field, value, params)
|
|
295
|
+
|
|
296
|
+
return True
|
|
297
|
+
|
|
137
298
|
def message(self, field: str, params: List[str]) -> str:
|
|
138
|
-
return f"The :attribute
|
|
299
|
+
return f"The :attribute field is excluded unless {params[0]} is {params[1]}."
|
|
300
|
+
|
|
301
|
+
class ExcludeWithRule(ExcludeRule):
|
|
302
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
303
|
+
if any(not self.is_empty(self.get_field_value(param, None)) for param in params):
|
|
304
|
+
return super().validate(field, value, params)
|
|
305
|
+
|
|
306
|
+
return True
|
|
307
|
+
|
|
308
|
+
class ExcludeWithoutRule(ExcludeRule):
|
|
309
|
+
def validate(self, field: str, value: Any, params: List[str]) -> bool:
|
|
310
|
+
if any(self.is_empty(self.get_field_value(param, None)) for param in params):
|
|
311
|
+
return super().validate(field, value, params)
|
|
312
|
+
|
|
313
|
+
return True
|
|
@@ -58,11 +58,11 @@ class RuleConflictChecker:
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
@classmethod
|
|
61
|
-
def check_conflicts(cls, rules: List['
|
|
61
|
+
def check_conflicts(cls, rules: List['Rule']) -> None:
|
|
62
62
|
"""Main method untuk mengecek semua jenis konflik.
|
|
63
63
|
|
|
64
64
|
Args:
|
|
65
|
-
rules: List of
|
|
65
|
+
rules: List of Rule objects to check
|
|
66
66
|
|
|
67
67
|
Raises:
|
|
68
68
|
ValueError: Untuk konflik kritis
|
|
@@ -114,7 +114,7 @@ class RuleErrorHandler:
|
|
|
114
114
|
return []
|
|
115
115
|
|
|
116
116
|
pairs = []
|
|
117
|
-
params = self._current_params.copy()
|
|
117
|
+
params = list(self._current_params).copy()
|
|
118
118
|
|
|
119
119
|
# Process parameters in pairs (field, value)
|
|
120
120
|
while len(params) >= 2:
|
|
@@ -129,9 +129,11 @@ class RuleErrorHandler:
|
|
|
129
129
|
for placeholder, replacement in replacements.items():
|
|
130
130
|
if replacement is not None:
|
|
131
131
|
# Use regex to avoid partial replacements
|
|
132
|
+
safe_replacement = re.escape(str(replacement))
|
|
133
|
+
safe_replacement = safe_replacement.replace('\\', r'')
|
|
132
134
|
message = re.sub(
|
|
133
135
|
re.escape(placeholder) + r'(?![a-zA-Z0-9_])',
|
|
134
|
-
|
|
136
|
+
safe_replacement,
|
|
135
137
|
message
|
|
136
138
|
)
|
|
137
139
|
return message
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from validator.factory import RuleFactory
|
|
2
2
|
from validator.services.rule_conflict import RuleConflictChecker
|
|
3
|
-
from validator.rules import
|
|
3
|
+
from validator.rules import Rule
|
|
4
4
|
from typing import Dict, List, Union, Tuple
|
|
5
5
|
from validator.exceptions import RuleNotFoundException
|
|
6
6
|
from collections.abc import Iterable, Sequence
|
|
@@ -14,7 +14,7 @@ class RulePreparer:
|
|
|
14
14
|
def __init__(self, rule_factory: RuleFactory):
|
|
15
15
|
self.rule_factory = rule_factory
|
|
16
16
|
|
|
17
|
-
def prepare(self, raw_rules: Dict[str, Union[str, List[Union[str,
|
|
17
|
+
def prepare(self, raw_rules: Dict[str, Union[str, List[Union[str, Rule]]]]) -> Dict[str, List[Rule]]:
|
|
18
18
|
"""Convert raw rules to prepared validation rules"""
|
|
19
19
|
prepared_rules = {}
|
|
20
20
|
for field, rule_input in raw_rules.items():
|
|
@@ -25,18 +25,18 @@ class RulePreparer:
|
|
|
25
25
|
prepared_rules[field] = self._deduplicate_rules(rules)
|
|
26
26
|
return prepared_rules
|
|
27
27
|
|
|
28
|
-
def _convert_to_rules(self, rule_input: Union[str, List[Union[str,
|
|
29
|
-
"""Convert mixed rule input to list of
|
|
28
|
+
def _convert_to_rules(self, rule_input: Union[str, List[Union[str, Rule]], Rule, Tuple[Union[str, Rule], str], Tuple[Union[str, Rule], str]]) -> List[Rule]:
|
|
29
|
+
"""Convert mixed rule input to list of Rule objects"""
|
|
30
30
|
if rule_input is None:
|
|
31
31
|
return []
|
|
32
|
-
if isinstance(rule_input,
|
|
32
|
+
if isinstance(rule_input, Rule):
|
|
33
33
|
return [rule_input]
|
|
34
|
-
if isinstance(rule_input, list):
|
|
34
|
+
if isinstance(rule_input, list | tuple):
|
|
35
35
|
rules = []
|
|
36
36
|
for r in rule_input:
|
|
37
37
|
if isinstance(r, str):
|
|
38
38
|
rules.extend(self._parse_rule_string(r))
|
|
39
|
-
elif isinstance(r,
|
|
39
|
+
elif isinstance(r, Rule):
|
|
40
40
|
rules.append(r)
|
|
41
41
|
elif isinstance(r, (tuple, list)):
|
|
42
42
|
if len(r) == 0:
|
|
@@ -50,10 +50,10 @@ class RulePreparer:
|
|
|
50
50
|
if isinstance(rule_name, str):
|
|
51
51
|
rule = RuleFactory.create_rule(rule_name)
|
|
52
52
|
rule.params = params
|
|
53
|
-
elif isinstance(rule_name,
|
|
53
|
+
elif isinstance(rule_name, Rule):
|
|
54
54
|
rule = rule_name
|
|
55
55
|
if params:
|
|
56
|
-
warnings.warn(f"Parameters {params} are ignored for
|
|
56
|
+
warnings.warn(f"Parameters {params} are ignored for Rule instance")
|
|
57
57
|
else:
|
|
58
58
|
raise ValueError(f"Invalid rule name type in {r}")
|
|
59
59
|
|
|
@@ -65,8 +65,8 @@ class RulePreparer:
|
|
|
65
65
|
return self._parse_rule_string(rule_input)
|
|
66
66
|
raise ValueError(f"Invalid rule input type: {type(rule_input)}")
|
|
67
67
|
|
|
68
|
-
def _parse_rule_string(self, rule_str: str) -> List[
|
|
69
|
-
"""Parse rule string into
|
|
68
|
+
def _parse_rule_string(self, rule_str: str) -> List[Rule]:
|
|
69
|
+
"""Parse rule string into Rule objects"""
|
|
70
70
|
rules = []
|
|
71
71
|
for rule_part in rule_str.split('|'):
|
|
72
72
|
rule_part = rule_part.strip()
|
|
@@ -93,22 +93,9 @@ class RulePreparer:
|
|
|
93
93
|
return rule_name, [p.strip() for p in param_str.split(',') if p.strip()]
|
|
94
94
|
|
|
95
95
|
def _parse_params(self, params: Union[Tuple, List, str, Enum]):
|
|
96
|
-
all_params = []
|
|
97
|
-
# seen = set()
|
|
98
|
-
# for param in params:
|
|
99
|
-
# if isinstance(param, Iterable) and not isinstance(param, str | bytes) and not inspect.isclass(param):
|
|
100
|
-
# for item in param:
|
|
101
|
-
# if item not in seen and item not in (None, '', [], {}) and (isinstance(item, (Number, Sequence)) or inspect.isclass(item)):
|
|
102
|
-
# seen.add(item)
|
|
103
|
-
# all_params.append(item)
|
|
104
|
-
# else:
|
|
105
|
-
# if param not in seen and param not in (None, '', [], {}) and (isinstance(param, (Number, Sequence)) or inspect.isclass(param)):
|
|
106
|
-
# seen.add(param)
|
|
107
|
-
# all_params.append(param)
|
|
108
|
-
|
|
109
96
|
return set(tuple(x) if isinstance(x, list) else x for x in params)
|
|
110
97
|
|
|
111
|
-
def _deduplicate_rules(self, rules: List[
|
|
98
|
+
def _deduplicate_rules(self, rules: List[Rule]) -> List[Rule]:
|
|
112
99
|
"""Remove duplicate rules based on name and parameters"""
|
|
113
100
|
seen = set()
|
|
114
101
|
unique = []
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
validator/__init__.py,sha256=udxDzUicPfxBOAQvzsnl3pHur9VUppKbWMgg35hpiww,244
|
|
2
|
-
validator/exceptions.py,sha256=y2v7CaXmeGFHWcnigtLl4U-sFta_jMiXkGKXWIIVglY,366
|
|
3
|
-
validator/factory.py,sha256=bImQNLhEJg5VTxtHiMYVb2EbHWvimTDqHq-UcA8uolw,812
|
|
4
|
-
validator/core/__init__.py,sha256=ZcqlXJSk03i_CVzmIN-nVe1UOyvwwO5jhbEj7f62Y_o,59
|
|
5
|
-
validator/core/validator.py,sha256=Yaw7__Hf-PuKcXaJ6yaARPEyeofEaoqt5-N3tJZ_jtg,12741
|
|
6
|
-
validator/database/__init__.py,sha256=O-cB6-MhNapJ3iwe5jvifbMfr1dPjXLtEdfNTKIu0hc,171
|
|
7
|
-
validator/database/detector.py,sha256=Vac7oVL26GjU6expGo01-6mgUtXqldr-jirzpYokZBM,9597
|
|
8
|
-
validator/database/manager.py,sha256=XJM_I0WaWfZWV710duAc32p1gtiP2or-MAj75WPw1oM,6478
|
|
9
|
-
validator/rules/__init__.py,sha256=nDE3qoI82qJTCbILLUWkXuwsMOmsDtB1m-3IGIvRfpY,919
|
|
10
|
-
validator/rules/array.py,sha256=3hgD_b2K61fG-xT1UdssHMOdIQKWw5vkdpovNn-kc8E,2835
|
|
11
|
-
validator/rules/base.py,sha256=hrGESfkdvqZrQ1yIK_ftVVOMUuyvNFoz-_qaqUucSy8,3474
|
|
12
|
-
validator/rules/basic.py,sha256=rL9THt3OE5QgtuxGCM_dklNpPKOdmuDP30EiSmvkgP8,9656
|
|
13
|
-
validator/rules/comparison.py,sha256=Tlg3AfjTd-W1RonmhT0PLP_mf4CSyhpl7EsmCEYzEAA,9446
|
|
14
|
-
validator/rules/conditional.py,sha256=M4x9g2LQTXNaNfb7dHRMs6vUseDu3WC1RB41QGiNABQ,5965
|
|
15
|
-
validator/rules/date.py,sha256=sPrq8OgHMyUxRAZch4yt7K3I0lInIIy0dUgRtAkcsMk,5313
|
|
16
|
-
validator/rules/files.py,sha256=eCrarOtaRWv1UBQuv5Y6-CcyHuncyLau8V8Ocpw0NVQ,6565
|
|
17
|
-
validator/rules/format.py,sha256=hyho3Cxcgjz4mqCkO_uvjrJPU1JEOK2IZL7F6Dh9oPg,3784
|
|
18
|
-
validator/rules/string.py,sha256=5NToIDOqBhmUYCg1y2y2zdQ4MMLyJzL0FCTM5XhzAgc,2951
|
|
19
|
-
validator/rules/type.py,sha256=B8YH1T1RBlRNNOmb-5uZKdwBc8Uk1Zj4h4xIzAtJC2M,1883
|
|
20
|
-
validator/rules/utilities.py,sha256=xysxDWoP1hJQyBZhNvdzTyMsFOWsEIxFBvYPljW1coA,5340
|
|
21
|
-
validator/services/__init__.py,sha256=zzKTmqL7v4niFGWHJBfWLqgJ0iTaW_69OzYZN8uInzQ,210
|
|
22
|
-
validator/services/rule_conflict.py,sha256=s1RJNUY5d0WtSMHkrKulBCgJ2BZL2GE0Eu5pdAoiIbM,4943
|
|
23
|
-
validator/services/rule_error_handler.py,sha256=MNgSxnakmc3f_nvGuapRmJ9woKt_-E8Wun39OCTrGB0,10273
|
|
24
|
-
validator/services/rule_preparer.py,sha256=jRcMNjqq2xyZjO64Pim8jWmja5DmTzf0V_uuHG0lJTg,5621
|
|
25
|
-
validator/utils/__init__.py,sha256=Yzo-xv285Be-a233M4duDdYtscuHiuBbPSX_C8yViJI,20
|
|
26
|
-
validator/utils/string.py,sha256=0YACzeEaWNEOR9_7O9A8D1ItIbtWfOJ8IfrzcB8VMYA,515
|
|
27
|
-
safeshield-1.3.2.dist-info/LICENSE,sha256=qugtRyKckyaks6hd2xyxOFSOYM6au1N80pMXuMTPvC4,1090
|
|
28
|
-
safeshield-1.3.2.dist-info/METADATA,sha256=V17d3w6UPE698CGSm83-5C3NOm-lZrCqLJYAy8ozrHM,1984
|
|
29
|
-
safeshield-1.3.2.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
30
|
-
safeshield-1.3.2.dist-info/top_level.txt,sha256=iUtV3dlHOIiMfLuY4pruY00lFni8JzOkQ3Nh1II19OE,10
|
|
31
|
-
safeshield-1.3.2.dist-info/RECORD,,
|