Exemplo n.º 1
0
def to_dict(obj: T, *,
            flags: SerializationFlags = SerializationFlags(''),
            locale: str = DEFAULT_LOCALE,
            depth: int = 3,
            uuid_hex: bool = True,
            inflection: bool = False,
            datetime_formatter: Optional[str] = None) -> Union[T, Dict[str, Any], Iterable[Dict[str, Any]], None]:
    """
    Converts a Python object to dictionary, with recursion over the inner objects
    :param obj: Input Python object
    :param flags: Flags applicable for converting, as like allow nulls
    :param locale: Localization info as like time-zone, calendar and date/time format
    :return: Python dictionary
    """

    inflector = Inflector()
    if obj is None:
        if flags.ReplaceNoneWithEmptyString:
            return ''
        else:
            return

    if depth == 0:
        return

    m = re.match(RegExPatterns.Locale, locale)
    t = type(obj)
    if m:
        locale = m.group()
    else:
        locale = DEFAULT_LOCALE

    if is_dataclass(obj):
        annotations = get_type_hints(type(obj))
        res = asdict(obj)

        return {inflector.underscore(k) if inflection else k: to_dict(res[k], flags=flags,
                                                                      locale=locale,
                                                                      depth=depth,
                                                                      inflection=inflection,
                                                                      datetime_formatter=datetime_formatter)
                for k in res
                if res[k] is not None
                or flags.IncludeNulls
                or (flags.ReplaceNoneWithEmptyString and annotations[k]) == str}
    elif issubclass(t, BaseGeometry):
        return mapping(obj)
    elif t in (int, str, bytes, float, bool):
        return obj
    elif issubclass(t, Enum):
        return obj.value
    elif t is Decimal:
        return float(obj)
    elif t is UUID:
        if uuid_hex:
            return obj.hex
        else:
            return obj
    elif t in (datetime, date, time):
        country: str = locale[-2:]
        tz = timezone(country_timezones[country][0])
        if locale == 'fa-IR':
            if t is datetime:
                if flags.IgnoreLocaleCalendar:
                    if flags.IgnoreLocaleTimeZone:
                        return obj.isoformat()
                    if obj.tzinfo is None:
                        return tz.fromutc(obj).isoformat()
                    else:
                        return obj.astimezone(tz)
                else:
                    if datetime_formatter:
                        formatter = lambda d: '{{:{}}}'.format(datetime_formatter).format(d)
                    else:
                        formatter = JalaliDatetime.isoformat

                    if flags.IgnoreLocaleTimeZone:
                        return formatter(JalaliDatetime(obj))
                    if obj.tzinfo is None:
                        return formatter(JalaliDatetime(tz.fromutc(obj)))
                    else:
                        return formatter(JalaliDatetime(obj.astimezone(tz)))
            elif t is date:
                if flags.IgnoreLocaleCalendar:
                    return obj.isoformat()
                else:
                    return JalaliDate(obj).isoformat()
            elif t is time:
                return obj.isoformat()
        else:
            return obj.isoformat()
    elif isinstance(obj, timedelta):
        return re.sub(r'0[YMDHS]', '',
                      'P{year}Y{month}M{day}DT{hour}H{minute}M{second}S'
                      .format(year=obj.days // 365,
                              month=(obj.days % 365) // 30,
                              day=obj.days % 30,
                              hour=obj.seconds // 3600,
                              minute=(obj.seconds % 3600) // 60,
                              second=obj.seconds % 60))

    elif isinstance(obj, collections.Mapping):
        return {inflector.underscore(k) if inflection else k: to_dict(obj[k],
                                                                      flags=flags,
                                                                      locale=locale,
                                                                      depth=depth - 1,
                                                                      inflection=inflection,
                                                                      datetime_formatter=datetime_formatter)
                for k in obj
                if obj[k] is not None
                or flags.IncludeNulls}

    elif isinstance(obj, Iterable) or isinstance(obj, collections.Sequence):

        gen = (to_dict(item,
                       flags=flags,
                       locale=locale,
                       depth=depth - 1,
                       inflection=inflection,
                       datetime_formatter=datetime_formatter)
               for item in obj
               if item is not None
               or flags.IncludeNulls)

        return t(gen) if isinstance(obj, collections.Sequence) else tuple(gen)
    else:
        return {attr: to_dict(getattr(obj, attr),
                              flags=flags,
                              locale=locale,
                              depth=depth - 1,
                              inflection=inflection,
                              datetime_formatter=datetime_formatter)
                for attr in vars(obj) if not attr.startswith('_')}