diff --git a/src/configuraptor/abs.py b/src/configuraptor/abs.py index 0c3accc..b9f73c6 100644 --- a/src/configuraptor/abs.py +++ b/src/configuraptor/abs.py @@ -11,7 +11,7 @@ # t_typelike is anything that can be type hinted T_typelike: typing.TypeAlias = type | types.UnionType # | typing.Union # t_data is anything that can be fed to _load_data -T_data_types = str | Path | dict[str, typing.Any] | None +T_data_types = str | Path | bytes | dict[str, typing.Any] | None T_data = T_data_types | list[T_data_types] # c = a config class instance, can be any (user-defined) class diff --git a/src/configuraptor/core.py b/src/configuraptor/core.py index 9a242c2..76ca304 100644 --- a/src/configuraptor/core.py +++ b/src/configuraptor/core.py @@ -70,6 +70,12 @@ def __load_data( E.g. class Tool will be mapped to key tool. It also deals with nested keys (tool.extra -> {"tool": {"extra": ...}} """ + if isinstance(data, bytes): + # instantly return, don't modify + # bytes as inputs -> bytes as output + # but since `T_data` is re-used, that's kind of hard to type for mypy. + return data # type: ignore + if isinstance(data, list): if not data: raise ValueError("Empty list passed!") @@ -82,10 +88,12 @@ def __load_data( if isinstance(data, str): data = Path(data) + if isinstance(data, Path): with data.open("rb") as f: loader = loaders.get(data.suffix or data.name) data = loader(f, data.resolve()) + if not data: return {} @@ -353,7 +361,7 @@ def _split_init(init: T_init) -> tuple[T_init_list, T_init_dict]: def _load_into_recurse( cls: typing.Type[C], - data: dict[str, typing.Any], + data: dict[str, typing.Any] | bytes, init: T_init = None, strict: bool = True, convert_types: bool = False, @@ -367,9 +375,12 @@ def _load_into_recurse( """ init_args, init_kwargs = _split_init(init) - if issubclass(cls, BinaryConfig): + if isinstance(data, bytes) or issubclass(cls, BinaryConfig): if not isinstance(data, (bytes, dict)): # pragma: no cover raise NotImplementedError("BinaryConfig can only deal with `bytes` or a dict of bytes as input.") + elif not issubclass(cls, BinaryConfig): # pragma: no cover + raise NotImplementedError("Only BinaryConfig can be used with `bytes` (or a dict of bytes) as input.") + inst = typing.cast(C, cls._parse_into(data)) elif dc.is_dataclass(cls): to_load = check_and_convert_data(cls, data, init_kwargs.keys(), strict=strict, convert_types=convert_types) diff --git a/tests/test_binary.py b/tests/test_binary.py index 47f1b8c..10059b4 100644 --- a/tests/test_binary.py +++ b/tests/test_binary.py @@ -63,7 +63,7 @@ def test_nested_binary_config(): assert inst.data1.name != inst.data2.name - assert inst._pack() == data1 + data2 + data3 + assert inst._pack() == data1 + data2 + data3 == asbytes(inst) class Version(BinaryConfig): @@ -85,7 +85,7 @@ def test_binary_config_with_external_block(): assert data.first.patch == 5 assert data.second.major == 0 - assert data._pack() == v1 + v2 + assert data._pack() == v1 + v2 == asbytes(data) class IsNumber(BinaryConfig):