Skip to content

Commit 50c5e51

Browse files
move the partial requirement download completion to the bottom of the prepare_more method
1 parent 15e61d6 commit 50c5e51

File tree

4 files changed

+46
-63
lines changed

4 files changed

+46
-63
lines changed

news/8896.trivial

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Move the batched download of lazily-fetched wheel files out of the ``Resolver.resolve()`` codepath, into ``PartialRequirementDownloadCompleter``. Consume this in the ``pip download``, ``pip wheel``, and ``pip install`` tasks.
1+
Separate the batched *download* of lazily-fetched wheel files from the preparation of regularly-downloaded requirements in ``RequirementPreparer.prepare_linked_requirements_more()``.

src/pip/_internal/network/download.py

Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,11 @@
2424
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
2525

2626
if MYPY_CHECK_RUNNING:
27-
from typing import Dict, Iterable, Optional, Tuple
27+
from typing import Iterable, Optional, Tuple
2828

2929
from pip._vendor.requests.models import Response
3030

3131
from pip._internal.models.link import Link
32-
from pip._internal.models.req_install import InstallRequirement
33-
from pip._internal.models.req_set import RequirementSet
3432
from pip._internal.network.session import PipSession
3533

3634
logger = logging.getLogger(__name__)
@@ -176,7 +174,7 @@ def __call__(self, link, location):
176174
return filepath, content_type
177175

178176

179-
class _BatchDownloader(object):
177+
class BatchDownloader(object):
180178

