safeshield 1.4.11__py3-none-any.whl → 1.5.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.
@@ -6,10 +6,9 @@ from typing import Any, Dict, List, Optional, Set, Union, Tuple, Type, Callable
6
6
  # =============================================
7
7
 
8
8
  class MinRule(Rule):
9
+ _count_parameter = 1
10
+
9
11
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
10
- if not params:
11
- return False
12
-
13
12
  try:
14
13
  min_val = float(params[0])
15
14
  except ValueError:
@@ -28,12 +27,20 @@ class MinRule(Rule):
28
27
 
29
28
  def message(self, field: str, params: List[str]) -> str:
30
29
  return f"The :attribute must be at least :min."
30
+
31
+ def replacements(self, field, value):
32
+ replacements = {
33
+ ':attribute': self._get_display_name(field),
34
+ ':input': value,
35
+ ':min': self._params[0],
36
+ }
37
+
38
+ return replacements
31
39
 
32
40
  class MaxRule(Rule):
41
+ _count_parameter = 1
42
+
33
43
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
34
- if not params or len(params) < 1:
35
- return False
36
-
37
44
  try:
38
45
  max_val = float(params[0])
39
46
 
@@ -91,12 +98,20 @@ class MaxRule(Rule):
91
98
  if any(hasattr(value, attr) for attr in file_attrs):
92
99
  return f"File :attribute must not exceed :max bytes"
93
100
  return f"The :attribute must not exceed :max"
101
+
102
+ def replacements(self, field, value):
103
+ replacements = {
104
+ ':attribute': self._get_display_name(field),
105
+ ':input': value,
106
+ ':max': self._params[0],
107
+ }
108
+
109
+ return replacements
94
110
 
95
111
  class BetweenRule(Rule):
112
+ _count_parameter = 2
113
+
96
114
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
97
- if len(params) != 2:
98
- return False
99
-
100
115
  try:
101
116
  min_val = float(params[0])
102
117
  max_val = float(params[1])
@@ -135,14 +150,23 @@ class BetweenRule(Rule):
135
150
  def message(self, field: str, params: List[str]) -> str:
136
151
  value = self.get_field_value(field)
137
152
  if hasattr(value, 'content_length') or hasattr(value, 'size'):
138
- return f"File :attribute must be between {params[0]} and {params[1]} bytes"
139
- return f"The :attribute must be between {params[0]} and {params[1]}"
153
+ return f"File :attribute must be between :min and :max bytes"
154
+ return f"The :attribute must be between :min and :max"
155
+
156
+ def replacements(self, field, value):
157
+ replacements = {
158
+ ':attribute': self._get_display_name(field),
159
+ ':input': value,
160
+ ':min': self._params[0],
161
+ ':max': self._params[1],
162
+ }
163
+
164
+ return replacements
140
165
 
141
166
  class SizeRule(Rule):
167
+ _count_parameter = 1
168
+
142
169
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
143
- if not params or len(params) < 1:
144
- return False
145
-
146
170
  try:
147
171
  target_size = float(params[0])
148
172
 
@@ -181,19 +205,29 @@ class SizeRule(Rule):
181
205
  def message(self, field: str, params: List[str]) -> str:
182
206
  value = self.get_field_value(field)
183
207
  if value is None:
184
- return f"The :attribute must be exactly {params[0]}"
208
+ return f"The :attribute must be exactly :size"
185
209
 
186
210
  # Check for file attributes
187
211
  file_attrs = ['content_length', 'size', 'fileno']
188
212
  if any(hasattr(value, attr) for attr in file_attrs):
189
- return f"File :attribute must be exactly {params[0]} bytes"
213
+ return f"File :attribute must be exactly :size bytes"
190
214
 
191
- return f"The :attribute must be exactly {params[0]}"
215
+ return f"The :attribute must be exactly :size"
192
216
 
217
+ def replacements(self, field, value):
218
+ replacements = {
219
+ ':attribute': self._get_display_name(field),
220
+ ':input': value,
221
+ ':size': self._params[0],
222
+ }
223
+
224
+ return replacements
193
225
 
194
226
  class UniqueRule(Rule):
