safeshield 1.4.3__py3-none-any.whl → 1.4.5__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.

Potentially problematic release.


This version of safeshield might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: safeshield
3
- Version: 1.4.3
3
+ Version: 1.4.5
4
4
  Summary: Library for Help Validation Control
5
5
  Home-page: https://github.com/WunsunTarniho/py-guard
6
6
  Author: Wunsun Tarniho
@@ -77,3 +77,9 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
77
77
 
78
78
  ## v1.4.3 (2024-07-20)
79
79
  - Fixed: Error when validation failed
80
+
81
+ ## v1.4.3 (2024-07-20)
82
+ - Fixed: Sometimes rule failed
83
+
84
+ ## v1.4.4 (2024-07-20)
85
+ - Fixed: Sometimes rule failed
@@ -2,14 +2,14 @@ validator/__init__.py,sha256=udxDzUicPfxBOAQvzsnl3pHur9VUppKbWMgg35hpiww,244
2
2
  validator/exceptions.py,sha256=y2v7CaXmeGFHWcnigtLl4U-sFta_jMiXkGKXWIIVglY,366
3
3
  validator/factory.py,sha256=JruOF0Mumk5PPqendNsO5TN4ZmPEGBNS7Nivly4_oUc,772
4
4
  validator/core/__init__.py,sha256=ZcqlXJSk03i_CVzmIN-nVe1UOyvwwO5jhbEj7f62Y_o,59
5
- validator/core/validator.py,sha256=pbfbTu6OvJiqyNu58OnIZI97a8x2r9T25IrWkBMJKTs,12684
5
+ validator/core/validator.py,sha256=la-kzp82iCii2bq4hV76F-7datWPb27vmyHvpg2itR0,12726
6
6
  validator/database/__init__.py,sha256=O-cB6-MhNapJ3iwe5jvifbMfr1dPjXLtEdfNTKIu0hc,171
7
7
  validator/database/detector.py,sha256=Vac7oVL26GjU6expGo01-6mgUtXqldr-jirzpYokZBM,9597
8
8
  validator/database/manager.py,sha256=XJM_I0WaWfZWV710duAc32p1gtiP2or-MAj75WPw1oM,6478
9
9
  validator/rules/__init__.py,sha256=z3Vk3R5CRgjeqyDWZxdofD2tBMTgdyYVuFmo1mKOTj4,830
10
10
  validator/rules/array.py,sha256=AqVoBR_chSqxPec14Av5KmR9NAByovXDNNu2oeId4-U,2528
11
- validator/rules/base.py,sha256=PadT5Ko3Zs_yr5EPxOfTw-IdS7uJKhGpaekF1k4tvYk,4359
12
- validator/rules/basic.py,sha256=GFdffZKW3Dzw7EOeZTyF1Oma59ZMo9zNK4gddkUMMc4,4892
11
+ validator/rules/base.py,sha256=LWOSigqnn65wges6HjpHEpyoYyDJhbBSabm96t8CXEs,4388
12
+ validator/rules/basic.py,sha256=LWYs4fPCA6_lTtOmtTgvjGqjVxxSdklj9fsCgSAMy2Y,4918
13
13
  validator/rules/boolean.py,sha256=vy6huFJ5JidpsoJ0WSvcydiU7a8aYFj225UswggSGAE,5748
14
14
  validator/rules/comparison.py,sha256=xkQ0xoqBjVlDSfgwTvruOAnXUmKlNt9kDjTcG5r4cFM,13473
15
15
  validator/rules/date.py,sha256=yolYaTIvQTN1LBje5SM8i7EmNzOxV_eUwnOokmqZrMs,5812
@@ -19,13 +19,13 @@ validator/rules/numeric.py,sha256=nkYVc8VtrWJ3Kt7JDLPsbg7ZaN3F0zJMnbf8Y5gxNsk,68
19
19
  validator/rules/string.py,sha256=p8ZQfd0XaWIjksg_8ta3f6PEnXlxjRzlSJx1GohZ7yk,5237
