Example #1
0
    def _get_field_meta(cls, field: Field, schema_type: SchemaType) -> Tuple[FieldMeta, bool]:
        required = True
        field_meta = FieldMeta(schema_type=schema_type)
        default_value = MISSING
        if field.default is not MISSING:
            # In case of default value given
            default_value = field.default
        elif field.default_factory is not MISSING and field.default_factory is not None:  # type: ignore
            # In case of a default factory given, we call it
            default_value = field.default_factory()  # type: ignore

        if default_value is not MISSING:
            field_meta.default = cls._encode_field(field.type, default_value, omit_none=False)
            required = False
        if field.metadata is not None:
            if "examples" in field.metadata:
                field_meta.examples = [
                    cls._encode_field(field.type, example, omit_none=False) for example in field.metadata["examples"]
                ]
            if "extensions" in field.metadata:
                field_meta.extensions = field.metadata["extensions"]
            if "description" in field.metadata:
                field_meta.description = field.metadata["description"]
            if "title" in field.metadata:
                field_meta.title = field.metadata["title"]
            if schema_type == SchemaType.OPENAPI_3:
                field_meta.read_only = field.metadata.get("read_only")
                if field_meta.read_only and default_value is MISSING:
                    warnings.warn(f"Read-only fields should have a default value")
                field_meta.write_only = field.metadata.get("write_only")
        return field_meta, required
Example #2
0
def get_dataclass_default(field: Field, omit_default: Optional[bool]) -> Any:
    if not omit_default:
        return MISSING
    # type ignore because of https://github.com/python/mypy/issues/6910
    if field.default_factory != MISSING:  # type: ignore
        return field.default_factory()  # type: ignore
    return field.default
Example #3
0
    def _get_field_meta(cls, field: Field,
                        schema_type: SchemaType) -> Tuple[FieldMeta, bool]:
        required = True
        field_meta = FieldMeta()
        default_value = None
        if field.default is not MISSING and field.default is not None:
            # In case of default value given
            default_value = field.default
        elif field.default_factory is not MISSING and field.default_factory is not None:  # type: ignore
            # In case of a default factory given, we call it
            default_value = field.default_factory()  # type: ignore

        if default_value is not None:
            field_meta.default = cls._encode_field(field.type,
                                                   default_value,
                                                   omit_none=False)
            required = False
        if field.metadata is not None:
            if "description" in field.metadata:
                field_meta.description = field.metadata["description"]
            if schema_type == SchemaType.OPENAPI_3:
                field_meta.read_only = field.metadata.get("read_only")
                if field_meta.read_only and default_value is None:
                    raise ValueError(
                        f"Read-only fields must have a default value")
                field_meta.write_only = field.metadata.get("write_only")
        return field_meta, required
Example #4
0
def get_default(field: Field, schema: Schema[T]) -> Any:
    if not schema.omit_default:
        return MISSING
    # type ignore because of https://github.com/python/mypy/issues/6910
    if field.default_factory != MISSING:  # type: ignore
        return field.default_factory()  # type: ignore
    return field.default
Example #5
0
def get_default_value_for_field(field: Field) -> Any:
    if field.default != MISSING:
        return field.default
    elif field.default_factory != MISSING:  # type: ignore
        return field.default_factory()  # type: ignore
    elif is_optional(field.type):
        return None
    raise DefaultValueNotFoundError()
Example #6
0
def _get_default_value_for_field(field: Field) -> Any:
    if field.default != MISSING:
        return field.default
    elif field.default_factory != MISSING:
        return field.default_factory()
    elif _is_optional(field.type):
        return None
    else:
        raise MissingValueError(field)
Example #7
0
def _get_dataclass_field_default(field: Field) -> Any:
    if field.default_factory is not MISSING:
        # pyre-fixme[29]: `Union[dataclasses._MISSING_TYPE,
        #  dataclasses._DefaultFactory[typing.Any]]` is not a function.
        return field.default_factory()
    elif field.default is not MISSING:
        return field.default
    else:
        return None
Example #8
0
def _convert_dataclass_field(value, field: dataclasses.Field):
    if value is not dataclasses.MISSING:
        return convert(value, field.type)
    if field.default is not dataclasses.MISSING:
        return field.default
    if field.default_factory is not dataclasses.MISSING:
        return field.default_factory()
    elif is_optional(field.type):
        return None
    else:
        raise MissingException
Example #9
0
def _get_field_default(field: dataclasses.Field):
    """
    Return a marshmallow default value given a dataclass default value

    >>> _get_field_default(dataclasses.field())
    <marshmallow.missing>
    """
    if field.default_factory is not dataclasses.MISSING:
        return field.default_factory()
    elif field.default is dataclasses.MISSING:
        return marshmallow.missing
    return field.default
Example #10
0
def _default_value(x: Field):
    """Return the default value of the input Field.

    Args:
        x (Field): input Field.

    Returns:
        object: default value of the input Field.
    """
    if x.default not in (MISSING, _MISSING):
        return x.default
    if x.default_factory not in (MISSING, _MISSING):
        return x.default_factory()
    return x.default
