Exemple #1
0
def _map_attr_to_type_for_class(
    cls: Type,
) -> Dict[attr.Attribute, Tuple[BuildableAttrFieldType, Optional[Type[Enum]]]]:
    """Helper function for _attribute_field_type_reference_for_class to map attributes
    to their BuildableAttrFieldType for a class if the attributes of the class aren't
    yet in the cached _class_structure_reference.
    """
    attr_field_types: Dict[
        attr.Attribute, Tuple[BuildableAttrFieldType, Optional[Type[Enum]]]
    ] = {}

    for attribute in attr.fields_dict(cls).values():
        if is_forward_ref(attribute):
            attr_field_types[attribute] = (BuildableAttrFieldType.FORWARD_REF, None)
        elif is_enum(attribute):
            enum_cls = get_enum_cls(attribute)

            if not enum_cls:
                raise ValueError(
                    f"Did not find enum class for enum attribute [{attribute}]"
                )

            attr_field_types[attribute] = (BuildableAttrFieldType.ENUM, enum_cls)
        elif is_date(attribute):
            attr_field_types[attribute] = (BuildableAttrFieldType.DATE, None)
        else:
            attr_field_types[attribute] = (BuildableAttrFieldType.OTHER, None)

    return attr_field_types
Exemple #2
0
 def schema_type_for_attribute(attribute: Any) -> str:
     # Race and ethnicity fields are the only ones that support list form. These
     # are converted to comma-separated lists stored as strings in BigQuery.
     if is_enum(attribute) or is_list(attribute) or is_str(attribute):
         return bigquery.enums.SqlTypeNames.STRING.value
     if is_int(attribute):
         return bigquery.enums.SqlTypeNames.INTEGER.value
     if is_float(attribute):
         return bigquery.enums.SqlTypeNames.FLOAT.value
     if is_date(attribute):
         return bigquery.enums.SqlTypeNames.DATE.value
     if is_bool(attribute):
         return bigquery.enums.SqlTypeNames.BOOLEAN.value
     raise ValueError(f"Unhandled attribute type for attribute: {attribute}")
Exemple #3
0
    def build_from_dictionary(cls, build_dict: Dict[str, Any]) -> \
            Optional['BuildableAttr']:
        """Builds a BuildableAttr with values from the given build_dict.

        Given build_dict must contain all required fields, and cannot contain
        any fields with attribute types of List or ForwardRef. Any date values
        must be in the format 'YYYY-MM-DD' if they are present.
        """
        if not attr.has(cls):
            raise Exception("Parent class must be an attr class")

        if not build_dict:
            raise ValueError("build_dict cannot be empty")

        cls_builder = cls.builder()

        for field, attribute in attr.fields_dict(cls).items():
            if field in build_dict:
                if is_list(attribute):
                    raise ValueError("build_dict should be a dictionary of "
                                     "flat values. Should not contain any "
                                     f"lists: {build_dict}.")
                if is_forward_ref(attribute):
                    # TODO(1886): Implement detection of non-ForwardRefs
                    # ForwardRef fields are expected to be references to other
                    # BuildableAttrs
                    raise ValueError("build_dict should be a dictionary of "
                                     "flat values. Should not contain any "
                                     f"ForwardRef fields: {build_dict}")

                if is_enum(attribute):
                    value = cls.extract_enum_value(build_dict, field,
                                                   attribute)
                elif is_date(attribute):
                    value = cls.extract_date_value(build_dict, field)
                else:
                    value = build_dict.get(field)

                setattr(cls_builder, field, value)

        return cls_builder.build()
Exemple #4
0
    def convert_field_value(field: attr.Attribute,
                            field_value: Union[str, EnumParser]) -> Any:
        if field_value is None:
            return None

        if is_forward_ref(field) or is_list(field):
            return field_value

        if isinstance(field_value, str):
            if not field_value or not field_value.strip():
                return None

        if field.name in converter_overrides:
            converter = converter_overrides[field.name]
            if not isinstance(field_value, converter.field_type):
                raise ValueError(
                    f"Found converter for field [{field.name}] in the converter_overrides, but expected "
                    f"field type [{converter.field_type}] does not match actual field type "
                    f"[{type(field_value)}]")
            return converter.convert(field_value)

        if isinstance(field_value, EnumParser):
            if is_enum(field):
                return field_value.parse()
            raise ValueError(
                f"Found field value [{field_value}] for field that is not an enum [{field}]."
            )

        if isinstance(field_value, str):
            if is_str(field):
                return normalize(field_value)
            if is_date(field):
                return parse_date(field_value)
            if is_int(field):
                return parse_int(field_value)
            if field.type in {bool, Union[bool, None]}:
                return parse_bool(field_value)

        raise ValueError(f"Unsupported field {field.name}")
    def _convert_forward(self, src: SrcBaseType,
                         populate_back_edges: bool) -> DstBaseType:
        """Converts the given src object to its entity/schema counterpart."""

        src_id = self._id_from_src_object(src)
        if src_id in self._converted_map:
            return self._converted_map[src_id]

        schema_cls: Type[DatabaseEntity] = self._get_schema_class(src)
        entity_cls: Type[Entity] = self._get_entity_class(src)

        if entity_cls is None or schema_cls is None:
            raise DatabaseConversionError("Both |entity_cls| and |schema_cls| "
                                          "should be not None")

        if isinstance(src, Entity):
            dst_builder: \
                Union[BuildableAttr.Builder, DatabaseEntity] = schema_cls()
        elif isinstance(src, DatabaseEntity):
            if not issubclass(entity_cls, BuildableAttr):
                raise DatabaseConversionError(
                    f"Expected [{entity_cls}] to be a subclass of "
                    f"BuildableAttr, but it is not")

            dst_builder = entity_cls.builder()
        else:
            raise DatabaseConversionError(
                "Unable to convert class [{0}]".format(src.__class__))

        for field, attribute in attr.fields_dict(entity_cls).items():
            if self._should_skip_field(entity_cls, field):
                continue

            if self._direction_checker.is_back_edge(src, field) and \
                    not populate_back_edges:
                continue

            v = getattr(src, field)

            if not isinstance(attribute, attr.Attribute):
                raise DatabaseConversionError(
                    f"Expected attribute with class [{attribute.__class__}] to "
                    f"be an instance of Attribute, but it is not")

            if isinstance(v, list):
                values = []
                for next_src in v:
                    if self._direction_checker.is_back_edge(src, field):
                        self._register_back_edge(src, next_src, field)
                        continue
                    values.append(
                        self._convert_forward(next_src, populate_back_edges))

                if not values:
                    continue

                value: Optional[Any] = values
            elif issubclass(type(v), Entity) or issubclass(
                    type(v), DatabaseEntity):
                next_src = v
                if self._direction_checker.is_back_edge(src, field):
                    self._register_back_edge(src, next_src, field)
                    continue
                value = self._convert_forward(v, populate_back_edges)
            elif v is None:
                value = None
            elif is_enum(attribute):
                value = self._convert_enum(src, field, attribute)
            else:
                value = v

            setattr(dst_builder, field, value)

        if isinstance(dst_builder, BuildableAttr.Builder):
            dst = dst_builder.build()
        elif isinstance(dst_builder, DatabaseEntity):
            dst = dst_builder
        else:
            raise DatabaseConversionError(
                f"Unexpected type [{type(dst_builder)}] for dst_builder")

        self._converted_map[src_id] = dst

        return dst