예제 #1
0
파일: base.py 프로젝트: willingc/edgedb
def _check_annotation(f_type, f_fullname, f_default):
    if typing_inspect.is_tuple_type(f_type):
        if f_default is not None:
            raise RuntimeError(
                f'invalid type annotation on {f_fullname}: '
                f'default is defined for tuple type')

        f_default = tuple

    elif typing_inspect.is_union_type(f_type):
        for t in typing_inspect.get_args(f_type, evaluate=True):
            _check_annotation(t, f_fullname, f_default)

    elif typing_inspect.is_generic_type(f_type):
        if f_default is not None:
            raise RuntimeError(
                f'invalid type annotation on {f_fullname}: '
                f'default is defined for container type '
                f'{f_type!r}')

        ot = typing_inspect.get_origin(f_type)
        if ot is None:
            raise RuntimeError(
                f'cannot find origin of a generic type {f_type}')

        if ot in (list, typing.List, collections.abc.Sequence):
            f_default = list
        elif ot in (set, typing.Set):
            f_default = set
        elif ot in (frozenset, typing.FrozenSet):
            f_default = frozenset
        elif ot in (dict, typing.Dict):
            f_default = dict
        else:
            raise RuntimeError(
                f'invalid type annotation on {f_fullname}: '
                f'{f_type!r} is not supported')

    elif f_type is not None:
        if not isinstance(f_type, type):
            raise RuntimeError(
                f'invalid type annotation on {f_fullname}: '
                f'{f_type!r} is not a type')

        if typeutils.is_container_type(f_type):
            if f_default is not None:
                raise RuntimeError(
                    f'invalid type annotation on {f_fullname}: '
                    f'default is defined for container type '
                    f'{f_type!r}')
            f_default = f_type

    return f_default
예제 #2
0
    def to_json_value(self):
        dct = {}
        dct['_tname'] = self.__class__.__name__

        for f in dataclasses.fields(self):
            f_type = f.type
            value = getattr(self, f.name)
            if (isinstance(f_type, type)
                    and issubclass(f_type, CompositeConfigType)
                    and value is not None):
                value = value.to_json_value()
            elif typing_inspect.is_generic_type(f_type):
                value = list(value)

            dct[f.name] = value

        return dct
예제 #3
0
def _check_type(type_, value, raise_error):
    if type_ is None:
        return

    if typing_inspect.is_union_type(type_):
        for t in typing_inspect.get_args(type_, evaluate=True):
            try:
                _check_type(t, value, raise_error)
            except TypeError:
                pass
            else:
                break
        else:
            raise_error(str(type_), value)

    elif typing_inspect.is_tuple_type(type_):
        _check_tuple_type(type_, value, raise_error, tuple)

    elif typing_inspect.is_generic_type(type_):
        ot = typing_inspect.get_origin(type_)

        if ot in (list, typing.List):
            _check_container_type(type_, value, raise_error, list)

        elif ot in (set, typing.Set):
            _check_container_type(type_, value, raise_error, set)

        elif ot in (frozenset, typing.FrozenSet):
            _check_container_type(type_, value, raise_error, frozenset)

        elif ot in (dict, typing.Dict):
            _check_mapping_type(type_, value, raise_error, dict)

        elif ot is not None:
            raise TypeError(f'unsupported typing type: {type_!r}')

    else:
        if value is not None and not isinstance(value, type_):
            raise_error(type_.__name__, value)
예제 #4
0
    def from_pyvalue(cls, data, *, allow_missing=False):
        if not isinstance(data, dict):
            raise cls._err(f'expected a dict value, got {type(data)!r}')

        spec = config.get_settings()

        data = dict(data)
        tname = data.pop('_tname', None)
        if tname is not None:
            if '::' in tname:
                tname = s_name.Name(tname).name
            cls = spec.get_type_by_name(tname)

        fields = {f.name: f for f in dataclasses.fields(cls)}

        items = {}
        inv_keys = []
        for fieldname, value in data.items():
            field = fields.get(fieldname)
            if field is None:
                if value is None:
                    # This may happen when data is produced by
                    # a polymorphic config query.
                    pass
                else:
                    inv_keys.append(fieldname)

                continue

            f_type = field.type

            if value is None:
                # Config queries return empty pointer values as None.
                continue

            if typing_inspect.is_generic_type(f_type):
                container = typing_inspect.get_origin(f_type)
                if container not in (frozenset, list):
                    raise RuntimeError(f'invalid type annotation on '
                                       f'{cls.__name__}.{fieldname}: '
                                       f'{f_type!r} is not supported')

                eltype = typing_inspect.get_args(f_type, evaluate=True)[0]
                if isinstance(value, eltype):
                    value = container((value, ))
                elif (typeutils.is_container(value)
                      and all(isinstance(v, eltype) for v in value)):
                    value = container(value)
                else:
                    raise cls._err(
                        f'invalid {fieldname!r} field value: expecting '
                        f'{eltype.__name__} or a list thereof, but got '
                        f'{type(value).__name__}')

            elif (issubclass(f_type, CompositeConfigType)
                  and isinstance(value, dict)):

                tname = value.get('_tname', None)
                if tname is not None:
                    if '::' in tname:
                        tname = s_name.Name(tname).name
                    actual_f_type = spec.get_type_by_name(tname)
                else:
                    actual_f_type = f_type
                    value['_tname'] = f_type.__name__

                value = actual_f_type.from_pyvalue(value)

            elif not isinstance(value, f_type):
                raise cls._err(
                    f'invalid {fieldname!r} field value: expecting '
                    f'{f_type.__name__}, but got {type(value).__name__}')

            items[fieldname] = value

        if inv_keys:
            inv_keys = ', '.join(repr(r) for r in inv_keys)
            raise cls._err(f'unknown fields: {inv_keys}')

        for fieldname, field in fields.items():
            if fieldname not in items and field.default is dataclasses.MISSING:
                if allow_missing:
                    items[fieldname] = None
                else:
                    raise cls._err(f'missing required field: {fieldname!r}')

        try:
            return cls(**items)
        except (TypeError, ValueError) as ex:
            raise cls._err(str(ex))