Example #11
0
def _add_type_list(group: argparse._ArgumentGroup, field: dataclasses.Field,
                   name: str):
    group.add_argument(
        f"--{name}_{field.name}",
        type=typing.get_args(field.type)[0],
        default=None
        if field.default_factory is dataclasses.MISSING  # type: ignore
        else field.default_factory(),  # type: ignore
        required=field.default_factory is dataclasses.MISSING,  # type: ignore
        choices=field.metadata.get(  # type: ignore
            Options.POSSIBLE_VALUES, Options.POSSIBLE_VALUES.value),
        help=field.metadata.get(Options.HELP_TEXT,
                                Options.HELP_TEXT.value),  # type: ignore
        nargs="*",
    )
Example #12
0
def _get_value_for_field(field: Field, data: Data, config: Config) -> Any:
    try:
        if field.name in config.prefixed:
            return _extract_nested_dict_for_prefix(config.prefixed[field.name],
                                                   data)
        elif field.name in config.flattened:
            return _extract_flattened_fields(field, data, config.remap)
        else:
            key_name = config.remap.get(field.name, field.name)
            return data[key_name]
    except KeyError:
        if _is_optional(field.type):
            return None
        elif field.default != MISSING:
            return field.default
        elif field.default_factory != MISSING:
            return field.default_factory()
        else:
            raise MissingValueError(field)
    def _get_default(catch_all_field: Field) -> Any:
        # access to the default factory currently causes
        # a false-positive mypy error (16. Dec 2019):
        # https://github.com/python/mypy/issues/6910

        # noinspection PyProtectedMember
        has_default = not isinstance(catch_all_field.default,
                                     dataclasses._MISSING_TYPE)
        # noinspection PyProtectedMember
        has_default_factory = not isinstance(catch_all_field.default_factory,
                                             # type: ignore
                                             dataclasses._MISSING_TYPE)
        default_value = _CatchAllUndefinedParameters._SentinelNoDefault
        if has_default:
            default_value = catch_all_field.default
        elif has_default_factory:
            # This might be unwanted if the default factory constructs
            # something expensive,
            # because we have to construct it again just for this test
            default_value = catch_all_field.default_factory()  # type: ignore

        return default_value
Example #14
0
    def _get_field_meta(cls, field: Field,
                        schema_type: SchemaType) -> Tuple[FieldMeta, bool]:
        required = True
        field_meta = FieldMeta()
        default_value = None
        if field.default is not MISSING and field.default is not None:
            # In case of default value given
            default_value = field.default
        elif field.default_factory is not MISSING and field.default_factory is not None:  # type: ignore
            # In case of a default factory given, we call it
            default_value = field.default_factory()  # type: ignore

        if default_value is not None:
            field_meta.default = cls._encode_field(field.type,
                                                   default_value,
                                                   omit_none=False)
            required = False

        if field.metadata is not None:
            if "description" in field.metadata:
                field_meta.description = field.metadata["description"]

        return field_meta, required
Example #15
0
def get_default(field: Field) -> Any:
    if field.default_factory is not MISSING:
        value = field.default_factory()
    else:
        value = field.default
    return value if value is not MISSING else None
Example #16
0
def colander_params(
    prop: dataclasses.Field,
    oid_prefix: str,
    schema: type,
    request: typing.Any,
    mode=None,
    **kwargs,
) -> dict:
    """
    Get parameters for ``colander.SchemaNode`` constructor from ``dataclass.Field``

    :param prop: ``dataclass.Field`` object
    :param oid_prefix: string prefix for use as field oid
    :param schema: ``dataclass`` based class
    :param request: ``request`` object. Accepts anything, as it is merely passed to validators
    :param mode: one of the following: ``'default'``, ``'edit'``, ``'edit-process'``
    :param kwargs: additional parameters to be included into output. Will override derived parameters.

    :return: dictionary of parameters for ``colander.SchemaNode``

    """
    t = dataclass_get_type(prop)

    default_value = None
    if t["type"] == dict:
        default_value = {}
    elif t["type"] == list:
        default_value = []
    elif t["type"] == set:
        default_value = set()

    if (not isinstance(prop.default, dataclasses._MISSING_TYPE)
            and prop.default is not None):
        default_value = prop.default
    elif (not isinstance(prop.default_factory, dataclasses._MISSING_TYPE)
          and prop.default_factory is not None):
        default_value = prop.default_factory()

    params = {
        "name": prop.name,
        "oid": "%s-%s" % (oid_prefix, prop.name),
        "missing": colander.required if t["required"] else default_value,
        "default": default_value,
    }

    if "deform.widget" in prop.metadata.keys():
        params["widget"] = copy.copy(prop.metadata["deform.widget"])
    elif "deform.widget_factory" in prop.metadata.keys():
        params["widget"] = prop.metadata["deform.widget_factory"](request)

    validators = prop.metadata.get("validators", None)
    if validators:
        params["validator"] = ValidatorsWrapper(validators,
                                                schema=schema,
                                                request=request,
                                                mode=mode)

    preparers = prop.metadata.get("preparers", None)
    if preparers:
        params["preparer"] = PreparersWrapper(preparers,
                                              schema=schema,
                                              request=request,
                                              mode=mode)

    title = prop.metadata.get("title", None)

    if title:
        params["title"] = title

    description = prop.metadata.get("description", None)
    if description:
        params["description"] = description

    params.update(kwargs)
    return params
