diff --git a/rules_python/whl.py b/rules_python/whl.py index 3e9d4f88fa..46211f1173 100644 --- a/rules_python/whl.py +++ b/rules_python/whl.py @@ -14,12 +14,17 @@ """The whl modules defines classes for interacting with Python packages.""" import argparse +import collections +import rfc822 import json import os import pkg_resources import re import zipfile +EXTRA_RE = re.compile("""^(?P.*?)(;\s*(?P.*?)(extra == '(?P.*?)')?)$""") +MayRequiresKey = collections.namedtuple('MayRequiresKey', ('condition', 'extra')) + class Wheel(object): @@ -66,7 +71,7 @@ def metadata(self): pass # fall back to METADATA file (https://www.python.org/dev/peps/pep-0427/) with whl.open(self._dist_info() + '/METADATA') as f: - return self._parse_metadata(f.read().decode("utf-8")) + return self._parse_metadata(f) def name(self): return self.metadata().get('name') @@ -107,9 +112,45 @@ def expand(self, directory): # _parse_metadata parses METADATA files according to https://www.python.org/dev/peps/pep-0314/ def _parse_metadata(self, content): - # TODO: handle fields other than just name - name_pattern = re.compile('Name: (.*)') - return { 'name': name_pattern.search(content).group(1) } + def get_header_value(header): + return header.strip().split(':', 2)[1].strip() + metadata = {} + pkg_info = rfc822.Message(content) + metadata['name'] = pkg_info.get('Name') + extras = [get_header_value(h) for h in pkg_info.getallmatchingheaders('Provides-Extra')] + if extras: + metadata['extras'] = list(set(extras)) + + reqs_dist = [get_header_value(h) for h in pkg_info.getallmatchingheaders('Requires-Dist')] + requires = collections.defaultdict(set) + for value in sorted(reqs_dist): + extra_match = EXTRA_RE.search(value) + if extra_match: + groupdict = extra_match.groupdict() + condition = groupdict['condition'] + extra = groupdict['extra'] + package = groupdict['package'] + if condition.endswith(' and '): + condition = condition[:-5] + else: + condition, extra = None, None + package = value + key = MayRequiresKey(condition, extra) + requires[key].add(package) + + if requires: + metadata['run_requires'] = [] + for key, value in requires.items(): + requirement = { + 'requires': list(value) + } + if key.extra: + requirement['extra'] = key.extra + if key.condition: + requirement['environment'] = key.condition + metadata['run_requires'].append(requirement) + + return metadata parser = argparse.ArgumentParser( diff --git a/tools/piptool.par b/tools/piptool.par index 11ec453cd7..56c267b7c3 100755 Binary files a/tools/piptool.par and b/tools/piptool.par differ diff --git a/tools/whltool.par b/tools/whltool.par index 7cb59c0fbe..83a61e7705 100755 Binary files a/tools/whltool.par and b/tools/whltool.par differ