Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
16 changes: 11 additions & 5 deletions src/pip/_internal/cli/cmdoptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@
between parses. pip parses general options twice internally, and shouldn't
pass on state. To be consistent, all options will follow this design.
"""

# The following comment should be removed at some point in the future.
# mypy: strict-optional=False

import os
import textwrap
import warnings
Expand Down Expand Up @@ -443,6 +439,7 @@ def editable() -> Option:

def _handle_src(option: Option, opt_str: str, value: str, parser: OptionParser) -> None:
value = os.path.abspath(value)
assert option.dest is not None
setattr(parser.values, option.dest, value)


Expand All @@ -466,12 +463,14 @@ def _handle_src(option: Option, opt_str: str, value: str, parser: OptionParser)

def _get_format_control(values: Values, option: Option) -> Any:
"""Get a format_control object."""
assert option.dest is not None
return getattr(values, option.dest)


def _handle_no_binary(
option: Option, opt_str: str, value: str, parser: OptionParser
) -> None:
assert parser.values is not None
existing = _get_format_control(parser.values, option)
FormatControl.handle_mutual_excludes(
value,
Expand All @@ -483,6 +482,7 @@ def _handle_no_binary(
def _handle_only_binary(
option: Option, opt_str: str, value: str, parser: OptionParser
) -> None:
assert parser.values is not None
existing = _get_format_control(parser.values, option)
FormatControl.handle_mutual_excludes(
value,
Expand Down Expand Up @@ -543,7 +543,9 @@ def only_binary() -> Option:


# This was made a separate function for unit-testing purposes.
def _convert_python_version(value: str) -> Tuple[Tuple[int, ...], Optional[str]]:
def _convert_python_version(
value: str,
) -> Tuple[Optional[Tuple[int, ...]], Optional[str]]:
"""
Convert a version string like "3", "37", or "3.7.3" into a tuple of ints.

Expand Down Expand Up @@ -586,6 +588,7 @@ def _handle_python_version(
)
raise_option_error(parser, option=option, msg=msg)

assert parser.values is not None
parser.values.python_version = version_info


