1import re 2 3from ._functools import method_cache 4 5 6# from jaraco.text 3.5 7class FoldedCase(str): 8 """ 9 A case insensitive string class; behaves just like str 10 except compares equal when the only variation is case. 11 12 >>> s = FoldedCase('hello world') 13 14 >>> s == 'Hello World' 15 True 16 17 >>> 'Hello World' == s 18 True 19 20 >>> s != 'Hello World' 21 False 22 23 >>> s.index('O') 24 4 25 26 >>> s.split('O') 27 ['hell', ' w', 'rld'] 28 29 >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta'])) 30 ['alpha', 'Beta', 'GAMMA'] 31 32 Sequence membership is straightforward. 33 34 >>> "Hello World" in [s] 35 True 36 >>> s in ["Hello World"] 37 True 38 39 You may test for set inclusion, but candidate and elements 40 must both be folded. 41 42 >>> FoldedCase("Hello World") in {s} 43 True 44 >>> s in {FoldedCase("Hello World")} 45 True 46 47 String inclusion works as long as the FoldedCase object 48 is on the right. 49 50 >>> "hello" in FoldedCase("Hello World") 51 True 52 53 But not if the FoldedCase object is on the left: 54 55 >>> FoldedCase('hello') in 'Hello World' 56 False 57 58 In that case, use in_: 59 60 >>> FoldedCase('hello').in_('Hello World') 61 True 62 63 >>> FoldedCase('hello') > FoldedCase('Hello') 64 False 65 """ 66 67 def __lt__(self, other): 68 return self.lower() < other.lower() 69 70 def __gt__(self, other): 71 return self.lower() > other.lower() 72 73 def __eq__(self, other): 74 return self.lower() == other.lower() 75 76 def __ne__(self, other): 77 return self.lower() != other.lower() 78 79 def __hash__(self): 80 return hash(self.lower()) 81 82 def __contains__(self, other): 83 return super(FoldedCase, self).lower().__contains__(other.lower()) 84 85 def in_(self, other): 86 "Does self appear in other?" 87 return self in FoldedCase(other) 88 89 # cache lower since it's likely to be called frequently. 90 @method_cache 91 def lower(self): 92 return super(FoldedCase, self).lower() 93 94 def index(self, sub): 95 return self.lower().index(sub.lower()) 96 97 def split(self, splitter=' ', maxsplit=0): 98 pattern = re.compile(re.escape(splitter), re.I) 99 return pattern.split(self, maxsplit) 100