20
20
  validator/rules/utilities.py,sha256=ns52WbIMTt6Gs85y9kwbTfBjhmX4cfNPwA5Uw6mTaT0,11924
21
21
  validator/services/__init__.py,sha256=zzKTmqL7v4niFGWHJBfWLqgJ0iTaW_69OzYZN8uInzQ,210
22
- validator/services/rule_conflict.py,sha256=JVOgZWIGOviTfcCC69sQ1Bq8nzwtW0UFOx-bbF35uTM,4923
23
- validator/services/rule_error_handler.py,sha256=0GoBZp12efTY1atu0MKpCXAyDWm_8DYSDrcqfjB6LM8,10415
22
+ validator/services/rule_conflict.py,sha256=T3IhWLmZsRUccJ4oFO-OKRjrc5Xt7r71kktxjjj2IA8,9505
23
+ validator/services/rule_error_handler.py,sha256=K6vq-pdVd5e4Xw7cpTguORy3VPWUJQa3PoeZZcDwTY8,10643
24
24
  validator/services/rule_preparer.py,sha256=4khRjdely_0Z5mxFwf1bKIid076_xDuNh2XBO_fGerE,4706
25
25
  validator/utils/__init__.py,sha256=Yzo-xv285Be-a233M4duDdYtscuHiuBbPSX_C8yViJI,20
26
26
  validator/utils/string.py,sha256=0YACzeEaWNEOR9_7O9A8D1ItIbtWfOJ8IfrzcB8VMYA,515
27
- safeshield-1.4.3.dist-info/LICENSE,sha256=qugtRyKckyaks6hd2xyxOFSOYM6au1N80pMXuMTPvC4,1090
28
- safeshield-1.4.3.dist-info/METADATA,sha256=F0LL5L_zO7VDQfciVKDMKX7ka_D1RBOp5UyjfDsukNY,2197
29
- safeshield-1.4.3.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
30
- safeshield-1.4.3.dist-info/top_level.txt,sha256=iUtV3dlHOIiMfLuY4pruY00lFni8JzOkQ3Nh1II19OE,10
31
- safeshield-1.4.3.dist-info/RECORD,,
27
+ safeshield-1.4.5.dist-info/LICENSE,sha256=qugtRyKckyaks6hd2xyxOFSOYM6au1N80pMXuMTPvC4,1090
28
+ safeshield-1.4.5.dist-info/METADATA,sha256=57CDuVbaonpjdkkMIPsptAWIxn5_TKP7zCWzJM8bBiU,2313
29
+ safeshield-1.4.5.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
30
+ safeshield-1.4.5.dist-info/top_level.txt,sha256=iUtV3dlHOIiMfLuY4pruY00lFni8JzOkQ3Nh1II19OE,10
31
+ safeshield-1.4.5.dist-info/RECORD,,
@@ -12,7 +12,7 @@ from collections.abc import Mapping
12
12
  class Validator:
13
13
  """Main validation class with proper abstractions"""
14
14
  PRIORITY_RULES = {
15
- 'bail', 'sometimes', 'exclude_unless', 'exclude_if', 'exclude_with', 'exclude_without', 'required', 'required_if', 'required_unless',
15
+ 'bail', 'sometimes', 'exclude', 'exclude_unless', 'exclude_if', 'exclude_with', 'exclude_without', 'required', 'required_if', 'required_unless',
16
16
  'required_with', 'required_without'
17
17
  }
18
18
 
@@ -61,7 +61,8 @@ class Validator:
61
61
  self.data = data or {}
62
62
  self._raw_rules = rules or {}
63
63
  self._stop_on_first_failure = False
64
- self._is_exclude = False
64
+ self._field_to_exclude = []
65
+
65
66
 
66
67
  # Initialize dependencies
67
68
  self.rule_preparer = RulePreparer(RuleFactory())
@@ -119,7 +120,7 @@ class Validator:
119
120
  )
120
121
 
121
122
  for rule in rules:
