|
| 1 | +import csv |
1 | 2 | import logging
|
2 | 3 | import os
|
3 |
| -from email.parser import FeedParser |
4 | 4 | from optparse import Values
|
5 |
| -from typing import Dict, Iterator, List |
| 5 | +from typing import Dict, Iterator, List, Optional |
6 | 6 |
|
7 |
| -from pip._vendor import pkg_resources |
8 | 7 | from pip._vendor.packaging.utils import canonicalize_name
|
9 | 8 |
|
10 | 9 | from pip._internal.cli.base_command import Command
|
11 | 10 | from pip._internal.cli.status_codes import ERROR, SUCCESS
|
| 11 | +from pip._internal.metadata import BaseDistribution, get_default_environment |
12 | 12 | from pip._internal.utils.misc import write_output
|
13 | 13 |
|
14 | 14 | logger = logging.getLogger(__name__)
|
@@ -58,85 +58,77 @@ def search_packages_info(query):
|
58 | 58 | pip generated 'installed-files.txt' in the distributions '.egg-info'
|
59 | 59 | directory.
|
60 | 60 | """
|
61 |
| - installed = {} |
62 |
| - for p in pkg_resources.working_set: |
63 |
| - installed[canonicalize_name(p.project_name)] = p |
| 61 | + env = get_default_environment() |
64 | 62 |
|
| 63 | + installed = { |
| 64 | + dist.canonical_name: dist |
| 65 | + for dist in env.iter_distributions() |
| 66 | + } |
65 | 67 | query_names = [canonicalize_name(name) for name in query]
|
66 | 68 | missing = sorted(
|
67 | 69 | [name for name, pkg in zip(query, query_names) if pkg not in installed]
|
68 | 70 | )
|
69 | 71 | if missing:
|
70 | 72 | logger.warning('Package(s) not found: %s', ', '.join(missing))
|
71 | 73 |
|
72 |
| - def get_requiring_packages(package_name): |
| 74 | + def get_requiring_packages(canonical_name): |
73 | 75 | # type: (str) -> List[str]
|
74 |
| - canonical_name = canonicalize_name(package_name) |
75 | 76 | return [
|
76 |
| - pkg.project_name for pkg in pkg_resources.working_set |
77 |
| - if canonical_name in |
78 |
| - [canonicalize_name(required.name) for required in |
79 |
| - pkg.requires()] |
| 77 | + dist.canonical_name |
| 78 | + for dist in env.iter_distributions() |
| 79 | + if canonical_name in { |
| 80 | + canonicalize_name(d.name) for d in dist.iter_dependencies() |
| 81 | + } |
80 | 82 | ]
|
81 | 83 |
|
82 |
| - for dist in [installed[pkg] for pkg in query_names if pkg in installed]: |
| 84 | + def _files_from_record(dist: BaseDistribution) -> Optional[Iterator[str]]: |
| 85 | + try: |
| 86 | + text = dist.read_text('RECORD') |
| 87 | + except FileNotFoundError: |
| 88 | + return None |
| 89 | + return (row[0] for row in csv.reader(text.splitlines())) |
| 90 | + |
| 91 | + def _files_from_installed_files(dist: BaseDistribution) -> Optional[Iterator[str]]: |
| 92 | + try: |
| 93 | + text = dist.read_text('installed-files.txt') |
| 94 | + except FileNotFoundError: |
| 95 | + return None |
| 96 | + return (p for p in text.splitlines(keepends=False) if p) |
| 97 | + |
| 98 | + for query_name in query_names: |
| 99 | + try: |
| 100 | + dist = installed[query_name] |
| 101 | + except KeyError: |
| 102 | + continue |
83 | 103 | package = {
|
84 | 104 | 'name': dist.project_name,
|
85 | 105 | 'version': dist.version,
|
86 | 106 | 'location': dist.location,
|
87 |
| - 'requires': [dep.project_name for dep in dist.requires()], |
88 |
| - 'required_by': get_requiring_packages(dist.project_name) |
| 107 | + 'requires': [req.nameroject_name for req in dist.iter_dependencies()], |
| 108 | + 'required_by': get_requiring_packages(dist.canonical_name), |
| 109 | + 'installer': dist.installer, |
| 110 | + 'metadata-version': dist.metadata_version, |
| 111 | + 'classifiers': dist.metadata.get_all('Classifiers'), |
89 | 112 | }
|
90 |
| - file_list = None |
91 |
| - metadata = '' |
92 |
| - if isinstance(dist, pkg_resources.DistInfoDistribution): |
93 |
| - # RECORDs should be part of .dist-info metadatas |
94 |
| - if dist.has_metadata('RECORD'): |
95 |
| - lines = dist.get_metadata_lines('RECORD') |
96 |
| - paths = [line.split(',')[0] for line in lines] |
97 |
| - paths = [os.path.join(dist.location, p) for p in paths] |
98 |
| - file_list = [os.path.relpath(p, dist.location) for p in paths] |
99 |
| - |
100 |
| - if dist.has_metadata('METADATA'): |
101 |
| - metadata = dist.get_metadata('METADATA') |
102 |
| - else: |
103 |
| - # Otherwise use pip's log for .egg-info's |
104 |
| - if dist.has_metadata('installed-files.txt'): |
105 |
| - paths = dist.get_metadata_lines('installed-files.txt') |
106 |
| - paths = [os.path.join(dist.egg_info, p) for p in paths] |
107 |
| - file_list = [os.path.relpath(p, dist.location) for p in paths] |
108 |
| - |
109 |
| - if dist.has_metadata('PKG-INFO'): |
110 |
| - metadata = dist.get_metadata('PKG-INFO') |
111 |
| - |
112 |
| - if dist.has_metadata('entry_points.txt'): |
113 |
| - entry_points = dist.get_metadata_lines('entry_points.txt') |
114 |
| - package['entry_points'] = entry_points |
115 |
| - |
116 |
| - if dist.has_metadata('INSTALLER'): |
117 |
| - for line in dist.get_metadata_lines('INSTALLER'): |
118 |
| - if line.strip(): |
119 |
| - package['installer'] = line.strip() |
120 |
| - break |
121 |
| - |
122 |
| - # @todo: Should pkg_resources.Distribution have a |
123 |
| - # `get_pkg_info` method? |
124 |
| - feed_parser = FeedParser() |
125 |
| - feed_parser.feed(metadata) |
126 |
| - pkg_info_dict = feed_parser.close() |
127 |
| - for key in ('metadata-version', 'summary', |
128 |
| - 'home-page', 'author', 'author-email', 'license'): |
129 |
| - package[key] = pkg_info_dict.get(key) |
130 |
| - |
131 |
| - # It looks like FeedParser cannot deal with repeated headers |
132 |
| - classifiers = [] |
133 |
| - for line in metadata.splitlines(): |
134 |
| - if line.startswith('Classifier: '): |
135 |
| - classifiers.append(line[len('Classifier: '):]) |
136 |
| - package['classifiers'] = classifiers |
137 |
| - |
138 |
| - if file_list: |
139 |
| - package['files'] = sorted(file_list) |
| 113 | + |
| 114 | + for key in ('Summary', 'Home-page', 'Author', 'Author-email', 'License'): |
| 115 | + package[key] = dist.metadata[key] |
| 116 | + |
| 117 | + try: |
| 118 | + entry_points_text = dist.read_text('entry_points.txt') |
| 119 | + package['entry_points'] = entry_points_text.splitlines(keepends=False) |
| 120 | + except FileNotFoundError: |
| 121 | + pass |
| 122 | + if dist.installer: |
| 123 | + package['installer'] = dist.installer |
| 124 | + |
| 125 | + files = ( |
| 126 | + _files_from_record(dist) or |
| 127 | + _files_from_installed_files(dist.read_text('installed-files.txt')) |
| 128 | + ) |
| 129 | + if files: |
| 130 | + package['files'] = sorted(os.path.relpath(p, dist.location) for p in files) |
| 131 | + |
140 | 132 | yield package
|
141 | 133 |
|
142 | 134 |
|
|
0 commit comments