safeshield 1.2.1__py3-none-any.whl → 1.3.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: safeshield
3
- Version: 1.2.1
3
+ Version: 1.3.2
4
4
  Summary: Library for Help Validation Control
5
5
  Home-page: https://github.com/WunsunTarniho/py-guard
6
6
  Author: Wunsun Tarniho
@@ -63,3 +63,9 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
63
63
 
64
64
  ## v1.2.1 (2024-07-20)
65
65
  - Fixed: Bug unique and exist rule.
66
+
67
+ ## v1.2.2 (2024-07-20)
68
+ - Fixed: Error when connect use postgres.
69
+
70
+ ## v1.3.2 (2024-07-20)
71
+ - Refinement: Groups Inheritance Rule Class.
@@ -0,0 +1,31 @@
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,,
@@ -221,18 +221,18 @@ class Validator:
221
221
  try:
222
222
  rule.set_validator(self)
223
223
 
224
- # Format field name untuk pesan error (otomatis tangkap path aktual)
225
224
  display_field = getattr(self, '_current_actual_path', field)
226
225
 
227
226
  if not rule.validate(field, value, getattr(rule, 'params', [])):
228
227
  msg = rule.message(display_field, getattr(rule, 'params', []))
229
- self.error_handler.add_error(display_field, rule.rule_name, msg, value)
228
+ self.error_handler.add_error(display_field, rule.rule_name, getattr(rule, 'params', []), msg, value)
229
+
230
230
  return False
231
231
  else:
232
232
  return True
233
233
  except Exception as e:
234
234
  display_field = getattr(self, '_current_actual_path', field)
235
- self.error_handler.add_error(display_field, rule.rule_name, str(e), value)
235
+ self.error_handler.add_error(display_field, rule.rule_name, getattr(rule, 'params', []), str(e), value)
236
236
  return False
237
237
 
238
238
  def _get_nested_value(self, path: str) -> Any:
@@ -46,7 +46,12 @@ class DatabaseManager:
46
46
  self.connect()
47
47
  cursor = None
48
48
  try:
49
- cursor = self.connection.cursor(dictionary=True)
49
+ if self._connection_params['type'] == 'postgresql':
50
+ from psycopg2.extras import RealDictCursor
51
+ cursor = self.connection.cursor(cursor_factory=RealDictCursor)
52
+ else:
53
+ cursor = self.connection.cursor(dictionary=True)
54
+
50
55
  yield cursor
51
56
  finally:
52
57
  if cursor:
validator/rules/array.py CHANGED
@@ -19,8 +19,8 @@ class ArrayRule(ValidationRule):
19
19
 
20
20
  def message(self, field: str, params: List[str]) -> str:
21
21
  if params:
22
- return f"The {field} must contain the keys: {', '.join(self._missing_keys)}."
23
- return f"The {field} must be an array."
22
+ return f"The :attribute must contain the keys: {', '.join(self._missing_keys)}."
23
+ return f"The :attribute must be an array."
24
24
 
25
25
  class ContainsRule(ValidationRule):
26
26
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
@@ -44,7 +44,7 @@ class ContainsRule(ValidationRule):
44
44
  return False
45
45
 
46
46
  def message(self, field: str, params: List[str]) -> str:
47
- return f"The :name must contain {params[0]}"
47
+ return f"The :attribute must contain {params[0]}"
48
48
 
49
49
  class DistinctRule(ValidationRule):
50
50
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
@@ -54,7 +54,7 @@ class DistinctRule(ValidationRule):
54
54
  return len(value) == len(set(value))
55
55
 
56
56
  def message(self, field: str, params: List[str]) -> str:
57
- return f"The :name must contain unique values"
57
+ return f"The :attribute must contain unique values"
58
58
 
59
59
  class InArrayRule(ValidationRule):
60
60
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
@@ -64,7 +64,7 @@ class InArrayRule(ValidationRule):
64
64
  return str(value) in params
65
65
 
66
66
  def message(self, field: str, params: List[str]) -> str:
67
- return f"The :name must be one of: {', '.join(params)}"
67
+ return f"The :attribute must be one of: {', '.join(params)}"
68
68
 
69
69
  class InArrayKeysRule(ValidationRule):
70
70
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
@@ -74,4 +74,4 @@ class InArrayKeysRule(ValidationRule):
74
74
  return any(key in value for key in params)
