Exemplo n.º 1
0
    def build_typed_field(self, field_name: str, type_info: TypeInfo,
                          extra_kwargs: KWArgs) -> SerializerFieldDefinition:
        """
        Create a serializer field for a typed dataclass field.
        """
        if type_info.is_mapping or type_info.is_many:
            field_class, field_kwargs = self.build_composite_field(
                field_name, type_info, extra_kwargs)
        elif dataclasses.is_dataclass(type_info.base_type):
            field_class, field_kwargs = self.build_dataclass_field(
                field_name, type_info)
        elif isinstance(type_info.base_type, type) and issubclass(
                type_info.base_type, Model):
            field_class, field_kwargs = self.build_relational_field(
                field_name, type_info)
        elif isinstance(type_info.base_type, type) and issubclass(
                type_info.base_type, Enum):
            field_class, field_kwargs = self.build_enum_field(
                field_name, type_info)
        elif typing_utils.is_literal_type(type_info.base_type):
            field_class, field_kwargs = self.build_literal_field(
                field_name, type_info)
        else:
            field_class, field_kwargs = self.build_standard_field(
                field_name, type_info)

        if type_info.is_optional:
            field_kwargs['required'] = False
            field_kwargs['allow_null'] = True

        if type_info.is_final:
            field_kwargs['read_only'] = True

        return field_class, field_kwargs
Exemplo n.º 2
0
    def test_literal(self):
        self.assertTrue(typing_utils.is_literal_type(types.Literal['a', 'b']))
        self.assertTrue(typing_utils.is_literal_type(types.Literal['a', 'b', None]))
        self.assertTrue(typing_utils.is_literal_type(types.Literal['a', 'b', types.Literal['c', 'd']]))
        self.assertTrue(typing_utils.is_literal_type(types.Literal['a', 'b', types.Literal['c', 'd', None]]))
        self.assertTrue(typing_utils.is_literal_type(types.Literal['a', 'b', types.Literal[1, 2]]))
        self.assertTrue(typing_utils.is_literal_type(types.Literal['a', 'b', types.Literal[1, 2, None]]))

        self.assertFalse(typing_utils.is_optional_type(types.Literal['a', 'b']))
        self.assertTrue(typing_utils.is_optional_type(types.Literal['a', 'b', None]))
        self.assertFalse(typing_utils.is_optional_type(types.Literal['a', 'b', types.Literal['c', 'd']]))
        self.assertTrue(typing_utils.is_optional_type(types.Literal['a', 'b', types.Literal['c', 'd', None]]))
        self.assertFalse(typing_utils.is_optional_type(types.Literal['a', 'b', types.Literal[1, 2]]))
        self.assertTrue(typing_utils.is_optional_type(types.Literal['a', 'b', types.Literal[1, 2, None]]))

        self.assertListEqual(typing_utils.get_literal_choices(types.Literal['a', 'b']), ['a', 'b'])
        self.assertListEqual(typing_utils.get_literal_choices(types.Literal['a', 'b', None]), ['a', 'b', None])
        self.assertListEqual(typing_utils.get_literal_choices(types.Literal['a', 'b', types.Literal['c', 'd']]),
                             ['a', 'b', 'c', 'd'])
        self.assertListEqual(typing_utils.get_literal_choices(types.Literal['a', 'b', types.Literal['c', 'd', None]]),
                             ['a', 'b', 'c', 'd', None])
        self.assertListEqual(typing_utils.get_literal_choices(types.Literal['a', 'b', types.Literal[1, 2]]),
                             ['a', 'b', 1, 2])
        self.assertListEqual(typing_utils.get_literal_choices(types.Literal['a', 'b', types.Literal[1, 2, None]]),
                             ['a', 'b', 1, 2, None])

        with self.assertRaises(ValueError):
            typing_utils.get_literal_choices(str)
Exemplo n.º 3
0
    def build_typed_field(self, field_name: str, type_info: TypeInfo,
                          extra_kwargs: KWArgs) -> SerializerFieldDefinition:
        """
        Create a serializer field for a typed dataclass field.
        """
        if type_info.is_mapping or type_info.is_many:
            field_class, field_kwargs = self.build_composite_field(
                field_name, type_info, extra_kwargs)
        elif dataclasses.is_dataclass(type_info.base_type):
            field_class, field_kwargs = self.build_dataclass_field(
                field_name, type_info)
        elif isinstance(type_info.base_type, type) and issubclass(
                type_info.base_type, Model):
            field_class, field_kwargs = self.build_relational_field(
                field_name, type_info)
        elif isinstance(type_info.base_type, type) and issubclass(
                type_info.base_type, Enum):
            field_class, field_kwargs = self.build_enum_field(
                field_name, type_info)
        elif typing_utils.is_literal_type(type_info.base_type):
            field_class, field_kwargs = self.build_literal_field(
                field_name, type_info)
        else:
            field_class, field_kwargs = self.build_standard_field(
                field_name, type_info)

        # Mark a field as not-required if it has a default value (factory) on the dataclass. This is consistent with the
        # constructor of dataclasses, where these fields are also made optional. Note that this is different from the
        # `typing.Optional[]` qualifier, which merely makes the field nullable, but still requires it to be passed. Of
        # course it makes sense for `Optional` fields to have `None` as a default value, but that's up to the user.
        field = self.dataclass_definition.fields[field_name]
        if field.default is not dataclasses.MISSING or field.default_factory is not dataclasses.MISSING:
            field_kwargs['required'] = False

            # Explicitly don't set the default argument here. Setting it would cause the default value to be inserted in
            # the native representation (`to_internal_value()` argument) if the field wasn't supplied by the user (for
            # non-partial updates). This in turn would cause `update()` to overwrite non-supplied fields with the
            # defaults, which is undesirable. Instead, let the dataclass constructor apply the default values when the
            # dataclass is instantiated.

        # Mark a field as nullable if it is declared as Optional[] (which has a confusing name).
        if type_info.is_nullable:
            field_kwargs['allow_null'] = True

        # The final qualifier declares that a variable or attribute should not be reassigned (PEP 591). Mark the field
        # as read only.
        if type_info.is_final:
            field_kwargs['read_only'] = True

        return field_class, field_kwargs