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

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

93 statements  

1"""Factory class for all the property types.""" 

2 

3from __future__ import annotations 

4 

5from typing import TYPE_CHECKING, Any, ClassVar 

6 

7from icalendar.caselessdict import CaselessDict 

8from icalendar.prop.adr import vAdr 

9from icalendar.prop.binary import vBinary 

10from icalendar.prop.boolean import vBoolean 

11from icalendar.prop.broken import vBroken 

12from icalendar.prop.cal_address import vCalAddress 

13from icalendar.prop.categories import vCategory 

14from icalendar.prop.dt import ( 

15 vDate, 

16 vDatetime, 

17 vDDDLists, 

18 vDDDTypes, 

19 vDuration, 

20 vPeriod, 

21 vTime, 

22 vUTCOffset, 

23) 

24from icalendar.prop.float import vFloat 

25from icalendar.prop.geo import vGeo 

26from icalendar.prop.inline import vInline 

27from icalendar.prop.n import vN 

28from icalendar.prop.org import vOrg 

29from icalendar.prop.recur import vFrequency, vRecur, vWeekday 

30from icalendar.prop.request_status import vRequestStatus 

31from icalendar.prop.text import vText 

32from icalendar.prop.uid import vUid 

33from icalendar.prop.unknown import vUnknown 

34from icalendar.prop.uri import vUri 

35from icalendar.prop.xml import vXml 

36from icalendar.prop.xml_reference import vXmlReference 

37 

38from .integer import vInt 

39 

40if TYPE_CHECKING: 

41 from icalendar.prop import VPROPERTY 

42 

43 

44class TypesFactory(CaselessDict): 

45 """Factory for all value types defined in :rfc:`5545` and subsequent. 

46 

47 The value and parameter names don't overlap. So one factory is enough for 

48 both kinds. 

49 """ 

50 

51 _instance: ClassVar[TypesFactory | None] = None 

52 

53 @staticmethod 

54 def instance() -> TypesFactory: 

55 """Return a singleton instance of this class.""" 

56 if TypesFactory._instance is None: 

57 TypesFactory._instance = TypesFactory() 

58 return TypesFactory._instance 

59 

60 def __init__(self, *args: Any, **kwargs: Any) -> None: 

61 """Set keys to upper for initial dict""" 

62 super().__init__(*args, **kwargs) 

63 self.all_types = ( 

64 vBinary, 

65 vBoolean, 

66 vCalAddress, 

67 vDDDLists, 

68 vDDDTypes, 

69 vDate, 

70 vDatetime, 

71 vDuration, 

72 vFloat, 

73 vFrequency, 

74 vGeo, 

75 vInline, 

76 vInt, 

77 vPeriod, 

78 vRecur, 

79 vText, 

80 vTime, 

81 vUTCOffset, 

82 vUri, 

83 vWeekday, 

84 vCategory, 

85 vAdr, 

86 vN, 

87 vOrg, 

88 vUid, 

89 vXmlReference, 

90 vXml, 

91 vUnknown, 

92 vRequestStatus, 

93 vBroken, 

94 ) 

95 self["binary"] = vBinary 

96 self["boolean"] = vBoolean 

97 self["cal-address"] = vCalAddress 

98 self["date"] = vDDDTypes 

99 self["date-time"] = vDDDTypes 

100 self["duration"] = vDDDTypes 

101 self["float"] = vFloat 

102 self["integer"] = vInt 

103 self["period"] = vPeriod 

104 self["recur"] = vRecur 

105 self["text"] = vText 

106 self["time"] = vTime 

107 self["uri"] = vUri 

108 self["utc-offset"] = vUTCOffset 

109 self["geo"] = vGeo 

110 self["inline"] = vInline 

111 self["date-time-list"] = vDDDLists 

112 self["categories"] = vCategory 

113 self["request-status-text"] = vRequestStatus # RFC 5545 and 6321 

114 self["adr"] = vAdr # RFC 6350 vCard 

