Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/icalendar/prop/integer.py: 73%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

79 statements  

1"""INT values from :rfc:`5545`.""" 

2 

3from typing import Any, ClassVar 

4from xml.etree.ElementTree import Element, SubElement 

5 

6from icalendar.compatibility import Self 

7from icalendar.error import JCalParsingError, XCalParsingError 

8from icalendar.parser import Parameters 

9from icalendar.parser.xcal.adapter import ElementAdapter 

10from icalendar.parser.xcal.base import XCalParser 

11from icalendar.parser.xcal.wrapper import from_xcal_wrapper 

12from icalendar.parser_tools import ICAL_TYPE 

13 

14 

15class vInt(int): 

16 """Integer 

17 

18 Value Name: 

19 INTEGER 

20 

21 Purpose: 

22 This value type is used to identify properties that contain a 

23 signed integer value. 

24 

25 Format Definition: 

26 This value type is defined by the following notation: 

27 

28 .. code-block:: text 

29 

30 integer = (["+"] / "-") 1*DIGIT 

31 

32 Description: 

33 If the property permits, multiple "integer" values are 

34 specified by a COMMA-separated list of values. The valid range 

35 for "integer" is -2147483648 to 2147483647. If the sign is not 

36 specified, then the value is assumed to be positive. 

37 

38 The ``__new__`` method creates a vInt instance: 

39 

40 Parameters: 

41 value: Integer value to encode. Must be within :attr:`min` to :attr:`max`. 

42 params: Optional parameter dictionary for the property. 

43 

44 Returns: 

45 vInt instance 

46 

47 Raises: 

48 ValueError: If the value is outside the RFC 5545 signed 32-bit integer range 

49 as defined in :attr:`min` and :attr:`max`. 

50 

51 Examples: 

52 

53 .. code-block:: text 

54 

55 1234567890 

56 -1234567890 

57 +1234567890 

58 432109876 

59 

60 .. code-block:: pycon 

61 

62 >>> from icalendar.prop import vInt 

63 >>> integer = vInt.from_ical('1234567890') 

64 >>> integer 

65 1234567890 

66 >>> integer = vInt.from_ical('-1234567890') 

67 >>> integer 

68 -1234567890 

69 >>> integer = vInt.from_ical('+1234567890') 

70 >>> integer 

71 1234567890 

72 >>> integer = vInt.from_ical('432109876') 

73 >>> integer 

74 432109876 

75 

76 Create a PRIORITY property (1 = highest priority): 

77 

78 .. code-block:: pycon 

79 

80 >>> priority = vInt(1) 

81 >>> priority 

82 1 

83 >>> priority.to_ical() 

84 b'1' 

85 

86 Create SEQUENCE property (for versioning): 

87 

88 .. code-block:: pycon 

89 

90 >>> sequence = vInt(3) 

91 >>> sequence.to_ical() 

92 b'3' 

93 """ 

94 

95 default_value: ClassVar[str] = "INTEGER" 

96 params: Parameters 

97 

98 min: ClassVar[int] = -2_147_483_648 

99 """min: The minimum valid value per :rfc:`5545#section-3.3.8` (``-2147483648``).""" 

100 max: ClassVar[int] = 2_147_483_647 

101 """max: The maximum valid value per :rfc:`5545#section-3.3.8` (``2147483647``).""" 

102 

103 def __new__( 

104 cls, 

105 *args, 

106 params: dict[str, Any] | None = None, 

107 _validate_range: bool = True, 

108 **kwargs, 

109 ): 

110 self = super().__new__(cls, *args, **kwargs) 

111 self.params = Parameters(params) 

112 if not (cls.min <= self <= cls.max) and _validate_range: 

113 raise ValueError( 

114 f"Integer {self} is outside the RFC 5545 range [{cls.min}, {cls.max}]" 

115 ) 

116 return self 

117 

118 def to_ical(self) -> bytes: 

119 return str(self).encode("utf-8") 

120 

121 @property 

122 def ical_value(self) -> int: 

123 """INTEGER property type according to :rfc:`5545#section-3.3.8`""" 

124 return int(self) 

125 

126 @classmethod 

127 def from_ical(cls, ical: ICAL_TYPE): 

128 try: 

129 value = int(ical) 