Expand Down Expand Up @@ -702,6 +705,7 @@ def _handle_no_cache_dir(
except ValueError as exc:
raise_option_error(parser, option=option, msg=str(exc))

assert parser.values is not None
# Originally, setting PIP_NO_CACHE_DIR to a value that strtobool()
# converted to 0 (like "false" or "no") caused cache_dir to be disabled
# rather than enabled (logic would say the latter). Thus, we disable
Expand Down Expand Up @@ -784,6 +788,7 @@ def _handle_no_use_pep517(
"""
raise_option_error(parser, option=option, msg=msg)

assert parser.values is not None
# Otherwise, --no-use-pep517 was passed via the command-line.
parser.values.use_pep517 = False

Expand Down Expand Up @@ -873,6 +878,7 @@ def _handle_merge_hash(
) -> None:
"""Given a value spelled "algo:digest", append the digest to a list
pointed to in a dict by the algo name."""
assert parser.values is not None
if not parser.values.hashes:
parser.values.hashes = {}
try:
Expand Down
10 changes: 5 additions & 5 deletions src/pip/_internal/index/package_finder.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
"""Routines related to PyPI, indexes"""

# The following comment should be removed at some point in the future.
# mypy: strict-optional=False

import functools
import itertools
import logging
Expand Down Expand Up @@ -234,7 +230,7 @@ def evaluate_link(self, link: Link) -> Tuple[bool, Optional[str]]:

def filter_unallowed_hashes(
candidates: List[InstallationCandidate],
hashes: Hashes,
hashes: Optional[Hashes],
project_name: str,
) -> List[InstallationCandidate]:
"""
Expand Down Expand Up @@ -523,6 +519,7 @@ def _sort_key(self, candidate: InstallationCandidate) -> CandidateSortingKey:
binary_preference = 1
if wheel.build_tag is not None:
match = re.match(r"^(\d+)(.*)$", wheel.build_tag)
assert match is not None
build_tag_groups = match.groups()
build_tag = (int(build_tag_groups[0]), build_tag_groups[1])
else: # sdist
Expand Down Expand Up @@ -735,6 +732,7 @@ def get_install_candidate(
self._log_skipped_link(link, reason=result)
return None

assert result is not None
return InstallationCandidate(
name=link_evaluator.project_name,
link=link,
Expand Down Expand Up @@ -924,6 +922,7 @@ def _format_versions(cand_iter: Iterable[InstallationCandidate]) -> str:
installed_version,
)
else:
assert best_candidate is not None
logger.debug(
"Existing installed version (%s) satisfies requirement "
"(most up-to-date version is %s)",
Expand All @@ -941,6 +940,7 @@ def _format_versions(cand_iter: Iterable[InstallationCandidate]) -> str:
)
raise BestVersionAlreadyInstalled

assert best_candidate is not None
logger.debug(
"Using version %s (newest of versions: %s)",
best_candidate.version,
Expand Down
5 changes: 1 addition & 4 deletions src/pip/_internal/locations/_distutils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
"""Locations where we look for configs, install stuff, etc"""

# The following comment should be removed at some point in the future.
# mypy: strict-optional=False

import logging
import os
import sys
Expand Down Expand Up @@ -87,6 +83,7 @@ def distutils_scheme(
prefix = i.install_userbase # type: ignore
else:
prefix = i.prefix
assert prefix is not None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably better to annotate prefix as str instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now the argument is defined as prefix: str = None. Are you suggesting to make the default an empty string or to make the argument required?

scheme["headers"] = os.path.join(
prefix,
"include",
Expand Down
3 changes: 2 additions & 1 deletion src/pip/_internal/operations/install/legacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@


class LegacyInstallFailure(Exception):
pass
def __init__(self, cause: Exception):
self.cause = cause
Comment on lines 21 to +23
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to call super().__init__(cause) as well.



def write_installed_files_from_setuptools_record(
Expand Down
26 changes: 15 additions & 11 deletions src/pip/_internal/operations/prepare.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
"""Prepares a distribution for installation
"""

# The following comment should be removed at some point in the future.
# mypy: strict-optional=False

import logging
import mimetypes
import os
Expand Down Expand Up @@ -187,6 +183,7 @@ def unpack_url(
"""
# non-editable vcs urls
if link.is_vcs:
assert location is not None
unpack_vcs_link(link, location)
return None

Expand Down Expand Up @@ -306,6 +303,7 @@ def __init__(

def _log_preparing_link(self, req: InstallRequirement) -> None:
"""Provide context for the requirement being prepared."""
assert req.link is not None
if req.link.is_file and not req.original_link_is_in_wheel_cache:
message = "Processing %s"
information = str(display_path(req.link.file_path))
Expand All @@ -325,6 +323,7 @@ def _ensure_link_req_src_dir(
self, req: InstallRequirement, parallel_builds: bool
) -> None:
"""Ensure source_dir of a linked InstallRequirement."""
assert req.link is not None
# Since source_dir is only set for editable requirements.
if req.link.is_wheel:
# We don't need to unpack wheels, so no need for a source
Expand All @@ -348,7 +347,7 @@ def _ensure_link_req_src_dir(
# installation.
# FIXME: this won't upgrade when there's an existing
# package unpacked in `req.source_dir`
if is_installable_dir(req.source_dir):
if req.source_dir is not None and is_installable_dir(req.source_dir):
raise PreviousBuildDirError(
"pip can't proceed with requirements '{}' due to a"
"pre-existing build directory ({}). This is likely "
Expand All @@ -370,10 +369,11 @@ def _get_linked_req_hashes(self, req: InstallRequirement) -> Hashes:
# and save repetition of conditions, but then we would
# report less-useful error messages for unhashable
# requirements, complaining that there's no hash provided.
if req.link.is_vcs:
raise VcsHashUnsupported()
if req.link.is_existing_dir():
raise DirectoryUrlHashUnsupported()
if req.link is not None:
if req.link.is_vcs:
raise VcsHashUnsupported()
if req.link.is_existing_dir():
raise DirectoryUrlHashUnsupported()

# Unpinned packages are asking for trouble when a new version
# is uploaded. This isn't a security check, but it saves users
Expand Down Expand Up @@ -487,7 +487,11 @@ def prepare_linked_requirements_more(
reqs = [req for req in reqs if req.needs_more_preparation]
for req in reqs:
# Determine if any of these requirements were already downloaded.
if self.download_dir is not None and req.link.is_wheel:
if (
self.download_dir is not None
and req.link is not None
and req.link.is_wheel
):
hashes = self._get_linked_req_hashes(req)
file_path = _check_download_dir(req.link, self.download_dir, hashes)
if file_path is not None:
Expand Down Expand Up @@ -521,7 +525,7 @@ def _prepare_linked_requirement(

if link.is_existing_dir() and self.in_tree_build:
local_file = None
elif link.url not in self._downloaded:
elif link.url not in self._downloaded and req.source_dir is not None:
try:
local_file = unpack_url(
link, req.source_dir, self._download, self.download_dir, hashes
Expand Down
26 changes: 18 additions & 8 deletions src/pip/_internal/req/req_install.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# The following comment should be removed at some point in the future.
# mypy: strict-optional=False

import logging
import os
import shutil
Expand Down Expand Up @@ -249,6 +246,7 @@ def name(self) -> Optional[str]:

@property
def specifier(self) -> SpecifierSet:
assert self.req is not None
return self.req.specifier

@property
Expand Down Expand Up @@ -299,7 +297,7 @@ def hashes(self, trust_internet: bool = True) -> Hashes:
"""
good_hashes = self.hash_options.copy()
link = self.link if trust_internet else self.original_link
if link and link.hash:
if link and link.hash_name and link.hash:
good_hashes.setdefault(link.hash_name, []).append(link.hash)
return Hashes(good_hashes)

Expand All @@ -310,7 +308,7 @@ def from_path(self) -> Optional[str]:
s = str(self.req)
if self.comes_from:
if isinstance(self.comes_from, str):
comes_from = self.comes_from
comes_from: Optional[str] = self.comes_from
else:
comes_from = self.comes_from.from_path()
if comes_from:
Expand Down Expand Up @@ -340,6 +338,7 @@ def ensure_build_location(

# When parallel builds are enabled, add a UUID to the build directory
# name so multiple builds do not interfere with each other.
assert self.name is not None
dir_name: str = canonicalize_name(self.name)
if parallel_builds:
dir_name = f"{dir_name}_{uuid.uuid4().hex}"
Expand Down Expand Up @@ -384,7 +383,7 @@ def _set_requirement(self) -> None:

def warn_on_mismatching_name(self) -> None:
metadata_name = canonicalize_name(self.metadata["Name"])
if canonicalize_name(self.req.name) == metadata_name:
if self.req is not None and canonicalize_name(self.req.name) == metadata_name:
# Everything is fine.
return

Expand Down Expand Up @@ -454,6 +453,7 @@ def is_wheel(self) -> bool:
# Things valid for sdists
@property
def unpacked_source_directory(self) -> str:
assert self.source_dir is not None
return os.path.join(
self.source_dir, self.link and self.link.subdirectory_fragment or ""
)
Expand Down Expand Up @@ -607,12 +607,17 @@ def metadata(self) -> Any:
return self._metadata

def get_dist(self) -> Distribution:
assert self.metadata_directory is not None
return _get_dist(self.metadata_directory)

def assert_source_matches_version(self) -> None:
assert self.source_dir
version = self.metadata["version"]
if self.req.specifier and version not in self.req.specifier:
if (
self.req is not None
and self.req.specifier
and version not in self.req.specifier
):
logger.warning(
"Requested %s, but installing version %s",
self,
Expand Down Expand Up @@ -705,6 +710,7 @@ def _clean_zip_name(name: str, prefix: str) -> str:
name = name.replace(os.path.sep, "/")
return name

assert self.name is not None
path = os.path.join(parentdir, path)
name = _clean_zip_name(path, rootdir)
return self.name + "/" + name
Expand Down Expand Up @@ -787,6 +793,8 @@ def install(
use_user_site: bool = False,
pycompile: bool = True,
) -> None:
assert self.name is not None

scheme = get_scheme(
self.name,
user=use_user_site,
Expand Down Expand Up @@ -866,7 +874,9 @@ def install(
)
except LegacyInstallFailure as exc:
self.install_succeeded = False
raise exc.__cause__
if exc.__cause__ is not None:
raise exc.__cause__
raise
except Exception:
self.install_succeeded = True
raise
Expand Down
11 changes: 6 additions & 5 deletions src/pip/_internal/resolution/legacy/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@
for sub-dependencies
a. "first found, wins" (where the order is breadth first)
"""

# The following comment should be removed at some point in the future.
# mypy: strict-optional=False

import logging
import sys
from collections import defaultdict
Expand Down Expand Up @@ -301,6 +297,8 @@ def _populate_link(self, req: InstallRequirement) -> None:

if self.wheel_cache is None or self.preparer.require_hashes:
return

assert req.link is not None
cache_entry = self.wheel_cache.get_cache_entry(
link=req.link,
package_name=req.name,
Expand Down Expand Up @@ -405,7 +403,9 @@ def add_req(subreq: Requirement, extras_requested: Iterable[str]) -> None:
with indent_log():
# We add req_to_install before its dependencies, so that we
# can refer to it when adding dependencies.
if not requirement_set.has_requirement(req_to_install.name):
if req_to_install.name is not None and not requirement_set.has_requirement(
req_to_install.name
):
# 'unnamed' requirements will get added here
# 'unnamed' requirements can only come from being directly
# provided by the user.
Expand Down Expand Up @@ -457,6 +457,7 @@ def schedule(req: InstallRequirement) -> None:
return
if req.constraint:
return
assert req.name is not None
ordered_reqs.add(req)
for dep in self._discovered_dependencies[req.name]:
schedule(dep)
Expand Down
Loading