mirror of
https://github.com/Stone-Red-Code/dvc.git
synced 2026-11-02 07:18:25 +01:00
There is more we can do during the `Fetching` stage, but it will require additional tweaks, so just keeping it simple here. Similar to https://github.com/iterative/dvc/pull/9974
214 lines
5.4 KiB
Python
214 lines
5.4 KiB
Python
import logging
|
|
from typing import List, Tuple
|
|
|
|
from dvc.exceptions import DownloadError
|
|
from dvc.ui import ui
|
|
from dvc_data.index import DataIndex, FileStorage
|
|
|
|
from . import locked
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def _make_index_onerror(onerror, rev):
|
|
def _onerror(entry, exc):
|
|
if onerror:
|
|
return onerror(rev, entry, exc)
|
|
|
|
return _onerror
|
|
|
|
|
|
def _collect_indexes( # noqa: PLR0913
|
|
repo,
|
|
targets=None,
|
|
remote=None,
|
|
all_branches=False,
|
|
with_deps=False,
|
|
all_tags=False,
|
|
recursive=False,
|
|
all_commits=False,
|
|
revs=None,
|
|
max_size=None,
|
|
types=None,
|
|
config=None,
|
|
onerror=None,
|
|
):
|
|
indexes = {}
|
|
collection_exc = None
|
|
|
|
config = config or {}
|
|
if remote:
|
|
core = config.get("core") or {}
|
|
core["remote"] = remote
|
|
config["core"] = core
|
|
|
|
for rev in repo.brancher(
|
|
revs=revs,
|
|
all_branches=all_branches,
|
|
all_tags=all_tags,
|
|
all_commits=all_commits,
|
|
):
|
|
try:
|
|
repo.config.merge(config)
|
|
|
|
idx = repo.index.targets_view(
|
|
targets,
|
|
with_deps=with_deps,
|
|
recursive=recursive,
|
|
max_size=max_size,
|
|
types=types,
|
|
)
|
|
|
|
data = idx.data["repo"]
|
|
data.onerror = _make_index_onerror(onerror, rev)
|
|
|
|
indexes[idx.data_tree.hash_info.value] = data
|
|
except Exception as exc: # pylint: disable=broad-except
|
|
if onerror:
|
|
onerror(rev, None, exc)
|
|
collection_exc = exc
|
|
logger.exception("failed to collect '%s'", rev or "workspace")
|
|
|
|
if not indexes and collection_exc:
|
|
raise collection_exc
|
|
|
|
return indexes
|
|
|
|
|
|
@locked
|
|
def fetch( # noqa: C901, PLR0913
|
|
self,
|
|
targets=None,
|
|
jobs=None,
|
|
remote=None,
|
|
all_branches=False,
|
|
with_deps=False,
|
|
all_tags=False,
|
|
recursive=False,
|
|
all_commits=False,
|
|
run_cache=False,
|
|
revs=None,
|
|
max_size=None,
|
|
types=None,
|
|
config=None,
|
|
onerror=None,
|
|
) -> int:
|
|
"""Download data items from a cloud and imported repositories
|
|
|
|
Returns:
|
|
int: number of successfully downloaded files
|
|
|
|
Raises:
|
|
DownloadError: thrown when there are failed downloads, either
|
|
during `cloud.pull` or trying to fetch imported files
|
|
|
|
config.NoRemoteError: thrown when downloading only local files and no
|
|
remote is configured
|
|
"""
|
|
from fsspec.utils import tokenize
|
|
|
|
from dvc_data.index.fetch import collect
|
|
from dvc_data.index.fetch import fetch as ifetch
|
|
|
|
if isinstance(targets, str):
|
|
targets = [targets]
|
|
|
|
failed_count = 0
|
|
transferred_count = 0
|
|
|
|
try:
|
|
if run_cache:
|
|
self.stage_cache.pull(remote)
|
|
except DownloadError as exc:
|
|
failed_count += exc.amount
|
|
|
|
indexes = _collect_indexes(
|
|
self,
|
|
targets=targets,
|
|
remote=remote,
|
|
all_branches=all_branches,
|
|
with_deps=with_deps,
|
|
all_tags=all_tags,
|
|
recursive=recursive,
|
|
all_commits=all_commits,
|
|
revs=revs,
|
|
max_size=max_size,
|
|
types=types,
|
|
config=config,
|
|
onerror=onerror,
|
|
)
|
|
|
|
cache_key = ("fetch", tokenize(sorted(indexes.keys())))
|
|
|
|
with ui.progress(
|
|
desc="Collecting",
|
|
unit="entry",
|
|
leave=True,
|
|
) as pb:
|
|
data = collect(
|
|
indexes.values(),
|
|
"remote",
|
|
cache_index=self.data_index,
|
|
cache_key=cache_key,
|
|
callback=pb.as_callback(),
|
|
)
|
|
data, unversioned_count = _log_unversioned(data)
|
|
failed_count += unversioned_count
|
|
|
|
with ui.progress(
|
|
desc="Fetching",
|
|
bar_format="{desc}",
|
|
leave=True,
|
|
) as pb:
|
|
try:
|
|
fetch_transferred, fetch_failed = ifetch(
|
|
data,
|
|
jobs=jobs,
|
|
callback=pb.as_callback(),
|
|
) # pylint: disable=assignment-from-no-return
|
|
finally:
|
|
for fs_index in data:
|
|
fs_index.close()
|
|
|
|
if fetch_transferred:
|
|
# NOTE: dropping cached index to force reloading from newly saved cache
|
|
self.drop_data_index()
|
|
|
|
transferred_count += fetch_transferred
|
|
failed_count += fetch_failed
|
|
if failed_count:
|
|
raise DownloadError(failed_count)
|
|
|
|
return transferred_count
|
|
|
|
|
|
def _log_unversioned(data: List["DataIndex"]) -> Tuple[List["DataIndex"], int]:
|
|
ret: List["DataIndex"] = []
|
|
unversioned: List[str] = []
|
|
for fs_index in data:
|
|
remote = fs_index.storage_map[()].remote
|
|
if not isinstance(remote, FileStorage) or not remote.fs.version_aware:
|
|
ret.append(fs_index)
|
|
continue
|
|
|
|
fs = remote.fs
|
|
index = DataIndex()
|
|
index.storage_map = fs_index.storage_map
|
|
for key, entry in fs_index.iteritems():
|
|
if entry.meta and not entry.meta.isdir and entry.meta.version_id is None:
|
|
unversioned.append(fs.unstrip_protocol(fs.path.join(remote.path, *key)))
|
|
else:
|
|
index[key] = entry
|
|
fs_index.close()
|
|
ret.append(index)
|
|
|
|
if unversioned:
|
|
logger.warning(
|
|
(
|
|
"Some files are missing cloud version information and will not be "
|
|
"fetched from the remote:\n%s"
|
|
),
|
|
"\n".join(unversioned),
|
|
)
|
|
return ret, len(unversioned)
|