def load_defaults(default: Union[str, dict, MinyDict]): """ Set the default keys from `defaults`: * str/path -> must point to a yaml/json/pickle file then MinyDict.from_X will be used * dict -> Convert that dictionnary to a resolved MinyDict * list -> Recursively calls load_defaults on each element, then sequentially update an (initially empty) MinyDict to allow for hierarchical defaults. Args: allow (Union[str, dict, MinyDict]): The set of allowed keys as a (Miny)dict or a path to a file that `minydra.MinyDict` will be able to load (as `json`, `pickle` or `yaml`) """ # `defaults` is a path: load it with MinyDict.from_X if isinstance(default, (str, pathlib.Path)): # resolve path to file default = resolve_path(default) # ensure it exists assert default.exists() assert default.is_file() # check for known file formats if default.suffix not in {".json", ".yaml", ".yml", ".pickle", ".pkl"}: raise ValueError(f"{str(default)} is not a valid file extension.") # Load from YAML if default.suffix in {".yaml", ".yml"}: default = MinyDict.from_yaml(default) # Load from Pickle elif default.suffix in {".pickle", ".pkl"}: default = MinyDict.from_pickle(default) # Load from JSON else: default = MinyDict.from_json(default) # `defaults` is a dictionnary: convert it to a resolved MinyDict elif isinstance(default, dict): default = MinyDict(default).resolve() # `defaults` is a list: recursively call load_defaults on each element # then sequentially merge all dictionaries to enable hierarchical defaults elif isinstance(default, list): defaults = [Parser.load_defaults(d) for d in default] default = MinyDict() for d in defaults: default.update(d, strict=False) assert isinstance(default, MinyDict) return default
def test_defaults(): examples = Path(__file__).resolve().parent.parent / "examples" d1 = examples / "demo.json" d2 = examples / "demo2.json" y1 = examples / "demo.yaml" p = MinyDict({"a": "2", "c": 3, "d": {"e": {"f": 4, "g": 5}}}) pkl = p.to_pickle(Path(__file__).resolve().parent / "test.pkl") with patch.object(sys, "argv", [""]): args = minydra.resolved_args(defaults=p) assert args == p with patch.object(sys, "argv", ["", "d.e.f=2"]): args = minydra.resolved_args(defaults=p) assert args.d.e.f == 2 with patch.object(sys, "argv", ["", f"@defaults={str(d1)}"]): args = minydra.resolved_args() del args["@defaults"] assert args.to_dict() == json.loads(d1.read_text()) with patch.object(sys, "argv", ["", f"@defaults={str(y1)}"]): args = minydra.resolved_args() del args["@defaults"] assert args.to_dict() == MinyDict.from_yaml(y1) with patch.object(sys, "argv", ["", f"@defaults={str(pkl)}"]): args = minydra.resolved_args() del args["@defaults"] assert args.to_dict() == MinyDict.from_pickle(pkl) Path(pkl).unlink() with pytest.raises(ValueError): with patch.object( sys, "argv", ["", f"@defaults={str(d1).replace('.json', '.py')}"] ): args = minydra.resolved_args() with pytest.raises(KeyError): with patch.object(sys, "argv", ["", f"@defaults={str(d1)}", "new_key=3"]): args = minydra.resolved_args() del args["@defaults"] assert args.to_dict() == json.loads(d1.read_text()) with patch.object( sys, "argv", ["", f"@defaults={str(d1)}", "@strict=false", "new_key=3"] ): args = minydra.resolved_args(keep_special_kwargs=False) target = json.loads(d1.read_text()) target["new_key"] = 3 assert args.to_dict() == target with patch.object( sys, "argv", ["", f"@defaults={str(d1)}", "@strict=false", "new_key=3"] ): args = minydra.resolved_args() del args["@defaults"] del args["@strict"] target = json.loads(d1.read_text()) target["new_key"] = 3 assert args.to_dict() == target double_defaults = f"['{str(d1)}', '{str(d2)}']" with patch.object(sys, "argv", ["", f"@defaults={double_defaults}", "new_key=3"]): args = minydra.resolved_args() d1d = MinyDict.from_json(d1) d2d = MinyDict.from_json(d2) d1d = d1d.update(d2d) del args["@defaults"] assert args == d1d
def test_dump_json(): d = MinyDict({"a": 1, "b": 2, "u": "x", "r": {"t": 5}}) p = Path(d.to_json("d.json", verbose=1)) assert MinyDict.from_json(p) == d p.unlink()