75
75
 
76
76
  def message(self, field: str, params: List[str]) -> str:
77
- return f"The :name must contain at least one of these keys: {', '.join(params)}"
77
+ return f"The :attribute must contain at least one of these keys: {', '.join(params)}"
validator/rules/basic.py CHANGED
@@ -5,33 +5,103 @@ from typing import Any, Dict, List, Optional, Set, Union, Tuple, Type
5
5
  # BASIC VALIDATION RULES
6
6
  # =============================================
7
7
 
8
+ class AnyOfRule(ValidationRule):
9
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
10
+ if not params:
11
+ return False
12
+ return any(self.get_field_value(param, param) == value for param in params)
13
+
14
+ def message(self, field: str, params: List[str]) -> str:
15
+ return f"The :attribute must be one of: {', '.join(params)}"
16
+
17
+ class BailRule(ValidationRule):
18
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
19
+ self.validator._stop_on_first_failure = True
20
+ return True
21
+
22
+ def message(self, field: str, params: List[str]) -> str:
23
+ return ""
24
+
8
25
  class RequiredRule(ValidationRule):
9
26
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
10
27
  return not self.is_empty(value)
11
28
 
12
29
  def message(self, field: str, params: List[str]) -> str:
13
- return f"The :name field is required."
30
+ return f"The :attribute field is required."
14
31
 
32
+ class ProhibitedRule(ValidationRule):
33
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
34
+ return self.is_empty(value)
35
+
36
+ def message(self, field: str, params: List[str]) -> str:
37
+ return "The :attribute field is must be empty."
38
+
15
39
  class NullableRule(ValidationRule):
16
40
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
17
41
  return True
18
42
 
19
43
  def message(self, field: str, params: List[str]) -> str:
20
- return f"The :name may be null."
44
+ return f"The :attribute may be null."
21
45
 
22
46
  class FilledRule(ValidationRule):
23
47
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
24
48
  return value not in ('', None)
25
49
 
26
50
  def message(self, field: str, params: List[str]) -> str:
27
- return f"The :name field must have a value."
51
+ return f"The :attribute field must have a value."
28
52
 
29
53
  class PresentRule(ValidationRule):
30
54
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
31
55
  return field in self.validator.data
32
56
 
33
57
  def message(self, field: str, params: List[str]) -> str:
34
- return f"The :name field must be present."
58
+ return f"The :attribute field must be present."
59
+
60
+ class MissingRule(ValidationRule):
61
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
62
+ return value is None
63
+
64
+ def message(self, field: str, params: List[str]) -> str:
65
+ return f"The :attribute field must be missing."
66
+
67
+ class ProhibitsRule(ValidationRule):
68
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
69
+ if not params or value is None:
70
+ return True
71
+ return all(self.get_field_value(param, param) in (None, 'None') for param in params)
72
+
73
+ def message(self, field: str, params: List[str]) -> str:
74
+ return f"When :attribute is present, {', '.join(params)} must be absent."
75
+
76
+ class AcceptedRule(ValidationRule):
77
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
78
+ value = self.get_field_value(value, value)
79
+
80
+ if isinstance(value, str):
81
+ return value.lower() in ['yes', 'on', '1', 1, True, 'true', 'True']
82
+ if isinstance(value, int):
83
+ return value == 1
84
+ if isinstance(value, bool):
85
+ return value
86
+ return False
87
+
88
+ def message(self, field: str, params: List[str]) -> str:
89
+ return f"The :attribute must be accepted."
90
+
91
+ class DeclinedRule(ValidationRule):
92
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
93
+ value = self.get_field_value(value, value)
94
+
95
+ if isinstance(value, str):
96
+ return value.lower() in ['no', 'off', '0', 0, False, 'false', 'False']
97
+ if isinstance(value, int):
98
+ return value == 0
99
+ if isinstance(value, bool):
100
+ return not value
101
+ return False
102
+
103
+ def message(self, field: str, params: List[str]) -> str:
104
+ return f"The :attribute must be declined."
35
105
 
36
106
  class SometimesRule(ValidationRule):
37
107
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
@@ -39,3 +109,129 @@ class SometimesRule(ValidationRule):
39
109
 
40
110
  def message(self, field: str, params: List[str]) -> str:
41
111
  return ""