227
+ _count_parameter = 1
228
+
195
229
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
196
- if not params or not hasattr(self.validator, 'db_manager') or not self.validator.db_manager:
230
+ if not hasattr(self.validator, 'db_manager') or not self.validator.db_manager:
197
231
  return False
198
232
 
199
233
  table = params[0]
@@ -213,8 +247,10 @@ class UniqueRule(Rule):
213
247
  return f"The :attribute has already been taken."
214
248
 
215
249
  class ExistsRule(Rule):
250
+ _count_parameter = 1
251
+
216
252
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
217
- if not params or not hasattr(self.validator, 'db_manager') or not self.validator.db_manager:
253
+ if not hasattr(self.validator, 'db_manager') or not self.validator.db_manager:
218
254
  return False
219
255
 
220
256
  table = params[0]
@@ -230,15 +266,28 @@ class ExistsRule(Rule):
230
266
  return f"The selected :attribute is invalid."
231
267
 
232
268
  class SameRule(Rule):
269
+ _count_parameter = 1
270
+
233
271
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
234
272
  if not params:
235
273
  return False
236
- return value == self.get_field_value(params[0])
274
+ return value == self.get_field_value(params[0], None)
237
275
 
238
276
  def message(self, field: str, params: List[str]) -> str:
239
277
  return f"The :attribute and :other must match."
278
+
279
+ def replacements(self, field, value):
280
+ replacements = {
281
+ ':attribute': self._get_display_name(field),
282
+ ':input': value,
283
+ ':other': self._get_display_name(self._params[0]),
284
+ }
285
+
286
+ return replacements
240
287
 
241
288
  class DifferentRule(Rule):
289
+ _count_parameter = 1
290
+
242
291
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
243
292
  if not params:
244
293
  return False
@@ -247,26 +296,56 @@ class DifferentRule(Rule):
247
296
 
248
297
  def message(self, field: str, params: List[str]) -> str:
249
298
  return f"The :attribute and :other must be different."
299
+
300
+ def replacements(self, field, value):
301
+ replacements = {
302
+ ':attribute': self._get_display_name(field),
303
+ ':input': value,
304
+ ':other': self._get_display_name(self._params[0]),
305
+ }
306
+
307
+ return replacements
250
308
 
251
309
  class InRule(Rule):
310
+ _count_parameter = 1
311
+
252
312
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
253
313
  allowed_values = self._parse_option_values(field, params)
254
314
  return (str(value) in allowed_values or value in allowed_values)
255
315
 
256
316
  def message(self, field: str, params: List[str]) -> str:
257
- allowed_values = self._parse_option_values(field, params)
258
- return f"The selected :attribute must be in : {', '.join(map(str, allowed_values))}"
317
+ return f"The selected :attribute must be in : :values"
318
+
319
+ def replacements(self, field, value):
320
+ replacements = {
321
+ ':attribute': self._get_display_name(field),
322
+ ':input': value,
323
+ ':values': ', '.join(self._get_display_name(self._params)),
324
+ }
325
+
326
+ return replacements
259
327
 
260
328
  class NotInRule(Rule):
329
+ _count_parameter = 1
330
+
261
331
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
262
- not_allowed_values = self._parse_option_values(field, params)
263
- return str(value) not in not_allowed_values
332
+ return not InRule.validate(self, field, value, params=params)
264
333
 
265
334
  def message(self, field: str, params: List[str]) -> str:
266
- not_allowed_values = self._parse_option_values(field, params)
267
- return f"The selected :attribute must be not in : {', '.join(map(str, not_allowed_values))}"
335
+ return f"The selected :attribute must be not in : :values"
336
+
337
+ def replacements(self, field, value):
338
+ replacements = {
339
+ ':attribute': self._get_display_name(field),
340
+ ':input': value,
341
+ ':values': ', '.join(self._get_display_name(self._params)),
342
+ }
343
+
344
+ return replacements
268
345
 
269
346
  class EnumRule(Rule):
347
+ _count_parameter = 1
348
+
270
349
  def __init__(self, *params):
271
350
  super().__init__()
272
351
  self._params: List[str] = list(params)
@@ -299,13 +378,21 @@ class EnumRule(Rule):
299
378
 
300
379
  def message(self, field: str, params: List[str]) -> str:
301
380
  allowed_values = self._parse_option_values(field, self.allowed_values)
