155 lines
4.3 KiB
Python
155 lines
4.3 KiB
Python
|
from email.parser import Parser
|
||
|
|
||
|
from ._compat import StringIO
|
||
|
from ._compat import must_decode
|
||
|
|
||
|
|
||
|
def parse(fp):
|
||
|
return Parser().parse(fp)
|
||
|
def get(msg, header):
|
||
|
return _collapse_leading_ws(header, msg.get(header))
|
||
|
def get_all(msg, header):
|
||
|
return [_collapse_leading_ws(header, x) for x in msg.get_all(header)]
|
||
|
|
||
|
def _collapse_leading_ws(header, txt):
|
||
|
"""
|
||
|
``Description`` header must preserve newlines; all others need not
|
||
|
"""
|
||
|
if header.lower() == 'description': # preserve newlines
|
||
|
return '\n'.join([x[8:] if x.startswith(' ' * 8) else x
|
||
|
for x in txt.strip().splitlines()])
|
||
|
else:
|
||
|
return ' '.join([x.strip() for x in txt.splitlines()])
|
||
|
|
||
|
|
||
|
HEADER_ATTRS_1_0 = ( # PEP 241
|
||
|
('Metadata-Version', 'metadata_version', False),
|
||
|
('Name', 'name', False),
|
||
|
('Version', 'version', False),
|
||
|
('Platform', 'platforms', True),
|
||
|
('Supported-Platform', 'supported_platforms', True),
|
||
|
('Summary', 'summary', False),
|
||
|
('Description', 'description', False),
|
||
|
('Keywords', 'keywords', False),
|
||
|
('Home-Page', 'home_page', False),
|
||
|
('Author', 'author', False),
|
||
|
('Author-email', 'author_email', False),
|
||
|
('License', 'license', False),
|
||
|
)
|
||
|
|
||
|
HEADER_ATTRS_1_1 = HEADER_ATTRS_1_0 + ( # PEP 314
|
||
|
('Classifier', 'classifiers', True),
|
||
|
('Download-URL', 'download_url', False),
|
||
|
('Requires', 'requires', True),
|
||
|
('Provides', 'provides', True),
|
||
|
('Obsoletes', 'obsoletes', True),
|
||
|
)
|
||
|
|
||
|
HEADER_ATTRS_1_2 = HEADER_ATTRS_1_1 + ( # PEP 345
|
||
|
('Maintainer', 'maintainer', False),
|
||
|
('Maintainer-email', 'maintainer_email', False),
|
||
|
('Requires-Python', 'requires_python', False),
|
||
|
('Requires-External', 'requires_external', True),
|
||
|
('Requires-Dist', 'requires_dist', True),
|
||
|
('Provides-Dist', 'provides_dist', True),
|
||
|
('Obsoletes-Dist', 'obsoletes_dist', True),
|
||
|
('Project-URL', 'project_urls', True),
|
||
|
)
|
||
|
|
||
|
HEADER_ATTRS_2_0 = HEADER_ATTRS_1_2 #XXX PEP 426?
|
||
|
|
||
|
HEADER_ATTRS_2_1 = HEADER_ATTRS_1_2 + ( # PEP 566
|
||
|
('Provides-Extra', 'provides_extras', True),
|
||
|
('Description-Content-Type', 'description_content_type', False)
|
||
|
)
|
||
|
|
||
|
HEADER_ATTRS_2_2 = HEADER_ATTRS_2_1 + ( # PEP 643
|
||
|
('Dynamic', 'dynamic', True),
|
||
|
)
|
||
|
|
||
|
HEADER_ATTRS = {
|
||
|
'1.0': HEADER_ATTRS_1_0,
|
||
|
'1.1': HEADER_ATTRS_1_1,
|
||
|
'1.2': HEADER_ATTRS_1_2,
|
||
|
'2.0': HEADER_ATTRS_2_0,
|
||
|
'2.1': HEADER_ATTRS_2_1,
|
||
|
'2.2': HEADER_ATTRS_2_2,
|
||
|
}
|
||
|
|
||
|
class Distribution(object):
|
||
|
metadata_version = None
|
||
|
# version 1.0
|
||
|
name = None
|
||
|
version = None
|
||
|
platforms = ()
|
||
|
supported_platforms = ()
|
||
|
summary = None
|
||
|
description = None
|
||
|
keywords = None
|
||
|
home_page = None
|
||
|
download_url = None
|
||
|
author = None
|
||
|
author_email = None
|
||
|
license = None
|
||
|
# version 1.1
|
||
|
classifiers = ()
|
||
|
requires = ()
|
||
|
provides = ()
|
||
|
obsoletes = ()
|
||
|
# version 1.2
|
||
|
maintainer = None
|
||
|
maintainer_email = None
|
||
|
requires_python = None
|
||
|
requires_external = ()
|
||
|
requires_dist = ()
|
||
|
provides_dist = ()
|
||
|
obsoletes_dist = ()
|
||
|
project_urls = ()
|
||
|
# version 2.1
|
||
|
provides_extras = ()
|
||
|
description_content_type = None
|
||
|
# version 2.2
|
||
|
dynamic = ()
|
||
|
|
||
|
def extractMetadata(self):
|
||
|
data = self.read()
|
||
|
self.parse(data)
|
||
|
|
||
|
def read(self):
|
||
|
raise NotImplementedError
|
||
|
|
||
|
def _getHeaderAttrs(self):
|
||
|
return HEADER_ATTRS.get(self.metadata_version, [])
|
||
|
|
||
|
def parse(self, data):
|
||
|
fp = StringIO(must_decode(data))
|
||
|
msg = parse(fp)
|
||
|
|
||
|
if 'Metadata-Version' in msg and self.metadata_version is None:
|
||
|
value = get(msg, 'Metadata-Version')
|
||
|
metadata_version = self.metadata_version = value
|
||
|
|
||
|
for header_name, attr_name, multiple in self._getHeaderAttrs():
|
||
|
|
||
|
if attr_name == 'metadata_version':
|
||
|
continue
|
||
|
|
||
|
if header_name in msg:
|
||
|
if multiple:
|
||
|
values = get_all(msg, header_name)
|
||
|
setattr(self, attr_name, values)
|
||
|
else:
|
||
|
value = get(msg, header_name)
|
||
|
if value != 'UNKNOWN':
|
||
|
setattr(self, attr_name, value)
|
||
|
|
||
|
body = msg.get_payload()
|
||
|
if body:
|
||
|
setattr(self, 'description', body)
|
||
|
|
||
|
def __iter__(self):
|
||
|
for header_name, attr_name, multiple in self._getHeaderAttrs():
|
||
|
yield attr_name
|
||
|
|
||
|
iterkeys = __iter__
|