diff --git a/dvc/dvcfile.py b/dvc/dvcfile.py index 6f0e5d8b7..33860729b 100644 --- a/dvc/dvcfile.py +++ b/dvc/dvcfile.py @@ -6,7 +6,7 @@ import os from voluptuous import MultipleInvalid from dvc.exceptions import DvcException -from dvc.parsing.versions import LOCKFILE_VERSION, META_KWD +from dvc.parsing.versions import LOCKFILE_VERSION, SCHEMA_KWD from dvc.stage import serialize from dvc.stage.exceptions import ( StageFileBadNameError, @@ -289,14 +289,14 @@ def get_lockfile_schema(d): return schema[version] -def migrate_lock_v1_to_v2(d, meta): +def migrate_lock_v1_to_v2(d, version_info): stages = {k: v for k, v in d.items()} for key in stages: d.pop(key) # forcing order, meta should always be at the top - d.update(meta) + d.update(version_info) d["stages"] = stages @@ -323,9 +323,9 @@ class Lockfile(FileMixin): return data @property - def meta(self): + def latest_version_info(self): version = LOCKFILE_VERSION.V2.value # pylint:disable=no-member - return {META_KWD: {"version": version}} + return {SCHEMA_KWD: version} def dump(self, stage, **kwargs): stage_data = serialize.to_lockfile(stage) @@ -336,10 +336,10 @@ class Lockfile(FileMixin): logger.info( "Migrating lock file '%s' from v1 to v2", self.relpath ) - migrate_lock_v1_to_v2(data, self.meta) + migrate_lock_v1_to_v2(data, self.latest_version_info) else: if not data: - data.update(self.meta) + data.update(self.latest_version_info) # order is important, meta should always be at the top logger.info("Generating lock file '%s'", self.relpath) diff --git a/dvc/parsing/versions.py b/dvc/parsing/versions.py index b0a82c1dc..80fcd18b6 100644 --- a/dvc/parsing/versions.py +++ b/dvc/parsing/versions.py @@ -3,13 +3,15 @@ from collections.abc import Mapping from voluptuous import validators -VERSION_KWD = "version" +SCHEMA_KWD = "schema" META_KWD = "meta" def lockfile_version_schema(value): expected = [LOCKFILE_VERSION.V2.value] # pylint: disable=no-member - msg = "invalid version {}, expected one of {}".format(value, expected) + msg = "invalid schema version {}, expected one of {}".format( + value, expected + ) return validators.Any(*expected, msg=msg)(value) @@ -26,13 +28,14 @@ class LOCKFILE_VERSION(VersionEnum): @classmethod def from_dict(cls, data): # 1) if it's empty or or is not a dict, use the latest one (V2). - # 2) use the `meta.version` - # 3) if it's not in any of the supported version, use the latest schema - # 4) if there's no version identifier, it's a V1 + # 2) use the `schema` identifier if it exists and is a supported + # version + # 3) if it's not in any of the supported version, use the latest one + # 4) if there's no identifier, it's a V1 if not data or not isinstance(data, Mapping): return cls(cls.V2) - version = data.get(META_KWD, {}).get(VERSION_KWD) + version = data.get(SCHEMA_KWD) if version: return cls(version if version in cls.all_versions() else cls.V2) return cls(cls.V1) diff --git a/dvc/schema.py b/dvc/schema.py index 143f46180..41fb905a7 100644 --- a/dvc/schema.py +++ b/dvc/schema.py @@ -4,7 +4,7 @@ from dvc import dependency, output from dvc.hash_info import HashInfo from dvc.output import CHECKSUMS_SCHEMA, BaseOutput from dvc.parsing import DO_KWD, FOREACH_KWD, VARS_KWD -from dvc.parsing.versions import META_KWD, VERSION_KWD, lockfile_version_schema +from dvc.parsing.versions import SCHEMA_KWD, lockfile_version_schema from dvc.stage.params import StageParams STAGES = "stages" @@ -40,7 +40,7 @@ LOCKFILE_STAGES_SCHEMA = {str: LOCK_FILE_STAGE_SCHEMA} LOCKFILE_V1_SCHEMA = LOCKFILE_STAGES_SCHEMA LOCKFILE_V2_SCHEMA = { STAGES: LOCKFILE_STAGES_SCHEMA, - META_KWD: {Required(VERSION_KWD): lockfile_version_schema}, + Required(SCHEMA_KWD): lockfile_version_schema, } OUT_PSTAGE_DETAILED_SCHEMA = { diff --git a/tests/func/test_lockfile.py b/tests/func/test_lockfile.py index 27c84455b..c55ebdafb 100644 --- a/tests/func/test_lockfile.py +++ b/tests/func/test_lockfile.py @@ -206,15 +206,14 @@ def test_migrates_v1_lockfile_to_v2_during_dump( assert "Migrating lock file 'dvc.lock' from v1 to v2" in caplog.messages d = load_yaml(tmp_dir / "dvc.lock") - assert d == {"stages": v1_repo_lock, "meta": {"version": "2.0"}} + assert d == {"stages": v1_repo_lock, "schema": "2.0"} @pytest.mark.parametrize( - "version_info", - [{"version": "1.1"}, {"version": "2.1"}, {"version": "3.0"}], + "version_info", [{"schema": "1.1"}, {"schema": "2.1"}, {"schema": "3.0"}], ) def test_lockfile_invalid_versions(tmp_dir, dvc, version_info): - lockdata = {"meta": version_info, "stages": {"foo": {"cmd": "echo foo"}}} + lockdata = {**version_info, "stages": {"foo": {"cmd": "echo foo"}}} dump_yaml("dvc.lock", lockdata) with pytest.raises(LockfileCorruptedError) as exc_info: Lockfile(dvc, tmp_dir / "dvc.lock").load() @@ -222,7 +221,7 @@ def test_lockfile_invalid_versions(tmp_dir, dvc, version_info): assert str(exc_info.value) == "Lockfile 'dvc.lock' is corrupted." assert ( str(exc_info.value.__cause__) == "'dvc.lock' format error: " - f"invalid version {version_info['version']}, " + f"invalid schema version {version_info['schema']}, " "expected one of ['2.0'] for dictionary value @ " - "data['meta']['version']" + "data['schema']" ) diff --git a/tests/func/test_run_multistage.py b/tests/func/test_run_multistage.py index dc74238bf..155d9ca80 100644 --- a/tests/func/test_run_multistage.py +++ b/tests/func/test_run_multistage.py @@ -420,8 +420,7 @@ def test_run_external_outputs( assert (tmp_dir / "dvc.yaml").read_text() == dvc_yaml assert (tmp_dir / "dvc.lock").read_text() == ( - "meta:\n" - " version: '2.0'\n" + "schema: '2.0'\n" "stages:\n" " mystage:\n" " cmd: mycmd\n" diff --git a/tests/unit/test_lockfile.py b/tests/unit/test_lockfile.py index 662fb4c3a..4b2c08d3b 100644 --- a/tests/unit/test_lockfile.py +++ b/tests/unit/test_lockfile.py @@ -10,8 +10,8 @@ def test_stage_dump_no_outs_deps(tmp_dir, dvc): lockfile = Lockfile(dvc, "path.lock") lockfile.dump(stage) assert lockfile.load() == { + "schema": "2.0", "stages": {"s1": {"cmd": "command"}}, - "meta": {"version": "2.0"}, } @@ -22,8 +22,8 @@ def test_stage_dump_when_already_exists(tmp_dir, dvc): lockfile = Lockfile(dvc, "path.lock") lockfile.dump(stage) assert lockfile.load() == { + "schema": "2.0", "stages": {**data, "s2": {"cmd": "command2"}}, - "meta": {"version": "2.0"}, } @@ -40,8 +40,8 @@ def test_stage_dump_with_deps_and_outs(tmp_dir, dvc): stage = PipelineStage(name="s2", repo=dvc, path="path", cmd="command2") lockfile.dump(stage) assert lockfile.load() == { + "schema": "2.0", "stages": {**data, "s2": {"cmd": "command2"}}, - "meta": {"version": "2.0"}, } @@ -52,8 +52,8 @@ def test_stage_overwrites_if_already_exists(tmp_dir, dvc): stage = PipelineStage(name="s2", repo=dvc, path="path", cmd="command3") lockfile.dump(stage) assert lockfile.load() == { + "schema": "2.0", "stages": {"s2": {"cmd": "command3"}}, - "meta": {"version": "2.0"}, }