2 from optparse
import Values
3 from typing
import Generator
, Iterable
, Iterator
, List
, NamedTuple
, Optional
5 from pip
._vendor
.packaging
.utils
import canonicalize_name
7 from pip
._internal
.cli
.base_command
import Command
8 from pip
._internal
.cli
.status_codes
import ERROR
, SUCCESS
9 from pip
._internal
.metadata
import BaseDistribution
, get_default_environment
10 from pip
._internal
.utils
.misc
import write_output
12 logger
= logging
.getLogger(__name__
)
15 class ShowCommand(Command
):
17 Show information about one or more installed packages.
19 The output is in RFC-compliant mail header format.
23 %prog [options] <package> ..."""
24 ignore_require_venv
= True
26 def add_options(self
) -> None:
27 self
.cmd_opts
.add_option(
33 help="Show the full list of installed files for each package.",
36 self
.parser
.insert_option_group(0, self
.cmd_opts
)
38 def run(self
, options
: Values
, args
: List
[str]) -> int:
40 logger
.warning("ERROR: Please provide a package name or names.")
44 results
= search_packages_info(query
)
46 results
, list_files
=options
.files
, verbose
=options
.verbose
52 class _PackageInfo(NamedTuple
):
56 editable_project_location
: Optional
[str]
58 required_by
: List
[str]
61 classifiers
: List
[str]
64 project_urls
: List
[str]
68 entry_points
: List
[str]
69 files
: Optional
[List
[str]]
72 def search_packages_info(query
: List
[str]) -> Generator
[_PackageInfo
, None, None]:
74 Gather details from installed distributions. Print distribution name,
75 version, location, and installed files. Installed files requires a
76 pip generated 'installed-files.txt' in the distributions '.egg-info'
79 env
= get_default_environment()
81 installed
= {dist.canonical_name: dist for dist in env.iter_all_distributions()}
82 query_names
= [canonicalize_name(name
) for name
in query
]
84 [name
for name
, pkg
in zip(query
, query_names
) if pkg
not in installed
]
87 logger
.warning("Package(s) not found: %s", ", ".join(missing
))
89 def _get_requiring_packages(current_dist
: BaseDistribution
) -> Iterator
[str]:
91 dist
.metadata
["Name"] or "UNKNOWN"
92 for dist
in installed
.values()
93 if current_dist
.canonical_name
94 in {canonicalize_name(d.name) for d in dist.iter_dependencies()}
97 for query_name
in query_names
:
99 dist
= installed
[query_name
]
103 requires
= sorted((req
.name
for req
in dist
.iter_dependencies()), key
=str.lower
)
104 required_by
= sorted(_get_requiring_packages(dist
), key
=str.lower
)
107 entry_points_text
= dist
.read_text("entry_points.txt")
108 entry_points
= entry_points_text
.splitlines(keepends
=False)
109 except FileNotFoundError
:
112 files_iter
= dist
.iter_declared_entries()
113 if files_iter
is None:
114 files
: Optional
[List
[str]] = None
116 files
= sorted(files_iter
)
118 metadata
= dist
.metadata
122 version
=str(dist
.version
),
123 location
=dist
.location
or "",
124 editable_project_location
=dist
.editable_project_location
,
126 required_by
=required_by
,
127 installer
=dist
.installer
,
128 metadata_version
=dist
.metadata_version
or "",
129 classifiers
=metadata
.get_all("Classifier", []),
130 summary
=metadata
.get("Summary", ""),
131 homepage
=metadata
.get("Home-page", ""),
132 project_urls
=metadata
.get_all("Project-URL", []),
133 author
=metadata
.get("Author", ""),
134 author_email
=metadata
.get("Author-email", ""),
135 license
=metadata
.get("License", ""),
136 entry_points
=entry_points
,
142 distributions
: Iterable
[_PackageInfo
],
147 Print the information from installed distributions found.
149 results_printed
= False
150 for i
, dist
in enumerate(distributions
):
151 results_printed
= True
155 write_output("Name: %s", dist
.name
)
156 write_output("Version: %s", dist
.version
)
157 write_output("Summary: %s", dist
.summary
)
158 write_output("Home-page: %s", dist
.homepage
)
159 write_output("Author: %s", dist
.author
)
160 write_output("Author-email: %s", dist
.author_email
)
161 write_output("License: %s", dist
.license
)
162 write_output("Location: %s", dist
.location
)
163 if dist
.editable_project_location
is not None:
165 "Editable project location: %s", dist
.editable_project_location
167 write_output("Requires: %s", ", ".join(dist
.requires
))
168 write_output("Required-by: %s", ", ".join(dist
.required_by
))
171 write_output("Metadata-Version: %s", dist
.metadata_version
)
172 write_output("Installer: %s", dist
.installer
)
173 write_output("Classifiers:")
174 for classifier
in dist
.classifiers
:
175 write_output(" %s", classifier
)
176 write_output("Entry-points:")
177 for entry
in dist
.entry_points
:
178 write_output(" %s", entry
.strip())
179 write_output("Project-URLs:")
180 for project_url
in dist
.project_urls
:
181 write_output(" %s", project_url
)
183 write_output("Files:")
184 if dist
.files
is None:
185 write_output("Cannot locate RECORD or installed-files.txt")
187 for line
in dist
.files
:
188 write_output(" %s", line
.strip())
189 return results_printed