33from typing import Any
44from typing import Generic
55from typing import TypeVar
6- from unicodedata import normalize
76
87from ..base import BaseModel
98from .expressions import AttrPath
@@ -102,29 +101,15 @@ def _is_present(value: Any) -> bool:
102101 return True
103102
104103
105- def _comparable (value : Any , case_exact : bool ) -> Any :
106- """Reduce a value to the form comparisons are performed on."""
107- if not isinstance (value , str ):
108- return value
109-
110- normalized = normalize ("NFC" , value )
111- if case_exact :
112- return normalized
113-
114- # Case folding does not preserve the normalization form, so NFC is applied
115- # to its result too, and every operand comes out in the same form.
116- return normalize ("NFC" , normalized .casefold ())
117-
118-
119104def _compare (
120- actual : Any , expected : Any , op : CompareOperator , * , case_exact : bool = False
105+ actual : Any , expected : Any , op : CompareOperator , resolved : AttributeBinding
121106) -> bool :
122107 """Apply a comparison operator to a single pair of values.
123108
124109 Values of incomparable types never match, rather than raising, so that a
125110 filter over a heterogeneous collection stays usable. ``actual`` is read
126111 from the resource, ``expected`` is what the filter compares it against, and
127- ``case_exact `` says whether strings compare case-sensitively .
112+ ``resolved `` is the attribute, which gives the form both are compared under .
128113 """
129114 if actual is None or expected is None :
130115 if op == CompareOperator .eq :
@@ -133,8 +118,8 @@ def _compare(
133118 return (actual is None ) != (expected is None )
134119 return False
135120
136- left = _comparable (actual , case_exact )
137- right = _comparable (expected , case_exact )
121+ left = resolved . comparable (actual )
122+ right = resolved . comparable (expected )
138123
139124 if op in (CompareOperator .co , CompareOperator .sw , CompareOperator .ew ):
140125 if not isinstance (left , str ) or not isinstance (right , str ):
@@ -230,21 +215,15 @@ def visit_comparison(self, node: Comparison) -> bool:
230215 actual = self ._read (resolved )
231216
232217 if not isinstance (actual , list ):
233- return _compare (actual , expected , node .op , case_exact = resolved . case_exact )
218+ return _compare (actual , expected , node .op , resolved )
234219
235220 # A filter on a multi-valued attribute matches if any of its values
236221 # matches. The RFC does not say what that means for "ne", so the
237222 # universal reading is used: no value equals the operand.
238223 if node .op == CompareOperator .ne :
239- return all (
240- _compare (item , expected , node .op , case_exact = resolved .case_exact )
241- for item in actual
242- )
224+ return all (_compare (item , expected , node .op , resolved ) for item in actual )
243225
244- return any (
245- _compare (item , expected , node .op , case_exact = resolved .case_exact )
246- for item in actual
247- )
226+ return any (_compare (item , expected , node .op , resolved ) for item in actual )
248227
249228 def visit_present (self , node : Present ) -> bool :
250229 resolved = self ._resolve (node .attr_path )
@@ -300,7 +279,7 @@ def _matches_item(
300279 type (item ), item , strict = self .strict , urn_prefix = resolved .urn
301280 ).visit (val_filter )
302281
303- return _ScalarEvaluator (item , case_exact = resolved . case_exact ).visit (val_filter )
282+ return _ScalarEvaluator (item , resolved ).visit (val_filter )
304283
305284
306285class _ScalarEvaluator (FilterVisitor [bool ]):
@@ -313,17 +292,17 @@ class _ScalarEvaluator(FilterVisitor[bool]):
313292 attribute name cannot match.
314293 """
315294
316- def __init__ (self , value : Any , * , case_exact : bool = False ):
295+ def __init__ (self , value : Any , resolved : AttributeBinding ):
317296 self .value = value
318- self .case_exact = case_exact
297+ self .resolved = resolved
319298
320299 def _targets_self (self , node : Comparison | Present ) -> bool :
321300 return node .attr_path .attr .lower () == "value" and not node .attr_path .sub_attr
322301
323302 def visit_comparison (self , node : Comparison ) -> bool :
324303 if not self ._targets_self (node ):
325304 return False
326- return _compare (self .value , node .value , node .op , case_exact = self .case_exact )
305+ return _compare (self .value , node .value , node .op , self .resolved )
327306
328307 def visit_present (self , node : Present ) -> bool :
329308 return self ._targets_self (node ) and _is_present (self .value )
0 commit comments