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

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

95 statements  

1"""CAL-ADDRESS 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 

8from icalendar.parser import Parameters 

9from icalendar.parser.xcal.base import XCalParser 

10from icalendar.parser.xcal.wrapper import from_xcal_wrapper 

11from icalendar.parser_tools import DEFAULT_ENCODING, to_unicode 

12 

13 

14class vCalAddress(str): 

15 r"""Calendar User Address 

16 

17 Value Name: 

18 CAL-ADDRESS 

19 

20 Purpose: 

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

22 calendar user address. 

23 

24 Description: 

25 The value is a URI as defined by [RFC3986] or any other 

26 IANA-registered form for a URI. When used to address an Internet 

27 email transport address for a calendar user, the value MUST be a 

28 mailto URI, as defined by [RFC2368]. 

29 

30 Example: 

31 ``mailto:`` is in front of the address. 

32 

33 .. code-block:: ics 

34 

35 mailto:jane_doe@example.com 

36 

37 Parsing: 

38 

39 .. code-block:: pycon 

40 

41 >>> from icalendar import vCalAddress 

42 >>> cal_address = vCalAddress.from_ical('mailto:jane_doe@example.com') 

43 >>> cal_address 

44 vCalAddress('mailto:jane_doe@example.com') 

45 

46 Encoding: 

47 

48 .. code-block:: pycon 

49 

50 >>> from icalendar import vCalAddress, Event 

51 >>> event = Event() 

52 >>> jane = vCalAddress("mailto:jane_doe@example.com") 

53 >>> jane.name = "Jane" 

54 >>> event["organizer"] = jane 

55 >>> print(event.to_ical().decode().replace('\\r\\n', '\\n').strip()) 

56 BEGIN:VEVENT 

57 ORGANIZER;CN=Jane:mailto:jane_doe@example.com 

58 END:VEVENT 

59 """ 

60 

61 default_value: ClassVar[str] = "CAL-ADDRESS" 

62 params: Parameters 

63 __slots__ = ("params",) 

64 

65 def __new__( 

66 cls, 

67 value: str | bytes, 

68 encoding: str = DEFAULT_ENCODING, 

69 /, 

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

71 ) -> Self: 

72 value = to_unicode(value, encoding=encoding) 

73 if "\r" in value or "\n" in value: 

74 raise ValueError( 

75 f"A CAL-ADDRESS value may not contain CR or LF characters: {value!r}" 

76 ) 

77 self = super().__new__(cls, value) 

78 self.params = Parameters(params) 

79 return self 

80 

81 def __repr__(self) -> str: 

82 return f"vCalAddress('{self}')" 

83 

84 def to_ical(self) -> bytes: 

85 return self.encode(DEFAULT_ENCODING) 

86 

87 @classmethod 

88 def from_ical(cls, ical: str | bytes) -> Self: 

89 return cls(ical) 

90 

91 @property 

92 def ical_value(self) -> str: 

93 """The ``mailto:`` part of the address.""" 

94 return str(self) 

95 

96 @property 

97 def email(self) -> str: 

98 """The email address without ``mailto:`` at the start.""" 

99 if self.lower().startswith("mailto:"): 

100 return self[7:] 

101 return str(self) 

102 

103 from icalendar.param import ( 

104 CN, 

105 CUTYPE, 

106 DELEGATED_FROM, 

107 DELEGATED_TO, 

108 DIR, 

109 LANGUAGE, 

110 MEMBER, 

111 PARTSTAT, 

112 ROLE, 

113 RSVP, 

114 SENT_BY, 

115 VALUE, 

116 ) 

117 

118 DELEGATED_FROM = DELEGATED_FROM 

119 """Specify the calendar users that delegated their participation. 

120 

121 This parameter can be specified on properties with a 

122 CAL-ADDRESS value type. This parameter specifies those calendar 

123 users that have delegated their participation in a group-scheduled 

124 event or to-do to the calendar user specified by the property. 

125 The individual calendar address parameter values MUST each be 

126 specified in a quoted-string. 

127 """ 

128 

129 DELEGATED_TO = DELEGATED_TO 

130 """Specify the calendar users to whom participation was delegated. 

131 

132 This parameter can be specified on properties with a 

133 CAL-ADDRESS value type. This parameter specifies those calendar 

134 users to whom participation in a group-scheduled event or to-do was 

135 delegated by the calendar user specified by the property. 

136 The individual calendar address parameter values MUST each be 

137 specified in a quoted-string. 

138 """ 

139 

140 MEMBER = MEMBER 

141 """Specify the group or list membership of a calendar user. 

142 

143 This parameter can be specified on properties with a 

144 CAL-ADDRESS value type. The parameter identifies the groups or 

145 list membership for the calendar user specified by the property. 

146 The parameter value is either a single calendar address in a 

147 quoted-string or a COMMA-separated list of calendar addresses, 

148 each in a quoted-string. The individual calendar address 

149 parameter values MUST each be specified in a quoted-string. 

150 """ 

151 

152 name = CN 

153 

154 @staticmethod 

155 def _get_email(email: str) -> str: 

156 """Extract email and add mailto: prefix if needed. 

