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

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

82 statements  

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

2 

3from __future__ import annotations 

4 

5from typing import 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.cal_address import vCalAddress 

12from icalendar.prop.categories import vCategory 

13from icalendar.prop.dt import ( 

14 vDate, 

15 vDatetime, 

16 vDDDLists, 

17 vDDDTypes, 

18 vDuration, 

19 vPeriod, 

20 vTime, 

21 vUTCOffset, 

22) 

23from icalendar.prop.float import vFloat 

24from icalendar.prop.geo import vGeo 

25from icalendar.prop.inline import vInline 

26from icalendar.prop.n import vN 

27from icalendar.prop.org import vOrg 

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

29from icalendar.prop.text import vText 

30from icalendar.prop.uid import vUid 

31from icalendar.prop.unknown import vUnknown 

32from icalendar.prop.uri import vUri 

33from icalendar.prop.xml_reference import vXmlReference 

34 

35from .integer import vInt 

36 

37 

38class TypesFactory(CaselessDict): 

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

40 

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

42 both kinds. 

43 """ 

44 

45 _instance: ClassVar[TypesFactory | None] = None 

46 

47 def instance() -> TypesFactory: 

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

49 if TypesFactory._instance is None: 

50 TypesFactory._instance = TypesFactory() 

51 return TypesFactory._instance 

52 

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

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

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

56 self.all_types = ( 

57 vBinary, 

58 vBoolean, 

59 vCalAddress, 

60 vDDDLists, 

61 vDDDTypes, 

62 vDate, 

63 vDatetime, 

64 vDuration, 

65 vFloat, 

66 vFrequency, 

67 vGeo, 

68 vInline, 

69 vInt, 

70 vPeriod, 

71 vRecur, 

72 vText, 

73 vTime, 

74 vUTCOffset, 

75 vUri, 

76 vWeekday, 

77 vCategory, 

78 vAdr, 

79 vN, 

80 vOrg, 

81 vUid, 

82 vXmlReference, 

83 vUnknown, 

84 ) 

85 self["binary"] = vBinary 

86 self["boolean"] = vBoolean 

87 self["cal-address"] = vCalAddress 

88 self["date"] = vDDDTypes 

89 self["date-time"] = vDDDTypes 

90 self["duration"] = vDDDTypes 

91 self["float"] = vFloat 

92 self["integer"] = vInt 

93 self["period"] = vPeriod 

94 self["recur"] = vRecur 

95 self["text"] = vText 

96 self["time"] = vTime 

97 self["uri"] = vUri 

98 self["utc-offset"] = vUTCOffset 

99 self["geo"] = vGeo 

100 self["inline"] = vInline 

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

102 self["categories"] = vCategory 

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

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

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

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

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

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

109 

110 ################################################# 

111 # Property types 

112 

113 # These are the default types 

114 types_map = CaselessDict( 

115 { 

116 #################################### 

117 # Property value types 

118 # Calendar Properties 

119 "calscale": "text", 

120 "method": "text", 

121 "prodid": "text", 

122 "version": "text", 

123 # Descriptive Component Properties 

124 "attach": "uri", 

125 "categories": "categories", 

126 "class": "text", 

127 # vCard Properties (RFC 6350) 

128 "adr": "adr", 

129 "n": "n", 

130 "org": "org", 

131 "comment": "text", 

132 "description": "text", 

133 "geo": "geo", 

134 "location": "text", 

135 "percent-complete": "integer", 

136 "priority": "integer", 

137 "resources": "text", 

138 "status": "text", 

139 "summary": "text", 

140 # RFC 9253 

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

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

143 "link": "uri", 

144 "concept": "uri", 

145 "refid": "text", 

146 # Date and Time Component Properties 

147 "completed": "date-time", 

148 "dtend": "date-time", 

149 "due": "date-time", 

150 "dtstart": "date-time", 

151 "duration": "duration", 

152 "freebusy": "period", 

153 "transp": "text", 

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

155 # Time Zone Component Properties 

156 "tzid": "text", 

157 "tzname": "text", 

158 "tzoffsetfrom": "utc-offset", 

159 "tzoffsetto": "utc-offset", 

160 "tzurl": "uri", 

161 # Relationship Component Properties 

162 "attendee": "cal-address", 

163 "contact": "text", 

164 "organizer": "cal-address", 

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

166 "related-to": "text", 

167 "url": "uri", 

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

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

170 "source": "uri", 

171 "uid": "text", 

172 # Recurrence Component Properties 

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

174 "exrule": "recur", 

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

176 "rrule": "recur", 

177 # Alarm Component Properties 

178 "action": "text", 

179 "repeat": "integer", 

180 "trigger": "duration", 

181 "acknowledged": "date-time", 

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

183 # Change Management Component Properties 

184 "created": "date-time", 

185 "dtstamp": "date-time", 

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

187 "sequence": "integer", 

188 # Miscellaneous Component Properties 

189 "request-status": "text", 

190 #################################### 

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

192 "altrep": "uri", 

193 "cn": "text", 

194 "cutype": "text", 

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

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

197 "dir": "uri", 

198 "encoding": "text", 

199 "fmttype": "text", 

200 "fbtype": "text", 

201 "language": "text", 

202 "member": "cal-address", 

203 "partstat": "text", 

204 "range": "text", 

205 "related": "text", 

206 "reltype": "text", 

207 "role": "text", 

208 "rsvp": "boolean", 

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

210 "value": "text", 

211 # rfc 9253 parameters 

212 "label": "text", 

213 "linkrel": "text", 

214 "gap": "duration", 

215 } 

216 ) 

217 

218 def for_property(self, name, value_param: str | None = None) -> type: 

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

220 

221 Parameters: 

222 name: Property or parameter name 

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

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

225 

226 Returns: 

227 The appropriate value type class. 

228 """ 

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

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

231 if value_param and value_param.lower() == "unknown": 

232 return self["unknown"] 

233 

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

235 # regardless of the VALUE parameter 

236 if name.upper() in ("RDATE", "EXDATE"): 

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

238 

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

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

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

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

243 return self[value_param] 

244 

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

246 return self[value_param] 

247 

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

249 

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

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

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

253 #: jCal type via the value class. 

254 _jcal_value_types = frozenset( 

255 { 

256 "binary", 

257 "boolean", 

258 "cal-address", 

259 "date", 

260 "date-time", 

261 "duration", 

262 "float", 

263 "integer", 

264 "period", 

265 "recur", 

266 "text", 

267 "time", 

268 "uri", 

269 "utc-offset", 

270 "unknown", 

271 } 

272 ) 

273 

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

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

276 

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

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

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

280 parameter is given, so it tells :func:`Component.from_jcal` whether a 

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

282 

283 Parameters: 

284 name: Property name. 

285 

286 Returns: 

287 The default jCal value type as a lowercase string. 

288 """ 

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

290 if internal in self._jcal_value_types: 

291 return internal 

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

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

294 # actually serialises to. 

295 try: 

296 return self[internal].examples()[0].VALUE.lower() 

297 except (KeyError, IndexError, AttributeError): 

298 return "unknown" 

299 

300 def to_ical(self, name, value): 

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

302 encoded string. 

303 """ 

304 type_class = self.for_property(name) 

305 return type_class(value).to_ical() 

306 

307 def from_ical(self, name, value): 

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

309 encoded string to a primitive python type. 

310 """ 

311 type_class = self.for_property(name) 

312 return type_class.from_ical(value) 

313 

314 

315__all__ = ["TypesFactory"]