Passed
Push — master ( 7dbe03...d2ae7d )
by Michael
05:22
created

JsonRpcRequest.is_notification()   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 3
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, typedefs, utils
5
6
7
__all__ = (
8
    'JsonRpcRequest',
9
    'JsonRpcBatchRequest',
10
)
11
12
13
@dataclass
14
class JsonRpcRequest:
15
    method_name: str
16
    id: typing.Optional[typedefs.JsonRpcIdType] = None
17
    jsonrpc: str = constants.VERSION_2_0
18
    extra_args:  typing.MutableMapping = field(default_factory=dict)
19
    context: typing.MutableMapping = field(default_factory=dict)
20
    params: typing.Any = constants.NOTHING  # Use `NOTHING`, because `None` is a valid value.
21
    # We don't convert `args`. So `args` can be `list`, `tuple` or other type.
22
    args: typing.Optional[typing.Sequence] = None
23
    # We don't convert `kwargs`. So `kwargs` can be `dict` or other type.
24
    kwargs: typing.Optional[typing.Mapping] = None
25
26
    def __post_init__(self) -> None:
27
        utils.validate_jsonrpc(self.jsonrpc)
28
29
        if self.params is constants.NOTHING:
30
            self.set_args_and_kwargs(self.args, self.kwargs)
31
        elif self.args is None and self.kwargs is None:
32
            self.set_params(self.params)
33
        else:
34
            raise errors.InvalidParams('Need use params or args with kwargs.')
35
36
    def set_params(self, params: typing.Any) -> None:
37
        self.params = params
38
        self.args, self.kwargs = utils.convert_params_to_args_and_kwargs(params)
39
40
    def set_args_and_kwargs(self,
41
                            args: typing.Optional[typing.Sequence] = None,
42
                            kwargs: typing.Optional[typing.Mapping] = None) -> None:
43
        self.params, self.args, self.kwargs = utils.parse_args_and_kwargs(args, kwargs)
44
45
    @property
46
    def is_notification(self) -> bool:
47
        return self.id is None
48
49
    @classmethod
50
    def load(cls, data: typing.Any, **kwargs) -> 'JsonRpcRequest':
51
        cls._validate_json_request(data)
52
53
        return cls(
54
            id=data.get('id'),
55
            method_name=data['method'],
56
            params=data.get('params', constants.NOTHING),
57
            jsonrpc=data['jsonrpc'],
58
            **kwargs,
59
        )
60
61
    def dump(self) -> typing.Mapping[str, typing.Any]:
62
        data: typing.Dict[str, typing.Any] = {
63
            'method': self.method_name,
64
            'jsonrpc': self.jsonrpc,
65
        }
66
67
        if not self.is_notification:
68
            data['id'] = self.id
69
70
        if self.params is not constants.NOTHING:
71
            data['params'] = self.params
72
73
        return data
74
75
    @staticmethod
76
    def _validate_json_request(data: typing.Any) -> None:
77
        if not isinstance(data, typing.Mapping):
78
            raise errors.InvalidRequest('A request must be of the dict type.')
79
80
        if not ({'method', 'jsonrpc'}) <= data.keys():
81
            raise errors.InvalidRequest('A request must contain "method" and "jsonrpc".')
82
83
        utils.validate_jsonrpc(data['jsonrpc'])
84
85
86
@dataclass
87
class JsonRpcBatchRequest:
88
    requests: typing.Tuple[JsonRpcRequest, ...] = field(default_factory=tuple)
89
90
    @property
91
    def is_notification(self) -> bool:
92
        return all(request.is_notification for request in self.requests)
93
94
    @classmethod
95
    def load(cls, data: typing.Any, **kwargs) -> 'JsonRpcBatchRequest':
96
        if not isinstance(data, typing.Sequence):
97
            raise errors.InvalidRequest('A batch request must be of the list type.')
98
99
        return cls(requests=tuple(
100
            JsonRpcRequest.load(item, **kwargs)
101
            for item in data
102
        ))
103
104
    def dump(self) -> typing.Tuple[typing.Mapping[str, typing.Any], ...]:
105
        return tuple(request.dump() for request in self.requests)
106