157 

158 Handles case-insensitive mailto: prefix checking. 

159 

160 Parameters: 

161 email: Email string that may or may not have mailto: prefix 

162 

163 Returns: 

164 Email string with mailto: prefix 

165 """ 

166 if not email.lower().startswith("mailto:"): 

167 return f"mailto:{email}" 

168 return email 

169 

170 @classmethod 

171 def new( 

172 cls, 

173 email: str, 

174 /, 

175 cn: str | None = None, 

176 cutype: str | None = None, 

177 delegated_from: str | None = None, 

178 delegated_to: str | None = None, 

179 directory: str | None = None, 

180 language: str | None = None, 

181 partstat: str | None = None, 

182 role: str | None = None, 

183 rsvp: bool | None = None, # noqa: FBT001, RUF100 

184 sent_by: str | None = None, 

185 ) -> Self: 

186 """Create a new vCalAddress with RFC 5545 parameters. 

187 

188 Creates a vCalAddress instance with automatic mailto: prefix handling 

189 and support for all standard RFC 5545 parameters. 

190 

191 Parameters: 

192 email: The email address (mailto: prefix added automatically if missing) 

193 cn: Common Name parameter 

194 cutype: Calendar user type (INDIVIDUAL, GROUP, RESOURCE, ROOM) 

195 delegated_from: Email of the calendar user that delegated 

196 delegated_to: Email of the calendar user that was delegated to 

197 directory: Reference to directory information 

198 language: Language for text values 

199 partstat: Participation status (NEEDS-ACTION, ACCEPTED, DECLINED, etc.) 

200 role: Role (REQ-PARTICIPANT, OPT-PARTICIPANT, NON-PARTICIPANT, CHAIR) 

201 rsvp: Whether RSVP is requested 

202 sent_by: Email of the calendar user acting on behalf of this user 

203 

204 Returns: 

205 vCalAddress: A new calendar address with specified parameters 

206 

207 Raises: 

208 TypeError: If email is not a string 

209 

210 Examples: 

211 Basic usage: 

212 

213 >>> from icalendar.prop import vCalAddress 

214 >>> addr = vCalAddress.new("test@test.com") 

215 >>> str(addr) 

216 'mailto:test@test.com' 

217 

218 With parameters: 

219 

220 >>> addr = vCalAddress.new("test@test.com", cn="Test User", role="CHAIR") 

221 >>> addr.params["CN"] 

222 'Test User' 

223 >>> addr.params["ROLE"] 

224 'CHAIR' 

225 """ 

226 if not isinstance(email, str): 

227 raise TypeError(f"Email must be a string, not {type(email).__name__}") 

228 

229 # Handle mailto: prefix (case-insensitive) 

230 email_with_prefix = cls._get_email(email) 

231 

232 # Create the address 

233 addr = cls(email_with_prefix) 

234 

235 # Set parameters if provided 

236 if cn is not None: 

237 addr.params["CN"] = cn 

238 if cutype is not None: 

239 addr.params["CUTYPE"] = cutype 

240 if delegated_from is not None: 

241 addr.params["DELEGATED-FROM"] = cls._get_email(delegated_from) 

242 if delegated_to is not None: 

243 addr.params["DELEGATED-TO"] = cls._get_email(delegated_to) 

244 if directory is not None: 

245 addr.params["DIR"] = directory 

246 if language is not None: 

247 addr.params["LANGUAGE"] = language 

248 if partstat is not None: 

249 addr.params["PARTSTAT"] = partstat 

250 if role is not None: 

251 addr.params["ROLE"] = role 

252 if rsvp is not None: 

253 addr.params["RSVP"] = "TRUE" if rsvp else "FALSE" 

254 if sent_by is not None: 

255 addr.params["SENT-BY"] = cls._get_email(sent_by) 

256 

257 return addr 

258 

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

260 """Return this property in jCal format.""" 

261 return [name, self.params.to_jcal(), self.VALUE.lower(), self.ical_value] 

262 

263 @classmethod 

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

265 """Examples of vCalAddress.""" 

266 return [cls.new("you@example.org", cn="You There")] 

267 

268 @classmethod 

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

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

271 

272 Parameters: 

273 jcal_property: The jCal property to parse. 

274 

275 Raises: 

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

277 """ 

278 JCalParsingError.validate_property(jcal_property, cls) 

279 JCalParsingError.validate_value_type(jcal_property[3], str, cls, 3) 

280 return cls( 

281 jcal_property[3], 

282 params=Parameters.from_jcal_property(jcal_property), 

283 ) 

284 

285 @classmethod 

286 @from_xcal_wrapper 

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

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

289 

290 Parameters: 

291 xml: The XML to parse or a parser. 

292 

293 Raises: 

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

295 """ 

296 element = parser.parse_tag(cls.default_value) 

297 return cls(element.get_xsd_token(), params=params) 

298 

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

300 """The xCal representation of this property according to :rfc:`6321`.""" 

301 self.params.to_xcal(element) 

302 element = SubElement(element, "cal-address") 

303 element.text = self.ical_value 

304 

305 

306__all__ = ["vCalAddress"]