1# 2# Copyright (c), 2018-2020, SISSA (International School for Advanced Studies). 3# All rights reserved. 4# This file is distributed under the terms of the MIT License. 5# See the file 'LICENSE' in the root directory of the present 6# distribution, or http://opensource.org/licenses/MIT. 7# 8# @author Davide Brunato <brunato@sissa.it> 9# 10import re 11import math 12from typing import Any, Optional, SupportsFloat, SupportsInt, Union, Type 13 14from ..helpers import collapse_white_spaces 15from .atomic_types import AtomicTypeMeta, AnyAtomicType 16 17 18class Float10(float, AnyAtomicType): 19 name = 'float' 20 xsd_version = '1.0' 21 pattern = re.compile( 22 r'^(?:[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[Ee][+-]?[0-9]+)? |[+-]?INF|NaN)$' 23 ) 24 25 def __new__(cls, value: Union[str, SupportsFloat]) -> 'Float10': 26 if isinstance(value, str): 27 value = collapse_white_spaces(value) 28 if value in {'INF', '-INF', 'NaN'} or cls.xsd_version != '1.0' and value == '+INF': 29 pass 30 elif value.lower() in {'inf', '+inf', '-inf', 'nan', 31 'infinity', '+infinity', '-infinity'}: 32 raise ValueError('invalid value {!r} for xs:{}'.format(value, cls.name)) 33 34 value = super().__new__(cls, value) 35 if value > 3.4028235E38: 36 return super().__new__(cls, 'INF') 37 elif value < -3.4028235E38: 38 return super().__new__(cls, '-INF') 39 elif -1e-37 < value < 1e-37: 40 return super().__new__(cls, -0.0 if str(value).startswith('-') else 0.0) 41 return value 42 43 def __hash__(self) -> int: 44 return super(Float10, self).__hash__() 45 46 def __eq__(self, other: object) -> bool: 47 if isinstance(other, self.__class__): 48 if super(Float10, self).__eq__(other): 49 return True 50 return math.isclose(self, other, rel_tol=1e-7, abs_tol=0.0) 51 return super(Float10, self).__eq__(other) 52 53 def __ne__(self, other: object) -> bool: 54 if isinstance(other, self.__class__): 55 if super(Float10, self).__eq__(other): 56 return False 57 return not math.isclose(self, other, rel_tol=1e-7, abs_tol=0.0) 58 return super(Float10, self).__ne__(other) 59 60 def __add__(self, other: object) -> Union[float, 'Float10', 'Float']: 61 if isinstance(other, (self.__class__, int)): 62 return self.__class__(super(Float10, self).__add__(other)) 63 elif isinstance(other, float): 64 return super(Float10, self).__add__(other) 65 return NotImplemented 66 67 def __radd__(self, other: object) -> Union[float, 'Float10', 'Float']: 68 if isinstance(other, (self.__class__, int)): 69 return self.__class__(super(Float10, self).__radd__(other)) 70 elif isinstance(other, float): 71 return super(Float10, self).__radd__(other) 72 return NotImplemented 73 74 def __sub__(self, other: object) -> Union[float, 'Float10', 'Float']: 75 if isinstance(other, (self.__class__, int)): 76 return self.__class__(super(Float10, self).__sub__(other)) 77 elif isinstance(other, float): 78 return super(Float10, self).__sub__(other) 79 return NotImplemented 80 81 def __rsub__(self, other: object) -> Union[float, 'Float10', 'Float']: 82 if isinstance(other, (self.__class__, int)): 83 return self.__class__(super(Float10, self).__rsub__(other)) 84 elif isinstance(other, float): 85 return super(Float10, self).__rsub__(other) 86 return NotImplemented 87 88 def __mul__(self, other: object) -> Union[float, 'Float10', 'Float']: 89 if isinstance(other, (self.__class__, int)): 90 return self.__class__(super(Float10, self).__mul__(other)) 91 elif isinstance(other, float): 92 return super(Float10, self).__mul__(other) 93 return NotImplemented 94 95 def __rmul__(self, other: object) -> Union[float, 'Float10', 'Float']: 96 if isinstance(other, (self.__class__, int)): 97 return self.__class__(super(Float10, self).__rmul__(other)) 98 elif isinstance(other, float): 99 return super(Float10, self).__rmul__(other) 100 return NotImplemented 101 102 def __truediv__(self, other: object) -> Union[float, 'Float10', 'Float']: 103 if isinstance(other, (self.__class__, int)): 104 return self.__class__(super(Float10, self).__truediv__(other)) 105 elif isinstance(other, float): 106 return super(Float10, self).__truediv__(other) 107 return NotImplemented 108 109 def __rtruediv__(self, other: object) -> Union[float, 'Float10', 'Float']: 110 if isinstance(other, (self.__class__, int)): 111 return self.__class__(super(Float10, self).__rtruediv__(other)) 112 elif isinstance(other, float): 113 return super(Float10, self).__rtruediv__(other) 114 return NotImplemented 115 116 def __mod__(self, other: object) -> Union[float, 'Float10', 'Float']: 117 if isinstance(other, (self.__class__, int)): 118 return self.__class__(super(Float10, self).__mod__(other)) 119 elif isinstance(other, float): 120 return super(Float10, self).__mod__(other) 121 return NotImplemented 122 123 def __rmod__(self, other: object) -> Union[float, 'Float10', 'Float']: 124 if isinstance(other, (self.__class__, int)): 125 return self.__class__(super(Float10, self).__rmod__(other)) 126 elif isinstance(other, float): 127 return super(Float10, self).__rmod__(other) 128 return NotImplemented 129 130 def __abs__(self) -> Union['Float10', 'Float']: 131 return self.__class__(super(Float10, self).__abs__()) 132 133 134class Float(Float10): 135 name = 'float' 136 xsd_version = '1.1' 137 138 139class Integer(int, metaclass=AtomicTypeMeta): 140 """A wrapper for emulating xs:integer and limited integer types.""" 141 name = 'integer' 142 pattern = re.compile(r'^[\-+]?[0-9]+$') 143 lower_bound: Optional[int] = None 144 higher_bound: Optional[int] = None 145 146 def __init__(self, value: Union[str, SupportsInt]) -> None: 147 if self.lower_bound is not None and self < self.lower_bound: 148 raise ValueError("value {} is too low for {!r}".format(value, self.__class__)) 149 elif self.higher_bound is not None and self >= self.higher_bound: 150 raise ValueError("value {} is too high for {!r}".format(value, self.__class__)) 151 super(Integer, self).__init__() 152 153 @classmethod 154 def __subclasshook__(cls, subclass: Type[Any]) -> bool: 155 if cls is Integer: 156 return issubclass(subclass, int) and not issubclass(subclass, bool) 157 return NotImplemented 158 159 @classmethod 160 def validate(cls, value: object) -> None: 161 if isinstance(value, cls): 162 return 163 elif isinstance(value, str): 164 if cls.pattern.match(value) is None: 165 raise cls.invalid_value(value) 166 else: 167 raise cls.invalid_type(value) 168 169 170class NonPositiveInteger(Integer): 171 name = 'nonPositiveInteger' 172 lower_bound, higher_bound = None, 1 173 174 175class NegativeInteger(NonPositiveInteger): 176 name = 'negativeInteger' 177 lower_bound, higher_bound = None, 0 178 179 180class Long(Integer): 181 name = 'long' 182 lower_bound, higher_bound = -2**63, 2**63 183 184 185class Int(Long): 186 name = 'int' 187 lower_bound, higher_bound = -2**31, 2**31 188 189 190class Short(Int): 191 name = 'short' 192 lower_bound, higher_bound = -2**15, 2**15 193 194 195class Byte(Short): 196 name = 'byte' 197 lower_bound, higher_bound = -2**7, 2**7 198 199 200class NonNegativeInteger(Integer): 201 name = 'nonNegativeInteger' 202 lower_bound = 0 203 higher_bound: Optional[int] = None 204 205 206class PositiveInteger(NonNegativeInteger): 207 name = 'positiveInteger' 208 lower_bound, higher_bound = 1, None 209 210 211class UnsignedLong(NonNegativeInteger): 212 name = 'unsignedLong' 213 lower_bound, higher_bound = 0, 2**64 214 215 216class UnsignedInt(UnsignedLong): 217 name = 'unsignedInt' 218 lower_bound, higher_bound = 0, 2**32 219 220 221class UnsignedShort(UnsignedInt): 222 name = 'unsignedShort' 223 lower_bound, higher_bound = 0, 2**16 224 225 226class UnsignedByte(UnsignedShort): 227 name = 'unsignedByte' 228 lower_bound, higher_bound = 0, 2**8 229