1""" 2QMP Data Models 3 4This module provides simplistic data classes that represent the few 5structures that the QMP spec mandates; they are used to verify incoming 6data to make sure it conforms to spec. 7""" 8# pylint: disable=too-few-public-methods 9 10from collections import abc 11import copy 12from typing import ( 13 Any, 14 Dict, 15 Mapping, 16 Optional, 17 Sequence, 18) 19 20 21class Model: 22 """ 23 Abstract data model, representing some QMP object of some kind. 24 25 :param raw: The raw object to be validated. 26 :raise KeyError: If any required fields are absent. 27 :raise TypeError: If any required fields have the wrong type. 28 """ 29 def __init__(self, raw: Mapping[str, Any]): 30 self._raw = raw 31 32 def _check_key(self, key: str) -> None: 33 if key not in self._raw: 34 raise KeyError(f"'{self._name}' object requires '{key}' member") 35 36 def _check_value(self, key: str, type_: type, typestr: str) -> None: 37 assert key in self._raw 38 if not isinstance(self._raw[key], type_): 39 raise TypeError( 40 f"'{self._name}' member '{key}' must be a {typestr}" 41 ) 42 43 def _check_member(self, key: str, type_: type, typestr: str) -> None: 44 self._check_key(key) 45 self._check_value(key, type_, typestr) 46 47 @property 48 def _name(self) -> str: 49 return type(self).__name__ 50 51 def __repr__(self) -> str: 52 return f"{self._name}({self._raw!r})" 53 54 55class Greeting(Model): 56 """ 57 Defined in qmp-spec.rst, section "Server Greeting". 58 59 :param raw: The raw Greeting object. 60 :raise KeyError: If any required fields are absent. 61 :raise TypeError: If any required fields have the wrong type. 62 """ 63 def __init__(self, raw: Mapping[str, Any]): 64 super().__init__(raw) 65 #: 'QMP' member 66 self.QMP: QMPGreeting # pylint: disable=invalid-name 67 68 self._check_member('QMP', abc.Mapping, "JSON object") 69 self.QMP = QMPGreeting(self._raw['QMP']) 70 71 def _asdict(self) -> Dict[str, object]: 72 """ 73 For compatibility with the iotests sync QMP wrapper. 74 75 The legacy QMP interface needs Greetings as a garden-variety Dict. 76 77 This interface is private in the hopes that it will be able to 78 be dropped again in the near-future. Caller beware! 79 """ 80 return dict(copy.deepcopy(self._raw)) 81 82 83class QMPGreeting(Model): 84 """ 85 Defined in qmp-spec.rst, section "Server Greeting". 86 87 :param raw: The raw QMPGreeting object. 88 :raise KeyError: If any required fields are absent. 89 :raise TypeError: If any required fields have the wrong type. 90 """ 91 def __init__(self, raw: Mapping[str, Any]): 92 super().__init__(raw) 93 #: 'version' member 94 self.version: Mapping[str, object] 95 #: 'capabilities' member 96 self.capabilities: Sequence[object] 97 98 self._check_member('version', abc.Mapping, "JSON object") 99 self.version = self._raw['version'] 100 101 self._check_member('capabilities', abc.Sequence, "JSON array") 102 self.capabilities = self._raw['capabilities'] 103 104 105class ErrorResponse(Model): 106 """ 107 Defined in qmp-spec.rst, section "Error". 108 109 :param raw: The raw ErrorResponse object. 110 :raise KeyError: If any required fields are absent. 111 :raise TypeError: If any required fields have the wrong type. 112 """ 113 def __init__(self, raw: Mapping[str, Any]): 114 super().__init__(raw) 115 #: 'error' member 116 self.error: ErrorInfo 117 #: 'id' member 118 self.id: Optional[object] = None # pylint: disable=invalid-name 119 120 self._check_member('error', abc.Mapping, "JSON object") 121 self.error = ErrorInfo(self._raw['error']) 122 123 if 'id' in raw: 124 self.id = raw['id'] 125 126 127class ErrorInfo(Model): 128 """ 129 Defined in qmp-spec.rst, section "Error". 130 131 :param raw: The raw ErrorInfo object. 132 :raise KeyError: If any required fields are absent. 133 :raise TypeError: If any required fields have the wrong type. 134 """ 135 def __init__(self, raw: Mapping[str, Any]): 136 super().__init__(raw) 137 #: 'class' member, with an underscore to avoid conflicts in Python. 138 self.class_: str 139 #: 'desc' member 140 self.desc: str 141 142 self._check_member('class', str, "string") 143 self.class_ = self._raw['class'] 144 145 self._check_member('desc', str, "string") 146 self.desc = self._raw['desc'] 147