112
+
113
+ class ExcludeRule(ValidationRule):
114
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
115
+ self.validator._is_exclude = True
116
+
117
+ return True
118
+
119
+ def message(self, field: str, params: List[str]) -> str:
120
+ return ""
121
+
122
+ class UniqueRule(ValidationRule):
123
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
124
+ if not params or not hasattr(self.validator, 'db_manager') or not self.validator.db_manager:
125
+ return False
126
+
127
+ table = params[0]
128
+ column = field if len(params) == 1 else params[1]
129
+
130
+ try:
131
+ ignore_id = None
132
+ if len(params) > 2 and params[2].startswith('ignore:'):
133
+ ignore_field = params[2].split(':')[1]
134
+ ignore_id = self.get_field_value(ignore_field)
135
+ return self.validator.db_manager.is_unique(table, column, value, ignore_id)
136
+ except Exception as e:
137
+ print(f"Database error in UniqueRule: {e}")
138
+ return False
139
+
140
+ def message(self, field: str, params: List[str]) -> str:
141
+ return f"The :attribute has already been taken."
142
+
143
+ class ExistsRule(ValidationRule):
144
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
145
+ if not params or not hasattr(self.validator, 'db_manager') or not self.validator.db_manager:
146
+ return False
147
+
148
+ table = params[0]
149
+ column = field if len(params) == 1 else params[1]
150
+
151
+ try:
152
+ return self.validator.db_manager.exists(table, column, value)
153
+ except Exception as e:
154
+ print(f"Database error in ExistsRule: {e}")
155
+ return False
156
+
157
+ def message(self, field: str, params: List[str]) -> str:
158
+ return f"The selected :attribute is invalid."
159
+
160
+ class ConfirmedRule(ValidationRule):
161
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
162
+ confirmation_field = f"{field}_confirmation"
163
+
164
+ return value == self.get_field_value(confirmation_field, '')
165
+
166
+ def message(self, field: str, params: List[str]) -> str:
167
+ return f"The :attribute confirmation does not match."
168
+
169
+ class SameRule(ValidationRule):
170
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
171
+ if not params:
172
+ return False
173
+ return value == self.get_field_value(params[0])
174
+
175
+ def message(self, field: str, params: List[str]) -> str:
176
+ return f"The :attribute and {params[0]} must match."
177
+
178
+ class DifferentRule(ValidationRule):
179
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
180
+ if not params:
181
+ return False
182
+ return value != self.get_field_value(params[0])
183
+
184
+ def message(self, field: str, params: List[str]) -> str:
185
+ return f"The :attribute and {params[0]} must be different."
186
+
187
+ class RegexRule(ValidationRule):
188
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
189
+ if not params or not isinstance(value, str):
190
+ return False
191
+ try:
192
+ return bool(re.fullmatch(params[0], value))
193
+ except re.error:
194
+ return False
195
+
196
+ def message(self, field: str, params: List[str]) -> str:
197
+ return f"The :attribute format is invalid."
198
+
199
+ class NotRegexRule(ValidationRule):
200
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
201
+ if not params or not isinstance(value, str):
202
+ return True
203
+ print(not bool(re.search(params[0], value)))
204
+ try:
205
+ return not bool(re.search(params[0], value))
206
+ except re.error:
207
+ return True
208
+
209
+ def message(self, field: str, params: List[str]) -> str:
210
+ return f"The :attribute format is invalid."
211
+
212
+ class InRule(ValidationRule):
213
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
214
+ allowed_values = self._parse_option_values(field, params)
215
+ return (str(value) in allowed_values or value in allowed_values)
216
+
217
+ def message(self, field: str, params: List[str]) -> str:
218
+ allowed_values = self._parse_option_values(field, params)
219
+ return f"The selected :attribute must be in : {', '.join(map(str, allowed_values))}"
220
+
221
+ class NotInRule(ValidationRule):
222
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
223
+ not_allowed_values = self._parse_option_values(field, params)
224
+ return str(value) not in not_allowed_values
225
+
226
+ def message(self, field: str, params: List[str]) -> str:
227
+ not_allowed_values = self._parse_option_values(field, params)
228
+ return f"The selected :attribute must be not in : {', '.join(map(str, not_allowed_values))}"
229
+
230
+ class EnumRule(ValidationRule):
231
+ def validate(self, field: str, value: Any, params: List[str]) -> bool:
232
+ allowed_values = self._parse_option_values(field, params)
233
+ return (str(value) in allowed_values or value in allowed_values)
234
+
235
+ def message(self, field: str, params: List[str]) -> str:
236
+ allowed_values = self._parse_option_values(field, params)
237
+ return f"The :attribute must be one of: {', '.join(map(str, allowed_values))}"
@@ -27,7 +27,7 @@ class MinRule(ValidationRule):
27
27
  return False
