Skip to content

Commit

Permalink
Honor case-insensitivity of metadata keys using FoldedCase.
Browse files Browse the repository at this point in the history
  • Loading branch information
jaraco committed Apr 18, 2021
1 parent 96c0fec commit 401bb33
Showing 1 changed file with 115 additions and 13 deletions.
128 changes: 115 additions & 13 deletions importlib_metadata/_adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import textwrap
import email.message

from ._functools import method_cache


class Message(email.message.Message):
def __new__(cls, orig: email.message.Message):
Expand Down Expand Up @@ -34,18 +36,22 @@ def json(self):
Convert PackageMetadata to a JSON-compatible format
per PEP 0566.
"""
# TODO: Need to match case-insensitive
multiple_use = {
'Classifier',
'Obsoletes-Dist',
'Platform',
'Project-URL',
'Provides-Dist',
'Provides-Extra',
'Requires-Dist',
'Requires-External',
'Supported-Platform',
}
multiple_use = set(
map(
FoldedCase,
[
'Classifier',
'Obsoletes-Dist',
'Platform',
'Project-URL',
'Provides-Dist',
'Provides-Extra',
'Requires-Dist',
'Requires-External',
'Supported-Platform',
],
)
)

def transform(key):
value = self.get_all(key) if key in multiple_use else self[key]
Expand All @@ -54,4 +60,100 @@ def transform(key):
tk = key.lower().replace('-', '_')
return tk, value

return dict(map(transform, self))
return dict(map(transform, map(FoldedCase, self)))


# from jaraco.text 3.5
class FoldedCase(str):
"""
A case insensitive string class; behaves just like str
except compares equal when the only variation is case.
>>> s = FoldedCase('hello world')
>>> s == 'Hello World'
True
>>> 'Hello World' == s
True
>>> s != 'Hello World'
False
>>> s.index('O')
4
>>> s.split('O')
['hell', ' w', 'rld']
>>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta']))
['alpha', 'Beta', 'GAMMA']
Sequence membership is straightforward.
>>> "Hello World" in [s]
True
>>> s in ["Hello World"]
True
You may test for set inclusion, but candidate and elements
must both be folded.
>>> FoldedCase("Hello World") in {s}
True
>>> s in {FoldedCase("Hello World")}
True
String inclusion works as long as the FoldedCase object
is on the right.
>>> "hello" in FoldedCase("Hello World")
True
But not if the FoldedCase object is on the left:
>>> FoldedCase('hello') in 'Hello World'
False
In that case, use in_:
>>> FoldedCase('hello').in_('Hello World')
True
>>> FoldedCase('hello') > FoldedCase('Hello')
False
"""

def __lt__(self, other):
return self.lower() < other.lower()

def __gt__(self, other):
return self.lower() > other.lower()

def __eq__(self, other):
return self.lower() == other.lower()

def __ne__(self, other):
return self.lower() != other.lower()

def __hash__(self):
return hash(self.lower())

def __contains__(self, other):
return super(FoldedCase, self).lower().__contains__(other.lower())

def in_(self, other):
"Does self appear in other?"
return self in FoldedCase(other)

# cache lower since it's likely to be called frequently.
@method_cache
def lower(self):
return super(FoldedCase, self).lower()

def index(self, sub):
return self.lower().index(sub.lower())

def split(self, splitter=' ', maxsplit=0):
pattern = re.compile(re.escape(splitter), re.I)
return pattern.split(self, maxsplit)

0 comments on commit 401bb33

Please sign in to comment.