summaryrefslogtreecommitdiff
path: root/src/pip/_internal/operations/freeze.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/pip/_internal/operations/freeze.py')
-rw-r--r--src/pip/_internal/operations/freeze.py193
1 files changed, 85 insertions, 108 deletions
diff --git a/src/pip/_internal/operations/freeze.py b/src/pip/_internal/operations/freeze.py
index defb20c7c..930d4c600 100644
--- a/src/pip/_internal/operations/freeze.py
+++ b/src/pip/_internal/operations/freeze.py
@@ -1,19 +1,8 @@
import collections
import logging
import os
-from typing import (
- Container,
- Dict,
- Iterable,
- Iterator,
- List,
- NamedTuple,
- Optional,
- Set,
- Union,
-)
+from typing import Container, Dict, Generator, Iterable, List, NamedTuple, Optional, Set
-from pip._vendor.packaging.requirements import Requirement
from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.packaging.version import Version
@@ -30,22 +19,20 @@ logger = logging.getLogger(__name__)
class _EditableInfo(NamedTuple):
- requirement: Optional[str]
- editable: bool
+ requirement: str
comments: List[str]
def freeze(
- requirement=None, # type: Optional[List[str]]
- local_only=False, # type: bool
- user_only=False, # type: bool
- paths=None, # type: Optional[List[str]]
- isolated=False, # type: bool
- exclude_editable=False, # type: bool
- skip=() # type: Container[str]
-):
- # type: (...) -> Iterator[str]
- installations = {} # type: Dict[str, FrozenRequirement]
+ requirement: Optional[List[str]] = None,
+ local_only: bool = False,
+ user_only: bool = False,
+ paths: Optional[List[str]] = None,
+ isolated: bool = False,
+ exclude_editable: bool = False,
+ skip: Container[str] = (),
+) -> Generator[str, None, None]:
+ installations: Dict[str, FrozenRequirement] = {}
dists = get_environment(paths).iter_installed_distributions(
local_only=local_only,
@@ -63,42 +50,50 @@ def freeze(
# should only be emitted once, even if the same option is in multiple
# requirements files, so we need to keep track of what has been emitted
# so that we don't emit it again if it's seen again
- emitted_options = set() # type: Set[str]
+ emitted_options: Set[str] = set()
# keep track of which files a requirement is in so that we can
# give an accurate warning if a requirement appears multiple times.
- req_files = collections.defaultdict(list) # type: Dict[str, List[str]]
+ req_files: Dict[str, List[str]] = collections.defaultdict(list)
for req_file_path in requirement:
with open(req_file_path) as req_file:
for line in req_file:
- if (not line.strip() or
- line.strip().startswith('#') or
- line.startswith((
- '-r', '--requirement',
- '-f', '--find-links',
- '-i', '--index-url',
- '--pre',
- '--trusted-host',
- '--process-dependency-links',
- '--extra-index-url',
- '--use-feature'))):
+ if (
+ not line.strip()
+ or line.strip().startswith("#")
+ or line.startswith(
+ (
+ "-r",
+ "--requirement",
+ "-f",
+ "--find-links",
+ "-i",
+ "--index-url",
+ "--pre",
+ "--trusted-host",
+ "--process-dependency-links",
+ "--extra-index-url",
+ "--use-feature",
+ )
+ )
+ ):
line = line.rstrip()
if line not in emitted_options:
emitted_options.add(line)
yield line
continue
- if line.startswith('-e') or line.startswith('--editable'):
- if line.startswith('-e'):
+ if line.startswith("-e") or line.startswith("--editable"):
+ if line.startswith("-e"):
line = line[2:].strip()
else:
- line = line[len('--editable'):].strip().lstrip('=')
+ line = line[len("--editable") :].strip().lstrip("=")
line_req = install_req_from_editable(
line,
isolated=isolated,
)
else:
line_req = install_req_from_line(
- COMMENT_RE.sub('', line).strip(),
+ COMMENT_RE.sub("", line).strip(),
isolated=isolated,
)
@@ -106,15 +101,15 @@ def freeze(
logger.info(
"Skipping line in requirement file [%s] because "
"it's not clear what it would install: %s",
- req_file_path, line.strip(),
+ req_file_path,
+ line.strip(),
)
logger.info(
" (add #egg=PackageName to the URL to avoid"
" this warning)"
)
else:
- line_req_canonical_name = canonicalize_name(
- line_req.name)
+ line_req_canonical_name = canonicalize_name(line_req.name)
if line_req_canonical_name not in installations:
# either it's not installed, or it is installed
# but has been processed already
@@ -123,14 +118,13 @@ def freeze(
"Requirement file [%s] contains %s, but "
"package %r is not installed",
req_file_path,
- COMMENT_RE.sub('', line).strip(),
- line_req.name
+ COMMENT_RE.sub("", line).strip(),
+ line_req.name,
)
else:
req_files[line_req.name].append(req_file_path)
else:
- yield str(installations[
- line_req_canonical_name]).rstrip()
+ yield str(installations[line_req_canonical_name]).rstrip()
del installations[line_req_canonical_name]
req_files[line_req.name].append(req_file_path)
@@ -138,15 +132,14 @@ def freeze(
# single requirements file or in different requirements files).
for name, files in req_files.items():
if len(files) > 1:
- logger.warning("Requirement %s included multiple times [%s]",
- name, ', '.join(sorted(set(files))))
+ logger.warning(
+ "Requirement %s included multiple times [%s]",
+ name,
+ ", ".join(sorted(set(files))),
+ )
- yield(
- '## The following requirements were added by '
- 'pip freeze:'
- )
- for installation in sorted(
- installations.values(), key=lambda x: x.name.lower()):
+ yield ("## The following requirements were added by pip freeze:")
+ for installation in sorted(installations.values(), key=lambda x: x.name.lower()):
if installation.canonical_name not in skip:
yield str(installation).rstrip()
@@ -159,21 +152,12 @@ def _format_as_name_version(dist: BaseDistribution) -> str:
def _get_editable_info(dist: BaseDistribution) -> _EditableInfo:
"""
- Compute and return values (req, editable, comments) for use in
+ Compute and return values (req, comments) for use in
FrozenRequirement.from_dist().
"""
- if not dist.editable:
- return _EditableInfo(requirement=None, editable=False, comments=[])
- if dist.location is None:
- display = _format_as_name_version(dist)
- logger.warning("Editable requirement not found on disk: %s", display)
- return _EditableInfo(
- requirement=None,
- editable=True,
- comments=[f"# Editable install not found ({display})"],
- )
-
- location = os.path.normcase(os.path.abspath(dist.location))
+ editable_project_location = dist.editable_project_location
+ assert editable_project_location
+ location = os.path.normcase(os.path.abspath(editable_project_location))
from pip._internal.vcs import RemoteNotFoundError, RemoteNotValidError, vcs
@@ -182,13 +166,13 @@ def _get_editable_info(dist: BaseDistribution) -> _EditableInfo:
if vcs_backend is None:
display = _format_as_name_version(dist)
logger.debug(
- 'No VCS found for editable requirement "%s" in: %r', display,
+ 'No VCS found for editable requirement "%s" in: %r',
+ display,
location,
)
return _EditableInfo(
requirement=location,
- editable=True,
- comments=[f'# Editable install with no version control ({display})'],
+ comments=[f"# Editable install with no version control ({display})"],
)
vcs_name = type(vcs_backend).__name__
@@ -199,50 +183,47 @@ def _get_editable_info(dist: BaseDistribution) -> _EditableInfo:
display = _format_as_name_version(dist)
return _EditableInfo(
requirement=location,
- editable=True,
- comments=[f'# Editable {vcs_name} install with no remote ({display})'],
+ comments=[f"# Editable {vcs_name} install with no remote ({display})"],
)
except RemoteNotValidError as ex:
display = _format_as_name_version(dist)
return _EditableInfo(
requirement=location,
- editable=True,
comments=[
f"# Editable {vcs_name} install ({display}) with either a deleted "
f"local remote or invalid URI:",
f"# '{ex.url}'",
],
)
-
except BadCommand:
logger.warning(
- 'cannot determine version of editable source in %s '
- '(%s command not found in path)',
+ "cannot determine version of editable source in %s "
+ "(%s command not found in path)",
location,
vcs_backend.name,
)
- return _EditableInfo(requirement=None, editable=True, comments=[])
-
+ return _EditableInfo(requirement=location, comments=[])
except InstallationError as exc:
- logger.warning(
- "Error when trying to get requirement for VCS system %s, "
- "falling back to uneditable format", exc
- )
+ logger.warning("Error when trying to get requirement for VCS system %s", exc)
else:
- return _EditableInfo(requirement=req, editable=True, comments=[])
+ return _EditableInfo(requirement=req, comments=[])
- logger.warning('Could not determine repository location of %s', location)
+ logger.warning("Could not determine repository location of %s", location)
return _EditableInfo(
- requirement=None,
- editable=False,
- comments=['## !! Could not determine repository location'],
+ requirement=location,
+ comments=["## !! Could not determine repository location"],
)
class FrozenRequirement:
- def __init__(self, name, req, editable, comments=()):
- # type: (str, Union[str, Requirement], bool, Iterable[str]) -> None
+ def __init__(
+ self,
+ name: str,
+ req: str,
+ editable: bool,
+ comments: Iterable[str] = (),
+ ) -> None:
self.name = name
self.canonical_name = canonicalize_name(name)
self.req = req
@@ -251,27 +232,23 @@ class FrozenRequirement:
@classmethod
def from_dist(cls, dist: BaseDistribution) -> "FrozenRequirement":
- # TODO `get_requirement_info` is taking care of editable requirements.
- # TODO This should be refactored when we will add detection of
- # editable that provide .dist-info metadata.
- req, editable, comments = _get_editable_info(dist)
- if req is None and not editable:
- # if PEP 610 metadata is present, attempt to use it
+ editable = dist.editable
+ if editable:
+ req, comments = _get_editable_info(dist)
+ else:
+ comments = []
direct_url = dist.direct_url
if direct_url:
- req = direct_url_as_pep440_direct_reference(
- direct_url, dist.raw_name
- )
- comments = []
- if req is None:
- # name==version requirement
- req = _format_as_name_version(dist)
+ # if PEP 610 metadata is present, use it
+ req = direct_url_as_pep440_direct_reference(direct_url, dist.raw_name)
+ else:
+ # name==version requirement
+ req = _format_as_name_version(dist)
return cls(dist.raw_name, req, editable, comments=comments)
- def __str__(self):
- # type: () -> str
+ def __str__(self) -> str:
req = self.req
if self.editable:
- req = f'-e {req}'
- return '\n'.join(list(self.comments) + [str(req)]) + '\n'
+ req = f"-e {req}"
+ return "\n".join(list(self.comments) + [str(req)]) + "\n"