28
28
 
29
29
  def message(self, field: str, params: List[str]) -> str:
30
- return f"The :name must be at least {params[0]}."
30
+ return f"The :attribute must be at least {params[0]}."
31
31
 
32
32
  class MaxRule(ValidationRule):
33
33
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
@@ -85,13 +85,13 @@ class MaxRule(ValidationRule):
85
85
  def message(self, field: str, params: List[str]) -> str:
86
86
  value = self.get_field_value(field)
87
87
  if value is None:
88
- return f"The {field} must not exceed {params[0]}"
88
+ return f"The :attribute must not exceed {params[0]}"
89
89
 
90
90
  # Check all possible file size attributes
91
91
  file_attrs = ['content_length', 'size', 'fileno']
92
92
  if any(hasattr(value, attr) for attr in file_attrs):
93
- return f"File {field} must not exceed {params[0]} bytes"
94
- return f"The {field} must not exceed {params[0]}"
93
+ return f"File :attribute must not exceed {params[0]} bytes"
94
+ return f"The :attribute must not exceed {params[0]}"
95
95
 
96
96
  class BetweenRule(ValidationRule):
97
97
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
@@ -136,8 +136,8 @@ class BetweenRule(ValidationRule):
136
136
  def message(self, field: str, params: List[str]) -> str:
137
137
  value = self.get_field_value(field)
138
138
  if hasattr(value, 'content_length') or hasattr(value, 'size'):
139
- return f"File {field} must be between {params[0]} and {params[1]} bytes"
140
- return f"The {field} must be between {params[0]} and {params[1]}"
139
+ return f"File :attribute must be between {params[0]} and {params[1]} bytes"
140
+ return f"The :attribute must be between {params[0]} and {params[1]}"
141
141
 
142
142
  class SizeRule(ValidationRule):
143
143
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
@@ -182,14 +182,14 @@ class SizeRule(ValidationRule):
182
182
  def message(self, field: str, params: List[str]) -> str:
183
183
  value = self.get_field_value(field)
184
184
  if value is None:
185
- return f"The {field} must be exactly {params[0]}"
185
+ return f"The :attribute must be exactly {params[0]}"
186
186
 
187
187
  # Check for file attributes
188
188
  file_attrs = ['content_length', 'size', 'fileno']
189
189
  if any(hasattr(value, attr) for attr in file_attrs):
190
- return f"File {field} must be exactly {params[0]} bytes"
190
+ return f"File :attribute must be exactly {params[0]} bytes"
191
191
 
192
- return f"The {field} must be exactly {params[0]}"
192
+ return f"The :attribute must be exactly {params[0]}"
193
193
 
194
194
  class DigitsRule(ValidationRule):
195
195
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
@@ -204,7 +204,7 @@ class DigitsRule(ValidationRule):
204
204
  return value.isdigit() and len(value) == digits
205
205
 
206
206
  def message(self, field: str, params: List[str]) -> str:
207
- return f"The :name must be {params[0]} digits."
207
+ return f"The :attribute must be {params[0]} digits."
208
208
 
209
209
  class DigitsBetweenRule(ValidationRule):
210
210
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
@@ -220,7 +220,7 @@ class DigitsBetweenRule(ValidationRule):
220
220
  return value.isdigit() and min_digits <= len(value) <= max_digits
221
221
 
222
222
  def message(self, field: str, params: List[str]) -> str:
223
- return f"The :name must be between {params[0]} and {params[1]} digits."
223
+ return f"The :attribute must be between {params[0]} and {params[1]} digits."
224
224
 
225
225
  class MultipleOfRule(ValidationRule):
226
226
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
@@ -237,4 +237,4 @@ class MultipleOfRule(ValidationRule):
237
237
  return False
238
238
 
239
239
  def message(self, field: str, params: List[str]) -> str:
240
- return f"The :name must be a multiple of {params[0]}."
240
+ return f"The :attribute must be a multiple of {params[0]}."