122
- if priority_only == (rule.rule_name in self.PRIORITY_RULES) and not self._is_exclude:
123
+ if priority_only == (rule.rule_name in self.PRIORITY_RULES):
123
124
  rule.set_field_exists(field_exists)
124
125
 
125
126
  # Skip validation for empty array with wildcard unless it's a required rule
@@ -127,10 +128,10 @@ class Validator:
127
128
  continue
128
129
 
129
130
  for value in values_to_validate:
130
- validated.append(self._apply_rule(field_pattern, value, rule))
131
+ if field_pattern not in self._field_to_exclude:
132
+ validated.append(self._apply_rule(field_pattern, value, rule))
131
133
 
132
134
  delattr(self, '_current_actual_path')
133
- self._is_exclude = False
134
135
 
135
136
  return all(valid for valid in validated)
136
137
 
validator/rules/base.py CHANGED
@@ -16,6 +16,7 @@ class Rule(ABC):
16
16
 
17
17
  def __init_subclass__(cls, name=None):
18
18
  cls.rule_name = cls.pascal_to_snake(cls.__name__) if not hasattr(cls, '_name') else cls._name
19
+ print(cls.__name__)
19
20
  cls._register_rule_class()
20
21
  cls._generate_rule_methods()
21
22
 
validator/rules/basic.py CHANGED
@@ -119,7 +119,7 @@ class ProhibitsRule(Rule):
119
119
  class SometimesRule(Rule):
120
120
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
121
121
  if value is None:
122
- self.validator._is_exclude = True
122
+ self.validator._field_to_exclude.append(field)
123
123
 
124
124
  return True
125
125
 
@@ -128,7 +128,7 @@ class SometimesRule(Rule):
128
128
 
129
129
  class ExcludeRule(Rule):
130
130
  def validate(self, field: str, value: Any, params: List[str]) -> bool:
131
- self.validator._is_exclude = True
131
+ self.validator._field_to_exclude.append(field)
132
132
 
133
133
  return True
134
134
 
@@ -4,70 +4,154 @@ import warnings
4
4
  class RuleConflictChecker:
5
5
  """Class untuk mendeteksi dan menangani konflik antar validation rules."""
6
6
 
