diff --git a/dvc/repo/experiments/executor.py b/dvc/repo/experiments/executor.py index 25674d9e2..711b50734 100644 --- a/dvc/repo/experiments/executor.py +++ b/dvc/repo/experiments/executor.py @@ -1,6 +1,7 @@ import logging import os import pickle +import sys from functools import partial from tempfile import TemporaryDirectory from typing import TYPE_CHECKING, Callable, Iterable, Optional, Tuple, Union @@ -343,4 +344,11 @@ class LocalExecutor(BaseExecutor): def cleanup(self): super().cleanup() logger.debug("Removing tmpdir '%s'", self._tmp_dir) - self._tmp_dir.cleanup() + try: + self._tmp_dir.cleanup() + except PermissionError: + if os.name == "nt" and sys.version_info < (3, 8): + # see https://bugs.python.org/issue26660 + remove(self._tmp_dir.name) + return + raise diff --git a/dvc/scm/git/backend/dulwich.py b/dvc/scm/git/backend/dulwich.py index c8bc9256f..478ea23cd 100644 --- a/dvc/scm/git/backend/dulwich.py +++ b/dvc/scm/git/backend/dulwich.py @@ -57,10 +57,28 @@ class DulwichBackend(BaseGitBackend): # pylint:disable=abstract-method return self.repo.commondir() def add(self, paths: Iterable[str]): - raise NotImplementedError + from dvc.utils.fs import walk_files + + if isinstance(paths, str): + paths = [paths] + + files = [] + for path in paths: + if not os.path.isabs(path): + path = os.path.join(self.root_dir, path) + if os.path.isdir(path): + files.extend(walk_files(path)) + else: + files.append(path) + + for fpath in files: + # NOTE: this doesn't check gitignore, same as GitPythonBackend.add + self.repo.stage(relpath(fpath, self.root_dir)) def commit(self, msg: str): - raise NotImplementedError + from dulwich.porcelain import commit + + commit(self.root_dir, message=msg) def checkout( self, branch: str, create_new: Optional[bool] = False, **kwargs, @@ -83,7 +101,14 @@ class DulwichBackend(BaseGitBackend): # pylint:disable=abstract-method raise NotImplementedError def is_tracked(self, path: str) -> bool: - raise NotImplementedError + from dvc.path_info import PathInfo + + rel = PathInfo(path).relative_to(self.root_dir).as_posix().encode() + rel_dir = rel + b"/" + for path in self.repo.open_index(): + if path == rel or path.startswith(rel_dir): + return True + return False def is_dirty(self, **kwargs) -> bool: raise NotImplementedError diff --git a/tests/func/experiments/test_experiments.py b/tests/func/experiments/test_experiments.py index 160add628..0955f0b4a 100644 --- a/tests/func/experiments/test_experiments.py +++ b/tests/func/experiments/test_experiments.py @@ -235,11 +235,19 @@ def test_update_py_params(tmp_dir, scm, dvc): "class Klass:\n def __init__(self):\n self.a = 222" ) + def _dos2unix(text): + if os.name != "nt": + return text + + # NOTE: git on windows will use CRLF, so we have to convert it to LF + # in order to compare with the original + return text.replace("\r\n", "\n") + tree = scm.get_tree(exp_a) with tree.open(tmp_dir / "params.py") as fobj: - assert fobj.read().strip() == result + assert _dos2unix(fobj.read().strip()) == result with tree.open(tmp_dir / "metrics.py") as fobj: - assert fobj.read().strip() == result + assert _dos2unix(fobj.read().strip()) == result tmp_dir.gen("params.py", "INT = 1\n") stage = dvc.run(