1from abc import abstractmethod 2from typing import TypeVar, Callable, Any 3 4CAPTURE_EXCEPTIONS = (Exception, ) 5_ValueType = TypeVar('_ValueType') 6 7 8def _to_exception(exception) -> Callable[[Any], Exception]: 9 """ 10 Overview: 11 Convert exception to callable exception. 12 Arguments: 13 - exception (:obj:`Exception`): The exception to be converted. 14 """ 15 16 if hasattr(exception, '__call__'): 17 return exception 18 elif isinstance(exception, Exception): 19 return lambda v: exception 20 elif isinstance(exception, str): 21 return lambda v: ValueError(exception) 22 else: 23 raise TypeError( 24 'Unknown type of exception, func, exception or str expected but {actual} found.'.format( 25 actual=repr(type(exception).__name__) 26 ) 27 ) 28 29 30def _to_loader(value) -> 'ILoaderClass': 31 """ 32 Overview: 33 Convert value to loader. 34 Arguments: 35 - value (:obj:`Any`): The value to be converted. 36 """ 37 38 if isinstance(value, ILoaderClass): 39 return value 40 elif isinstance(value, tuple): 41 if len(value) == 2: 42 _predict, _exception = value 43 _load = None 44 elif len(value) == 3: 45 _predict, _load, _exception = value 46 else: 47 raise ValueError('Tuple\'s length should be 2 or 3, but {actual} found.'.format(actual=repr(len(value)))) 48 49 _exception = _to_exception(_exception) 50 51 def _load_tuple(value_): 52 if not _predict(value_): 53 raise _exception(value_) 54 55 return (_load or (lambda v: v))(value_) 56 57 return _to_loader(_load_tuple) 58 elif isinstance(value, type): 59 60 def _load_type(value_): 61 if not isinstance(value_, value): 62 raise TypeError( 63 'type not match, {expect} expected but {actual} found'.format( 64 expect=repr(value.__name__), actual=repr(type(value_).__name__) 65 ) 66 ) 67 return value_ 68 69 return _to_loader(_load_type) 70 elif hasattr(value, '__call__'): 71 72 class _Loader(ILoaderClass): 73 74 def _load(self, value_): 75 return value(value_) 76 77 return _Loader() 78 elif isinstance(value, bool): 79 return _to_loader((lambda v: value, ValueError('assertion false'))) 80 elif value is None: 81 return _to_loader( 82 ( 83 lambda v: v is None, lambda v: 84 TypeError('type not match, none expected but {actual} found'.format(actual=repr(type(v).__name__))) 85 ) 86 ) 87 else: 88 return _to_loader(lambda v: value) 89 90 91Loader = _to_loader 92 93 94def _reset_exception(loader, eg: Callable[[Any, Exception], Exception]): 95 """ 96 Overview: 97 Reset exception of loader. 98 """ 99 100 loader = Loader(loader) 101 102 def _load(value): 103 try: 104 return loader(value) 105 except CAPTURE_EXCEPTIONS as err: 106 raise eg(value, err) 107 108 return Loader(_load) 109 110 111class ILoaderClass: 112 """ 113 Overview: 114 Base class of loader. 115 Interfaces: 116 ``__init__``, ``_load``, ``load``, ``check``, ``__call__``, ``__and__``, ``__or__``, ``__rshift__`` 117 """ 118 119 @abstractmethod 120 def _load(self, value: _ValueType) -> _ValueType: 121 """ 122 Overview: 123 Load the value. 124 Arguments: 125 - value (:obj:`_ValueType`): The value to be loaded. 126 """ 127 128 raise NotImplementedError 129 130 def __load(self, value: _ValueType) -> _ValueType: 131 """ 132 Overview: 133 Load the value. 134 Arguments: 135 - value (:obj:`_ValueType`): The value to be loaded. 136 """ 137 138 return self._load(value) 139 140 def __check(self, value: _ValueType) -> bool: 141 """ 142 Overview: 143 Check whether the value is valid. 144 Arguments: 145 - value (:obj:`_ValueType`): The value to be checked. 146 """ 147 148 try: 149 self._load(value) 150 except CAPTURE_EXCEPTIONS: 151 return False 152 else: 153 return True 154 155 def load(self, value: _ValueType) -> _ValueType: 156 """ 157 Overview: 158 Load the value. 159 Arguments: 160 - value (:obj:`_ValueType`): The value to be loaded. 161 """ 162 163 return self.__load(value) 164 165 def check(self, value: _ValueType) -> bool: 166 """ 167 Overview: 168 Check whether the value is valid. 169 Arguments: 170 - value (:obj:`_ValueType`): The value to be checked. 171 """ 172 173 return self.__check(value) 174 175 def __call__(self, value: _ValueType) -> _ValueType: 176 """ 177 Overview: 178 Load the value. 179 Arguments: 180 - value (:obj:`_ValueType`): The value to be loaded. 181 """ 182 183 return self.__load(value) 184 185 def __and__(self, other) -> 'ILoaderClass': 186 """ 187 Overview: 188 Combine two loaders. 189 Arguments: 190 - other (:obj:`ILoaderClass`): The other loader. 191 """ 192 193 def _load(value: _ValueType) -> _ValueType: 194 self.load(value) 195 return Loader(other).load(value) 196 197 return Loader(_load) 198 199 def __rand__(self, other) -> 'ILoaderClass': 200 """ 201 Overview: 202 Combine two loaders. 203 Arguments: 204 - other (:obj:`ILoaderClass`): The other loader. 205 """ 206 207 return Loader(other) & self 208 209 def __or__(self, other) -> 'ILoaderClass': 210 """ 211 Overview: 212 Combine two loaders. 213 Arguments: 214 - other (:obj:`ILoaderClass`): The other loader. 215 """ 216 217 def _load(value: _ValueType) -> _ValueType: 218 try: 219 return self.load(value) 220 except CAPTURE_EXCEPTIONS: 221 return Loader(other).load(value) 222 223 return Loader(_load) 224 225 def __ror__(self, other) -> 'ILoaderClass': 226 """ 227 Overview: 228 Combine two loaders. 229 Arguments: 230 - other (:obj:`ILoaderClass`): The other loader. 231 """ 232 233 return Loader(other) | self 234 235 def __rshift__(self, other) -> 'ILoaderClass': 236 """ 237 Overview: 238 Combine two loaders. 239 Arguments: 240 - other (:obj:`ILoaderClass`): The other loader. 241 """ 242 243 def _load(value: _ValueType) -> _ValueType: 244 _return_value = self.load(value) 245 return _to_loader(other).load(_return_value) 246 247 return Loader(_load) 248 249 def __rrshift__(self, other) -> 'ILoaderClass': 250 """ 251 Overview: 252 Combine two loaders. 253 Arguments: 254 - other (:obj:`ILoaderClass`): The other loader. 255 """ 256 257 return Loader(other) >> self