181179
def __init__(
182180
self,
@@ -210,37 +208,3 @@ def __call__(self, links, location):
210208
content_file.write(chunk)
211209
content_type = resp.headers.get('Content-Type', '')
212210
yield link, (filepath, content_type)
213-
214-
215-
def complete_partial_requirement_downloads(
216-
session, # type: PipSession
217-
progress_bar, # type: str
218-
req_set, # type: RequirementSet
219-
download_dir, # type: str
220-
):
221-
# type: (...) -> None
222-
"""Download any requirements which were only partially downloaded with
223-
--use-feature=fast-deps."""
224-
batch_downloader = _BatchDownloader(session, progress_bar)
225-
226-
reqs_to_fully_download = [
227-
r for r in req_set.requirements.values()
228-
if r.needs_more_preparation
229-
]
230-
231-
# Map each link to the requirement that owns it. This allows us to set
232-
# `req.local_file_path` on the appropriate requirement after passing
233-
# all the links at once into BatchDownloader.
234-
links_to_fully_download = {} # type: Dict[Link, InstallRequirement]
235-
for req in reqs_to_fully_download:
236-
assert req.link
237-
links_to_fully_download[req.link] = req
238-
239-
batch_download = batch_downloader(
240-
links_to_fully_download.keys(),
241-
download_dir,
242-
)
243-
for link, (filepath, _) in batch_download:
244-
logger.debug("Downloading link %s to %s", link, filepath)
245-
req = links_to_fully_download[link]
246-
req.local_file_path = filepath

src/pip/_internal/operations/prepare.py

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,7 @@
2626
VcsHashUnsupported,
2727
)
2828
from pip._internal.models.wheel import Wheel
29-
from pip._internal.network.download import (
30-
Downloader,
31-
complete_partial_requirement_downloads,
32-
)
29+
from pip._internal.network.download import BatchDownloader, Downloader
3330
from pip._internal.network.lazy_wheel import (
3431
HTTPRangeRequestUnsupported,
3532
dist_from_wheel_url,
@@ -56,7 +53,6 @@
5653

5754
from pip._internal.index.package_finder import PackageFinder
5855
from pip._internal.models.link import Link
59-
from pip._internal.models.req_set import RequirementSet
6056
from pip._internal.network.session import PipSession
6157
from pip._internal.req.req_install import InstallRequirement
6258
from pip._internal.req.req_tracker import RequirementTracker
@@ -329,6 +325,7 @@ def __init__(
329325
self.req_tracker = req_tracker
330326
self._session = session
331327
self._download = Downloader(session, progress_bar)
328+
self._batch_download = BatchDownloader(session, progress_bar)
332329
self.finder = finder
333330

334331
# Where still-packed archives should be written to. If None, they are
@@ -356,10 +353,6 @@ def __init__(
356353

357354
# Should wheels be downloaded lazily?
358355
self.use_lazy_wheel = lazy_wheel
359-
# TODO: this field is only needed in
360-
# .complete_partial_requirements(). When the v1 resolver can be
361-
# removed, partial downloads can be completed outside of the resolver.
362-
self._progress_bar = progress_bar
363356

364357
# Memoized downloaded files, as mapping of url: (path, mime type)
365358
self._downloaded = {} # type: Dict[str, Tuple[str, str]]
@@ -498,16 +491,38 @@ def _fetch_metadata_using_lazy_wheel(self, link):
498491
logger.debug('%s does not support range requests', url)
499492
return None
500493

501-
def complete_partial_requirements(self, req_set):
502-
# type: (RequirementSet) -> None
494+
def _complete_partial_requirements(
495+
self,
496+
partially_downloaded_reqs, # type: Iterable[InstallRequirement]
497+
parallel_builds=False, # type: bool
498+
):
499+
# type: (...) -> None
503500
"""Download any requirements which were only fetched by metadata."""
504-
download_location = self.wheel_download_dir or self.download_dir
505-
complete_partial_requirement_downloads(
506-
self._session,
507-
self._progress_bar,
508-
req_set,
509-
download_location,
501+
# Download to a temporary directory. These will be copied over as
502+
# needed for downstream 'download', 'wheel', and 'install' commands.
503+
temp_dir = TempDirectory(kind="unpack", globally_managed=True).path
504+
505+
# Map each link to the requirement that owns it. This allows us to set
506+
# `req.local_file_path` on the appropriate requirement after passing
507+
# all the links at once into BatchDownloader.
508+
links_to_fully_download = {} # type: Dict[Link, InstallRequirement]
509+
for req in partially_downloaded_reqs:
510+
assert req.link
511+
links_to_fully_download[req.link] = req
512+
513+
batch_download = self._batch_download(
514+
links_to_fully_download.keys(),
515+
temp_dir,
510516
)
517+
for link, (filepath, _) in batch_download:
518+
logger.debug("Downloading link %s to %s", link, filepath)
519+
req = links_to_fully_download[link]
520+
req.local_file_path = filepath
521+
522+
# This step is necessary to ensure all lazy wheels are processed
523+
# successfully by the 'download', 'wheel', and 'install' commands.
524+
for req in partially_downloaded_reqs:
525+
self._prepare_linked_requirement(req, parallel_builds)
511526

512527
def prepare_linked_requirement(self, req, parallel_builds=False):
513528
# type: (InstallRequirement, bool) -> Distribution
@@ -537,11 +552,20 @@ def prepare_linked_requirements_more(self, reqs, parallel_builds=False):
537552
req.needs_more_preparation = False
538553

539554
# Prepare requirements we found were already downloaded for some
540-
# reason. The other downloads will be completed elsewhere.
555+
# reason. The other downloads will be completed separately.
556+
partially_downloaded_reqs = [] # type: List[InstallRequirement]
541557
for req in reqs:
542-
if not req.needs_more_preparation:
558+
if req.needs_more_preparation:
559+
partially_downloaded_reqs.append(req)
560+
else:
543561
self._prepare_linked_requirement(req, parallel_builds)
544562

563+
# TODO: separate this part out from RequirementPreparer when the v1
564+
# resolver can be removed!
565+
self._complete_partial_requirements(
566+
partially_downloaded_reqs, parallel_builds=parallel_builds,
567+
)
568+
545569
def _prepare_linked_requirement(self, req, parallel_builds):
546570
# type: (InstallRequirement, bool) -> Distribution
547571
assert req.link

src/pip/_internal/resolution/resolvelib/resolver.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,6 @@ def resolve(self, root_reqs, check_supported_wheels):
162162

163163
reqs = req_set.all_requirements
164164
self.factory.preparer.prepare_linked_requirements_more(reqs)
165-
166-
# TODO: extricate this call from the resolver.resolve() code path once
167-
# we can drop the v1 resolver.
168-
self.factory.preparer.complete_partial_requirements(req_set)
169-
170165
return req_set
171166

172167
def get_installation_order(self, req_set):

0 commit comments

Comments
 (0)