130 except Exception as e: 

131 raise ValueError(f"Expected int, got: {ical}") from e 

132 return cls(value) 

133 

134 @classmethod 

135 def examples(cls) -> list[Self]: 

136 """Examples of vInt.""" 

137 return [cls(1000), cls(-42)] 

138 

139 from icalendar.param import VALUE 

140 

141 def to_jcal(self, name: str) -> list: 

142 """The jCal representation of this property according to :rfc:`7265`.""" 

143 return [name, self.params.to_jcal(), self.VALUE.lower(), int(self)] 

144 

145 @classmethod 

146 def from_jcal(cls, jcal_property: list) -> Self: 

147 """Parse jCal from :rfc:`7265`. 

148 

149 Parameters: 

150 jcal_property: The jCal property to parse. 

151 

152 Raises: 

153 ~error.JCalParsingError: If the provided jCal is invalid. 

154 """ 

155 JCalParsingError.validate_property(jcal_property, cls) 

156 JCalParsingError.validate_value_type(jcal_property[3], int, cls, 3) 

157 return cls( 

158 jcal_property[3], 

159 params=Parameters.from_jcal_property(jcal_property), 

160 ) 

161 

162 @classmethod 

163 def parse_jcal_value(cls, value: Any) -> int: 

164 """Parse a jCal value for vInt. 

165 

166 Raises: 

167 ~error.JCalParsingError: If the value is not an int. 

168 """ 

169 JCalParsingError.validate_value_type(value, int, cls) 

170 return cls(value) 

171 

172 @classmethod 

173 @from_xcal_wrapper 

174 def from_xcal(cls, parser: XCalParser, params: Parameters) -> Self: 

175 """Parse xCal from :rfc:`6321`. 

176 

177 Parameters: 

178 element: The xCal element to parse. 

179 

180 Raises: 

181 ~error.XCalParsingError: If the provided xCal is invalid. 

182 """ 

183 element = parser.parse_tag(cls.default_value) 

184 return cls.from_xcal_element(element, params) 

185 

186 XCAL_TYPE = "xsd:integer" 

187 

188 @classmethod 

189 def from_xcal_element(cls, element: ElementAdapter, params: Parameters) -> Self: 

190 """Parse xCal from :rfc:`6321`. 

191 

192 Parameters: 

193 element: The xCal element to parse. 

194 

195 Raises: 

196 ~error.XCalParsingError: If the provided xCal is invalid. 

197 """ 

198 try: 

199 value = int(element.get_xsd_token()) 

200 except (TypeError, ValueError) as e: 

201 raise XCalParsingError( 

202 f"Expected {cls.XCAL_TYPE}", 

203 element.get_xsd_token(), 

204 element, 

205 ) from e 

206 # xCal integers are xsd:integers. They do not have a value range. 

207 return cls(value, params=params, _validate_range=False) 

208 

209 @classmethod 

210 @from_xcal_wrapper 

211 def from_xcal_in_recur(cls, parser: XCalParser, params: Parameters) -> Self: 

212 """Parse xCal from :rfc:`6321`. 

213 

214 Parameters: 

215 element: The xCal element to parse. 

216 

217 Raises: 

218 ~error.XCalParsingError: If the provided xCal is invalid. 

219 """ 

220 element = parser.parse_tag() 

221 return cls.from_xcal_element(element, params) 

222 

223 def to_xcal(self, element: Element) -> None: 

224 """Add the xCal representation of this property according to :rfc:`6321`.""" 

225 self.params.to_xcal(element) 

226 element = SubElement(element, self.default_value.lower()) 

227 element.text = self.to_ical().decode() 

228 

229 

230class vNonNegativeInt(vInt): 

231 """This is an integer that is supposed to be at least 0. 

232 

233 See https://www.rfc-editor.org/rfc/inline-errata/rfc6321.html#btn_3050 

234 """ 

235 

236 min: ClassVar[int] = 0 

237 

238 @classmethod 

239 def examples(cls) -> list[vInt]: 

240 """Examples of vPositiveInt.""" 

241 return [cls(1000), cls(0)] 

242 

243 XCAL_TYPE = "xsd:nonNegativeInteger" 

244 

245 

246__all__ = ["vInt", "vNonNegativeInt"]