Passed
Pull Request — main (#158)
by
unknown
01:43
created

pincer.utils.api_object.HTTPMeta.__new__()   A

Complexity

Conditions 3

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 9
rs 10
c 0
b 0
f 0
cc 3
nop 3
1
# Copyright Pincer 2021-Present
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 inspect import getfullargspec
10
from typing import Dict, Tuple, Union, Generic, TypeVar, Any, TYPE_CHECKING, \
11
    Optional, List
12
13
from .types import MissingType
14
15
if TYPE_CHECKING:
16
    from ..client import Client
17
    from ..core.http import HTTPClient
18
19
T = TypeVar("T")
20
21
22
def _asdict_ignore_none(obj: Generic[T]) -> Union[Tuple, Dict, T]:
23
    """
24
    Returns a dict from a dataclass that ignores
25
    all values that are None
26
    Modification of _asdict_inner from dataclasses
27
28
    :param obj:
29
        Dataclass obj
30
    """
31
32
    if _is_dataclass_instance(obj):
33
        result = []
34
        for f in fields(obj):
35
            value = _asdict_ignore_none(getattr(obj, f.name))
36
37
            if isinstance(value, Enum):
38
                result.append((f.name, value.value))
39
            # This if statement was added to the function
40
            elif not isinstance(value, MissingType):
41
                result.append((f.name, value))
42
43
        return dict(result)
44
45
    elif isinstance(obj, tuple) and hasattr(obj, '_fields'):
46
        return type(obj)(*[_asdict_ignore_none(v) for v in obj])
47
48
    elif isinstance(obj, (list, tuple)):
49
        return type(obj)(_asdict_ignore_none(v) for v in obj)
50
51
    elif isinstance(obj, dict):
52
        return type(obj)(
53
            (
54
                _asdict_ignore_none(k),
55
                _asdict_ignore_none(v)
56
            ) for k, v in obj.items()
57
        )
58
    else:
59
        return copy.deepcopy(obj)
60
61
62
class HTTPMeta(type):
63
    __meta_items: List[str] = ["_client", "_http"]
64
    __ori_annotations: Dict[str, type] = {}
65
66
    def __new__(mcs, name, base, mapping):
67
        for key in HTTPMeta.__meta_items:
68
            if mapping.get("__annotations__") and \
69
                    (value := mapping["__annotations__"].get(key)):
0 ignored issues
show
introduced by
invalid syntax (<unknown>, line 69)
Loading history...
70
                HTTPMeta.__ori_annotations.update({key: value})
71
                del mapping["__annotations__"][key]
72
73
        http_object = super().__new__(mcs, name, base, mapping)
74
75
        if getattr(http_object, "__annotations__", None):
76
            for k, v in HTTPMeta.__ori_annotations.items():
77
                http_object.__annotations__[k] = v
78
                setattr(http_object, k, None)
79
80
        return http_object
81
82
83
@dataclass
84
class APIObject(metaclass=HTTPMeta):
85
    """
86
    Represents an object which has been fetched from the Discord API.
87
    """
88
    _client: Client
89
    _http: HTTPClient
90
91
    # def __post_init__(self):
92
    #     fin = {
93
    #         key: _eval_type(ForwardRef(value), globals(), globals())
94
    #         for key, value in self.__annotations__.items()
95
    #     }
96
    #
97
    #     # pprint(self.__annotations__)
98
    #     # pprint(get_type_hints(self))
99
    #     print(fin)
100
    #     print("Post init", self)
101
102
    @classmethod
103
    def from_dict(
104
            cls: Generic[T],
105
            data: Dict[str, Union[str, bool, int, Any]]
106
    ) -> T:
107
        """
108
        Parse an API object from a dictionary.
109
        """
110
        if isinstance(data, cls):
111
            return data
112
113
        # Disable inspection for IDE because this is valid code for the
114
        # inherited classes:
115
        # noinspection PyArgumentList
116
        return cls(**dict(map(
117
            lambda key: (
118
                key,
119
                data[key].value if isinstance(data[key], Enum) else data[key]
120
            ),
121
            filter(
122
                lambda object_argument: data.get(object_argument) is not None,
123
                getfullargspec(cls.__init__).args
124
            )
125
        )))
126
127
    def to_dict(self) -> Dict:
128
        """
129
        Transform the current object to a dictionary representation.
130
        """
131
        return _asdict_ignore_none(self)
132