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

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

75 statements  

1"""CAL-ADDRESS values from :rfc:`5545`.""" 

2 

3from typing import Any, ClassVar 

4 

5from icalendar.compatibility import Self 

6from icalendar.error import JCalParsingError 

7from icalendar.parser import Parameters 

8from icalendar.parser_tools import DEFAULT_ENCODING, to_unicode 

9 

10 

11class vCalAddress(str): 

12 r"""Calendar User Address 

13 

14 Value Name: 

15 CAL-ADDRESS 

16 

17 Purpose: 

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

19 calendar user address. 

20 

21 Description: 

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

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

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

25 mailto URI, as defined by [RFC2368]. 

26 

27 Example: 

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

29 

30 .. code-block:: text 

31 

32 mailto:jane_doe@example.com 

33 

34 Parsing: 

35 

36 .. code-block:: pycon 

37 

38 >>> from icalendar import vCalAddress 

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

40 >>> cal_address 

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

42 

43 Encoding: 

44 

45 .. code-block:: pycon 

46 

47 >>> from icalendar import vCalAddress, Event 

48 >>> event = Event() 

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

50 >>> jane.name = "Jane" 

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

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

53 BEGIN:VEVENT 

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

55 END:VEVENT 

56 """ 

57 

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

59 params: Parameters 

60 __slots__ = ("params",) 

61 

62 def __new__( 

63 cls, 

64 value: str | bytes, 

65 encoding: str = DEFAULT_ENCODING, 

66 /, 

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

68 ) -> Self: 

69 value = to_unicode(value, encoding=encoding) 

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

71 self.params = Parameters(params) 

72 return self 

73 

74 def __repr__(self) -> str: 

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

76 

77 def to_ical(self) -> bytes: 

78 return self.encode(DEFAULT_ENCODING) 

79 

80 @classmethod 

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

82 return cls(ical) 

83 

84 @property 

85 def ical_value(self) -> str: 

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

87 return str(self) 

88 

89 @property 

90 def email(self) -> str: 

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

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

93 return self[7:] 

94 return str(self) 

95 

96 from icalendar.param import ( 

97 CN, 

98 CUTYPE, 

99 DELEGATED_FROM, 

100 DELEGATED_TO, 

101 DIR, 

102 LANGUAGE, 

103 PARTSTAT, 

104 ROLE, 

105 RSVP, 

106 SENT_BY, 

107 VALUE, 

108 ) 

109 

110 name = CN 

111 

112 @staticmethod 

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

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

115 

116 Handles case-insensitive mailto: prefix checking. 

117 

118 Parameters: 

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

120 

121 Returns: 

122 Email string with mailto: prefix 

123 """ 

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

125 return f"mailto:{email}" 

126 return email 

127 

128 @classmethod 

129 def new( 

130 cls, 

131 email: str, 

132 /, 

133 cn: str | None = None, 

134 cutype: str | None = None, 

135 delegated_from: str | None = None, 

136 delegated_to: str | None = None, 

137 directory: str | None = None, 

138 language: str | None = None, 

139 partstat: str | None = None, 

140 role: str | None = None, 

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

142 sent_by: str | None = None, 

143 ) -> Self: 

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

145 

146 Creates a vCalAddress instance with automatic mailto: prefix handling 

147 and support for all standard RFC 5545 parameters. 

148 

149 Parameters: 

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

151 cn: Common Name parameter 

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

153 delegated_from: Email of the calendar user that delegated 

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

155 directory: Reference to directory information 

156 language: Language for text values 

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

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

159 rsvp: Whether RSVP is requested 

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

161 

162 Returns: 

163 vCalAddress: A new calendar address with specified parameters 

164 

165 Raises: 

166 TypeError: If email is not a string 

167 

168 Examples: 

169 Basic usage: 

170 

171 >>> from icalendar.prop import vCalAddress 

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

173 >>> str(addr) 

174 'mailto:test@test.com' 

175 

176 With parameters: 

177 

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

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

180 'Test User' 

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

182 'CHAIR' 

183 """ 

184 if not isinstance(email, str): 

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

186 

187 # Handle mailto: prefix (case-insensitive) 

188 email_with_prefix = cls._get_email(email) 

189 

190 # Create the address 

191 addr = cls(email_with_prefix) 

192 

193 # Set parameters if provided 

194 if cn is not None: 

195 addr.params["CN"] = cn 

196 if cutype is not None: 

197 addr.params["CUTYPE"] = cutype 

198 if delegated_from is not None: 

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

200 if delegated_to is not None: 

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

202 if directory is not None: 

203 addr.params["DIR"] = directory 

204 if language is not None: 

205 addr.params["LANGUAGE"] = language 

206 if partstat is not None: 

207 addr.params["PARTSTAT"] = partstat 

208 if role is not None: 

209 addr.params["ROLE"] = role 

210 if rsvp is not None: 

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

212 if sent_by is not None: 

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

214 

215 return addr 

216 

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

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

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

220 

221 @classmethod 

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

223 """Examples of vCalAddress.""" 

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

225 

226 @classmethod 

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

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

229 

230 Parameters: 

231 jcal_property: The jCal property to parse. 

232 

233 Raises: 

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

235 """ 

236 JCalParsingError.validate_property(jcal_property, cls) 

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

238 return cls( 

239 jcal_property[3], 

240 params=Parameters.from_jcal_property(jcal_property), 

241 ) 

242 

243 

244__all__ = ["vCalAddress"]