302
- return f"The :attribute must be one of: {', '.join(map(str, allowed_values))}"
381
+ return f"The :attribute must be one of: :values"
382
+
383
+ def replacements(self, field, value):
384
+ replacements = {
385
+ ':attribute': self._get_display_name(field),
386
+ ':input': value,
387
+ ':values': ', '.join(map(str, self.allowed_values)),
388
+ }
389
+
390
+ return replacements
303
391
 
304
392
  class ContainsRule(Rule):
393
+ _count_parameter = 1
394
+
305
395
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
306
- if not params:
307
- return False
308
-
309
396
  for search_value in params:
310
397
  if isinstance(value, str) and search_value in value:
311
398
  return True
@@ -322,8 +409,13 @@ class ContainsRule(Rule):
322
409
  return False
323
410
 
324
411
  def message(self, field: str, params: List[str]) -> str:
325
- if len(params) == 1:
326
- return f"The {field} must contain {params[0]}"
412
+ return f"The :attribute must contain at least one of: :values"
413
+
414
+ def replacements(self, field, value):
415
+ replacements = {
416
+ ':attribute': self._get_display_name(field),
417
+ ':input': value,
418
+ ':values': ', '.join(self._params),
419
+ }
327
420
 
328
- joined_params = ", ".join(params[:-1]) + f" or {params[-1]}"
329
- return f"The {field} must contain at least one of: {joined_params}"
421
+ return replacements
validator/rules/date.py CHANGED
@@ -19,12 +19,17 @@ class DateRule(Rule):
19
19
  return f"The :attribute is not a valid date."
20
20
 
21
21
  class DateEqualsRule(Rule):
22
+ _count_parameter = 1
23
+
22
24
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
23
- if not params or not isinstance(value, str):
24
- return False
25
25
 
26
26
  try:
27
- date1 = parse(value)
27
+ if isinstance(value, str):
28
+ date1 = parse(value)
29
+ elif isinstance(value, datetime):
30
+ date1 = value
31
+ else:
32
+ return False
28
33
 
29
34
  params = list(params)
30
35
  params[0] = self.get_field_value(params[0], params[0])
@@ -35,15 +40,22 @@ class DateEqualsRule(Rule):
35
40
  return False
36
41
 
37
42
  def message(self, field: str, params: List[str]) -> str:
38
- return f"The :attribute must be equal to {params[0]}."
43
+ return f"The :attribute must be equal to :value."
44
+
45
+ def replacements(self, field, value):
46
+ replacements = {
47
+ ':attribute': self._get_display_name(field),
48
+ ':input': value,
49
+ ':value': self.get_field_value(self._params[0], self._params[0])
50
+ }
51
+
52
+ return replacements
39
53
 
40
54
  class AfterRule(Rule):
55
+ _count_parameter = 1
56
+
41
57
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
42
- if not params or len(params) < 1:
43
- return False
44
-
45
58
  try:
46
- # Parse input date
47
59
  if isinstance(value, str):
48
60
  date_value = parse(value)
49
61
  elif isinstance(value, datetime):
@@ -62,12 +74,21 @@ class AfterRule(Rule):
62
74
  return False
63
75
 
64
76
  def message(self, field: str, params: List[str]) -> str:
65
- return f"The :attribute must be after {params[0]}"
77
+ return f"The :attribute must be after :value"
78
+
79
+ def replacements(self, field, value):
80
+ replacements = {
81
+ ':attribute': self._get_display_name(field),
82
+ ':input': value,
83
+ ':value': self.get_field_value(self._params[0], self._params[0])
84
+ }
85
+
86
+ return replacements
66
87
 
67
88
  class AfterOrEqualRule(Rule):
89
+ _count_parameter = 1
90
+
68
91
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
69
- if not params or len(params) < 1:
70
- return False
71
92
  try:
72
93
  if isinstance(value, str):
73
94
  date_value = parse(value)
@@ -86,13 +107,21 @@ class AfterOrEqualRule(Rule):
86
107
  return False
87
108
 
88
109
  def message(self, field: str, params: List[str]) -> str:
