diff --git a/dargs/dargs.py b/dargs/dargs.py index fa06bf1..6a880e8 100644 --- a/dargs/dargs.py +++ b/dargs/dargs.py @@ -536,12 +536,13 @@ def check_value( A deep copy of ``value`` is made internally so the caller's data is not mutated. """ + ref_base_dir = None if allow_ref: value = deepcopy(value) # Resolve a root reference before validating its type or running # its extra check; traversal only resolves descendants. if isinstance(value, dict): - _resolve_ref(value, allow_ref) + ref_base_dir = _resolve_ref(value, allow_ref) # ``traverse_value`` only checks descendants, so validate the root value # explicitly before descending into any sub-fields or variants. self._check_data(value, []) @@ -551,6 +552,7 @@ def check_value( value_hook=Argument._check_data, sub_hook=Argument._check_strict if strict else _DUMMYHOOK, allow_ref=allow_ref, + _ref_base_dir=ref_base_dir, ) def _check_exist(self, argdict: dict, path: list[str] | None = None) -> None: diff --git a/tests/test_ref.py b/tests/test_ref.py index 45cc319..c5a4db3 100644 --- a/tests/test_ref.py +++ b/tests/test_ref.py @@ -147,6 +147,37 @@ def test_ref_check_value(self) -> None: ) ca.check_value({"$ref": ref_path}, allow_ref=True) + def test_ref_check_value_nested_relative_ref_uses_containing_file(self) -> None: + """Nested refs from a root ref resolve beside the declaring file.""" + source_dir = os.path.join(self._tmpdir, "source") + cwd_dir = os.path.join(self._tmpdir, "cwd") + os.mkdir(source_dir) + os.mkdir(cwd_dir) + + inner_path = os.path.join(source_dir, "inner.json") + outer_path = os.path.join(source_dir, "outer.json") + with open(inner_path, "w") as f: + json.dump({"value": 11}, f) + with open(outer_path, "w") as f: + json.dump({"nested": {"$ref": "inner.json"}}, f) + + # A different file in the working directory makes accidental CWD-based + # resolution fail type validation instead of hiding the regression. + with open(os.path.join(cwd_dir, "inner.json"), "w") as f: + json.dump({"value": "wrong"}, f) + + ca = Argument( + "base", + dict, + [Argument("nested", dict, [Argument("value", int)])], + ) + original_cwd = os.getcwd() + try: + os.chdir(cwd_dir) + ca.check_value({"$ref": outer_path}, allow_ref=True) + finally: + os.chdir(original_cwd) + def test_ref_check_value_root_extra_check(self) -> None: """Root extra checks run against the contents loaded from ``$ref``.""" ref_path = self._write_json("ref_root_extra.json", {"sub1": 5})