Esempio n. 1
0
def load_fields_values(obj: Any,
                       fields: Iterable[konfi.Field],
                       mapping: Mapping,
                       *,
                       ignore_unknown: bool = False) -> None:
    """Load the values for the fields from the mapping.

    This is done recursively.

    Args:
        obj: Object to load fields into
        fields: Fields to load
        mapping: Mapping to get field values from
        ignore_unknown: If `True`, excessive keys in the mapping are ignored.

    Raises:
        PathError: If there is an issue with a field.
            If multiple fields have an error, `MultiPathError` is raised.
    """
    _field_by_keys: Dict[str,
                         konfi.Field] = {field.key: field
                                         for field in fields}

    field_errors: List[PathError] = []

    for key, value in mapping.items():
        try:
            field = _field_by_keys[key]
        except KeyError:
            if ignore_unknown:
                continue
            else:
                raise PathError([key], f"unexpected config key: {key!r}")

        if konfi.is_template_like(field.value_type) and isinstance(
                value, Mapping):
            sub_obj = _get_sub_obj(obj, field)
            loader = functools.partial(load_fields_values, sub_obj,
                                       konfi.fields(sub_obj), value)
        else:
            loader = functools.partial(load_field_value, obj, field, value)

        try:
            loader()
        except PathError as e:
            field_errors.append(e)
        except Exception as e:
            err = FieldError([key], field, str(e))
            err.__cause__ = e
            field_errors.append(err)

    if len(field_errors) == 1:
        raise field_errors[0]
    elif field_errors:
        raise MultiPathError((), field_errors, "")
Esempio n. 2
0
    def iter_fields(parent: Any, path: List[str],
                    fields: Iterable[konfi.Field]) -> None:
        for field in fields:
            key_path = [*path, field.key]

            if konfi.is_template_like(field.value_type):
                sub_obj = _get_sub_obj(obj, field)
                yield from iter_fields(sub_obj, key_path,
                                       konfi.fields(field.value_type))
            else:
                yield QualifiedField(parent, key_path, field)
Esempio n. 3
0
def test_template_mro():
    class NonTemplate:
        a: str

    @konfi.template()
    class Template:
        b: int
        c: int

    @konfi.template()
    class Normal(NonTemplate, Template):
        d: str

    assert tuple(field.attribute
                 for field in konfi.fields(Normal)) == ("b", "c", "d")

    @konfi.template(template_bases_only=False)
    class Inherit(NonTemplate, Template):
        c: str

    assert set(field.attribute
               for field in konfi.fields(Inherit)) == {"b", "c", "a"}
Esempio n. 4
0
    def load_into(self, obj: Any, template: Type) -> None:
        try:
            import yaml
        except ImportError as e:
            raise ImportError("Couldn't import 'pyyaml' package. Make sure it's installed.") from e

        try:
            with open(self._path, "r") as f:
                data = yaml.safe_load(f)
        except FileNotFoundError as e:
            if self._ignore_not_found:
                return
            else:
                raise e

        load_fields_values(obj, konfi.fields(template), data)
Esempio n. 5
0
    def load_into(self, obj: Any, template: type) -> None:
        try:
            import toml
        except ImportError as e:
            raise ImportError(
                "Couldn't import the 'toml' package. Make sure it's installed."
            ) from e

        try:
            data = toml.load(self._path)
        except FileNotFoundError as e:
            if self._ignore_not_found:
                return
            else:
                raise e

        load_fields_values(obj, konfi.fields(template), data)
Esempio n. 6
0
def iter_fields_recursively(obj: Any,
                            template: Any) -> Iterator[QualifiedField]:
    """Iterate over all fields of the template recursively.

     Yields:
          `QualifiedField` instances.
     """
    def iter_fields(parent: Any, path: List[str],
                    fields: Iterable[konfi.Field]) -> None:
        for field in fields:
            key_path = [*path, field.key]

            if konfi.is_template_like(field.value_type):
                sub_obj = _get_sub_obj(obj, field)
                yield from iter_fields(sub_obj, key_path,
                                       konfi.fields(field.value_type))
            else:
                yield QualifiedField(parent, key_path, field)

    yield from iter_fields(obj, [], konfi.fields(template))