7
- # Daftar konflik kritis yang akan memunculkan exception
8
7
  CRITICAL_CONFLICTS = [
9
- # Mutually exclusive presence
10
- ('required', 'nullable'),
11
- ('required', 'sometimes'),
12
- ('filled', 'prohibited_if'),
8
+ ('required', 'nullable'), # Cannot be both mandatory and optional
9
+ ('required', 'sometimes'), # Sometimes overrides required
10
+ ('filled', 'prohibited'), # Cannot require and prohibit simultaneously
11
+ ('present', 'missing'), # Field can't be both present and missing
12
+ ('accepted', 'declined'), # Values must be opposite
13
+ ('same', 'different'), # Direct logical opposites
13
14
 
14
- # Type conflicts
15
- ('numeric', 'email'),
15
+ ('required', 'nullable'), # Cannot be both mandatory and optional
16
+ ('required', 'sometimes'), # Sometimes overrides required
17
+ ('filled', 'prohibited'), # Cannot require and prohibit simultaneously
18
+ ('present', 'missing'), # Field can't be both present and missing
19
+ ('accepted', 'declined'), # Values must be opposite
20
+ ('same', 'different'), # Direct logical opposites
21
+
22
+ # Numeric vs Other Types
16
23
  ('numeric', 'array'),
17
- ('boolean', 'integer'),
18
- ('boolean', 'numeric'),
19
- ('array', 'string'),
24
+ ('numeric', 'boolean'),
25
+ ('numeric', 'email'),
26
+ ('numeric', 'date'),
27
+
28
+ # String vs Other Types
29
+ ('string', 'array'),
30
+ ('string', 'file'),
31
+ ('string', 'json'),
20
32
 
21
- # Format conflicts
33
+ # Special Types
34
+ ('boolean', 'integer'),
35
+ ('file', 'image'), # All images are files but not vice versa
36
+ ('uuid', 'ulid'), # Similar but incompatible formats
37
+ ('hex', 'alpha_num'),
38
+
39
+ # Email/URL/IP Conflicts
22
40
  ('email', 'ip'),
23
- ('uuid', 'ulid'),
24
- ('json', 'timezone'),
41
+ ('email', 'url'),
42
+ ('url', 'json'),
43
+
44
+ # Date/Time Conflicts
45
+ ('date', 'timezone'),
46
+ ('date_format', 'date_equals'),
47
+ ('after', 'before'), # Illogical date ranges
48
+
49
+ # Special Formats
50
+ ('regex', 'not_regex'), # Direct pattern negation
51
+ ('ascii', 'alpha_dash'), # ASCII vs extended charset
52
+ ('uppercase', 'lowercase'), # Case transformation conflicts
53
+
54
+ # Direct Value Checks
55
+ ('in', 'not_in'),
56
+ ('starts_with', 'doesnt_start_with'),
57
+ ('ends_with', 'doesnt_end_with'),
58
+
59
+ # Range Conflicts
60
+ ('between', 'digits_between'),
61
+ ('min', 'gt'),
62
+ ('max', 'lt'),
63
+ ('size', 'max_digits'),
64
+
65
+ # Numeric Constraints
66
+ ('multiple_of', 'digits_between'),
67
+ ('integer', 'decimal'),
68
+
69
+ # Required Group
70
+ ('required_if', 'exclude_if'),
71
+ ('required_unless', 'exclude_unless'),
72
+
73
+ # Presence Group
74
+ ('present_if', 'missing_if'),
75
+ ('present_unless', 'missing_unless'),
76
+
77
+ # Acceptance Group
78
+ ('accepted_if', 'declined_if'),
79
+ ('accepted_unless', 'declined_unless'),
25
80
 
26
- # Value requirement conflicts
27
- ('accepted', 'declined'),
28
- ('same', 'different')
81
+ # Prohibited Group
82
+ ('prohibited_if', 'required_if'),
83
+ ('prohibited_unless', 'required_unless'),
84
+
85
+ ('file', 'dimensions'), # Dimensions only for images
86
+ ('image', 'mime_types'),
87
+ ('extensions', 'mimes'),
88
+ ('file', 'json'), # Can't be both file and JSON
89
+
90
+ ('array', 'distinct'), # Distinct requires array
91
+ ('in_array', 'contains'), # Similar containment checks
92
+ ('array_keys', 'in_array'),
29
93
  ]
30
94
 
31
- # Daftar konflik fungsional yang hanya memunculkan warning
32
95
  WARNING_CONFLICTS = [
33
- # Overlapping validation
34
- ('between', 'digits_between'),
35
- ('min', 'size'),
36
- ('max', 'size'),
37
- ('confirmed', 'same'),
38
- ('in', 'not_in'),
96
+ ('integer', 'numeric'), # integer implies numeric check
97
+ ('digits', 'digits_between'), # digits:5 is same as digits_between:5,5
98
+ ('decimal', 'numeric'), # decimal is subset of numeric
99
+ ('multiple_of', 'numeric'), # multiple_of requires numeric
100
+ ('min', 'gt'), # similar lower-bound checks
101
+ ('max', 'lt'), # similar upper-bound checks
102
+ ('digits_between', 'between'), # overlapping digit vs value ranges
39
103
 
40
- # Redundant type checks
41
- ('integer', 'numeric'),
42
- ('alpha_num', 'alpha_dash'),
43
- ('starts_with', 'ends_with'),
104
+ ('alpha', 'alpha_num'), # alpha_num includes alpha
105
+ ('alpha_dash', 'alpha_num'), # alpha_dash extends alpha_num
106
+ ('ascii', 'alpha'), # alpha implies ASCII
107
+ ('uppercase', 'lowercase'), # mutually exclusive cases
108
+ ('starts_with', 'ends_with'), # potentially redundant patterns
109
+ ('regex', 'ascii'), # regex might duplicate ASCII check
110
+ ('contains', 'in_array'), # similar containment checks
44
111
 
45
- # Similar format checks
46
- ('url', 'json'),
47
- ('ulid', 'uuid')
112
+ ('after', 'after_or_equal'), # _or_equal is more inclusive
113
+ ('before', 'before_or_equal'), # _or_equal is more inclusive
114
+ ('date_format', 'date'), # date implies format validation
115
+ ('timezone', 'date_format'), # timezone implies format
116
+ ('date_equals', 'after_or_equal'), # potential overlap
117
+
118
+ ('file', 'image'), # image implies file check
119
+ ('mime_types', 'mime_type_by_extension'), # similar type checks
120
+ ('dimensions', 'image'), # dimensions requires image
121
+
122
+ ('array', 'distinct'), # distinct requires array
123
+ ('in_array', 'contains'), # similar element checks
124
+ ('array_keys', 'in_array'), # similar key existence checks
125
+
126
+ ('required_if', 'required_with'), # similar conditional logic
127
+ ('prohibited_if', 'prohibited_unless'), # inverse conditions
128
+ ('present_with', 'missing_with'), # mutually aware rules
129
+
130
+ ('string', 'alpha'), # string conversion implied
131
+ ('numeric', 'integer'), # numeric conversion implied
132
+ ('boolean', 'accepted'), # boolean conversion implied
48
133
  ]
