|
14 | 14 | """The whl modules defines classes for interacting with Python packages.""" |
15 | 15 |
|
16 | 16 | import argparse |
| 17 | +import collections |
| 18 | +import rfc822 |
17 | 19 | import json |
18 | 20 | import os |
19 | 21 | import pkg_resources |
20 | 22 | import re |
21 | 23 | import zipfile |
22 | 24 |
|
| 25 | +EXTRA_RE = re.compile("""^(?P<package>.*?)(;\s*(?P<condition>.*?)(extra == '(?P<extra>.*?)')?)$""") |
| 26 | +MayRequiresKey = collections.namedtuple('MayRequiresKey', ('condition', 'extra')) |
| 27 | + |
23 | 28 |
|
24 | 29 | class Wheel(object): |
25 | 30 |
|
@@ -66,7 +71,7 @@ def metadata(self): |
66 | 71 | pass |
67 | 72 | # fall back to METADATA file (https://www.python.org/dev/peps/pep-0427/) |
68 | 73 | with whl.open(self._dist_info() + '/METADATA') as f: |
69 | | - return self._parse_metadata(f.read().decode("utf-8")) |
| 74 | + return self._parse_metadata(f) |
70 | 75 |
|
71 | 76 | def name(self): |
72 | 77 | return self.metadata().get('name') |
@@ -107,9 +112,45 @@ def expand(self, directory): |
107 | 112 |
|
108 | 113 | # _parse_metadata parses METADATA files according to https://www.python.org/dev/peps/pep-0314/ |
109 | 114 | def _parse_metadata(self, content): |
110 | | - # TODO: handle fields other than just name |
111 | | - name_pattern = re.compile('Name: (.*)') |
112 | | - return { 'name': name_pattern.search(content).group(1) } |
| 115 | + def get_header_value(header): |
| 116 | + return header.strip().split(':', 2)[1].strip() |
| 117 | + metadata = {} |
| 118 | + pkg_info = rfc822.Message(content) |
| 119 | + metadata['name'] = pkg_info.get('Name') |
| 120 | + extras = [get_header_value(h) for h in pkg_info.getallmatchingheaders('Provides-Extra')] |
| 121 | + if extras: |
| 122 | + metadata['extras'] = list(set(extras)) |
| 123 | + |
| 124 | + reqs_dist = [get_header_value(h) for h in pkg_info.getallmatchingheaders('Requires-Dist')] |
| 125 | + requires = collections.defaultdict(set) |
| 126 | + for value in sorted(reqs_dist): |
| 127 | + extra_match = EXTRA_RE.search(value) |
| 128 | + if extra_match: |
| 129 | + groupdict = extra_match.groupdict() |
| 130 | + condition = groupdict['condition'] |
| 131 | + extra = groupdict['extra'] |
| 132 | + package = groupdict['package'] |
| 133 | + if condition.endswith(' and '): |
| 134 | + condition = condition[:-5] |
| 135 | + else: |
| 136 | + condition, extra = None, None |
| 137 | + package = value |
| 138 | + key = MayRequiresKey(condition, extra) |
| 139 | + requires[key].add(package) |
| 140 | + |
| 141 | + if requires: |
| 142 | + metadata['run_requires'] = [] |
| 143 | + for key, value in requires.items(): |
| 144 | + requirement = { |
| 145 | + 'requires': list(value) |
| 146 | + } |
| 147 | + if key.extra: |
| 148 | + requirement['extra'] = key.extra |
| 149 | + if key.condition: |
| 150 | + requirement['environment'] = key.condition |
| 151 | + metadata['run_requires'].append(requirement) |
| 152 | + |
| 153 | + return metadata |
113 | 154 |
|
114 | 155 |
|
115 | 156 | parser = argparse.ArgumentParser( |
|
0 commit comments