89
- return f"The :attribute must be after or equal to {params[0]}"
110
+ return f"The :attribute must be after or equal to :value"
111
+
112
+ def replacements(self, field, value):
113
+ replacements = {
114
+ ':attribute': self._get_display_name(field),
115
+ ':input': value,
116
+ ':value': self.get_field_value(self._params[0], self._params[0])
117
+ }
118
+
119
+ return replacements
90
120
 
91
121
  class BeforeRule(Rule):
122
+ _count_parameter = 1
123
+
92
124
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
93
- if not params or len(params) < 1:
94
- return False
95
-
96
125
  value = self.get_field_value(value, value)
97
126
 
98
127
  try:
@@ -104,7 +133,7 @@ class BeforeRule(Rule):
104
133
  return False
105
134
 
106
135
  params = list(params)
107
- params[0] = self.get_field_value(params[0], params[0])
136
+ params[0] = self.get_field_value(params[0], params[0])
108
137
  compare_date = parse(params[0])
109
138
 
110
139
  return date_value < compare_date
@@ -113,13 +142,21 @@ class BeforeRule(Rule):
113
142
  return False
114
143
 
115
144
  def message(self, field: str, params: List[str]) -> str:
116
- return f"The :attribute must be before {params[0]}"
145
+ return f"The :attribute must be before :value"
146
+
147
+ def replacements(self, field, value):
148
+ replacements = {
149
+ ':attribute': self._get_display_name(field),
150
+ ':input': value,
151
+ ':value': self.get_field_value(self._params[0], self._params[0])
152
+ }
153
+
154
+ return replacements
117
155
 
118
156
  class BeforeOrEqualRule(Rule):
157
+ _count_parameter = 1
158
+
119
159
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
120
- if not params or len(params) < 1:
121
- return False
122
-
123
160
  value = self.get_field_value(value, value)
124
161
 
125
162
  try:
@@ -140,13 +177,21 @@ class BeforeOrEqualRule(Rule):
140
177
  return False
141
178
 
142
179
  def message(self, field: str, params: List[str]) -> str:
143
- return f"The :attribute must be before or equal to {params[0]}"
180
+ return f"The :attribute must be before or equal to :value"
181
+
182
+ def replacements(self, field, value):
183
+ replacements = {
184
+ ':attribute': self._get_display_name(field),
185
+ ':input': value,
186
+ ':value': self.get_field_value(self._params[0], self._params[0])
187
+ }
188
+
189
+ return replacements
144
190
 
145
191
  class DateFormatRule(Rule):
192
+ _count_parameter = 1
193
+
146
194
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
147
- if not params or len(params) < 1 or not isinstance(value, str):
148
- return False
149
-
150
195
  try:
151
196
  datetime.strptime(value, params[0])
152
197
  return True
@@ -154,12 +199,19 @@ class DateFormatRule(Rule):
154
199
  return False
155
200
 
156
201
  def message(self, field: str, params: List[str]) -> str:
157
- return f"The :attribute must match the format {params[0]}"
202
+ return f"The :attribute must match the format :format"
203
+
204
+ def replacements(self, field, value):
205
+ replacements = {
206
+ ':attribute': self._get_display_name(field),
207
+ ':input': value,
208
+ ':format': self.get_field_value(self._params[0], self._params[0])
209
+ }
210
+
211
+ return replacements
158
212
 
159
213
  class TimezoneRule(Rule):
160
214
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
161
- if not isinstance(value, str):
162
- return False
163
215
  try:
164
216
  zoneinfo.ZoneInfo(value)
165
217
  return True
validator/rules/files.py CHANGED
@@ -34,7 +34,7 @@ class FileRule(Rule):
34
34
  class DimensionsRule(Rule):
35
35
  def __init__(self, *params):
36
36
  super().__init__(*params)
37
-
37
+ self._count_parameter = 1
38
38
  self._constraints = {
39
39
  'min_width': 0,
40
40
  'min_height': 0,
@@ -44,7 +44,7 @@ class DimensionsRule(Rule):
44
44
  'height': None,
45
45
  'ratio': None
46
46
  }
47
-
47
+
48
48
  def maxWidth(self, value: int) -> 'DimensionsRule':
49
49
  self._constraints['width'] = None
50
50
  self._constraints['max_width'] = value
@@ -195,11 +195,25 @@ class DimensionsRule(Rule):
195
195
 
