forked from iterative/dvc
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
dvc: retain formatting and comments in .dvc files (iterative#1885)
* test: remove never used copy of load_stage_file() * dvc: save comments and custom formatting in stage files * Add a couple of TODOs * test: test repro doesn't clear comments and formatting in .dvc As part of this I started writing pytest-like tests and added commonly used fixtures. * test: simplify and prettify dvc formatting retained test Also test that line comments after md5 fields are retained. * test: fix tests * dvc: fix stage and metrics opens not closed * dvc: always use utf-8 in stage files It used to encode in system default encoding. Not sure if PyYAML was handling this silently or we were just lucky all the way. * test: unit test apply_diff() * test: remove TODOs and ASK * dvc: polish dvc-formatting PR * dvc: move stage load/dump utils to a new stage utils file * dvc: clean up apply_diff() * test: test stage md5 ignores comments * test: remove pytests starting marker
- Loading branch information
Showing
22 changed files
with
356 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,47 @@ | ||
from __future__ import unicode_literals | ||
from __future__ import absolute_import, unicode_literals | ||
from dvc.utils.compat import Mapping | ||
|
||
|
||
# just simple check for Nones and emtpy strings | ||
def compact(args): | ||
return list(filter(bool, args)) | ||
|
||
|
||
def apply_diff(src, dest): | ||
"""Recursively apply changes from stc to dest""" | ||
Seq = (list, tuple) | ||
Container = (Mapping, list, tuple) | ||
|
||
def is_same_type(a, b): | ||
return any( | ||
isinstance(a, t) and isinstance(b, t) | ||
for t in [str, Mapping, Seq, bool] | ||
) | ||
|
||
if isinstance(src, Mapping) and isinstance(dest, Mapping): | ||
for key, value in src.items(): | ||
if isinstance(value, Container) and is_same_type( | ||
value, dest.get(key) | ||
): | ||
apply_diff(value, dest[key]) | ||
elif key not in dest or value != dest[key]: | ||
dest[key] = value | ||
for key in set(dest) - set(src): | ||
del dest[key] | ||
elif isinstance(src, Seq) and isinstance(dest, Seq): | ||
if len(src) != len(dest): | ||
dest[:] = src | ||
else: | ||
for i, value in enumerate(src): | ||
if isinstance(value, Container) and is_same_type( | ||
value, dest[i] | ||
): | ||
apply_diff(value, dest[i]) | ||
elif value != dest[i]: | ||
dest[i] = value | ||
else: | ||
raise AssertionError( | ||
"Can't apply diff from {} to {}".format( | ||
src.__class__.__name__, dest.__class__.__name__ | ||
) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
from dvc.utils.compat import open | ||
|
||
from ruamel.yaml.error import YAMLError | ||
from ruamel.yaml import YAML | ||
|
||
from dvc.exceptions import StageFileCorruptedError | ||
|
||
|
||
def load_stage_file(path): | ||
with open(path, "r", encoding="utf-8") as fd: | ||
return load_stage_fd(fd, path) | ||
|
||
|
||
def load_stage_fd(fd, path): | ||
try: | ||
yaml = YAML() | ||
return yaml.load(fd) or {} | ||
except YAMLError as exc: | ||
raise StageFileCorruptedError(path, cause=exc) | ||
|
||
|
||
def dump_stage_file(path, data): | ||
with open(path, "w", encoding="utf-8") as fd: | ||
yaml = YAML() | ||
yaml.default_flow_style = False | ||
yaml.dump(data, fd) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import pytest | ||
from git import Repo | ||
from git.exc import GitCommandNotFound | ||
|
||
from dvc.repo import Repo as DvcRepo | ||
from .basic_env import TestDirFixture, logger | ||
|
||
|
||
@pytest.fixture(autouse=True) | ||
def debug(): | ||
logger.setLevel("DEBUG") | ||
|
||
|
||
# Wrap class like fixture as pytest-like one to avoid code duplication | ||
@pytest.fixture | ||
def repo_dir(): | ||
old_fixture = TestDirFixture() | ||
old_fixture.setUp() | ||
try: | ||
yield old_fixture | ||
finally: | ||
old_fixture.tearDown() | ||
|
||
|
||
# NOTE: this duplicates code from GitFixture, | ||
# would fix itself once class-based fixtures are removed | ||
@pytest.fixture | ||
def git(repo_dir): | ||
# NOTE: handles EAGAIN error on BSD systems (osx in our case). | ||
# Otherwise when running tests you might get this exception: | ||
# | ||
# GitCommandNotFound: Cmd('git') not found due to: | ||
# OSError('[Errno 35] Resource temporarily unavailable') | ||
retries = 5 | ||
while retries: | ||
try: | ||
git = Repo.init() | ||
except GitCommandNotFound: | ||
retries -= 1 | ||
continue | ||
break | ||
|
||
git.index.add([repo_dir.CODE]) | ||
git.index.commit("add code") | ||
return git | ||
|
||
|
||
@pytest.fixture | ||
def dvc(repo_dir, git): | ||
dvc = DvcRepo.init(repo_dir._root_dir) | ||
dvc.scm.commit("init dvc") | ||
return dvc |
Oops, something went wrong.