diff --git a/dvc/command/base.py b/dvc/command/base.py index 4725cbbe36..f4d44b2425 100644 --- a/dvc/command/base.py +++ b/dvc/command/base.py @@ -55,16 +55,6 @@ def default_targets(self): logger.warning(msg) return [Stage.STAGE_FILE] - def run_cmd(self): - from dvc.lock import LockError - - try: - with self.repo.lock: - return self.run() - except LockError: - logger.exception("failed to lock before running a command") - return 1 - # Abstract methods that have to be implemented by any inheritance class def run(self): pass @@ -73,6 +63,3 @@ def run(self): class CmdBaseNoRepo(CmdBase): def __init__(self, args): self.args = args - - def run_cmd(self): - return self.run() diff --git a/dvc/command/destroy.py b/dvc/command/destroy.py index 1fad83cbf2..ca739de4ae 100644 --- a/dvc/command/destroy.py +++ b/dvc/command/destroy.py @@ -12,7 +12,7 @@ class CmdDestroy(CmdBase): - def run_cmd(self): + def run(self): try: statement = ( "This will destroy all information about your pipelines," diff --git a/dvc/command/install.py b/dvc/command/install.py index 4967eee6e9..081714fde5 100644 --- a/dvc/command/install.py +++ b/dvc/command/install.py @@ -10,7 +10,7 @@ class CmdInstall(CmdBase): - def run_cmd(self): + def run(self): try: self.repo.install() except Exception: diff --git a/dvc/main.py b/dvc/main.py index 71687c4772..4c55862bfc 100644 --- a/dvc/main.py +++ b/dvc/main.py @@ -5,6 +5,7 @@ import logging from dvc.cli import parse_args +from dvc.lock import LockError from dvc.config import ConfigError from dvc.analytics import Analytics from dvc.exceptions import NotDvcRepoError, DvcParserError @@ -37,7 +38,10 @@ def main(argv=None): logger.setLevel(logging.DEBUG) cmd = args.func(args) - ret = cmd.run_cmd() + ret = cmd.run() + except LockError: + logger.exception("failed to lock before running a command") + ret = 250 except ConfigError: logger.exception("configuration error") ret = 251 diff --git a/dvc/repo/__init__.py b/dvc/repo/__init__.py index 16317a68fc..1879669a3b 100644 --- a/dvc/repo/__init__.py +++ b/dvc/repo/__init__.py @@ -5,6 +5,7 @@ from contextlib import contextmanager from itertools import chain +from functools import wraps from funcy import cached_property from dvc.config import Config @@ -23,6 +24,15 @@ logger = logging.getLogger(__name__) +def locked(f): + @wraps(f) + def wrapper(repo, *args, **kwargs): + with repo.lock: + return f(repo, *args, **kwargs) + + return wrapper + + class Repo(object): DVC_DIR = ".dvc" @@ -36,9 +46,9 @@ class Repo(object): from dvc.repo.imp import imp from dvc.repo.imp_url import imp_url from dvc.repo.reproduce import reproduce - from dvc.repo.checkout import checkout + from dvc.repo.checkout import _checkout from dvc.repo.push import push - from dvc.repo.fetch import fetch + from dvc.repo.fetch import _fetch from dvc.repo.pull import pull from dvc.repo.status import status from dvc.repo.gc import gc @@ -486,3 +496,11 @@ def clone(url, to_path, rev=None): git.close() return Repo(to_path) + + @locked + def checkout(self, *args, **kwargs): + return self._checkout(*args, **kwargs) + + @locked + def fetch(self, *args, **kwargs): + return self._fetch(*args, **kwargs) diff --git a/dvc/repo/add.py b/dvc/repo/add.py index e059368f20..6822b8f633 100644 --- a/dvc/repo/add.py +++ b/dvc/repo/add.py @@ -7,10 +7,13 @@ from dvc.utils import walk_files, LARGE_DIR_SIZE from dvc.exceptions import RecursiveAddingWhileUsingFilename +from . import locked + logger = logging.getLogger(__name__) +@locked @scm_context def add(repo, target, recursive=False, no_commit=False, fname=None): if recursive and fname: diff --git a/dvc/repo/checkout.py b/dvc/repo/checkout.py index de7e77cea1..e638aa468e 100644 --- a/dvc/repo/checkout.py +++ b/dvc/repo/checkout.py @@ -23,7 +23,9 @@ def get_all_files_numbers(stages): return sum(stage.get_all_files_number() for stage in stages) -def checkout(self, target=None, with_deps=False, force=False, recursive=False): +def _checkout( + self, target=None, with_deps=False, force=False, recursive=False +): from dvc.stage import StageFileDoesNotExistError, StageFileBadNameError all_stages = self.stages() diff --git a/dvc/repo/commit.py b/dvc/repo/commit.py index eb297493a8..4f48e1dab3 100644 --- a/dvc/repo/commit.py +++ b/dvc/repo/commit.py @@ -1,3 +1,7 @@ +from . import locked + + +@locked def commit(self, target, with_deps=False, recursive=False, force=False): stages = self.collect(target, with_deps=with_deps, recursive=recursive) with self.state: diff --git a/dvc/repo/diff.py b/dvc/repo/diff.py index f2ee3dc330..cdfe489075 100644 --- a/dvc/repo/diff.py +++ b/dvc/repo/diff.py @@ -14,6 +14,8 @@ DIFF_EQUAL, ) +from . import locked + DIFF_TARGET = "target" DIFF_IS_DIR = "is_dir" @@ -220,6 +222,7 @@ def _diff_royal(self, target, diff_dct): return _diff_file(self, target, diff_dct) +@locked def diff(self, a_ref, target=None, b_ref=None): """Gerenates diff message string output diff --git a/dvc/repo/fetch.py b/dvc/repo/fetch.py index 3132e30d33..d72b787a7d 100644 --- a/dvc/repo/fetch.py +++ b/dvc/repo/fetch.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals -def fetch( +def _fetch( self, targets=None, jobs=None, diff --git a/dvc/repo/gc.py b/dvc/repo/gc.py index c334b5ebe0..b6719a6b07 100644 --- a/dvc/repo/gc.py +++ b/dvc/repo/gc.py @@ -3,6 +3,8 @@ import collections import logging +from . import locked + logger = logging.getLogger(__name__) @@ -53,6 +55,7 @@ def _do_gc(typ, func, clist): logger.info("No unused {} cache to remove.".format(typ)) +@locked def gc( self, all_branches=False, diff --git a/dvc/repo/imp_url.py b/dvc/repo/imp_url.py index 9125c9340a..366570c4d9 100644 --- a/dvc/repo/imp_url.py +++ b/dvc/repo/imp_url.py @@ -1,7 +1,10 @@ from dvc.utils.compat import pathlib from dvc.repo.scm_context import scm_context +from . import locked as locked_repo + +@locked_repo @scm_context def imp_url(self, url, out=None, fname=None, erepo=None, locked=True): from dvc.stage import Stage diff --git a/dvc/repo/lock.py b/dvc/repo/lock.py index 618fb7aa2c..6da2576040 100644 --- a/dvc/repo/lock.py +++ b/dvc/repo/lock.py @@ -1,3 +1,7 @@ +from . import locked + + +@locked def lock(self, target, unlock=False): from dvc.stage import Stage diff --git a/dvc/repo/move.py b/dvc/repo/move.py index 84c506ca74..58c9777f46 100644 --- a/dvc/repo/move.py +++ b/dvc/repo/move.py @@ -3,6 +3,8 @@ from dvc.exceptions import MoveNotDataSourceError from dvc.repo.scm_context import scm_context +from . import locked + def _expand_target_path(from_path, to_path): if os.path.isdir(to_path): @@ -10,6 +12,7 @@ def _expand_target_path(from_path, to_path): return to_path +@locked @scm_context def move(self, from_path, to_path): """ diff --git a/dvc/repo/pull.py b/dvc/repo/pull.py index 34221498fa..851d83bb30 100644 --- a/dvc/repo/pull.py +++ b/dvc/repo/pull.py @@ -1,6 +1,9 @@ from __future__ import unicode_literals +from . import locked + +@locked def pull( self, targets=None, @@ -12,7 +15,7 @@ def pull( force=False, recursive=False, ): - processed_files_count = self.fetch( + processed_files_count = self._fetch( targets, jobs, remote=remote, @@ -22,7 +25,7 @@ def pull( recursive=recursive, ) for target in targets or [None]: - self.checkout( + self._checkout( target=target, with_deps=with_deps, force=force, diff --git a/dvc/repo/push.py b/dvc/repo/push.py index f2e84d7788..a307399640 100644 --- a/dvc/repo/push.py +++ b/dvc/repo/push.py @@ -1,6 +1,9 @@ from __future__ import unicode_literals +from . import locked + +@locked def push( self, targets=None, diff --git a/dvc/repo/remove.py b/dvc/repo/remove.py index 80792e4972..6c751e565d 100644 --- a/dvc/repo/remove.py +++ b/dvc/repo/remove.py @@ -1,3 +1,7 @@ +from . import locked + + +@locked def remove(self, target, outs_only=False): from dvc.stage import Stage diff --git a/dvc/repo/reproduce.py b/dvc/repo/reproduce.py index ea3271f271..1b7b5b372e 100644 --- a/dvc/repo/reproduce.py +++ b/dvc/repo/reproduce.py @@ -7,6 +7,9 @@ from dvc.repo.scm_context import scm_context from dvc.utils import relpath +from . import locked + + logger = logging.getLogger(__name__) @@ -29,6 +32,7 @@ def _reproduce_stage(stages, node, **kwargs): return [stage] +@locked @scm_context def reproduce( self, diff --git a/dvc/repo/run.py b/dvc/repo/run.py index 5c711b1a6f..0fd4728c7f 100644 --- a/dvc/repo/run.py +++ b/dvc/repo/run.py @@ -1,8 +1,10 @@ from __future__ import unicode_literals -from dvc.repo.scm_context import scm_context +from . import locked +from .scm_context import scm_context +@locked @scm_context def run(self, no_exec=False, **kwargs): from dvc.stage import Stage diff --git a/dvc/repo/status.py b/dvc/repo/status.py index f25dd92269..c5da186d03 100644 --- a/dvc/repo/status.py +++ b/dvc/repo/status.py @@ -3,6 +3,8 @@ import logging from funcy.py3 import cat +from . import locked + logger = logging.getLogger(__name__) @@ -91,6 +93,7 @@ def _cloud_status( return ret +@locked def status( self, targets=None, diff --git a/dvc/repo/update.py b/dvc/repo/update.py index bf97fca4e8..05c2f27cf4 100644 --- a/dvc/repo/update.py +++ b/dvc/repo/update.py @@ -1,3 +1,7 @@ +from . import locked + + +@locked def update(self, target): from dvc.stage import Stage