diff --git a/dvc/repo/__init__.py b/dvc/repo/__init__.py index 9e8149aca..37c653334 100644 --- a/dvc/repo/__init__.py +++ b/dvc/repo/__init__.py @@ -6,12 +6,10 @@ from typing import TYPE_CHECKING from funcy import cached_property, cat -from dvc.config import Config from dvc.exceptions import FileMissingError from dvc.exceptions import IsADirectoryError as DvcIsADirectoryError from dvc.exceptions import NotDvcRepoError, OutputNotFoundError from dvc.path_info import PathInfo -from dvc.scm.base import SCMError from dvc.utils.fs import path_isin from .graph import build_graph, build_outs_graph, get_pipelines @@ -94,6 +92,7 @@ class Repo: assert bool(scm) == bool(rev) from dvc.scm import SCM + from dvc.scm.base import SCMError from dvc.scm.git import Git from dvc.utils.fs import makedirs @@ -131,6 +130,7 @@ class Repo: config=None, ): from dvc.cache import Cache + from dvc.config import Config from dvc.data_cloud import DataCloud from dvc.lock import LockNoop, make_lock from dvc.repo.live import Live diff --git a/dvc/repo/fetch.py b/dvc/repo/fetch.py index a3e322131..475091e28 100644 --- a/dvc/repo/fetch.py +++ b/dvc/repo/fetch.py @@ -2,7 +2,6 @@ import logging from dvc.config import NoRemoteError from dvc.exceptions import DownloadError -from dvc.scm.base import CloneError from . import locked @@ -81,6 +80,7 @@ def fetch( def _fetch_external(self, repo_url, repo_rev, files, jobs): from dvc.external_repo import external_repo + from dvc.scm.base import CloneError failed, downloaded = 0, 0 cache = self.cache.local diff --git a/dvc/repo/get.py b/dvc/repo/get.py index 67443a063..68474dffb 100644 --- a/dvc/repo/get.py +++ b/dvc/repo/get.py @@ -1,8 +1,6 @@ import logging import os -import shortuuid - from dvc.exceptions import DvcException from dvc.utils import resolve_output from dvc.utils.fs import remove @@ -19,6 +17,8 @@ class GetDVCFileError(DvcException): def get(url, path, out=None, rev=None): + import shortuuid + from dvc.dvcfile import is_valid_filename from dvc.external_repo import external_repo diff --git a/dvc/repo/live.py b/dvc/repo/live.py index 126d15893..42a5e7b03 100644 --- a/dvc/repo/live.py +++ b/dvc/repo/live.py @@ -1,14 +1,18 @@ import contextlib import os -from typing import List +from typing import TYPE_CHECKING, List, Optional from dvc.exceptions import MetricDoesNotExistError, MetricsError -from dvc.output import BaseOutput -from dvc.path_info import PathInfo -from dvc.repo import Repo + +if TYPE_CHECKING: + from dvc.output import BaseOutput + from dvc.path_info import PathInfo + from dvc.repo import Repo -def summary_path_info(out: BaseOutput) -> PathInfo: +def summary_path_info(out: "BaseOutput") -> Optional["PathInfo"]: + from dvc.output import BaseOutput + assert out.live has_summary = True if isinstance(out.live, dict): @@ -19,7 +23,7 @@ def summary_path_info(out: BaseOutput) -> PathInfo: class Live: - def __init__(self, repo: Repo): + def __init__(self, repo: "Repo"): self.repo = repo def show(self, target: str, revs: List[str] = None): diff --git a/dvc/repo/plots/__init__.py b/dvc/repo/plots/__init__.py index 7703345c9..9b752f329 100644 --- a/dvc/repo/plots/__init__.py +++ b/dvc/repo/plots/__init__.py @@ -9,8 +9,6 @@ from dvc.exceptions import ( NoMetricsFoundError, NoMetricsParsedError, ) -from dvc.repo.collect import collect -from dvc.repo.plots.data import PlotParsingError from dvc.utils import relpath if TYPE_CHECKING: @@ -95,6 +93,8 @@ class Plots: @staticmethod def render(data, revs=None, props=None, templates=None): """Renders plots""" + from dvc.repo.plots.data import PlotParsingError + props = props or {} # Merge data by plot file and apply overriding props @@ -213,6 +213,8 @@ def _collect_plots( rev: str = None, recursive: bool = False, ) -> Dict[str, Dict]: + from dvc.repo.collect import collect + plots, path_infos = collect( repo, output_filter=_is_plot, diff --git a/dvc/repo/run.py b/dvc/repo/run.py index 1125383a9..045524b5a 100644 --- a/dvc/repo/run.py +++ b/dvc/repo/run.py @@ -3,14 +3,6 @@ from contextlib import suppress from funcy import concat, first, lfilter, without -from dvc.exceptions import InvalidArgumentError -from dvc.stage.exceptions import ( - DuplicateStageName, - InvalidStageName, - StageFileAlreadyExistsError, -) - -from ..exceptions import OutputDuplicationError from . import locked from .scm_context import scm_context @@ -61,6 +53,10 @@ def _get_file_path(kwargs): def _check_stage_exists(dvcfile, stage): from dvc.stage import PipelineStage + from dvc.stage.exceptions import ( + DuplicateStageName, + StageFileAlreadyExistsError, + ) if not dvcfile.exists(): return @@ -80,7 +76,9 @@ def _check_stage_exists(dvcfile, stage): @scm_context def run(self, fname=None, no_exec=False, single_stage=False, **kwargs): from dvc.dvcfile import PIPELINE_FILE, Dvcfile + from dvc.exceptions import InvalidArgumentError, OutputDuplicationError from dvc.stage import PipelineStage, Stage, create_stage, restore_meta + from dvc.stage.exceptions import InvalidStageName if not kwargs.get("cmd"): raise InvalidArgumentError("command is not specified") diff --git a/dvc/repo/stage.py b/dvc/repo/stage.py index eaf137b0c..1d90af8d3 100644 --- a/dvc/repo/stage.py +++ b/dvc/repo/stage.py @@ -5,15 +5,12 @@ import typing from contextlib import suppress from typing import Iterable, List, NamedTuple, Optional, Set, Tuple -from dvc.dvcfile import PIPELINE_FILE, Dvcfile, is_valid_filename from dvc.exceptions import ( DvcException, NoOutputOrStageError, OutputNotFoundError, ) from dvc.path_info import PathInfo -from dvc.repo.graph import collect_inside_path, collect_pipeline -from dvc.stage.exceptions import StageFileDoesNotExistError, StageNotFound from dvc.utils import parse_target, relpath logger = logging.getLogger(__name__) @@ -26,6 +23,8 @@ if typing.TYPE_CHECKING: from dvc.stage.loader import StageLoader from dvc.types import OptStr +PIPELINE_FILE = "dvc.yaml" + class StageInfo(NamedTuple): stage: "Stage" @@ -38,6 +37,8 @@ StageSet = Set["Stage"] def _collect_with_deps(stages: StageList, graph: "DiGraph") -> StageSet: + from dvc.repo.graph import collect_pipeline + res: StageSet = set() for stage in stages: res.update(collect_pipeline(stage, graph=graph)) @@ -47,6 +48,7 @@ def _collect_with_deps(stages: StageList, graph: "DiGraph") -> StageSet: def _maybe_collect_from_dvc_yaml( loader: "StageLoad", target, with_deps: bool, **load_kwargs, ) -> StageIter: + from dvc.stage.exceptions import StageNotFound stages: StageList = [] if loader.tree.exists(PIPELINE_FILE): @@ -62,6 +64,8 @@ def _collect_specific_target( recursive: bool, accept_group: bool, ) -> Tuple[StageIter, "OptStr", "OptStr"]: + from dvc.dvcfile import is_valid_filename + # Optimization: do not collect the graph for a specific target file, name = parse_target(target) @@ -163,6 +167,7 @@ class StageLoad: glob: if true, `name` is considered as a glob, which is used to filter list of stages from the given `path`. """ + from dvc.dvcfile import Dvcfile from dvc.stage.loader import SingleStageLoader, StageLoader path = self._get_filepath(path, name) @@ -185,6 +190,8 @@ class StageLoad: path: if not provided, default `dvc.yaml` is assumed. name: required for `dvc.yaml` files, ignored for `.dvc` files. """ + from dvc.dvcfile import Dvcfile + path = self._get_filepath(path, name) dvcfile = Dvcfile(self.repo, path) @@ -251,6 +258,8 @@ class StageLoad: return list(graph) if graph else self.repo.stages if recursive and self.repo.tree.isdir(target): + from dvc.repo.graph import collect_inside_path + path = os.path.abspath(target) return collect_inside_path(path, graph or self.graph) @@ -301,6 +310,12 @@ class StageLoad: except OutputNotFoundError: pass + from dvc.dvcfile import is_valid_filename + from dvc.stage.exceptions import ( + StageFileDoesNotExistError, + StageNotFound, + ) + try: stages = self.collect( target, @@ -331,6 +346,8 @@ class StageLoad: Otherwise, the file will be skipped, and dvc will partially load the stages. """ + from dvc.dvcfile import is_valid_filename + stages = [] outs = set() for root, dirs, files in self.tree.walk(self.repo.root_dir):