Skip to content

Commit

Permalink
Add support for --resume-retries option
Browse files Browse the repository at this point in the history
- Added —resume-retries option to allow resuming incomplete downloads
- Setting —resume-retries=N allows pip to make N attempts to resume downloading, in case of dropped or timed out connections
- Each resume attempt uses the values specified for —retries and —timeout internally

Signed-off-by: gmargaritis <[email protected]>
  • Loading branch information
gmargaritis committed Oct 4, 2024
1 parent a091ca1 commit 16fb735
Show file tree
Hide file tree
Showing 10 changed files with 180 additions and 152 deletions.
1 change: 0 additions & 1 deletion news/11180.feature.rst

This file was deleted.

23 changes: 6 additions & 17 deletions src/pip/_internal/cli/cmdoptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1028,23 +1028,13 @@ def check_list_path_option(options: Values) -> None:
help=("Enable deprecated functionality, that will be removed in the future."),
)

incomplete_downloads: Callable[..., Option] = partial(
resume_retries: Callable[..., Option] = partial(
Option,
"--incomplete-downloads",
dest="resume_incomplete",
choices=["resume", "discard"],
default="discard",
metavar="policy",
help="How to handle an incomplete download: resume, discard (default to %default).",
)

incomplete_download_retries: Callable[..., Option] = partial(
Option,
"--incomplete-download-retries",
dest="resume_attempts",
"--resume-retries",
dest="resume_retries",
type="int",
default=5,
help="Maximum number of resumption retries for incomplete download "
default=0,
help="Maximum number of resumption retries for incomplete downloads"
"(default %default times).",
)

Expand Down Expand Up @@ -1080,8 +1070,7 @@ def check_list_path_option(options: Values) -> None:
no_python_version_warning,
use_new_feature,
use_deprecated_feature,
incomplete_downloads,
incomplete_download_retries,
resume_retries,
],
}

Expand Down
13 changes: 11 additions & 2 deletions src/pip/_internal/cli/progress_bars.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,17 @@ def get_download_progress_renderer(
Returns a callable, that takes an iterable to "wrap".
"""
if bar_type == "on":
return functools.partial(_rich_progress_bar, bar_type=bar_type, size=size, initial_progress=initial_progress,)
return functools.partial(
_rich_progress_bar,
bar_type=bar_type,
size=size,
initial_progress=initial_progress,
)
elif bar_type == "raw":
return functools.partial(_raw_progress_bar, size=size, initial_progress=initial_progress,)
return functools.partial(
_raw_progress_bar,
size=size,
initial_progress=initial_progress,
)
else:
return iter # no-op, when passed an iterator
5 changes: 1 addition & 4 deletions src/pip/_internal/cli/req_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,6 @@ def make_requirement_preparer(
"fast-deps has no effect when used with the legacy resolver."
)

resume_incomplete = options.resume_incomplete == "resume"

return RequirementPreparer(
build_dir=temp_build_dir_path,
src_dir=options.src_dir,
Expand All @@ -144,8 +142,7 @@ def make_requirement_preparer(
lazy_wheel=lazy_wheel,
verbosity=verbosity,
legacy_resolver=legacy_resolver,
resume_incomplete=resume_incomplete,
resume_attempts=options.resume_attempts,
resume_retries=options.resume_retries,
)

@classmethod
Expand Down
28 changes: 8 additions & 20 deletions src/pip/_internal/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -776,35 +776,23 @@ def __init__(self, *, distribution: "BaseDistribution") -> None:
hint_stmt=None,
)


class IncompleteDownloadError(DiagnosticPipError):
"""Raised when the downloader receives fewer bytes than advertised
in the Content-Length header."""

reference = "incomplete-download-error"

def __init__(
self, link: str, resume_incomplete: bool, resume_attempts: int
) -> None:
if resume_incomplete:
message = (
"Download failed after {} attempts because not enough bytes are"
" received. The incomplete file has been cleaned up."
).format(resume_attempts)
hint = "Use --incomplete-download-retries to configure resume retry limit."
else:
message = (
"Download failed because not enough bytes are received."
" The incomplete file has been cleaned up."
)
hint = (
"Use --incomplete-downloads=resume to make pip retry failed download."
)
def __init__(self, link: str, resume_retries: int) -> None:
message = (
f"Download failed after {resume_retries} attempts because not enough"
" bytes were received. The incomplete file has been cleaned up."
)
hint = "Use --resume-retries to configure resume retry limit."

super().__init__(
message=message,
context="File: {}\n"
"Resume failed download: {}\n"
"Resume retry limit: {}".format(link, resume_incomplete, resume_attempts),
context=f"File: {link}\nResume retry limit: {resume_retries}",
hint_stmt=hint,
note_stmt="This is an issue with network connectivity, not pip.",
)
Loading

0 comments on commit 16fb735

Please sign in to comment.