-
-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
259 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import re | ||
import textwrap | ||
import email.message | ||
|
||
from ._text import FoldedCase | ||
|
||
|
||
class Message(email.message.Message): | ||
multiple_use_keys = set( | ||
map( | ||
FoldedCase, | ||
[ | ||
'Classifier', | ||
'Obsoletes-Dist', | ||
'Platform', | ||
'Project-URL', | ||
'Provides-Dist', | ||
'Provides-Extra', | ||
'Requires-Dist', | ||
'Requires-External', | ||
'Supported-Platform', | ||
], | ||
) | ||
) | ||
""" | ||
Keys that may be indicated multiple times per PEP 566. | ||
""" | ||
|
||
def __new__(cls, orig: email.message.Message): | ||
res = super().__new__(cls) | ||
vars(res).update(vars(orig)) | ||
return res | ||
|
||
def __init__(self, *args, **kwargs): | ||
self._headers = self._repair_headers() | ||
|
||
# suppress spurious error from mypy | ||
def __iter__(self): | ||
return super().__iter__() | ||
|
||
def _repair_headers(self): | ||
def redent(value): | ||
"Correct for RFC822 indentation" | ||
if not value or '\n' not in value: | ||
return value | ||
return textwrap.dedent(' ' * 8 + value) | ||
|
||
headers = [(key, redent(value)) for key, value in vars(self)['_headers']] | ||
if self._payload: | ||
headers.append(('Description', self.get_payload())) | ||
return headers | ||
|
||
@property | ||
def json(self): | ||
""" | ||
Convert PackageMetadata to a JSON-compatible format | ||
per PEP 0566. | ||
""" | ||
|
||
def transform(key): | ||
value = self.get_all(key) if key in self.multiple_use_keys else self[key] | ||
if key == 'Keywords': | ||
value = re.split(r'\s+', value) | ||
tk = key.lower().replace('-', '_') | ||
return tk, value | ||
|
||
return dict(map(transform, map(FoldedCase, self))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
from ._compat import Protocol | ||
from typing import Any, Dict, Iterator, List, TypeVar, Union | ||
|
||
|
||
_T = TypeVar("_T") | ||
|
||
|
||
class PackageMetadata(Protocol): | ||
def __len__(self) -> int: | ||
... # pragma: no cover | ||
|
||
def __contains__(self, item: str) -> bool: | ||
... # pragma: no cover | ||
|
||
def __getitem__(self, key: str) -> str: | ||
... # pragma: no cover | ||
|
||
def __iter__(self) -> Iterator[str]: | ||
... # pragma: no cover | ||
|
||
def get_all(self, name: str, failobj: _T = ...) -> Union[List[Any], _T]: | ||
""" | ||
Return all values associated with a possibly multi-valued key. | ||
""" | ||
|
||
@property | ||
def json(self) -> Dict[str, Union[str, List[str]]]: | ||
""" | ||
A JSON-compatible form of the metadata. | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import re | ||
|
||
from ._functools import method_cache | ||
|
||
|
||
# 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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters