diff --git a/dvc/api.py b/dvc/api.py index f85141470..e8cc7bd22 100644 --- a/dvc/api.py +++ b/dvc/api.py @@ -12,8 +12,9 @@ except ImportError: import ruamel.yaml from voluptuous import Schema, Optional, Invalid, ALLOW_EXTRA -from dvc.utils.compat import urlparse, builtin_str, FileNotFoundError +from dvc.utils.compat import urlparse, builtin_str from dvc.repo import Repo +from dvc.exceptions import SummonError, FileMissingError from dvc.external_repo import external_repo @@ -128,19 +129,21 @@ def _get_object_from_summoners_file(name, path): Given a summonable object's name, search for it on the given file and bring it to life. """ - with open(path, "r") as fobj: - try: + try: + with open(path, "r") as fobj: content = summon_schema(ruamel.yaml.safe_load(fobj.read())) objects = content["objects"] return next(x for x in objects if x["name"] == name) - except FileNotFoundError: - pass # XXX: No such YAML file with path: '' - except ruamel.yaml.ScannerError: - pass # XXX: Failed to parse YAML correctly - except Invalid: - pass # XXX: YAML file dosen't match with the schema - except StopIteration: - pass # XXX: No such object with name: '' + except FileMissingError as exc: + # XXX: Prints ugly absolute path. + raise SummonError("No such YAML file with path: '{}'".format(path)) + except ruamel.yaml.YAMLError as exc: + raise SummonError("Failed to parse YAML correctly", exc) + except Invalid as exc: + # XXX: Doesn't point out the error. + raise SummonError("YAML file dosen't match with the schema", exc) + except StopIteration: + raise SummonError("No such object with name '{}'".format(name)) def _invoke_method(call, args, path): diff --git a/dvc/exceptions.py b/dvc/exceptions.py index 7151e2e2f..18ec671f2 100644 --- a/dvc/exceptions.py +++ b/dvc/exceptions.py @@ -353,3 +353,7 @@ class PathMissingError(DvcException): " neighther as an output nor a git-handled file." ) super(PathMissingError, self).__init__(msg.format(path, repo)) + + +class SummonError(DvcException): + pass diff --git a/tests/func/test_api.py b/tests/func/test_api.py index 49c09ed6e..0d90b26ad 100644 --- a/tests/func/test_api.py +++ b/tests/func/test_api.py @@ -13,6 +13,7 @@ from dvc.main import main from dvc.path_info import URLInfo from dvc.remote.config import RemoteConfig from dvc.utils.compat import fspath +from dvc.exceptions import SummonError from tests.remotes import Azure, GCP, HDFS, Local, OSS, S3, SSH @@ -151,14 +152,18 @@ def test_summon(tmp_dir, erepo_dir, dvc, monkeypatch): different_objects["objects"][0]["summon"]["args"]["x"] = 100 dvcsummon_yaml = ruamel.yaml.dump(objects) - non_canon_yaml = ruamel.yaml.dump(different_objects) + different_yaml = ruamel.yaml.dump(different_objects) + invalid_yaml = ruamel.yaml.dump({"name": "sum"}) + not_yaml = "a: - this is not a valid YAML file" with monkeypatch.context() as m: m.chdir(fspath(erepo_dir)) erepo_dir.dvc_gen("number", "100", commit="Add number.dvc") erepo_dir.scm_gen("dvcsummon.yaml", dvcsummon_yaml) - erepo_dir.scm_gen("non-canon.yaml", non_canon_yaml) + erepo_dir.scm_gen("different.yaml", different_yaml) + erepo_dir.scm_gen("invalid.yaml", invalid_yaml) + erepo_dir.scm_gen("not_yaml.yaml", not_yaml) erepo_dir.scm_gen( "calculator.py", "def add_to_num(x): return x + int(open('number').read())", @@ -169,4 +174,16 @@ def test_summon(tmp_dir, erepo_dir, dvc, monkeypatch): assert api.summon("sum", repo=repo_url) == 101 assert api.summon("sum", repo=repo_url, args={"x": 2}) == 102 - assert api.summon("sum", repo=repo_url, fname="non-canon.yaml") == 200 + assert api.summon("sum", repo=repo_url, fname="different.yaml") == 200 + + with pytest.raises(SummonError): + api.summon("sum", repo=repo_url, fname="not-existent-file.yaml") + + with pytest.raises(SummonError): + api.summon("non-existent", repo=repo_url) + + with pytest.raises(SummonError): + api.summon("sum", repo=repo_url, fname="invalid.yaml") + + with pytest.raises(SummonError): + api.summon("sum", repo=repo_url, fname="not_yaml.yaml")