Example #17
0
def default_field_value(f: Field) -> Any:
    if callable(f.default_factory):  # type: ignore
        return f.default_factory()  # type: ignore
    else:
        return f.default
Example #18
0
def get_dataclass_default(field: Field) -> Any:
    # type ignore because of https://github.com/python/mypy/issues/6910
    if field.default_factory != MISSING:  # type: ignore
        return field.default_factory()  # type: ignore
    return field.default
Example #19
0
    def _parse_dataclass_field(parser: ArgumentParser,
                               field: dataclasses.Field):
        field_name = f"--{field.name}"
        kwargs = field.metadata.copy()
        # field.metadata is not used at all by Data Classes,
        # it is provided as a third-party extension mechanism.
        if isinstance(field.type, str):
            raise RuntimeError(
                "Unresolved type detected, which should have been done with the help of "
                "`typing.get_type_hints` method by default")

        origin_type = getattr(field.type, "__origin__", field.type)
        if origin_type is Union:
            if len(field.type.__args__) != 2 or type(
                    None) not in field.type.__args__:
                raise ValueError(
                    "Only `Union[X, NoneType]` (i.e., `Optional[X]`) is allowed for `Union`"
                )
            if bool not in field.type.__args__:
                # filter `NoneType` in Union (except for `Union[bool, NoneType]`)
                field.type = (field.type.__args__[0] if isinstance(
                    None, field.type.__args__[1]) else field.type.__args__[1])
                origin_type = getattr(field.type, "__origin__", field.type)

        # A variable to store kwargs for a boolean field, if needed
        # so that we can init a `no_*` complement argument (see below)
        bool_kwargs = {}
        if isinstance(field.type, type) and issubclass(field.type, Enum):
            kwargs["choices"] = [x.value for x in field.type]
            kwargs["type"] = type(kwargs["choices"][0])
            if field.default is not dataclasses.MISSING:
                kwargs["default"] = field.default
            else:
                kwargs["required"] = True
        elif field.type is bool or field.type is Optional[bool]:
            # Copy the currect kwargs to use to instantiate a `no_*` complement argument below.
            # We do not initialize it here because the `no_*` alternative must be instantiated after the real argument
            bool_kwargs = copy(kwargs)

            # Hack because type=bool in argparse does not behave as we want.
            kwargs["type"] = string_to_bool
            if field.type is bool or (field.default is not None
                                      and field.default
                                      is not dataclasses.MISSING):
                # Default value is False if we have no default when of type bool.
                default = False if field.default is dataclasses.MISSING else field.default
                # This is the value that will get picked if we don't include --field_name in any way
                kwargs["default"] = default
                # This tells argparse we accept 0 or 1 value after --field_name
                kwargs["nargs"] = "?"
                # This is the value that will get picked if we do --field_name (without value)
                kwargs["const"] = True
        elif isclass(origin_type) and issubclass(origin_type, list):
            kwargs["type"] = field.type.__args__[0]
            kwargs["nargs"] = "+"
            if field.default_factory is not dataclasses.MISSING:
                kwargs["default"] = field.default_factory()
            elif field.default is dataclasses.MISSING:
                kwargs["required"] = True
        else:
            kwargs["type"] = field.type
            if field.default is not dataclasses.MISSING:
                kwargs["default"] = field.default
            elif field.default_factory is not dataclasses.MISSING:
                kwargs["default"] = field.default_factory()
            else:
                kwargs["required"] = True
        parser.add_argument(field_name, **kwargs)

        # Add a complement `no_*` argument for a boolean field AFTER the initial field has already been added.
        # Order is important for arguments with the same destination!
        # We use a copy of earlier kwargs because the original kwargs have changed a lot before reaching down
        # here and we do not need those changes/additional keys.
        if field.default is True and (field.type is bool
                                      or field.type is Optional[bool]):
            bool_kwargs["default"] = False
            parser.add_argument(f"--no_{field.name}",
                                action="store_false",
                                dest=field.name,
                                **bool_kwargs)
Example #20
0
 def has_default(self, field: dataclasses.Field) -> FieldDefault:
     if field.default != dataclasses.MISSING:
         return FieldDefault(True, field.default)
     if field.default_factory != dataclasses.MISSING:
         return FieldDefault(True, field.default_factory())
     return FieldDefault()