196
196
  return f"The :attribute image must satisfy: {', '.join(constraints)}"
197
197
 
198
+ def replacements(self, field, value):
199
+ replacements = {
200
+ ':attribute': self._get_display_name(field),
201
+ ':input': value,
202
+ ':width': self._constraints['width'],
203
+ ':min_width': self._constraints['min_width'],
204
+ ':max_width': self._constraints['max_width'],
205
+ ':height': self._constraints['height'],
206
+ ':min_height': self._constraints['min_height'],
207
+ ':max_height': self._constraints['max_height'],
208
+ ':ratio': self._constraints['ratio'],
209
+ }
210
+
211
+ return replacements
212
+
198
213
  class ExtensionsRule(Rule):
214
+ _count_parameter = 1
215
+
199
216
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
200
- if not params:
201
- return False
202
-
203
217
  filename = (
204
218
  value.filename if hasattr(value, 'filename')
205
219
  else str(value)
@@ -212,7 +226,16 @@ class ExtensionsRule(Rule):
212
226
  return ext in [e.lower().strip() for e in params]
213
227
 
214
228
  def message(self, field: str, params: List[str]) -> str:
215
- return f"File :attribute must have one of these extensions: {', '.join(params)}"
229
+ return f"File :attribute must have one of these extensions: :values"
230
+
231
+ def replacements(self, field, value):
232
+ replacements = {
233
+ ':attribute': self._get_display_name(field),
234
+ ':input': value,
235
+ ':values': ', '.join(self._params)
236
+ }
237
+
238
+ return replacements
216
239
 
217
240
  class ImageRule(Rule):
218
241
  VALID_EXTENSIONS = {'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'}
@@ -243,10 +266,9 @@ class ImageRule(Rule):
243
266
  return f"The :attribute must be a valid image file (JPEG, PNG, GIF, BMP, or WebP)"
244
267
 
245
268
  class MimeTypesRule(Rule):
269
+ _count_parameter = 1
270
+
246
271
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
247
- if not params:
248
- return False
249
-
250
272
  # Check from content_type attribute
251
273
  if hasattr(value, 'content_type'):
252
274
  return value.content_type in params
@@ -258,10 +280,20 @@ class MimeTypesRule(Rule):
258
280
  return False
259
281
 
260
282
  def message(self, field: str, params: List[str]) -> str:
261
- return f"File :attribute must be one of these types: {', '.join(params)}"
283
+ return f"File :attribute must be one of these types: :values"
284
+
285
+ def replacements(self, field, value):
286
+ replacements = {
287
+ ':attribute': self._get_display_name(field),
288
+ ':input': value,
289
+ ':values': ', '.join(self._params)
290
+ }
291
+
292
+ return replacements
262
293
 
263
294
  class MimeTypeByExtensionRule(Rule):
264
295
  _name = 'mimes'
296
+ _count_parameter = 1
265
297
 
266
298
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
267
299
  if value is None or not hasattr(value, 'filename'):
@@ -276,4 +308,13 @@ class MimeTypeByExtensionRule(Rule):
276
308
  return mime_type in params if mime_type else False
277
309
 
278
310
  def message(self, field: str, params: List[str]) -> str:
279
- return f"The :attribute must be one of these types: {', '.join(params)}"
311
+ return f"The :attribute must be one of these types: :values"
312
+
313
+ def replacements(self, field, value):
314
+ replacements = {
315
+ ':attribute': self._get_display_name(field),
316
+ ':input': value,
317
+ ':values': ', '.join(self._params)
318
+ }
319
+
320
+ return replacements
validator/rules/format.py CHANGED
@@ -122,6 +122,15 @@ class EmailRule(Rule):
122
122
  base_msg += " (Unicode allowed)"
123
123
 
124
124
  return f"{base_msg}."
125
+
126
+ def replacements(self, field, value):
127
+ replacements = {
128
+ ':attribute': self._get_display_name(field),
129
+ ':input': value,
130
+ ':values': ', '.join(self._params if self._params else ['rfc']),
131
+ }
132
+
133
+ return replacements
125
134
 
126
135
  class UrlRule(Rule):
127
136
  def validate(self, field: str, value: Any, params: List[str]) -> bool: