Passed
Push — master ( 0d356b...652bd4 )
by Michael
11:41 queued 05:19
created

DuplicatedResults.__bool__()   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 2
rs 10
c 0
b 0
f 0
cc 1
nop 1
1
import typing
2
from dataclasses import dataclass, field
3
4
from .. import constants, errors, utils
5
6
7
__all__ = (
8
    'JsonRpcResponse',
9
    'JsonRpcBatchResponse',
10
    'UnlinkedResults',
11
    'DuplicatedResults',
12
)
13
14
15
@dataclass
16
class JsonRpcResponse:
17
    jsonrpc: str = constants.VERSION_2_0
18
    id: typing.Any = constants.NOTHING
19
    result: typing.Any = constants.NOTHING
20
    error: typing.Optional[errors.JsonRpcError] = None
21
    context: dict = field(default_factory=dict)
22
23
    @property
24
    def is_notification(self) -> bool:
25
        return self.id in constants.EMPTY_VALUES
26
27
    @classmethod
28
    def from_dict(cls, data: dict, *, error_map: typing.Optional[dict] = None, **kwargs) -> 'JsonRpcResponse':
29
        cls._validate_json_response(data)
30
31
        response = cls(
32
            id=data.get('id', constants.NOTHING),
33
            jsonrpc=data.get('jsonrpc', constants.VERSION_2_0),
34
            result=data.get('result'),
35
            **kwargs,
36
        )
37
38
        if 'error' in data:
39
            cls._add_error(response, data['error'], error_map=error_map)
40
41
        return response
42
43
    def to_dict(self) -> typing.Optional[dict]:
44
        data = {'jsonrpc': self.jsonrpc}
45
46
        if self.id in constants.EMPTY_VALUES:
47
            data['id'] = None
48
        else:
49
            data['id'] = self.id
50
51
        if self.error in constants.EMPTY_VALUES:
52
            data['result'] = self.result
53
        else:
54
            data['error'] = {'code': self.error.code, 'message': self.error.message}
55
56
            if self.error.data is not None:
57
                data['error']['data'] = self.error.data
58
59
        return data
60
61
    @staticmethod
62
    def _validate_json_response(data: typing.Any) -> None:
63
        if not isinstance(data, dict):
64
            raise errors.InvalidRequest
65
66
        utils.validate_jsonrpc(data.get('jsonrpc'))
67
68
        if 'result' not in data and 'error' not in data:
69
            raise errors.InvalidRequest('"result" or "error" not found in data.', data={'raw_response': data})
70
71
    @staticmethod
72
    def _add_error(response: 'JsonRpcResponse', error: typing.Any, *, error_map: typing.Optional[dict] = None) -> None:
73
        if not isinstance(error, dict):
74
            raise errors.InvalidRequest
75
76
        if not ({'code', 'message'}) <= error.keys():
77
            raise errors.InvalidRequest
78
79
        if error_map:
80
            exception_class = error_map.get(error['code'], errors.JsonRpcError)
81
        else:
82
            exception_class = errors.JsonRpcError
83
84
        response.error = exception_class(
85
            message=error['message'],
86
            data=error.get('data'),
87
            code=error['code'],
88
        )
89
90
91
@dataclass
92
class JsonRpcBatchResponse:
93
    responses: typing.List[JsonRpcResponse] = field(default_factory=list)
94
95
    def to_list(self) -> typing.List[dict]:
96
        return [response.to_dict() for response in self.responses]
97
98
    @classmethod
99
    def from_list(cls, data: list, *, error_map: typing.Optional[dict] = None, **kwargs) -> 'JsonRpcBatchResponse':
100
        responses = [
101
            JsonRpcResponse.from_dict(item, error_map=error_map, **kwargs)
102
            for item in data
103
        ]
104
105
        return cls(responses=responses)
106
107
108
@dataclass
109
class UnlinkedResults:
110
    data: list = field(default_factory=list)
111
112
    def __bool__(self) -> bool:
113
        return len(self.data) > 0
114
115
    def get(self) -> list:
116
        return self.data
117
118
    def add(self, value: typing.Any) -> None:
119
        self.data.append(value)
120
121
122
@dataclass
123
class DuplicatedResults:
124
    data: list = field(default_factory=list)
125
126
    def __bool__(self) -> bool:
127
        return len(self.data) > 0
128
129
    def get(self) -> list:
130
        return self.data
131
132
    def add(self, value: typing.Any) -> None:
133
        self.data.append(value)
134