49
134
 
50
- # Grup rules yang saling terkait
51
135
  REQUIRED_GROUPS = {
52
- 'required_with', 'required_with_all',
53
- 'required_without', 'required_without_all'
136
+ 'required_if', 'required_unless', 'required_with', 'required_with_all',
137
+ 'required_without', 'required_without_all', 'required_if_accepted', 'required_if_declined'
54
138
  }
55
-
139
+
56
140
  PROHIBITED_GROUPS = {
57
- 'prohibited_if', 'prohibited_unless'
141
+ 'prohibited_if', 'prohibited_unless', 'prohibited_if_accepted', 'prohibited_if_declined'
142
+ }
143
+
144
+ EXCLUSION_GROUPS = {
145
+ 'exclude_if', 'exclude_unless', 'exclude_with', 'exclude_without'
146
+ }
147
+
148
+ PRESENCE_GROUPS = {
149
+ 'present_if', 'present_unless', 'present_with', 'present_with_all',
150
+ 'missing_if', 'missing_unless', 'missing_with', 'missing_with_all'
58
151
  }
59
152
 
60
153
  @classmethod
61
154
  def check_conflicts(cls, rules: List['Rule']) -> None:
62
- """Main method untuk mengecek semua jenis konflik.
63
-
64
- Args:
65
- rules: List of Rule objects to check
66
-
67
- Raises:
68
- ValueError: Untuk konflik kritis
69
- UserWarning: Untuk konflik fungsional/potensial
70
- """
71
155
  rule_names = {r.rule_name for r in rules}
72
156
  params_map = {r.rule_name: r.params for r in rules}
73
157
 
@@ -162,12 +162,15 @@ class RuleErrorHandler:
162
162
  # Rules with field-value pairs (field1,value1,field2,value2,...)
163
163
  if rule in {
164
164
  'required_if', 'required_unless', 'exclude_if', 'exclude_unless',
165
+ 'accepted_if', 'accepted_all_if', 'declined_if', 'declined_all_if',
166
+ 'accepted_unless', 'declined_unless',
165
167
  'missing_if', 'missing_unless', 'present_if', 'present_unless'
166
168
  }:
167
169
  return self._current_params[::2] # Take every even index
168
170
 
169
- # Rules with just field references
171
+ # Rules with multi field references
170
172
  if rule in {
173
+ 'required_with', 'required_with_all', 'required_without', 'required_without_all',
171
174
  'prohibits', 'exclude_with', 'exclude_without',
172
175
  'missing_with', 'missing_with_all',
173
176
  'present_with', 'present_with_all'