22import textwrap
33import email .message
44
5+ from ._functools import method_cache
6+
57
68class Message (email .message .Message ):
79 def __new__ (cls , orig : email .message .Message ):
@@ -34,18 +36,22 @@ def json(self):
3436 Convert PackageMetadata to a JSON-compatible format
3537 per PEP 0566.
3638 """
37- # TODO: Need to match case-insensitive
38- multiple_use = {
39- 'Classifier' ,
40- 'Obsoletes-Dist' ,
41- 'Platform' ,
42- 'Project-URL' ,
43- 'Provides-Dist' ,
44- 'Provides-Extra' ,
45- 'Requires-Dist' ,
46- 'Requires-External' ,
47- 'Supported-Platform' ,
48- }
39+ multiple_use = set (
40+ map (
41+ FoldedCase ,
42+ [
43+ 'Classifier' ,
44+ 'Obsoletes-Dist' ,
45+ 'Platform' ,
46+ 'Project-URL' ,
47+ 'Provides-Dist' ,
48+ 'Provides-Extra' ,
49+ 'Requires-Dist' ,
50+ 'Requires-External' ,
51+ 'Supported-Platform' ,
52+ ],
53+ )
54+ )
4955
5056 def transform (key ):
5157 value = self .get_all (key ) if key in multiple_use else self [key ]
@@ -54,4 +60,100 @@ def transform(key):
5460 tk = key .lower ().replace ('-' , '_' )
5561 return tk , value
5662
57- return dict (map (transform , self ))
63+ return dict (map (transform , map (FoldedCase , self )))
64+
65+
66+ # from jaraco.text 3.5
67+ class FoldedCase (str ):
68+ """
69+ A case insensitive string class; behaves just like str
70+ except compares equal when the only variation is case.
71+
72+ >>> s = FoldedCase('hello world')
73+
74+ >>> s == 'Hello World'
75+ True
76+
77+ >>> 'Hello World' == s
78+ True
79+
80+ >>> s != 'Hello World'
81+ False
82+
83+ >>> s.index('O')
84+ 4
85+
86+ >>> s.split('O')
87+ ['hell', ' w', 'rld']
88+
89+ >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta']))
90+ ['alpha', 'Beta', 'GAMMA']
91+
92+ Sequence membership is straightforward.
93+
94+ >>> "Hello World" in [s]
95+ True
96+ >>> s in ["Hello World"]
97+ True
98+
99+ You may test for set inclusion, but candidate and elements
100+ must both be folded.
101+
102+ >>> FoldedCase("Hello World") in {s}
103+ True
104+ >>> s in {FoldedCase("Hello World")}
105+ True
106+
107+ String inclusion works as long as the FoldedCase object
108+ is on the right.
109+
110+ >>> "hello" in FoldedCase("Hello World")
111+ True
112+
113+ But not if the FoldedCase object is on the left:
114+
115+ >>> FoldedCase('hello') in 'Hello World'
116+ False
117+
118+ In that case, use in_:
119+
120+ >>> FoldedCase('hello').in_('Hello World')
121+ True
122+
123+ >>> FoldedCase('hello') > FoldedCase('Hello')
124+ False
125+ """
126+
127+ def __lt__ (self , other ):
128+ return self .lower () < other .lower ()
129+
130+ def __gt__ (self , other ):
131+ return self .lower () > other .lower ()
132+
133+ def __eq__ (self , other ):
134+ return self .lower () == other .lower ()
135+
136+ def __ne__ (self , other ):
137+ return self .lower () != other .lower ()
138+
139+ def __hash__ (self ):
140+ return hash (self .lower ())
141+
142+ def __contains__ (self , other ):
143+ return super (FoldedCase , self ).lower ().__contains__ (other .lower ())
144+
145+ def in_ (self , other ):
146+ "Does self appear in other?"
147+ return self in FoldedCase (other )
148+
149+ # cache lower since it's likely to be called frequently.
150+ @method_cache
151+ def lower (self ):
152+ return super (FoldedCase , self ).lower ()
153+
154+ def index (self , sub ):
155+ return self .lower ().index (sub .lower ())
156+
157+ def split (self , splitter = ' ' , maxsplit = 0 ):
158+ pattern = re .compile (re .escape (splitter ), re .I )
159+ return pattern .split (self , maxsplit )
0 commit comments