@@ -49,42 +49,48 @@ def isOperatorObject(obj: Any) -> bool:
4949 return False
5050 return True
5151
52- def getType ( attributeValue ) -> str :
53- t = type ( attributeValue )
52+ def _is_numeric ( v : Any ) -> bool :
53+ return isinstance ( v , ( int , float )) and not isinstance ( v , bool )
5454
55+ def getType (attributeValue ) -> str :
5556 if attributeValue is None :
5657 return "null"
57- if t is int or t is float :
58+ if isinstance (attributeValue , bool ):
59+ return "boolean"
60+ if _is_numeric (attributeValue ):
5861 return "number"
59- if t is str :
62+ if isinstance ( attributeValue , str ) :
6063 return "string"
61- if t is list or t is set :
64+ if isinstance ( attributeValue , ( list , set )) :
6265 return "array"
63- if t is dict :
66+ if isinstance ( attributeValue , dict ) :
6467 return "object"
65- if t is bool :
66- return "boolean"
6768 return "unknown"
6869
6970def getPath (attributes , path ):
7071 current = attributes
7172 for segment in path .split ("." ):
72- if type (current ) is dict and segment in current :
73+ if isinstance (current , dict ) and segment in current :
7374 current = current [segment ]
7475 else :
7576 return None
7677 return current
7778
78- def evalConditionValue (conditionValue , attributeValue , savedGroups ) -> bool :
79- if type (conditionValue ) is dict and isOperatorObject (conditionValue ):
79+ def evalConditionValue (conditionValue , attributeValue , savedGroups , insensitive : bool = False ) -> bool :
80+ if isinstance (conditionValue , dict ) and isOperatorObject (conditionValue ):
8081 for key , value in conditionValue .items ():
8182 if not evalOperatorCondition (key , attributeValue , value , savedGroups ):
8283 return False
8384 return True
85+
86+ # Simple equality comparison with optional case-insensitivity
87+ if insensitive and isinstance (conditionValue , str ) and isinstance (attributeValue , str ):
88+ return conditionValue .lower () == attributeValue .lower ()
89+
8490 return bool (conditionValue == attributeValue )
8591
8692def elemMatch (condition , attributeValue , savedGroups ) -> bool :
87- if not type (attributeValue ) is list :
93+ if not isinstance (attributeValue , list ) :
8894 return False
8995
9096 for item in attributeValue :
@@ -98,13 +104,13 @@ def elemMatch(condition, attributeValue, savedGroups) -> bool:
98104 return False
99105
100106def compare (val1 , val2 ) -> int :
101- if ( type ( val1 ) is int or type ( val1 ) is float ) and not ( type ( val2 ) is int or type ( val2 ) is float ):
107+ if _is_numeric ( val1 ) and not _is_numeric ( val2 ):
102108 if (val2 is None ):
103109 val2 = 0
104110 else :
105111 val2 = float (val2 )
106112
107- if ( type ( val2 ) is int or type ( val2 ) is float ) and not ( type ( val1 ) is int or type ( val1 ) is float ):
113+ if _is_numeric ( val2 ) and not _is_numeric ( val1 ):
108114 if (val1 is None ):
109115 val1 = 0
110116 else :
@@ -160,13 +166,13 @@ def evalOperatorCondition(operator, attributeValue, conditionValue, savedGroups)
160166 elif operator == "$vgte" :
161167 return paddedVersionString (attributeValue ) >= paddedVersionString (conditionValue )
162168 elif operator == "$inGroup" :
163- if not type (conditionValue ) is str :
169+ if not isinstance (conditionValue , str ) :
164170 return False
165171 if not conditionValue in savedGroups :
166172 return False
167173 return isIn (savedGroups [conditionValue ] or [], attributeValue )
168174 elif operator == "$notInGroup" :
169- if not type (conditionValue ) is str :
175+ if not isinstance (conditionValue , str ) :
170176 return False
171177 if not conditionValue in savedGroups :
172178 return True
@@ -177,31 +183,54 @@ def evalOperatorCondition(operator, attributeValue, conditionValue, savedGroups)
177183 return bool (r .search (attributeValue ))
178184 except Exception :
179185 return False
186+ elif operator == "$regexi" :
187+ try :
188+ r = re .compile (conditionValue , re .IGNORECASE )
189+ return bool (r .search (attributeValue ))
190+ except Exception :
191+ return False
192+ elif operator == "$notRegex" :
193+ try :
194+ r = re .compile (conditionValue )
195+ return not bool (r .search (attributeValue ))
196+ except Exception :
197+ return False
198+ elif operator == "$notRegexi" :
199+ try :
200+ r = re .compile (conditionValue , re .IGNORECASE )
201+ return not bool (r .search (attributeValue ))
202+ except Exception :
203+ return False
180204 elif operator == "$in" :
181- if not type (conditionValue ) is list :
205+ if not isinstance (conditionValue , list ) :
182206 return False
183207 return isIn (conditionValue , attributeValue )
184208 elif operator == "$nin" :
185- if not type (conditionValue ) is list :
209+ if not isinstance (conditionValue , list ) :
186210 return False
187211 return not isIn (conditionValue , attributeValue )
212+ elif operator == "$ini" :
213+ if not isinstance (conditionValue , list ):
214+ return False
215+ return isIn (conditionValue , attributeValue , insensitive = True )
216+ elif operator == "$nini" :
217+ if not isinstance (conditionValue , list ):
218+ return False
219+ return not isIn (conditionValue , attributeValue , insensitive = True )
188220 elif operator == "$elemMatch" :
189221 return elemMatch (conditionValue , attributeValue , savedGroups )
190222 elif operator == "$size" :
191- if not ( type ( attributeValue ) is list ):
223+ if not isinstance ( attributeValue , list ):
192224 return False
193225 return evalConditionValue (conditionValue , len (attributeValue ), savedGroups )
194226 elif operator == "$all" :
195- if not ( type ( attributeValue ) is list ):
227+ if not isinstance ( conditionValue , list ):
196228 return False
197- for cond in conditionValue :
198- passing = False
199- for attr in attributeValue :
200- if evalConditionValue (cond , attr , savedGroups ):
201- passing = True
202- if not passing :
203- return False
204- return True
229+ return isInAll (conditionValue , attributeValue , savedGroups , insensitive = False )
230+ elif operator == "$alli" :
231+ if not isinstance (conditionValue , list ):
232+ return False
233+ return isInAll (conditionValue , attributeValue , savedGroups , insensitive = True )
205234 elif operator == "$exists" :
206235 if not conditionValue :
207236 return attributeValue is None
@@ -214,10 +243,10 @@ def evalOperatorCondition(operator, attributeValue, conditionValue, savedGroups)
214243
215244def paddedVersionString (input ) -> str :
216245 # If input is a number, convert to a string
217- if type (input ) is int or type ( input ) is float :
246+ if _is_numeric (input ):
218247 input = str (input )
219248
220- if not input or type (input ) is not str :
249+ if not input or not isinstance (input , str ) :
221250 input = "0"
222251
223252 # Remove build info and leading `v` if any
@@ -235,11 +264,41 @@ def paddedVersionString(input) -> str:
235264 return "-" .join ([v .rjust (5 , " " ) if re .match (r"^[0-9]+$" , v ) else v for v in parts ])
236265
237266
238- def isIn (conditionValue , attributeValue ) -> bool :
239- if type (attributeValue ) is list :
267+ def isIn (conditionValue , attributeValue , insensitive : bool = False ) -> bool :
268+ if insensitive :
269+ # Helper function to case-fold values (lowercase for strings)
270+ def case_fold (val ):
271+ return val .lower () if isinstance (val , str ) else val
272+
273+ # Do an intersection if attribute is an array (insensitive)
274+ if isinstance (attributeValue , list ):
275+ return any (
276+ case_fold (el ) == case_fold (exp )
277+ for el in attributeValue
278+ for exp in conditionValue
279+ )
280+ return any (case_fold (attributeValue ) == case_fold (exp ) for exp in conditionValue )
281+
282+ # Case-sensitive behavior (original)
283+ if isinstance (attributeValue , list ):
240284 return bool (set (conditionValue ) & set (attributeValue ))
241285 return attributeValue in conditionValue
242286
287+ def isInAll (conditionValue , attributeValue , savedGroups , insensitive : bool = False ) -> bool :
288+ """Check if attributeValue (array) contains all elements in conditionValue"""
289+ if not isinstance (attributeValue , list ):
290+ return False
291+
292+ for cond in conditionValue :
293+ passing = False
294+ for attr in attributeValue :
295+ if evalConditionValue (cond , attr , savedGroups , insensitive ):
296+ passing = True
297+ break
298+ if not passing :
299+ return False
300+ return True
301+
243302def _getOrigHashValue (
244303 eval_context : EvaluationContext ,
245304 attr : Optional [str ] = "id" ,
0 commit comments