Passed
Push — main ( f2e2cb...8ba93a )
by
unknown
02:33
created

pincer.utils.api_object.APIObject.from_dict()   A

Complexity

Conditions 3

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
dl 0
loc 19
rs 9.85
c 0
b 0
f 0
cc 3
nop 2
1
# Copyright Pincer 2021-Present
0 ignored issues
show
introduced by
Missing module docstring
Loading history...
2
# Full MIT License can be found in `LICENSE` at the project root.
3
4
from __future__ import annotations
5
6
import copy
7
from dataclasses import dataclass, fields, _is_dataclass_instance
8
from enum import Enum
9
from typing import Dict, Tuple, Union, Generic, TypeVar, Any, Optional
10
11
from .types import MissingType
12
13
T = TypeVar("T")
14
15
16
def _asdict_ignore_none(obj: Generic[T]) -> Union[Tuple, Dict, T]:
17
    """
18
    Returns a dict from a dataclass that ignores
19
    all values that are None
20
    Modification of _asdict_inner from dataclasses
21
22
    :param obj:
23
        Dataclass obj
24
    """
25
26
    if _is_dataclass_instance(obj):
0 ignored issues
show
unused-code introduced by
Unnecessary "elif" after "return"
Loading history...
27
        result = []
28
        for f in fields(obj):
29
            value = _asdict_ignore_none(getattr(obj, f.name))
30
31
            if isinstance(value, Enum):
32
                result.append((f.name, value.value))
33
            # This if statement was added to the function
34
            elif not isinstance(value, MissingType):
35
                result.append((f.name, value))
36
37
        return dict(result)
38
39
    elif isinstance(obj, tuple) and hasattr(obj, '_fields'):
40
        return type(obj)(*[_asdict_ignore_none(v) for v in obj])
41
42
    elif isinstance(obj, (list, tuple)):
43
        return type(obj)(_asdict_ignore_none(v) for v in obj)
44
45
    elif isinstance(obj, dict):
46
        return type(obj)(
47
            (
48
                _asdict_ignore_none(k),
49
                _asdict_ignore_none(v)
50
            ) for k, v in obj.items()
51
        )
52
    else:
53
        return copy.deepcopy(obj)
54
55
56
class HTTPMeta(type):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
57
    # TODO: Fix typehints
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
58
    __attrs = {
59
        "_client": Optional[Any],
60
        "_http": Optional[Any]
61
    }
62
63
    def __new__(mcs, *args, **kwargs):
0 ignored issues
show
Coding Style Best Practice introduced by
Metaclass class method __new__ should have 'cls' as first argument
Loading history...
64
        http_object = super().__new__(mcs, *args, **kwargs)
65
66
        if getattr(http_object, "__annotations__", None):
67
            for k, v in HTTPMeta.__attrs.items():
68
                http_object.__annotations__[k] = v
69
                setattr(http_object, k, None)
70
71
        return http_object
72
73
74
@dataclass
75
class APIObject(metaclass=HTTPMeta):
76
    """
77
    Represents an object which has been fetched from the Discord API.
78
    """
79
80
    # def __post_init__(self):
81
    #     fin = {
82
    #         key: _eval_type(ForwardRef(value), globals(), globals())
83
    #         for key, value in self.__annotations__.items()
84
    #     }
85
    #
86
    #     # pprint(self.__annotations__)
87
    #     # pprint(get_type_hints(self))
88
    #     print(fin)
89
    #     print("Post init", self)
90
91
    @classmethod
92
    def from_dict(
93
            cls: Generic[T],
94
            data: Dict[str, Union[str, bool, int, Any]]
95
    ) -> T:
96
        """
97
        Parse an API object from a dictionary.
98
        """
99
        if isinstance(data, cls):
100
            return data
101
102
        # Disable inspection for IDE because this is valid code for the
103
        # inherited classes:
104
        # noinspection PyArgumentList
105
        return cls(
106
            **{
107
                key: (
108
                    value.value if isinstance(value, Enum) else value
109
                ) for key, value in data.items()
110
            }
111
        )
112
113
    def to_dict(self) -> Dict:
114
        """
115
        Transform the current object to a dictionary representation.
116
        """
117
        return _asdict_ignore_none(self)
118