Skip to content

Commit

Permalink
feat(node): add ability to load changes from yaml and json
Browse files Browse the repository at this point in the history
  • Loading branch information
Rizhiy committed Mar 11, 2024
1 parent 6632a77 commit 22e1c20
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 0 deletions.
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,33 @@ assert cfg.NAME == "Hello World!"
assert cfg.DICT.FOO == "BAR"
```

1. You can also load from YAML or JSON:

```yaml
# my_changes.yaml
DICT:
FOO: BAR
```
```json
# my_changes.json
{
"DICT": {
"INT": 2
}
}
```

```python
# main.py
from project.config import schema

cfg = schema.load_updates_from_file("my_changes.yaml")
assert cfg.DICT.FOO == "BAR"
cfg = schema.load_updates_from_file("my_changes.json")
assert cfg.DICT.INT == 2
```

## Development

- Install dev dependencies: `pip install -e ".[dev]"`
Expand Down
23 changes: 23 additions & 0 deletions pycs/node.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import inspect
import json
import logging
import warnings
from collections import UserDict
Expand Down Expand Up @@ -175,6 +176,28 @@ def load(cfg_path: Union[Path, str]) -> CfgNode:
cfg.propagate_changes()
return cfg

def load_updates_from_file(self, updates_path: Union[Path, str]) -> CfgNode:
updates_path = Path(updates_path)
suffix = updates_path.suffix
with updates_path.open() as f:
if suffix == ".py":
module = import_module(updates_path)
updates: MutableMapping = module.cfg
elif suffix == ".json":
updates = json.load(f)
elif suffix == ".yaml":
updates = yaml.safe_load(f)
else:
raise ValueError(f"Can't load changes from filetype with suffix '{suffix}'")
cfg = self.init_cfg()
cfg.update(updates)

if hasattr(cfg, "NAME"):
cfg.NAME = cfg.NAME or _cfg_path_to_name(updates_path, cfg._root_name) # noqa: SLF001 Same class

cfg.propagate_changes()
return cfg

@staticmethod
def validate_required(cfg: CfgNode) -> None:
if cfg.leaf_spec is not None and cfg.leaf_spec.required and len(cfg) == 0:
Expand Down
Empty file added tests/data/node/__init__.py
Empty file.
9 changes: 9 additions & 0 deletions tests/data/node/json_cfg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"BOOL": true,
"INT": 1,
"FLOAT": 1.1,
"STR": "json",
"NESTED": {
"FOO": "zoo"
}
}
9 changes: 9 additions & 0 deletions tests/data/node/python_cfg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from pycs import CN

cfg = CN()
cfg.BOOL = True
cfg.INT = 1
cfg.FLOAT = 1.1
cfg.STR = "py"
cfg.NESTED = CN()
cfg.NESTED.FOO = "zoo"
8 changes: 8 additions & 0 deletions tests/data/node/yaml_cfg.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
BOOL: true
INT: 1
FLOAT: 1.1
STR: yaml

NESTED:
FOO: zoo
31 changes: 31 additions & 0 deletions tests/test_node.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from __future__ import annotations

from pathlib import Path

import pytest

from pycs import CL, CN
from pycs.errors import MissingRequiredError, NodeReassignmentError, SchemaFrozenError, TypeMismatchError

DATA_DIR = Path(__file__).parent / "data" / "node"


class Quux:
def __str__(self):
Expand Down Expand Up @@ -299,3 +303,30 @@ def test_update_both(self, cfg: CN):
cfg.update({"FOO": "both"}, new="bar")
assert cfg.FOO == "both"
assert cfg.new == "bar"


@pytest.mark.parametrize("filename", ["json_cfg.json", "yaml_cfg.yaml", "python_cfg.py"])
def test_load_updates_from_file(filename):
schema = CN()
schema.NAME = ""
schema.BOOL = False
schema.INT = 0
schema.FLOAT = 0.0
schema.STR = ""
schema.NESTED = CN()
schema.NESTED.FOO = "bar"
schema.DEFAULT = "default"

cfg_path = DATA_DIR / filename
cfg = schema.load_updates_from_file(cfg_path)

assert cfg.NAME == cfg_path.stem # noqa: SIM300
assert cfg.BOOL
assert cfg.INT == 1
assert cfg.FLOAT == 1.1
assert cfg.STR == cfg_path.suffix[1:] # noqa: SIM300
assert cfg.NESTED.FOO == "zoo"
assert cfg.DEFAULT == "default"

# Test that original was not modified
assert not schema.BOOL

0 comments on commit 22e1c20

Please sign in to comment.