2
2
import textwrap
3
3
import email .message
4
4
5
+ from ._functools import method_cache
6
+
5
7
6
8
class Message (email .message .Message ):
7
9
def __new__ (cls , orig : email .message .Message ):
@@ -34,18 +36,22 @@ def json(self):
34
36
Convert PackageMetadata to a JSON-compatible format
35
37
per PEP 0566.
36
38
"""
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
+ )
49
55
50
56
def transform (key ):
51
57
value = self .get_all (key ) if key in multiple_use else self [key ]
@@ -54,4 +60,100 @@ def transform(key):
54
60
tk = key .lower ().replace ('-' , '_' )
55
61
return tk , value
56
62
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