mirror of
https://github.com/Stone-Red-Code/dvc.git
synced 2026-11-11 07:18:28 +01:00
Move to yaml1.2 (#4415)
* use yaml 1.2 everywhere This commit: * gets rid of pyyaml * uses ruamel everywhere * tries to unite toml and yaml However, this will require a few more changes that will come soonish. Eg: Current `parse_yaml` and `parse_toml` behavior could be error-prone, so, it's better to introduce `tree` inside these serialization methods (which I will try to tackle next). Another is, yaml load can be slow for json. We could speed it up just by using `json.load(s)` here (also in upcoming patch). * Fix tests
This commit is contained in:
@@ -2,12 +2,11 @@ import os
|
||||
from collections import defaultdict
|
||||
|
||||
import dpath.util
|
||||
import toml
|
||||
import yaml
|
||||
from voluptuous import Any
|
||||
|
||||
from dvc.dependency.local import LocalDependency
|
||||
from dvc.exceptions import DvcException
|
||||
from dvc.utils.serialize import PARSERS, ParseError
|
||||
|
||||
|
||||
class MissingParamsError(DvcException):
|
||||
@@ -22,8 +21,6 @@ class ParamsDependency(LocalDependency):
|
||||
PARAM_PARAMS = "params"
|
||||
PARAM_SCHEMA = {PARAM_PARAMS: Any(dict, list, None)}
|
||||
DEFAULT_PARAMS_FILE = "params.yaml"
|
||||
PARAMS_FILE_LOADERS = defaultdict(lambda: yaml.safe_load)
|
||||
PARAMS_FILE_LOADERS.update({".toml": toml.load})
|
||||
|
||||
def __init__(self, stage, path, params):
|
||||
info = {}
|
||||
@@ -88,12 +85,12 @@ class ParamsDependency(LocalDependency):
|
||||
if not self.exists:
|
||||
return {}
|
||||
|
||||
suffix = self.path_info.suffix.lower()
|
||||
parser = PARSERS[suffix]
|
||||
with self.repo.tree.open(self.path_info, "r") as fobj:
|
||||
try:
|
||||
config = self.PARAMS_FILE_LOADERS[
|
||||
self.path_info.suffix.lower()
|
||||
](fobj)
|
||||
except (yaml.YAMLError, toml.TomlDecodeError) as exc:
|
||||
config = parser(fobj.read(), self.path_info)
|
||||
except ParseError as exc:
|
||||
raise BadParamFileError(
|
||||
f"Unable to read parameters from '{self}'"
|
||||
) from exc
|
||||
|
||||
@@ -182,22 +182,6 @@ class NoPlotsError(DvcException):
|
||||
)
|
||||
|
||||
|
||||
class YAMLFileCorruptedError(DvcException):
|
||||
def __init__(self, path):
|
||||
path = relpath(path)
|
||||
super().__init__(
|
||||
f"unable to read: '{path}', YAML file structure is corrupted"
|
||||
)
|
||||
|
||||
|
||||
class TOMLFileCorruptedError(DvcException):
|
||||
def __init__(self, path):
|
||||
path = relpath(path)
|
||||
super().__init__(
|
||||
f"unable to read: '{path}', TOML file structure is corrupted"
|
||||
)
|
||||
|
||||
|
||||
class RecursiveAddingWhileUsingFilename(DvcException):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
import yaml
|
||||
|
||||
from dvc.exceptions import NoMetricsError
|
||||
from dvc.path_info import PathInfo
|
||||
from dvc.repo import locked
|
||||
from dvc.repo.tree import RepoTree
|
||||
from dvc.utils.serialize import YAMLFileCorruptedError, parse_yaml
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -72,8 +71,8 @@ def _read_metrics(repo, metrics, rev):
|
||||
try:
|
||||
with tree.open(metric, "r") as fobj:
|
||||
# NOTE this also supports JSON
|
||||
val = yaml.safe_load(fobj)
|
||||
except (FileNotFoundError, yaml.YAMLError):
|
||||
val = parse_yaml(fobj.read(), metric)
|
||||
except (FileNotFoundError, YAMLFileCorruptedError):
|
||||
logger.debug(
|
||||
"failed to read '%s' on '%s'", metric, rev, exc_info=True
|
||||
)
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import logging
|
||||
|
||||
import toml
|
||||
import yaml
|
||||
|
||||
from dvc.dependency.param import ParamsDependency
|
||||
from dvc.exceptions import DvcException
|
||||
from dvc.path_info import PathInfo
|
||||
from dvc.repo import locked
|
||||
from dvc.utils.serialize import PARSERS, ParseError
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -33,12 +31,12 @@ def _read_params(repo, configs, rev):
|
||||
if not repo.tree.exists(config):
|
||||
continue
|
||||
|
||||
suffix = config.suffix.lower()
|
||||
parser = PARSERS[suffix]
|
||||
with repo.tree.open(config, "r") as fobj:
|
||||
try:
|
||||
res[str(config)] = ParamsDependency.PARAMS_FILE_LOADERS[
|
||||
config.suffix.lower()
|
||||
](fobj)
|
||||
except (yaml.YAMLError, toml.TomlDecodeError):
|
||||
res[str(config)] = parser(fobj.read(), config)
|
||||
except ParseError:
|
||||
logger.debug(
|
||||
"failed to read '%s' on '%s'", config, rev, exc_info=True
|
||||
)
|
||||
|
||||
+2
-14
@@ -5,9 +5,8 @@ import os
|
||||
from collections import OrderedDict
|
||||
from copy import copy
|
||||
|
||||
import yaml
|
||||
from funcy import first
|
||||
from yaml import SafeLoader
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
from dvc.exceptions import DvcException
|
||||
|
||||
@@ -208,18 +207,7 @@ class CSVPlotData(PlotData):
|
||||
|
||||
class YAMLPlotData(PlotData):
|
||||
def raw(self, **kwargs):
|
||||
class OrderedLoader(SafeLoader):
|
||||
pass
|
||||
|
||||
def construct_mapping(loader, node):
|
||||
loader.flatten_mapping(node)
|
||||
return OrderedDict(loader.construct_pairs(node))
|
||||
|
||||
OrderedLoader.add_constructor(
|
||||
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, construct_mapping
|
||||
)
|
||||
|
||||
return yaml.load(self.content, OrderedLoader)
|
||||
return YAML().load(self.content)
|
||||
|
||||
def _processors(self):
|
||||
parent_processors = super()._processors()
|
||||
|
||||
@@ -120,9 +120,10 @@ def reproduce(
|
||||
|
||||
def _parse_params(path_params):
|
||||
from flatten_json import unflatten
|
||||
from yaml import YAMLError, safe_load
|
||||
from ruamel.yaml import YAMLError
|
||||
|
||||
from dvc.dependency.param import ParamsDependency
|
||||
from dvc.utils.serialize import loads_yaml
|
||||
|
||||
ret = {}
|
||||
for path_param in path_params:
|
||||
@@ -133,7 +134,7 @@ def _parse_params(path_params):
|
||||
try:
|
||||
# interpret value strings using YAML rules
|
||||
key, value = param_str.split("=")
|
||||
params[key] = safe_load(value)
|
||||
params[key] = loads_yaml(value)
|
||||
except (ValueError, YAMLError):
|
||||
raise InvalidArgumentError(
|
||||
f"Invalid param/value pair '{param_str}'"
|
||||
|
||||
+3
-8
@@ -5,7 +5,6 @@ import os
|
||||
import shlex
|
||||
from functools import partial
|
||||
|
||||
import yaml
|
||||
from funcy import cached_property
|
||||
from pathspec.patterns import GitWildMatchPattern
|
||||
|
||||
@@ -20,6 +19,7 @@ from dvc.scm.base import (
|
||||
)
|
||||
from dvc.utils import fix_env, is_binary, relpath
|
||||
from dvc.utils.fs import path_isin
|
||||
from dvc.utils.serialize import dump_yaml, load_yaml
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -330,11 +330,7 @@ class Git(Base):
|
||||
return
|
||||
|
||||
config_path = os.path.join(self.root_dir, ".pre-commit-config.yaml")
|
||||
|
||||
config = {}
|
||||
if os.path.exists(config_path):
|
||||
with open(config_path) as fobj:
|
||||
config = yaml.safe_load(fobj)
|
||||
config = load_yaml(config_path) if os.path.exists(config_path) else {}
|
||||
|
||||
entry = {
|
||||
"repo": "https://github.com/iterative/dvc",
|
||||
@@ -363,8 +359,7 @@ class Git(Base):
|
||||
return
|
||||
|
||||
config["repos"].append(entry)
|
||||
with open(config_path, "w+") as fobj:
|
||||
yaml.dump(config, fobj)
|
||||
dump_yaml(config_path, config)
|
||||
|
||||
def cleanup_ignores(self):
|
||||
for path in self.ignored_paths:
|
||||
|
||||
+3
-5
@@ -2,7 +2,6 @@ import logging
|
||||
import os
|
||||
from contextlib import contextmanager
|
||||
|
||||
import yaml
|
||||
from funcy import first
|
||||
from voluptuous import Invalid
|
||||
|
||||
@@ -10,7 +9,7 @@ from dvc.cache.local import _log_exceptions
|
||||
from dvc.schema import COMPILED_LOCK_FILE_STAGE_SCHEMA
|
||||
from dvc.utils import dict_sha256, relpath
|
||||
from dvc.utils.fs import makedirs
|
||||
from dvc.utils.serialize import dump_yaml
|
||||
from dvc.utils.serialize import YAMLFileCorruptedError, dump_yaml, load_yaml
|
||||
|
||||
from .loader import StageLoader
|
||||
from .serialize import to_single_stage_lockfile
|
||||
@@ -54,11 +53,10 @@ class StageCache:
|
||||
path = self._get_cache_path(key, value)
|
||||
|
||||
try:
|
||||
with open(path) as fobj:
|
||||
return COMPILED_LOCK_FILE_STAGE_SCHEMA(yaml.safe_load(fobj))
|
||||
return COMPILED_LOCK_FILE_STAGE_SCHEMA(load_yaml(path))
|
||||
except FileNotFoundError:
|
||||
return None
|
||||
except (yaml.error.YAMLError, Invalid):
|
||||
except (YAMLFileCorruptedError, Invalid):
|
||||
logger.warning("corrupted cache file '%s'.", relpath(path))
|
||||
os.unlink(path)
|
||||
return None
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""Helpers for other modules."""
|
||||
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import math
|
||||
@@ -12,7 +11,6 @@ import time
|
||||
|
||||
import colorama
|
||||
import nanotime
|
||||
from ruamel.yaml import YAML
|
||||
from shortuuid import uuid
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -237,18 +235,6 @@ def current_timestamp():
|
||||
return int(nanotime.timestamp(time.time()))
|
||||
|
||||
|
||||
def from_yaml_string(s):
|
||||
return YAML().load(io.StringIO(s))
|
||||
|
||||
|
||||
def to_yaml_string(data):
|
||||
stream = io.StringIO()
|
||||
yaml = YAML()
|
||||
yaml.default_flow_style = False
|
||||
yaml.dump(data, stream)
|
||||
return stream.getvalue()
|
||||
|
||||
|
||||
def colorize(message, color=None):
|
||||
"""Returns a message in a specified color."""
|
||||
if not color:
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
from collections import defaultdict
|
||||
|
||||
from ._common import * # noqa, pylint: disable=wildcard-import
|
||||
from ._toml import * # noqa, pylint: disable=wildcard-import
|
||||
from ._yaml import * # noqa, pylint: disable=wildcard-import
|
||||
|
||||
PARSERS = defaultdict(lambda: parse_yaml) # noqa: F405
|
||||
PARSERS.update({".toml": parse_toml}) # noqa: F405
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
"""Common utilities for serialize."""
|
||||
|
||||
from dvc.exceptions import DvcException
|
||||
from dvc.utils import relpath
|
||||
|
||||
|
||||
class ParseError(DvcException):
|
||||
"""Errors while parsing files"""
|
||||
|
||||
def __init__(self, path, message):
|
||||
path = relpath(path)
|
||||
super().__init__(f"unable to read: '{path}', {message}")
|
||||
@@ -1,6 +1,17 @@
|
||||
import toml
|
||||
from funcy import reraise
|
||||
|
||||
from dvc.exceptions import TOMLFileCorruptedError
|
||||
from ._common import ParseError
|
||||
|
||||
|
||||
class TOMLFileCorruptedError(ParseError):
|
||||
def __init__(self, path):
|
||||
super().__init__(path, "TOML file structure is corrupted")
|
||||
|
||||
|
||||
def parse_toml(text, path, decoder=None):
|
||||
with reraise(toml.TomlDecodeError, TOMLFileCorruptedError(path)):
|
||||
return toml.loads(text, decoder=decoder)
|
||||
|
||||
|
||||
def parse_toml_for_update(text, path):
|
||||
@@ -10,12 +21,10 @@ def parse_toml_for_update(text, path):
|
||||
keys may be re-ordered between load/dump, but this function will at
|
||||
least preserve comments.
|
||||
"""
|
||||
try:
|
||||
return toml.loads(text, decoder=toml.TomlPreserveCommentDecoder())
|
||||
except toml.TomlDecodeError as exc:
|
||||
raise TOMLFileCorruptedError(path) from exc
|
||||
decoder = toml.TomlPreserveCommentDecoder()
|
||||
return parse_toml(text, path, decoder=decoder)
|
||||
|
||||
|
||||
def dump_toml(path, data):
|
||||
with open(path, "w", encoding="utf-8") as fobj:
|
||||
with open(path, "w+", encoding="utf-8") as fobj:
|
||||
toml.dump(data, fobj, encoder=toml.TomlPreserveCommentEncoder())
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import io
|
||||
from collections import OrderedDict
|
||||
|
||||
from funcy import reraise
|
||||
from ruamel.yaml import YAML
|
||||
from ruamel.yaml.error import YAMLError
|
||||
|
||||
from dvc.exceptions import YAMLFileCorruptedError
|
||||
from ._common import ParseError
|
||||
|
||||
try:
|
||||
from yaml import CSafeLoader as SafeLoader
|
||||
except ImportError:
|
||||
from yaml import SafeLoader
|
||||
|
||||
class YAMLFileCorruptedError(ParseError):
|
||||
def __init__(self, path):
|
||||
super().__init__(path, "YAML file structure is corrupted")
|
||||
|
||||
|
||||
def load_yaml(path):
|
||||
@@ -16,13 +18,10 @@ def load_yaml(path):
|
||||
return parse_yaml(fd.read(), path)
|
||||
|
||||
|
||||
def parse_yaml(text, path):
|
||||
try:
|
||||
import yaml
|
||||
|
||||
return yaml.load(text, Loader=SafeLoader) or {}
|
||||
except yaml.error.YAMLError as exc:
|
||||
raise YAMLFileCorruptedError(path) from exc
|
||||
def parse_yaml(text, path, typ="safe"):
|
||||
yaml = YAML(typ=typ)
|
||||
with reraise(YAMLError, YAMLFileCorruptedError(path)):
|
||||
return yaml.load(text) or {}
|
||||
|
||||
|
||||
def parse_yaml_for_update(text, path):
|
||||
@@ -34,20 +33,30 @@ def parse_yaml_for_update(text, path):
|
||||
|
||||
This one is, however, several times slower than simple `parse_yaml()`.
|
||||
"""
|
||||
try:
|
||||
yaml = YAML()
|
||||
return yaml.load(text) or {}
|
||||
except YAMLError as exc:
|
||||
raise YAMLFileCorruptedError(path) from exc
|
||||
return parse_yaml(text, path, typ="rt")
|
||||
|
||||
|
||||
def _get_yaml():
|
||||
yaml = YAML()
|
||||
yaml.default_flow_style = False
|
||||
|
||||
# tell Dumper to represent OrderedDict as normal dict
|
||||
yaml_repr_cls = yaml.Representer
|
||||
yaml_repr_cls.add_representer(OrderedDict, yaml_repr_cls.represent_dict)
|
||||
return yaml
|
||||
|
||||
|
||||
def dump_yaml(path, data):
|
||||
with open(path, "w", encoding="utf-8") as fd:
|
||||
yaml = YAML()
|
||||
yaml.default_flow_style = False
|
||||
# tell Dumper to represent OrderedDict as
|
||||
# normal dict
|
||||
yaml.Representer.add_representer(
|
||||
OrderedDict, yaml.Representer.represent_dict
|
||||
)
|
||||
yaml = _get_yaml()
|
||||
with open(path, "w+", encoding="utf-8") as fd:
|
||||
yaml.dump(data, fd)
|
||||
|
||||
|
||||
def loads_yaml(s):
|
||||
return YAML(typ="safe").load(s)
|
||||
|
||||
|
||||
def dumps_yaml(d):
|
||||
stream = io.StringIO()
|
||||
YAML().dump(d, stream)
|
||||
return stream.getvalue()
|
||||
|
||||
@@ -60,7 +60,6 @@ install_requires = [
|
||||
"grandalf==0.6",
|
||||
"distro>=1.3.0",
|
||||
"appdirs>=1.4.3",
|
||||
"PyYAML>=5.1.2,<5.4", # Compatibility with awscli
|
||||
"ruamel.yaml>=0.16.1",
|
||||
"toml>=0.10.1",
|
||||
"funcy>=1.14",
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import json
|
||||
import logging
|
||||
|
||||
import yaml
|
||||
|
||||
from dvc.main import main
|
||||
from dvc.utils.serialize import dump_yaml
|
||||
|
||||
|
||||
def test_metrics_diff_simple(tmp_dir, scm, dvc, run_copy_metrics):
|
||||
@@ -24,7 +23,7 @@ def test_metrics_diff_simple(tmp_dir, scm, dvc, run_copy_metrics):
|
||||
def test_metrics_diff_yaml(tmp_dir, scm, dvc, run_copy_metrics):
|
||||
def _gen(val):
|
||||
metrics = {"a": {"b": {"c": val, "d": 1, "e": str(val)}}}
|
||||
tmp_dir.gen({"m_temp.yaml": yaml.dump(metrics)})
|
||||
dump_yaml("m_temp.yaml", metrics)
|
||||
run_copy_metrics(
|
||||
"m_temp.yaml", "m.yaml", metrics=["m.yaml"], commit=str(val)
|
||||
)
|
||||
|
||||
@@ -6,7 +6,6 @@ import shutil
|
||||
from collections import OrderedDict
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
from funcy import first
|
||||
|
||||
from dvc.repo.plots.data import (
|
||||
@@ -21,6 +20,7 @@ from dvc.repo.plots.template import (
|
||||
NoFieldInDataError,
|
||||
TemplateNotFoundError,
|
||||
)
|
||||
from dvc.utils.serialize import dump_yaml, dumps_yaml
|
||||
|
||||
|
||||
def _write_csv(metric, filename, header=True):
|
||||
@@ -493,9 +493,7 @@ def test_plot_default_choose_column(tmp_dir, scm, dvc, run_copy_metrics):
|
||||
|
||||
def test_plot_yaml(tmp_dir, scm, dvc, run_copy_metrics):
|
||||
metric = [{"val": 2}, {"val": 3}]
|
||||
with open("metric_t.yaml", "w") as fobj:
|
||||
yaml.dump(metric, fobj)
|
||||
|
||||
dump_yaml("metric_t.yaml", metric)
|
||||
run_copy_metrics(
|
||||
"metric_t.yaml", "metric.yaml", plots_no_cache=["metric.yaml"]
|
||||
)
|
||||
@@ -543,7 +541,7 @@ def test_load_metric_from_dict_yaml(tmp_dir):
|
||||
metric = [{"acccuracy": 1, "loss": 2}, {"accuracy": 3, "loss": 4}]
|
||||
dmetric = {"train": metric}
|
||||
|
||||
plot_data = YAMLPlotData("-", "revision", yaml.dump(dmetric))
|
||||
plot_data = YAMLPlotData("-", "revision", dumps_yaml(dmetric))
|
||||
|
||||
expected = metric
|
||||
for d in expected:
|
||||
|
||||
@@ -17,7 +17,6 @@ from dvc.exceptions import (
|
||||
OutputDuplicationError,
|
||||
OverlappingOutputPathsError,
|
||||
RecursiveAddingWhileUsingFilename,
|
||||
YAMLFileCorruptedError,
|
||||
)
|
||||
from dvc.main import main
|
||||
from dvc.output.base import OutputAlreadyTrackedError, OutputIsStageFileError
|
||||
@@ -27,7 +26,7 @@ from dvc.system import System
|
||||
from dvc.tree.local import LocalTree
|
||||
from dvc.utils import LARGE_DIR_SIZE, file_md5, relpath
|
||||
from dvc.utils.fs import path_isin
|
||||
from dvc.utils.serialize import load_yaml
|
||||
from dvc.utils.serialize import YAMLFileCorruptedError, load_yaml
|
||||
from tests.basic_env import TestDvc
|
||||
from tests.utils import get_gitignore_content
|
||||
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
from collections import OrderedDict
|
||||
from operator import itemgetter
|
||||
|
||||
import yaml
|
||||
|
||||
from dvc.dvcfile import PIPELINE_LOCK
|
||||
from dvc.stage.utils import split_params_deps
|
||||
from dvc.utils.fs import remove
|
||||
from dvc.utils.serialize import parse_yaml_for_update
|
||||
from dvc.utils.serialize import dumps_yaml, parse_yaml_for_update
|
||||
from tests.func.test_run_multistage import supported_params
|
||||
|
||||
FS_STRUCTURE = {
|
||||
"foo": "bar\nfoobar",
|
||||
"bar": "foo\nfoobar",
|
||||
"foobar": "foobar\nbar",
|
||||
"params.yaml": yaml.dump(supported_params),
|
||||
"params2.yaml": yaml.dump(supported_params),
|
||||
"params.yaml": dumps_yaml(supported_params),
|
||||
"params2.yaml": dumps_yaml(supported_params),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ from copy import deepcopy
|
||||
from textwrap import dedent
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
from funcy import lsplit
|
||||
|
||||
from dvc.dvcfile import PIPELINE_FILE, PIPELINE_LOCK
|
||||
@@ -474,11 +473,8 @@ def test_repro_multiple_params(tmp_dir, dvc):
|
||||
from dvc.stage.utils import split_params_deps
|
||||
from tests.func.test_run_multistage import supported_params
|
||||
|
||||
with (tmp_dir / "params2.yaml").open("w+") as f:
|
||||
yaml.dump(supported_params, f)
|
||||
|
||||
with (tmp_dir / "params.yaml").open("w+") as f:
|
||||
yaml.dump(supported_params, f)
|
||||
dump_yaml(tmp_dir / "params2.yaml", supported_params)
|
||||
dump_yaml(tmp_dir / "params.yaml", supported_params)
|
||||
|
||||
(tmp_dir / "foo").write_text("foo")
|
||||
stage = dvc.run(
|
||||
@@ -518,9 +514,8 @@ def test_repro_multiple_params(tmp_dir, dvc):
|
||||
assert set(defaults) == {"answer", "floats", "nested.nested1"}
|
||||
|
||||
assert not dvc.reproduce(stage.addressing)
|
||||
with (tmp_dir / "params.yaml").open("w+") as f:
|
||||
params = deepcopy(supported_params)
|
||||
params["answer"] = 43
|
||||
yaml.dump(params, f)
|
||||
params = deepcopy(supported_params)
|
||||
params["answer"] = 43
|
||||
dump_yaml(tmp_dir / "params.yaml", params)
|
||||
|
||||
assert dvc.reproduce(stage.addressing) == [stage]
|
||||
|
||||
@@ -2,12 +2,11 @@ import os
|
||||
import textwrap
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from dvc.exceptions import InvalidArgumentError
|
||||
from dvc.repo import Repo
|
||||
from dvc.stage.exceptions import DuplicateStageName, InvalidStageName
|
||||
from dvc.utils.serialize import parse_yaml_for_update
|
||||
from dvc.utils.serialize import dump_yaml, parse_yaml_for_update
|
||||
|
||||
|
||||
def test_run_with_name(tmp_dir, dvc, run_copy):
|
||||
@@ -236,9 +235,7 @@ supported_params = {
|
||||
def test_run_params_default(tmp_dir, dvc):
|
||||
from dvc.dependency import ParamsDependency
|
||||
|
||||
with (tmp_dir / "params.yaml").open("w+") as f:
|
||||
yaml.dump(supported_params, f)
|
||||
|
||||
dump_yaml(tmp_dir / "params.yaml", supported_params)
|
||||
stage = dvc.run(
|
||||
name="read_params",
|
||||
params=["nested.nested1.nested2"],
|
||||
@@ -261,9 +258,7 @@ def test_run_params_default(tmp_dir, dvc):
|
||||
def test_run_params_custom_file(tmp_dir, dvc):
|
||||
from dvc.dependency import ParamsDependency
|
||||
|
||||
with (tmp_dir / "params2.yaml").open("w+") as f:
|
||||
yaml.dump(supported_params, f)
|
||||
|
||||
dump_yaml(tmp_dir / "params2.yaml", supported_params)
|
||||
stage = dvc.run(
|
||||
name="read_params",
|
||||
params=["params2.yaml:lists"],
|
||||
@@ -286,9 +281,7 @@ def test_run_params_custom_file(tmp_dir, dvc):
|
||||
def test_run_params_no_exec(tmp_dir, dvc):
|
||||
from dvc.dependency import ParamsDependency
|
||||
|
||||
with (tmp_dir / "params2.yaml").open("w+") as f:
|
||||
yaml.dump(supported_params, f)
|
||||
|
||||
dump_yaml(tmp_dir / "params2.yaml", supported_params)
|
||||
stage = dvc.run(
|
||||
name="read_params",
|
||||
params=["params2.yaml:lists"],
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import pytest
|
||||
import toml
|
||||
import yaml
|
||||
|
||||
from dvc.dependency import ParamsDependency, loadd_from, loads_params
|
||||
from dvc.dependency.param import BadParamFileError, MissingParamsError
|
||||
from dvc.stage import Stage
|
||||
from dvc.utils.serialize import load_yaml
|
||||
from dvc.utils.serialize import dump_toml, dump_yaml, load_yaml
|
||||
|
||||
PARAMS = {
|
||||
"foo": 1,
|
||||
@@ -92,9 +90,8 @@ def test_read_params_unsupported_format(tmp_dir, dvc):
|
||||
|
||||
|
||||
def test_read_params_nested(tmp_dir, dvc):
|
||||
tmp_dir.gen(
|
||||
DEFAULT_PARAMS_FILE,
|
||||
yaml.dump({"some": {"path": {"foo": ["val1", "val2"]}}}),
|
||||
dump_yaml(
|
||||
DEFAULT_PARAMS_FILE, {"some": {"path": {"foo": ["val1", "val2"]}}}
|
||||
)
|
||||
dep = ParamsDependency(Stage(dvc), None, ["some.path.foo"])
|
||||
assert dep.read_params() == {"some.path.foo": ["val1", "val2"]}
|
||||
@@ -102,20 +99,14 @@ def test_read_params_nested(tmp_dir, dvc):
|
||||
|
||||
def test_read_params_default_loader(tmp_dir, dvc):
|
||||
parameters_file = "parameters.foo"
|
||||
tmp_dir.gen(
|
||||
parameters_file,
|
||||
yaml.dump({"some": {"path": {"foo": ["val1", "val2"]}}}),
|
||||
)
|
||||
dump_yaml(parameters_file, {"some": {"path": {"foo": ["val1", "val2"]}}})
|
||||
dep = ParamsDependency(Stage(dvc), parameters_file, ["some.path.foo"])
|
||||
assert dep.read_params() == {"some.path.foo": ["val1", "val2"]}
|
||||
|
||||
|
||||
def test_read_params_wrong_suffix(tmp_dir, dvc):
|
||||
parameters_file = "parameters.toml"
|
||||
tmp_dir.gen(
|
||||
parameters_file,
|
||||
yaml.dump({"some": {"path": {"foo": ["val1", "val2"]}}}),
|
||||
)
|
||||
dump_yaml(parameters_file, {"some": {"path": {"foo": ["val1", "val2"]}}})
|
||||
dep = ParamsDependency(Stage(dvc), parameters_file, ["some.path.foo"])
|
||||
with pytest.raises(BadParamFileError):
|
||||
dep.read_params()
|
||||
@@ -123,10 +114,7 @@ def test_read_params_wrong_suffix(tmp_dir, dvc):
|
||||
|
||||
def test_read_params_toml(tmp_dir, dvc):
|
||||
parameters_file = "parameters.toml"
|
||||
tmp_dir.gen(
|
||||
parameters_file,
|
||||
toml.dumps({"some": {"path": {"foo": ["val1", "val2"]}}}),
|
||||
)
|
||||
dump_toml(parameters_file, {"some": {"path": {"foo": ["val1", "val2"]}}})
|
||||
dep = ParamsDependency(Stage(dvc), parameters_file, ["some.path.foo"])
|
||||
assert dep.read_params() == {"some.path.foo": ["val1", "val2"]}
|
||||
|
||||
@@ -144,13 +132,10 @@ def test_save_info_missing_param(tmp_dir, dvc):
|
||||
dep.save_info()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param_value",
|
||||
["", "false", "[]", "{}", "null", "no", "off"]
|
||||
# we use pyyaml to load params.yaml, which only supports YAML 1.1
|
||||
# so, some of the above are boolean values
|
||||
)
|
||||
@pytest.mark.regression_4184
|
||||
@pytest.mark.parametrize("param_value", ["", "false", "[]", "{}", "null"])
|
||||
def test_params_with_false_values(tmp_dir, dvc, param_value):
|
||||
"""These falsy params values should not ignored by `status` on loading."""
|
||||
key = "param"
|
||||
dep = ParamsDependency(Stage(dvc), DEFAULT_PARAMS_FILE, [key])
|
||||
(tmp_dir / DEFAULT_PARAMS_FILE).write_text(f"{key}: {param_value}")
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from dvc.dvcfile import Lockfile, LockfileCorruptedError
|
||||
from dvc.stage import PipelineStage
|
||||
from dvc.utils.serialize import dump_yaml
|
||||
|
||||
|
||||
def test_stage_dump_no_outs_deps(tmp_dir, dvc):
|
||||
@@ -14,8 +14,7 @@ def test_stage_dump_no_outs_deps(tmp_dir, dvc):
|
||||
|
||||
def test_stage_dump_when_already_exists(tmp_dir, dvc):
|
||||
data = {"s1": {"cmd": "command", "deps": [], "outs": []}}
|
||||
with open("path.lock", "w+") as f:
|
||||
yaml.dump(data, f)
|
||||
dump_yaml("path.lock", data)
|
||||
stage = PipelineStage(name="s2", repo=dvc, path="path", cmd="command2")
|
||||
lockfile = Lockfile(dvc, "path.lock")
|
||||
lockfile.dump(stage)
|
||||
@@ -33,9 +32,7 @@ def test_stage_dump_with_deps_and_outs(tmp_dir, dvc):
|
||||
"outs": [{"md5": "2.txt", "path": "checksum"}],
|
||||
}
|
||||
}
|
||||
with open("path.lock", "w+") as f:
|
||||
yaml.dump(data, f)
|
||||
|
||||
dump_yaml("path.lock", data)
|
||||
lockfile = Lockfile(dvc, "path.lock")
|
||||
stage = PipelineStage(name="s2", repo=dvc, path="path", cmd="command2")
|
||||
lockfile.dump(stage)
|
||||
@@ -77,8 +74,7 @@ def test_load_when_lockfile_does_not_exist(tmp_dir, dvc):
|
||||
],
|
||||
)
|
||||
def test_load_when_lockfile_is_corrupted(tmp_dir, dvc, corrupt_data):
|
||||
with open("Dvcfile.lock", "w+") as f:
|
||||
yaml.dump(corrupt_data, f)
|
||||
dump_yaml("Dvcfile.lock", corrupt_data)
|
||||
lockfile = Lockfile(dvc, "Dvcfile.lock")
|
||||
with pytest.raises(LockfileCorruptedError) as exc_info:
|
||||
lockfile.load()
|
||||
|
||||
Reference in New Issue
Block a user