115 self["n"] = vN # RFC 6350 vCard 

116 self["org"] = vOrg # RFC 6350 vCard 

117 self["unknown"] = vUnknown # RFC 7265 

118 self["uid"] = vUid # RFC 9253 

119 self["xml-reference"] = vXmlReference # RFC 9253 

120 self["xml-text"] = vXml # RFC 6321 

121 self["x-broken"] = vBroken 

122 

123 ################################################# 

124 # Property types 

125 

126 # These are the default types 

127 types_map = CaselessDict( 

128 { 

129 #################################### 

130 # Property value types 

131 # Calendar Properties 

132 "calscale": "text", 

133 "method": "text", 

134 "prodid": "text", 

135 "version": "text", 

136 # Descriptive Component Properties 

137 "attach": "uri", 

138 "categories": "categories", 

139 "class": "text", 

140 # vCard Properties (RFC 6350) 

141 "adr": "adr", 

142 "n": "n", 

143 "org": "org", 

144 "comment": "text", 

145 "description": "text", 

146 "geo": "geo", 

147 "location": "text", 

148 "percent-complete": "integer", 

149 "priority": "integer", 

150 "resources": "text", 

151 "status": "text", 

152 "summary": "text", 

153 # RFC 9253 

154 # link should be uri, xml-reference or uid 

155 # uri is likely most helpful if people forget to set VALUE 

156 "link": "uri", 

157 "concept": "uri", 

158 "refid": "text", 

159 # Date and Time Component Properties 

160 "completed": "date-time", 

161 "dtend": "date-time", 

162 "due": "date-time", 

163 "dtstart": "date-time", 

164 "duration": "duration", 

165 "freebusy": "period", 

166 "transp": "text", 

167 "refresh-interval": "duration", # RFC 7986 

168 # Time Zone Component Properties 

169 "tzid": "text", 

170 "tzname": "text", 

171 "tzoffsetfrom": "utc-offset", 

172 "tzoffsetto": "utc-offset", 

173 "tzurl": "uri", 

174 # Relationship Component Properties 

175 "attendee": "cal-address", 

176 "contact": "text", 

177 "organizer": "cal-address", 

178 "recurrence-id": "date-time", 

179 "related-to": "text", 

180 "url": "uri", 

181 "conference": "uri", # RFC 7986 

182 "image": "unknown", # RFC 7986 has no default value type 

183 "source": "uri", 

184 "uid": "text", 

185 # Recurrence Component Properties 

186 "exdate": "date-time-list", 

187 "exrule": "recur", 

188 "rdate": "date-time-list", 

189 "rrule": "recur", 

190 # Alarm Component Properties 

191 "action": "text", 

192 "repeat": "integer", 

193 "trigger": "duration", 

194 "acknowledged": "date-time", 

195 "proximity": "text", # RFC 9074 

196 # Change Management Component Properties 

197 "created": "date-time", 

198 "dtstamp": "date-time", 

199 "last-modified": "date-time", 

200 "sequence": "integer", 

201 # XML property (RFC 6321) 

202 "xml": "xml-text", 

203 # Miscellaneous Component Properties 

204 "request-status": "request-status-text", 

205 #################################### 

206 # parameter types (luckily there is no name overlap) 

207 "altrep": "uri", 

208 "cn": "text", 

209 "cutype": "text", 

210 "delegated-from": "cal-address", 

211 "delegated-to": "cal-address", 

212 "dir": "uri", 

213 "encoding": "text", 

214 "fmttype": "text", 

215 "fbtype": "text", 

216 "language": "text", 

217 "member": "cal-address", 

218 "partstat": "text", 

219 "range": "text", 

220 "related": "text", 

221 "reltype": "text", 

222 "role": "text", 

223 "rsvp": "boolean", 

224 "sent-by": "cal-address", 

225 "value": "text", 

226 # rfc 9253 parameters 

227 "label": "text", 

228 "linkrel": "text", 

229 "gap": "duration", 

230 } 

231 ) 

232 

