Esempio n. 1
0
def parse_dict(
    ctor: _CtorType,
    node: yaml.MappingNode,
    keys: Mapping[str, Any],
    res_type: Type[_AstType],
    *,
    ret_name: Optional[str] = None,
    extra: Optional[Mapping[str, Union[str, LocalPath]]] = None,
    preprocess: Optional[Callable[
        [_CtorType, yaml.MappingNode, Dict[str, Any]], Dict[str, Any]]] = None,
    find_res_type: Optional[
        Callable[[_CtorType, yaml.MappingNode, Type[_AstType], Dict[str, Any]],
                 Type[_AstType], ]] = None,
) -> _AstType:
    if extra is None:
        extra = {}
    if ret_name is None:
        ret_name = res_type.__name__
    if not isinstance(node, yaml.MappingNode):
        node_id = node.id
        raise ConstructorError(
            None,
            None,
            f"expected a mapping node, but found '{node_id}'",
            node.start_mark,
        )
    node_start = mark2pos(node.start_mark)
    node_end = mark2pos(node.end_mark)

    data = dict(extra)
    for k, v in node.value:
        key = ctor.construct_object(k)  # type: ignore[no-untyped-call]
        if key not in keys:
            raise ConstructorError(
                f"while constructing a '{ret_name}'",
                node.start_mark,
                f"unexpected key '{key}'",
                k.start_mark,
            )
        item_ctor: Any = keys[key]
        tmp: Any
        value: Any
        if item_ctor is None:
            # Get constructor from tag
            value = ctor.construct_object(v)  # type: ignore[no-untyped-call]
        elif isinstance(item_ctor, type) and issubclass(item_ctor, enum.Enum):
            tmp = str(
                ctor.construct_object(v))  # type: ignore[no-untyped-call]
            value = item_ctor(tmp)
        elif isinstance(item_ctor, ast.Base):
            assert isinstance(
                v, ast.Base), f"[{type(v)}] '{v}' should be ast.Base derived"
            value = v
        elif isinstance(item_ctor, SimpleCompound):
            value = item_ctor.construct(ctor, v)
        elif isinstance(item_ctor, type) and issubclass(item_ctor, Expr):
            tmp = ctor.construct_object(v)  # type: ignore[no-untyped-call]
            value = item_ctor(mark2pos(v.start_mark), mark2pos(v.end_mark),
                              tmp)
        else:
            raise ConstructorError(
                f"while constructing a '{ret_name}'",
                node.start_mark,
                f"unexpected value tag '{v.tag}' for key '{key}[{item_ctor}]'",
                k.start_mark,
            )
        data[key] = value

    if preprocess is not None:
        data = preprocess(ctor, node, dict(data))
    if find_res_type is not None:
        res_type = find_res_type(ctor, node, res_type, dict(data))
        ret_name = res_type.__name__

    optional_fields: Dict[str, Any] = {}
    found_fields = data.keys() | {"_start", "_end"}
    field_names = set()
    for f in dataclasses.fields(res_type):
        field_names.add(f.name)
        if f.name == "_specified_fields":
            optional_fields[f.name] = set(data.keys())
        elif f.name not in found_fields:
            key = f.name
            item_ctor = keys[key]
            if f.default is not dataclasses.MISSING:
                optional_fields[f.name] = f.default
            elif (item_ctor is None or isinstance(item_ctor, SimpleCompound)
                  or isinstance(item_ctor, ast.Base)):
                if f.metadata.get("allow_none", False):
                    optional_fields[f.name] = None
                else:
                    raise ConstructorError(
                        f"while constructing a '{ret_name}', "
                        f"missing mandatory key '{f.name}'",
                        node.start_mark,
                    )
            elif isinstance(item_ctor, type) and issubclass(item_ctor, Expr):
                default_expr = f.metadata.get("default_expr", None)
                if default_expr:
                    fake_node = dataclasses.replace(node_start, line=0, col=0)
                    optional_fields[f.name] = item_ctor(
                        fake_node, fake_node, default_expr)
                elif not item_ctor.allow_none:
                    raise ConstructorError(
                        f"while constructing a '{ret_name}', "
                        f"missing mandatory key '{f.name}'",
                        node.start_mark,
                    )
                else:
                    optional_fields[f.name] = item_ctor(
                        node_start, node_end, None)
            elif isinstance(item_ctor, type) and issubclass(
                    item_ctor, enum.Enum):
                if f.metadata.get("allow_none", False):
                    optional_fields[f.name] = None
            else:
                raise ConstructorError(
                    f"while constructing a '{ret_name}', "
                    f"unexpected '{f.name}' constructor type '{item_ctor!r}'",
                    node.start_mark,
                )

    actual_names = found_fields | optional_fields.keys()
    missing_names = field_names - actual_names
    sep = ","
    if missing_names:
        raise ConstructorError(
            f"while constructing a '{ret_name}', "
            f"missing fields '{sep.join(missing_names)}'",
            node.start_mark,
        )
    unknown_names = actual_names - field_names
    if unknown_names:
        raise ConstructorError(
            f"while constructing a '{ret_name}', "
            f"unexpected fields '{sep.join(missing_names)}'",
            node.start_mark,
        )
    return res_type(
        _start=node_start,
        _end=node_end,
        **data,
        **optional_fields,
    )
Esempio n. 2
0
def check_extra(node: yaml.Node, dct: Dict[str, Any], ret: Dict[str, Any],
                name: str) -> None:
    diff = dct.keys() - ret.keys()
    if diff:
        raise ConstructorError(f"{','.join(diff)} are not allowed in {name}",
                               node.start_mark)
Esempio n. 3
0
 def _invalid_node(self, node, expected):
     raise ConstructorError(
         None, None,
         'expected {0}, but found {1}'.format(expected,
                                              node.id), node.start_mark)