Skip to content

Commit a46aa92

Browse files
committed
Don't crash on invalid yaml files in directories
1 parent 178c886 commit a46aa92

File tree

1 file changed

+26
-13
lines changed

1 file changed

+26
-13
lines changed

midi_app_controller/models/utils.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import itertools
12
import os
23
import uuid
34
from pathlib import Path
45
from typing import Any, Optional
6+
from warnings import warn
57

68
import yaml
7-
from pydantic import BaseModel
9+
from pydantic import BaseModel, ValidationError
810

911
from midi_app_controller.config import Config
1012
from midi_app_controller.gui.utils import is_subpath
@@ -19,6 +21,11 @@ def _path_representer(dumper, data):
1921
yaml.SafeDumper.add_multi_representer(Path, _path_representer)
2022

2123

24+
def _abs_listdir(d: Path) -> list[Path]:
25+
"""List the contents of directory as absolute paths."""
26+
return [d / p for p in os.listdir(d)]
27+
28+
2229
class YamlBaseModel(BaseModel):
2330
@classmethod
2431
def load_from(cls, path: Path) -> "YamlBaseModel":
@@ -43,8 +50,9 @@ def load_from(cls, path: Path) -> "YamlBaseModel":
4350
def load_all_from(
4451
cls, directories: list[Path]
4552
) -> list[tuple["YamlBaseModel", Path]]:
46-
"""Creates models initialized with data from all YAML files in
47-
multiple directories.
53+
"""Return models with data from all YAML files in multiple directories.
54+
55+
If a yaml file fails to load, it is skipped and a warning is emitted.
4856
4957
Parameters
5058
----------
@@ -56,16 +64,21 @@ def load_all_from(
5664
list[tuple[cls, Path]]
5765
List of created models with paths to corresponding YAML files.
5866
"""
59-
return [
60-
(
61-
cls.load_from(directory / filename),
62-
directory / filename,
63-
)
64-
for directory in directories
65-
if directory.exists()
66-
for filename in os.listdir(directory)
67-
if filename.lower().endswith((".yaml", ".yml"))
68-
]
67+
all_models = []
68+
real_directories = filter(os.path.exists, directories)
69+
fns = itertools.chain(*map(_abs_listdir, real_directories))
70+
yamls = (fn for fn in fns if fn.suffix in {".yaml", ".yml"})
71+
for fn in yamls:
72+
try:
73+
model = cls.load_from(fn)
74+
all_models.append((model, fn))
75+
except (ValidationError, Exception) as e:
76+
warn(
77+
f"Unable to load model from file {fn}; got error:\n"
78+
f"{e.__class__}: {e}",
79+
stacklevel=2,
80+
)
81+
return all_models
6982

7083
def save_to(self, path: Path) -> None:
7184
"""Saves the model's data to a YAML file.

0 commit comments

Comments
 (0)