233 def for_property(self, name, value_param: str | None = None) -> type[VPROPERTY]: 

234 """Returns the type class for a property or parameter. 

235 

236 Parameters: 

237 name: Property or parameter name 

238 value_param: Optional ``VALUE`` parameter, for example, 

239 "DATE", "DATE-TIME", or other string. 

240 

241 Returns: 

242 The appropriate value type class. 

243 """ 

244 name = name.upper() 

245 value_param = value_param.upper() if value_param else None 

246 # RFC 7265's UNKNOWN type is always represented verbatim, even for 

247 # properties such as RDATE/EXDATE that normally use list parsing. 

248 if value_param and value_param == "UNKNOWN": 

249 return self["unknown"] 

250 

251 # Special case: RDATE and EXDATE always use vDDDLists to support list values 

252 # regardless of the VALUE parameter 

253 if name in ("RDATE", "EXDATE") and ( 

254 value_param is None or value_param in vDDDTypes.VALUE_MAP 

255 ): 

256 return self["date-time-list"] 

257 # Only use VALUE parameter for known properties that support multiple value 

258 # types (like DTSTART, DTEND, etc. which can be DATE or DATE-TIME) 

259 # For unknown/custom properties, always use the default type from types_map 

260 if value_param and name in self.types_map and value_param in self: 

261 # vCategory also has TEXT as VALUE. 

262 # It should be preferred over TEXT 

263 # This checks the case that the value type actually matches. 

264 value_type: VPROPERTY = self[self.types_map[name]] 

265 if value_type.default_value == value_param: 

266 return value_type 

267 return self[value_param] 

268 if value_param and (value_param in self) and value_param != "IMAGE": 

269 return self[value_param] 

270 

271 return self[self.types_map.get(name, "unknown")] 

272 

273 #: jCal value types defined by :rfc:`7265` that are used verbatim as the 

274 #: type field. Internal type keys outside of this set (for example 

275 #: ``"categories"`` or ``"date-time-list"``) are mapped to their concrete 

276 #: jCal type via the value class. 

277 _jcal_value_types = frozenset( 

278 { 

279 "binary", 

280 "boolean", 

281 "cal-address", 

282 "date", 

283 "date-time", 

284 "duration", 

285 "float", 

286 "integer", 

287 "period", 

288 "recur", 

289 "text", 

290 "time", 

291 "uri", 

292 "utc-offset", 

293 "unknown", 

294 } 

295 ) 

296 

297 def default_value_type(self, name: str) -> str: 

298 """Return the default jCal value type for a property. 

299 

300 The result is a lowercase :rfc:`7265` value type, for example 

301 ``"date-time"`` for ``DTSTART`` or ``"duration"`` for ``TRIGGER``. 

302 This is the value type a property uses when no explicit ``VALUE`` 

303 parameter is given, so it tells :meth:`Component.from_jcal <icalendar.cal.component.Component.from_jcal>` whether a 

304 ``VALUE`` parameter has to be restored from the jCal type field. 

305 

306 Parameters: 

307 name: Property name. 

308 

309 Returns: 

310 The default jCal value type as a lowercase string. 

311 """ 

312 internal = self.types_map.get(name, "unknown").lower() 

313 if internal in self._jcal_value_types: 

314 return internal 

315 # Internal keys such as ``categories`` or ``date-time-list`` are not 

316 # jCal value types themselves; ask the value class for the type it 

317 # actually serialises to. 

318 return self[internal].default_value.lower() 

319 

320 def to_ical(self, name, value): 

321 """Encodes a named value from a primitive python type to an icalendar 

322 encoded string. 

323 """ 

324 type_class = self.for_property(name) 

325 return type_class(value).to_ical() 

326 

327 def from_ical(self, name, value): 

328 """Decodes a named property or parameter value from an icalendar 

329 encoded string to a primitive python type. 

330 """ 

331 type_class = self.for_property(name) 

332 return type_class.from_ical(value) 

333 

334 

335__all__ = ["TypesFactory"]