Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/icalendar/attr.py: 29%

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

491 statements  

1"""Attributes of Components and properties.""" 

2 

3from __future__ import annotations 

4 

5import itertools 

6from datetime import date, datetime, timedelta 

7from typing import TYPE_CHECKING, Literal, TypeAlias 

8 

9from icalendar.enums import BUSYTYPE, CLASS, STATUS, TRANSP, StrEnum 

10from icalendar.error import IncompleteComponent, InvalidCalendar 

11from icalendar.parser_tools import SEQUENCE_TYPES 

12from icalendar.prop import ( 

13 vCalAddress, 

14 vCategory, 

15 vDDDTypes, 

16 vDuration, 

17 vRecur, 

18 vText, 

19 vUid, 

20 vUri, 

21 vXmlReference, 

22) 

23from icalendar.prop.conference import Conference 

24from icalendar.prop.image import Image 

25from icalendar.timezone import tzp 

26from icalendar.tools import is_date 

27 

28if TYPE_CHECKING: 

29 from collections.abc import Sequence 

30 

31 from icalendar.cal import Component 

32 

33 

34def _get_rdates( 

35 self: Component, 

36) -> list[tuple[date, None] | tuple[datetime, None] | tuple[datetime, datetime]]: 

37 """The RDATE property defines the list of DATE-TIME values for recurring components. 

38 

39 RDATE is defined in :rfc:`5545`. 

40 The return value is a list of tuples ``(start, end)``. 

41 

42 ``start`` can be a :class:`datetime.date` or a :class:`datetime.datetime`, 

43 with and without timezone. 

44 

45 ``end`` is :obj:`None` if the end is not specified and a :class:`datetime.datetime` 

46 if the end is specified. 

47 

48 Value Type: 

49 The default value type for this property is DATE-TIME. 

50 The value type can be set to DATE or PERIOD. 

51 

52 Property Parameters: 

53 IANA, non-standard, value data type, and time 

54 zone identifier property parameters can be specified on this 

55 property. 

56 

57 Conformance: 

58 This property can be specified in recurring "VEVENT", 

59 "VTODO", and "VJOURNAL" calendar components as well as in the 

60 "STANDARD" and "DAYLIGHT" sub-components of the "VTIMEZONE" 

61 calendar component. 

62 

63 Description: 

64 This property can appear along with the "RRULE" 

65 property to define an aggregate set of repeating occurrences. 

66 When they both appear in a recurring component, the recurrence 

67 instances are defined by the union of occurrences defined by both 

68 the "RDATE" and "RRULE". 

69 

70 The recurrence dates, if specified, are used in computing the 

71 recurrence set. The recurrence set is the complete set of 

72 recurrence instances for a calendar component. The recurrence set 

73 is generated by considering the initial "DTSTART" property along 

74 with the "RRULE", "RDATE", and "EXDATE" properties contained 

75 within the recurring component. The "DTSTART" property defines 

76 the first instance in the recurrence set. The "DTSTART" property 

77 value SHOULD match the pattern of the recurrence rule, if 

78 specified. The recurrence set generated with a "DTSTART" property 

79 value that doesn't match the pattern of the rule is undefined. 

80 The final recurrence set is generated by gathering all of the 

81 start DATE-TIME values generated by any of the specified "RRULE" 

82 and "RDATE" properties, and then excluding any start DATE-TIME 

83 values specified by "EXDATE" properties. This implies that start 

84 DATE-TIME values specified by "EXDATE" properties take precedence 

85 over those specified by inclusion properties (i.e., "RDATE" and 

86 "RRULE"). Where duplicate instances are generated by the "RRULE" 

87 and "RDATE" properties, only one recurrence is considered. 

88 Duplicate instances are ignored. 

89 

90 Example: 

91 Below, we set one RDATE in a list and get the resulting tuple of start and end. 

92 

93 .. code-block:: pycon 

94 

95 >>> from icalendar import Event 

96 >>> from datetime import datetime 

97 >>> event = Event() 

98 

99 # Add a list of recurrence dates 

100 >>> event.add("RDATE", [datetime(2025, 4, 28, 16, 5)]) 

101 >>> event.rdates 

102 [(datetime.datetime(2025, 4, 28, 16, 5), None)] 

103 

104 .. note:: 

105 

106 You cannot modify the RDATE value by modifying the result. 

107 Use :func:`icalendar.cal.Component.add` to add values. 

108 

109 If you want to compute recurrences, have a look at 

110 `Related Projects <https://github.com/collective/icalendar/blob/main/README.rst#related-projects>`_. 

111 

112 """ 

113 result = [] 

114 rdates = self.get("RDATE", []) 

115 for rdates in (rdates,) if not isinstance(rdates, list) else rdates: 

116 for dts in rdates.dts: 

117 rdate = dts.dt 

118 if isinstance(rdate, tuple): 

119 # we have a period as rdate 

120 if isinstance(rdate[1], timedelta): 

121 result.append((rdate[0], rdate[0] + rdate[1])) 

122 else: 

123 result.append(rdate) 

124 else: 

125 # we have a date/datetime 

126 result.append((rdate, None)) 

127 return result 

128 

129 

130rdates_property = property(_get_rdates) 

131 

132 

133def _get_exdates(self: Component) -> list[date | datetime]: 

134 """EXDATE defines the list of DATE-TIME exceptions for recurring components. 

135 

136 EXDATE is defined in :rfc:`5545`. 

137 

138 Value Type: 

139 The default value type for this property is DATE-TIME. 

140 The value type can be set to DATE. 

141 

142 Property Parameters: 

143 IANA, non-standard, value data type, and time 

144 zone identifier property parameters can be specified on this 

145 property. 

146 

147 Conformance: 

148 This property can be specified in recurring "VEVENT", 

149 "VTODO", and "VJOURNAL" calendar components as well as in the 

150 "STANDARD" and "DAYLIGHT" sub-components of the "VTIMEZONE" 

151 calendar component. 

152 

153 Description: 

154 The exception dates, if specified, are used in 

155 computing the recurrence set. The recurrence set is the complete 

156 set of recurrence instances for a calendar component. The 

157 recurrence set is generated by considering the initial "DTSTART" 

158 property along with the "RRULE", "RDATE", and "EXDATE" properties 

159 contained within the recurring component. The "DTSTART" property 

160 defines the first instance in the recurrence set. The "DTSTART" 

161 property value SHOULD match the pattern of the recurrence rule, if 

162 specified. The recurrence set generated with a "DTSTART" property 

163 value that doesn't match the pattern of the rule is undefined. 

164 The final recurrence set is generated by gathering all of the 

165 start DATE-TIME values generated by any of the specified "RRULE" 

166 and "RDATE" properties, and then excluding any start DATE-TIME 

167 values specified by "EXDATE" properties. This implies that start 

168 DATE-TIME values specified by "EXDATE" properties take precedence 

169 over those specified by inclusion properties (i.e., "RDATE" and 

170 "RRULE"). When duplicate instances are generated by the "RRULE" 

171 and "RDATE" properties, only one recurrence is considered. 

172 Duplicate instances are ignored. 

173 

174 The "EXDATE" property can be used to exclude the value specified 

175 in "DTSTART". However, in such cases, the original "DTSTART" date 

176 MUST still be maintained by the calendaring and scheduling system 

177 because the original "DTSTART" value has inherent usage 

178 dependencies by other properties such as the "RECURRENCE-ID". 

179 

180 Example: 

181 Below, we add an exdate in a list and get the resulting list of exdates. 

182 

183 .. code-block:: pycon 

184 

185 >>> from icalendar import Event 

186 >>> from datetime import datetime 

187 >>> event = Event() 

188 

189 # Add a list of excluded dates 

190 >>> event.add("EXDATE", [datetime(2025, 4, 28, 16, 5)]) 

191 >>> event.exdates 

192 [datetime.datetime(2025, 4, 28, 16, 5)] 

193 

194 .. note:: 

195 

196 You cannot modify the EXDATE value by modifying the result. 

197 Use :func:`icalendar.cal.Component.add` to add values. 

198 

199 If you want to compute recurrences, have a look at 

200 `Related Projects <https://github.com/collective/icalendar/blob/main/README.rst#related-projects>`_. 

201 

202 """ 

203 result = [] 

204 exdates = self.get("EXDATE", []) 

205 for exdates in (exdates,) if not isinstance(exdates, list) else exdates: 

206 for dts in exdates.dts: 

207 exdate = dts.dt 

208 # we have a date/datetime 

209 result.append(exdate) 

210 return result 

211 

212 

213exdates_property = property(_get_exdates) 

214 

215 

216def _get_rrules(self: Component) -> list[vRecur]: 

217 """RRULE defines a rule or repeating pattern for recurring components. 

218 

219 RRULE is defined in :rfc:`5545`. 

220 :rfc:`7529` adds the ``SKIP`` parameter :class:`icalendar.prop.vSkip`. 

221 

222 Property Parameters: 

223 IANA and non-standard property parameters can 

224 be specified on this property. 

225 

226 Conformance: 

227 This property can be specified in recurring "VEVENT", 

228 "VTODO", and "VJOURNAL" calendar components as well as in the 

229 "STANDARD" and "DAYLIGHT" sub-components of the "VTIMEZONE" 

230 calendar component, but it SHOULD NOT be specified more than once. 

231 The recurrence set generated with multiple "RRULE" properties is 

232 undefined. 

233 

234 Description: 

235 The recurrence rule, if specified, is used in computing 

236 the recurrence set. The recurrence set is the complete set of 

237 recurrence instances for a calendar component. The recurrence set 

238 is generated by considering the initial "DTSTART" property along 

239 with the "RRULE", "RDATE", and "EXDATE" properties contained 

240 within the recurring component. The "DTSTART" property defines 

241 the first instance in the recurrence set. The "DTSTART" property 

242 value SHOULD be synchronized with the recurrence rule, if 

243 specified. The recurrence set generated with a "DTSTART" property 

244 value not synchronized with the recurrence rule is undefined. The 

245 final recurrence set is generated by gathering all of the start 

246 DATE-TIME values generated by any of the specified "RRULE" and 

247 "RDATE" properties, and then excluding any start DATE-TIME values 

248 specified by "EXDATE" properties. This implies that start DATE- 

249 TIME values specified by "EXDATE" properties take precedence over 

250 those specified by inclusion properties (i.e., "RDATE" and 

251 "RRULE"). Where duplicate instances are generated by the "RRULE" 

252 and "RDATE" properties, only one recurrence is considered. 

253 Duplicate instances are ignored. 

254 

255 The "DTSTART" property specified within the iCalendar object 

256 defines the first instance of the recurrence. In most cases, a 

257 "DTSTART" property of DATE-TIME value type used with a recurrence 

258 rule, should be specified as a date with local time and time zone 

259 reference to make sure all the recurrence instances start at the 

260 same local time regardless of time zone changes. 

261 

262 If the duration of the recurring component is specified with the 

263 "DTEND" or "DUE" property, then the same exact duration will apply 

264 to all the members of the generated recurrence set. Else, if the 

265 duration of the recurring component is specified with the 

266 "DURATION" property, then the same nominal duration will apply to 

267 all the members of the generated recurrence set and the exact 

268 duration of each recurrence instance will depend on its specific 

269 start time. For example, recurrence instances of a nominal 

270 duration of one day will have an exact duration of more or less 

271 than 24 hours on a day where a time zone shift occurs. The 

272 duration of a specific recurrence may be modified in an exception 

273 component or simply by using an "RDATE" property of PERIOD value 

274 type. 

275 

276 Examples: 

277 Daily for 10 occurrences: 

278 

279 .. code-block:: pycon 

280 

281 >>> from icalendar import Event 

282 >>> from datetime import datetime 

283 >>> from zoneinfo import ZoneInfo 

284 >>> event = Event() 

285 >>> event.start = datetime(1997, 9, 2, 9, 0, tzinfo=ZoneInfo("America/New_York")) 

286 >>> event.add("RRULE", "FREQ=DAILY;COUNT=10") 

287 >>> print(event.to_ical()) 

288 BEGIN:VEVENT 

289 DTSTART;TZID=America/New_York:19970902T090000 

290 RRULE:FREQ=DAILY;COUNT=10 

291 END:VEVENT 

292 >>> event.rrules 

293 [vRecur({'FREQ': ['DAILY'], 'COUNT': [10]})] 

294 

295 Daily until December 24, 1997: 

296 

297 .. code-block:: pycon 

298 

299 >>> from icalendar import Event, vRecur 

300 >>> from datetime import datetime 

301 >>> from zoneinfo import ZoneInfo 

302 >>> event = Event() 

303 >>> event.start = datetime(1997, 9, 2, 9, 0, tzinfo=ZoneInfo("America/New_York")) 

304 >>> event.add("RRULE", vRecur({"FREQ": ["DAILY"]}, until=datetime(1997, 12, 24, tzinfo=ZoneInfo("UTC")))) 

305 >>> print(event.to_ical()) 

306 BEGIN:VEVENT 

307 DTSTART;TZID=America/New_York:19970902T090000 

308 RRULE:FREQ=DAILY;UNTIL=19971224T000000Z 

309 END:VEVENT 

310 >>> event.rrules 

311 [vRecur({'FREQ': ['DAILY'], 'UNTIL': [datetime.datetime(1997, 12, 24, 0, 0, tzinfo=ZoneInfo(key='UTC'))]})] 

312 

313 .. note:: 

314 

315 You cannot modify the RRULE value by modifying the result. 

316 Use :func:`icalendar.cal.Component.add` to add values. 

317 

318 If you want to compute recurrences, have a look at 

319 `Related Projects <https://github.com/collective/icalendar/blob/main/README.rst#related-projects>`_. 

320 

321 """ # noqa: E501 

322 rrules = self.get("RRULE", []) 

323 if not isinstance(rrules, list): 

324 return [rrules] 

325 return rrules 

326 

327 

328rrules_property = property(_get_rrules) 

329 

330 

331def multi_language_text_property( 

332 main_prop: str, compatibility_prop: str | None, doc: str 

333) -> property: 

334 """This creates a text property. 

335 

336 This property can be defined several times with different ``LANGUAGE`` parameters. 

337 

338 Parameters: 

339 main_prop (str): The property to set and get, such as ``NAME`` 

340 compatibility_prop (str): An old property used before, such as ``X-WR-CALNAME`` 

341 doc (str): The documentation string 

342 """ 

343 

344 def fget(self: Component) -> str | None: 

345 """Get the property""" 

346 result = self.get(main_prop) 

347 if result is None and compatibility_prop is not None: 

348 result = self.get(compatibility_prop) 

349 if isinstance(result, list): 

350 for item in result: 

351 if "LANGUAGE" not in item.params: 

352 return item 

353 return result 

354 

355 def fset(self: Component, value: str | None): 

356 """Set the property.""" 

357 fdel(self) 

358 if value is not None: 

359 self.add(main_prop, value) 

360 

361 def fdel(self: Component): 

362 """Delete the property.""" 

363 self.pop(main_prop, None) 

364 if compatibility_prop is not None: 

365 self.pop(compatibility_prop, None) 

366 

367 return property(fget, fset, fdel, doc) 

368 

369 

370def single_int_property(prop: str, default: int, doc: str) -> property: 

371 """Create a property for an int value that exists only once. 

372 

373 Parameters: 

374 prop: The name of the property 

375 default: The default value 

376 doc: The documentation string 

377 """ 

378 

379 def fget(self: Component) -> int: 

380 """Get the property""" 

381 try: 

382 return int(self.get(prop, default)) 

383 except ValueError as e: 

384 raise InvalidCalendar(f"{prop} must be an int") from e 

385 

386 def fset(self: Component, value: int | None): 

387 """Set the property.""" 

388 fdel(self) 

389 if value is not None: 

390 self.add(prop, value) 

391 

392 def fdel(self: Component): 

393 """Delete the property.""" 

394 self.pop(prop, None) 

395 

396 return property(fget, fset, fdel, doc) 

397 

398 

399def single_utc_property(name: str, docs: str) -> property: 

400 """Create a property to access a value of datetime in UTC timezone. 

401 

402 Parameters: 

403 name: name of the property 

404 docs: documentation string 

405 """ 

406 docs = ( 

407 f"""The {name} property. datetime in UTC 

408 

409 All values will be converted to a datetime in UTC. 

410 """ 

411 + docs 

412 ) 

413 

414 def fget(self: Component) -> datetime | None: 

415 """Get the value.""" 

416 if name not in self: 

417 return None 

418 dt = self.get(name) 

419 if isinstance(dt, vText): 

420 # we might be in an attribute that is not typed 

421 value = vDDDTypes.from_ical(dt) 

422 else: 

423 value = getattr(dt, "dt", dt) 

424 if value is None or not isinstance(value, date): 

425 raise InvalidCalendar(f"{name} must be a datetime in UTC, not {value}") 

426 return tzp.localize_utc(value) 

427 

428 def fset(self: Component, value: datetime | None): 

429 """Set the value""" 

430 if value is None: 

431 fdel(self) 

432 return 

433 if not isinstance(value, date): 

434 raise TypeError(f"{name} takes a datetime in UTC, not {value}") 

435 fdel(self) 

436 self.add(name, tzp.localize_utc(value)) 

437 

438 def fdel(self: Component): 

439 """Delete the property.""" 

440 self.pop(name, None) 

441 

442 return property(fget, fset, fdel, doc=docs) 

443 

444 

445def single_string_property( 

446 name: str, docs: str, other_name: str | None = None, default: str = "" 

447) -> property: 

448 """Create a property to access a single string value.""" 

449 

450 def fget(self: Component) -> str: 

451 """Get the value.""" 

452 result = self.get( 

453 name, None if other_name is None else self.get(other_name, None) 

454 ) 

455 if result is None or result == []: 

456 return default 

457 if isinstance(result, list): 

458 return result[0] 

459 return result 

460 

461 def fset(self: Component, value: str | None): 

462 """Set the value. 

463 

464 Setting the value to None will delete it. 

465 """ 

466 fdel(self) 

467 if value is not None: 

468 self.add(name, value) 

469 

470 def fdel(self: Component): 

471 """Delete the property.""" 

472 self.pop(name, None) 

473 if other_name is not None: 

474 self.pop(other_name, None) 

475 

476 return property(fget, fset, fdel, doc=docs) 

477 

478 

479color_property = single_string_property( 

480 "COLOR", 

481 """This property specifies a color used for displaying the component. 

482 

483 This implements :rfc:`7986` ``COLOR`` property. 

484 

485 Property Parameters: 

486 IANA and non-standard property parameters can 

487 be specified on this property. 

488 

489 Conformance: 

490 This property can be specified once in an iCalendar 

491 object or in ``VEVENT``, ``VTODO``, or ``VJOURNAL`` calendar components. 

492 

493 Description: 

494 This property specifies a color that clients MAY use 

495 when presenting the relevant data to a user. Typically, this 

496 would appear as the "background" color of events or tasks. The 

497 value is a case-insensitive color name taken from the CSS3 set of 

498 names, defined in Section 4.3 of `W3C.REC-css3-color-20110607 <https://www.w3.org/TR/css-color-3/>`_. 

499 

500 Example: 

501 ``"turquoise"``, ``"#ffffff"`` 

502 

503 .. code-block:: pycon 

504 

505 >>> from icalendar import Todo 

506 >>> todo = Todo() 

507 >>> todo.color = "green" 

508 >>> print(todo.to_ical()) 

509 BEGIN:VTODO 

510 COLOR:green 

511 END:VTODO 

512 """, 

513) 

514 

515sequence_property = single_int_property( 

516 "SEQUENCE", 

517 0, 

518 """This property defines the revision sequence number of the calendar component within a sequence of revisions. 

519 

520Value Type: 

521 INTEGER 

522 

523Property Parameters: 

524 IANA and non-standard property parameters can be specified on this property. 

525 

526Conformance: 

527 The property can be specified in "VEVENT", "VTODO", or 

528 "VJOURNAL" calendar component. 

529 

530Description: 

531 When a calendar component is created, its sequence 

532 number is 0. It is monotonically incremented by the "Organizer's" 

533 CUA each time the "Organizer" makes a significant revision to the 

534 calendar component. 

535 

536 The "Organizer" includes this property in an iCalendar object that 

537 it sends to an "Attendee" to specify the current version of the 

538 calendar component. 

539 

540 The "Attendee" includes this property in an iCalendar object that 

541 it sends to the "Organizer" to specify the version of the calendar 

542 component to which the "Attendee" is referring. 

543 

544 A change to the sequence number is not the mechanism that an 

545 "Organizer" uses to request a response from the "Attendees". The 

546 "RSVP" parameter on the "ATTENDEE" property is used by the 

547 "Organizer" to indicate that a response from the "Attendees" is 

548 requested. 

549 

550 Recurrence instances of a recurring component MAY have different 

551 sequence numbers. 

552 

553Examples: 

554 The following is an example of this property for a calendar 

555 component that was just created by the "Organizer": 

556 

557 .. code-block:: pycon 

558 

559 >>> from icalendar import Event 

560 >>> event = Event() 

561 >>> event.sequence 

562 0 

563 

564 The following is an example of this property for a calendar 

565 component that has been revised 10 different times by the 

566 "Organizer": 

567 

568 .. code-block:: pycon 

569 

570 >>> from icalendar import Calendar 

571 >>> calendar = Calendar.example("issue_156_RDATE_with_PERIOD_TZID_khal") 

572 >>> event = calendar.events[0] 

573 >>> event.sequence 

574 10 

575 """, # noqa: E501 

576) 

577 

578 

579def _get_categories(component: Component) -> list[str]: 

580 """Get all the categories.""" 

581 categories: vCategory | list[vCategory] | None = component.get("CATEGORIES") 

582 if isinstance(categories, list): 

583 _set_categories( 

584 component, 

585 list(itertools.chain.from_iterable(cat.cats for cat in categories)), 

586 ) 

587 return _get_categories(component) 

588 if categories is None: 

589 categories = vCategory([]) 

590 component.add("CATEGORIES", categories) 

591 return categories.cats 

592 

593 

594def _set_categories(component: Component, cats: Sequence[str] | None) -> None: 

595 """Set the categories.""" 

596 if not cats and cats != []: 

597 _del_categories(component) 

598 return 

599 component["CATEGORIES"] = categories = vCategory(cats) 

600 if isinstance(cats, list): 

601 cats.clear() 

602 cats.extend(categories.cats) 

603 categories.cats = cats 

604 

605 

606def _del_categories(component: Component) -> None: 

607 """Delete the categories.""" 

608 component.pop("CATEGORIES", None) 

609 

610 

611categories_property = property( 

612 _get_categories, 

613 _set_categories, 

614 _del_categories, 

615 """This property defines the categories for a component. 

616 

617Property Parameters: 

618 IANA, non-standard, and language property parameters can be specified on this 

619 property. 

620 

621Conformance: 

622 The property can be specified within "VEVENT", "VTODO", or "VJOURNAL" calendar 

623 components. 

624 Since :rfc:`7986` it can also be defined on a "VCALENDAR" component. 

625 

626Description: 

627 This property is used to specify categories or subtypes 

628 of the calendar component. The categories are useful in searching 

629 for a calendar component of a particular type and category. 

630 Within the "VEVENT", "VTODO", or "VJOURNAL" calendar components, 

631 more than one category can be specified as a COMMA-separated list 

632 of categories. 

633 

634Example: 

635 Below, we add the categories to an event: 

636 

637 .. code-block:: pycon 

638 

639 >>> from icalendar import Event 

640 >>> event = Event() 

641 >>> event.categories = ["Work", "Meeting"] 

642 >>> print(event.to_ical()) 

643 BEGIN:VEVENT 

644 CATEGORIES:Work,Meeting 

645 END:VEVENT 

646 >>> event.categories.append("Lecture") 

647 >>> event.categories == ["Work", "Meeting", "Lecture"] 

648 True 

649 

650.. note:: 

651 

652 At present, we do not take the LANGUAGE parameter into account. 

653 

654.. seealso:: 

655 

656 :attr:`Component.concepts` 

657""", 

658) 

659 

660 

661def _get_attendees(self: Component) -> list[vCalAddress]: 

662 """Get attendees.""" 

663 value = self.get("ATTENDEE") 

664 if value is None: 

665 value = [] 

666 self["ATTENDEE"] = value 

667 return value 

668 if isinstance(value, vCalAddress): 

669 return [value] 

670 return value 

671 

672 

673def _set_attendees(self: Component, value: list[vCalAddress] | vCalAddress | None): 

674 """Set attendees.""" 

675 _del_attendees(self) 

676 if value is None: 

677 return 

678 if not isinstance(value, list): 

679 value = [value] 

680 self["ATTENDEE"] = value 

681 

682 

683def _del_attendees(self: Component): 

684 """Delete all attendees.""" 

685 self.pop("ATTENDEE", None) 

686 

687 

688attendees_property = property( 

689 _get_attendees, 

690 _set_attendees, 

691 _del_attendees, 

692 """ATTENDEE defines one or more "Attendees" within a calendar component. 

693 

694Conformance: 

695 This property MUST be specified in an iCalendar object 

696 that specifies a group-scheduled calendar entity. This property 

697 MUST NOT be specified in an iCalendar object when publishing the 

698 calendar information (e.g., NOT in an iCalendar object that 

699 specifies the publication of a calendar user's busy time, event, 

700 to-do, or journal). This property is not specified in an 

701 iCalendar object that specifies only a time zone definition or 

702 that defines calendar components that are not group-scheduled 

703 components, but are components only on a single user's calendar. 

704 

705Description: 

706 This property MUST only be specified within calendar 

707 components to specify participants, non-participants, and the 

708 chair of a group-scheduled calendar entity. The property is 

709 specified within an "EMAIL" category of the "VALARM" calendar 

710 component to specify an email address that is to receive the email 

711 type of iCalendar alarm. 

712 

713Examples: 

714 Add a new attendee to an existing event. 

715 

716 .. code-block:: pycon 

717 

718 >>> from icalendar import Event, vCalAddress 

719 >>> event = Event() 

720 >>> event.attendees.append(vCalAddress("mailto:me@my-domain.com")) 

721 >>> print(event.to_ical()) 

722 BEGIN:VEVENT 

723 ATTENDEE:mailto:me@my-domain.com 

724 END:VEVENT 

725 

726 Create an email alarm with several attendees: 

727 

728 >>> from icalendar import Alarm, vCalAddress 

729 >>> alarm = Alarm.new(attendees = [ 

730 ... vCalAddress("mailto:me@my-domain.com"), 

731 ... vCalAddress("mailto:you@my-domain.com"), 

732 ... ], summary = "Email alarm") 

733 >>> print(alarm.to_ical()) 

734 BEGIN:VALARM 

735 ATTENDEE:mailto:me@my-domain.com 

736 ATTENDEE:mailto:you@my-domain.com 

737 SUMMARY:Email alarm 

738 END:VALARM 

739""", 

740) 

741 

742uid_property = single_string_property( 

743 "UID", 

744 """UID specifies the persistent, globally unique identifier for a component. 

745 

746We recommend using :func:`uuid.uuid4` to generate new values. 

747 

748Returns: 

749 The value of the UID property as a string or ``""`` if no value is set. 

750 

751Description: 

752 The "UID" itself MUST be a globally unique identifier. 

753 The generator of the identifier MUST guarantee that the identifier 

754 is unique. 

755 

756 This is the method for correlating scheduling messages with the 

757 referenced "VEVENT", "VTODO", or "VJOURNAL" calendar component. 

758 The full range of calendar components specified by a recurrence 

759 set is referenced by referring to just the "UID" property value 

760 corresponding to the calendar component. The "RECURRENCE-ID" 

761 property allows the reference to an individual instance within the 

762 recurrence set. 

763 

764 This property is an important method for group-scheduling 

765 applications to match requests with later replies, modifications, 

766 or deletion requests. Calendaring and scheduling applications 

767 MUST generate this property in "VEVENT", "VTODO", and "VJOURNAL" 

768 calendar components to assure interoperability with other group- 

769 scheduling applications. This identifier is created by the 

770 calendar system that generates an iCalendar object. 

771 

772 Implementations MUST be able to receive and persist values of at 

773 least 255 octets for this property, but they MUST NOT truncate 

774 values in the middle of a UTF-8 multi-octet sequence. 

775 

776 :rfc:`7986` states that UID can be used, for 

777 example, to identify duplicate calendar streams that a client may 

778 have been given access to. It can be used in conjunction with the 

779 "LAST-MODIFIED" property also specified on the "VCALENDAR" object 

780 to identify the most recent version of a calendar. 

781 

782Conformance: 

783 :rfc:`5545` states that the "UID" property can be specified on "VEVENT", "VTODO", 

784 and "VJOURNAL" calendar components. 

785 :rfc:`7986` modifies the definition of the "UID" property to 

786 allow it to be defined in an iCalendar object. 

787 :rfc:`9074` adds a "UID" property to "VALARM" components to allow a unique 

788 identifier to be specified. The value of this property can then be used 

789 to refer uniquely to the "VALARM" component. 

790 

791 This property can be specified once only. 

792 

793Security: 

794 :rfc:`7986` states that UID values MUST NOT include any data that 

795 might identify a user, host, domain, or any other security- or 

796 privacy-sensitive information. It is RECOMMENDED that calendar user 

797 agents now generate "UID" values that are hex-encoded random 

798 Universally Unique Identifier (UUID) values as defined in 

799 Sections 4.4 and 4.5 of :rfc:`4122`. 

800 You can use the :mod:`uuid` module to generate new UUIDs. 

801 

802Compatibility: 

803 For Alarms, ``X-ALARMUID`` is also considered. 

804 

805Examples: 

806 The following is an example of such a property value: 

807 ``5FC53010-1267-4F8E-BC28-1D7AE55A7C99``. 

808 

809 Set the UID of a calendar: 

810 

811 .. code-block:: pycon 

812 

813 >>> from icalendar import Calendar 

814 >>> from uuid import uuid4 

815 >>> calendar = Calendar() 

816 >>> calendar.uid = uuid4() 

817 >>> print(calendar.to_ical()) 

818 BEGIN:VCALENDAR 

819 UID:d755cef5-2311-46ed-a0e1-6733c9e15c63 

820 END:VCALENDAR 

821 

822""", 

823) 

824 

825summary_property = multi_language_text_property( 

826 "SUMMARY", 

827 None, 

828 """SUMMARY defines a short summary or subject for the calendar component. 

829 

830Property Parameters: 

831 IANA, non-standard, alternate text 

832 representation, and language property parameters can be specified 

833 on this property. 

834 

835Conformance: 

836 The property can be specified in "VEVENT", "VTODO", 

837 "VJOURNAL", or "VALARM" calendar components. 

838 

839Description: 

840 This property is used in the "VEVENT", "VTODO", and 

841 "VJOURNAL" calendar components to capture a short, one-line 

842 summary about the activity or journal entry. 

843 

844 This property is used in the "VALARM" calendar component to 

845 capture the subject of an EMAIL category of alarm. 

846 

847Examples: 

848 The following is an example of this property: 

849 

850 .. code-block:: pycon 

851 

852 SUMMARY:Department Party 

853""", 

854) 

855 

856description_property = multi_language_text_property( 

857 "DESCRIPTION", 

858 None, 

859 """DESCRIPTION provides a more complete description of the calendar component than that provided by the "SUMMARY" property. 

860 

861Property Parameters: 

862 IANA, non-standard, alternate text 

863 representation, and language property parameters can be specified 

864 on this property. 

865 

866Conformance: 

867 The property can be specified in the "VEVENT", "VTODO", 

868 "VJOURNAL", or "VALARM" calendar components. The property can be 

869 specified multiple times only within a "VJOURNAL" calendar 

870 component. 

871 

872Description: 

873 This property is used in the "VEVENT" and "VTODO" to 

874 capture lengthy textual descriptions associated with the activity. 

875 

876 This property is used in the "VALARM" calendar component to 

877 capture the display text for a DISPLAY category of alarm, and to 

878 capture the body text for an EMAIL category of alarm. 

879 

880Examples: 

881 The following is an example of this property with formatted 

882 line breaks in the property value: 

883 

884 .. code-block:: pycon 

885 

886 DESCRIPTION:Meeting to provide technical review for "Phoenix" 

887 design.\\nHappy Face Conference Room. Phoenix design team 

888 MUST attend this meeting.\\nRSVP to team leader. 

889 

890 """, # noqa: E501 

891) 

892 

893 

894def create_single_property( 

895 prop: str, 

896 value_attr: str | None, 

897 value_type: tuple[type], 

898 type_def: type, 

899 doc: str, 

900 vProp: type = vDDDTypes, # noqa: N803 

901): 

902 """Create a single property getter and setter. 

903 

904 :param prop: The name of the property. 

905 :param value_attr: The name of the attribute to get the value from. 

906 :param value_type: The type of the value. 

907 :param type_def: The type of the property. 

908 :param doc: The docstring of the property. 

909 :param vProp: The type of the property from :mod:`icalendar.prop`. 

910 """ 

911 

912 def p_get(self: Component): 

913 default = object() 

914 result = self.get(prop, default) 

915 if result is default: 

916 return None 

917 if isinstance(result, list): 

918 raise InvalidCalendar(f"Multiple {prop} defined.") 

919 value = result if value_attr is None else getattr(result, value_attr, result) 

920 if not isinstance(value, value_type): 

921 raise InvalidCalendar( 

922 f"{prop} must be either a " 

923 f"{' or '.join(t.__name__ for t in value_type)}," 

924 f" not {value}." 

925 ) 

926 return value 

927 

928 def p_set(self: Component, value) -> None: 

929 if value is None: 

930 p_del(self) 

931 return 

932 if not isinstance(value, value_type): 

933 raise TypeError( 

934 f"Use {' or '.join(t.__name__ for t in value_type)}, " 

935 f"not {type(value).__name__}." 

936 ) 

937 self[prop] = vProp(value) 

938 if prop in self.exclusive: 

939 for other_prop in self.exclusive: 

940 if other_prop != prop: 

941 self.pop(other_prop, None) 

942 

943 p_set.__annotations__["value"] = p_get.__annotations__["return"] = type_def | None 

944 

945 def p_del(self: Component): 

946 self.pop(prop) 

947 

948 p_doc = f"""The {prop} property. 

949 

950 {doc} 

951 

952 Accepted values: {", ".join(t.__name__ for t in value_type)}. 

953 If the attribute has invalid values, we raise InvalidCalendar. 

954 If the value is absent, we return None. 

955 You can also delete the value with del or by setting it to None. 

956 """ 

957 return property(p_get, p_set, p_del, p_doc) 

958 

959 

960X_MOZ_SNOOZE_TIME_property = single_utc_property( 

961 "X-MOZ-SNOOZE-TIME", "Thunderbird: Alarms before this time are snoozed." 

962) 

963X_MOZ_LASTACK_property = single_utc_property( 

964 "X-MOZ-LASTACK", "Thunderbird: Alarms before this time are acknowledged." 

965) 

966 

967 

968def property_get_duration(self: Component) -> timedelta | None: 

969 """Getter for property DURATION.""" 

970 default = object() 

971 duration = self.get("duration", default) 

972 if isinstance(duration, vDDDTypes): 

973 return duration.dt 

974 if isinstance(duration, vDuration): 

975 return duration.td 

976 if duration is not default and not isinstance(duration, timedelta): 

977 raise InvalidCalendar( 

978 f"DURATION must be a timedelta, not {type(duration).__name__}." 

979 ) 

980 return None 

981 

982 

983def property_set_duration(self: Component, value: timedelta | None): 

984 """Setter for property DURATION.""" 

985 if value is None: 

986 self.pop("duration", None) 

987 return 

988 if not isinstance(value, timedelta): 

989 raise TypeError(f"Use timedelta, not {type(value).__name__}.") 

990 self["duration"] = vDuration(value) 

991 self.pop("DTEND") 

992 self.pop("DUE") 

993 

994 

995def property_del_duration(self: Component): 

996 """Delete property DURATION.""" 

997 self.pop("DURATION") 

998 

999 

1000property_doc_duration_template = """The DURATION property. 

1001 

1002The "DTSTART" property for a "{component}" specifies the inclusive 

1003start of the {component}. 

1004The "DURATION" property in conjunction with the DTSTART property 

1005for a "{component}" calendar component specifies the non-inclusive end 

1006of the event. 

1007 

1008If you would like to calculate the duration of a {component}, do not use this. 

1009Instead use the duration property (lower case). 

1010""" 

1011 

1012 

1013def duration_property(component: str) -> property: 

1014 """Return the duration property.""" 

1015 return property( 

1016 property_get_duration, 

1017 property_set_duration, 

1018 property_del_duration, 

1019 property_doc_duration_template.format(component=component), 

1020 ) 

1021 

1022 

1023def multi_text_property(name: str, docs: str) -> property: 

1024 """Get a property that can occur several times and is text. 

1025 

1026 Examples: Journal.descriptions, Event.comments 

1027 """ 

1028 

1029 def fget(self: Component) -> list[str]: 

1030 """Get the values.""" 

1031 descriptions = self.get(name) 

1032 if descriptions is None: 

1033 return [] 

1034 if not isinstance(descriptions, SEQUENCE_TYPES): 

1035 return [descriptions] 

1036 return descriptions 

1037 

1038 def fset(self: Component, values: str | Sequence[str] | None): 

1039 """Set the values.""" 

1040 fdel(self) 

1041 if values is None: 

1042 return 

1043 if isinstance(values, str): 

1044 self.add(name, values) 

1045 else: 

1046 for description in values: 

1047 self.add(name, description) 

1048 

1049 def fdel(self: Component): 

1050 """Delete the values.""" 

1051 self.pop(name) 

1052 

1053 return property(fget, fset, fdel, docs) 

1054 

1055 

1056descriptions_property = multi_text_property( 

1057 "DESCRIPTION", 

1058 """DESCRIPTION provides a more complete description of the calendar component than that provided by the "SUMMARY" property. 

1059 

1060Property Parameters: 

1061 IANA, non-standard, alternate text 

1062 representation, and language property parameters can be specified 

1063 on this property. 

1064 

1065Conformance: 

1066 The property can be 

1067 specified multiple times only within a "VJOURNAL" calendar component. 

1068 

1069Description: 

1070 This property is used in the "VJOURNAL" calendar component to 

1071 capture one or more textual journal entries. 

1072 

1073Examples: 

1074 The following is an example of this property with formatted 

1075 line breaks in the property value: 

1076 

1077 .. code-block:: pycon 

1078 

1079 DESCRIPTION:Meeting to provide technical review for "Phoenix" 

1080 design.\\nHappy Face Conference Room. Phoenix design team 

1081 MUST attend this meeting.\\nRSVP to team leader. 

1082 

1083""", # noqa: E501 

1084) 

1085 

1086comments_property = multi_text_property( 

1087 "COMMENT", 

1088 """COMMENT is used to specify a comment to the calendar user. 

1089 

1090Purpose: 

1091 This property specifies non-processing information intended 

1092 to provide a comment to the calendar user. 

1093 

1094Conformance: 

1095 In :rfc:`5545`, this property can be specified multiple times in 

1096 "VEVENT", "VTODO", "VJOURNAL", and "VFREEBUSY" calendar components 

1097 as well as in the "STANDARD" and "DAYLIGHT" sub-components. 

1098 In :rfc:`7953`, this property can be specified multiple times in 

1099 "VAVAILABILITY" and "VAVAILABLE". 

1100 

1101Property Parameters: 

1102 IANA, non-standard, alternate text 

1103 representation, and language property parameters can be specified 

1104 on this property. 

1105 

1106""", 

1107) 

1108 

1109 

1110def _get_organizer(self: Component) -> vCalAddress | None: 

1111 """ORGANIZER defines the organizer for a calendar component. 

1112 

1113 Property Parameters: 

1114 IANA, non-standard, language, common name, 

1115 directory entry reference, and sent-by property parameters can be 

1116 specified on this property. 

1117 

1118 Conformance: 

1119 This property MUST be specified in an iCalendar object 

1120 that specifies a group-scheduled calendar entity. This property 

1121 MUST be specified in an iCalendar object that specifies the 

1122 publication of a calendar user's busy time. This property MUST 

1123 NOT be specified in an iCalendar object that specifies only a time 

1124 zone definition or that defines calendar components that are not 

1125 group-scheduled components, but are components only on a single 

1126 user's calendar. 

1127 

1128 Description: 

1129 This property is specified within the "VEVENT", 

1130 "VTODO", and "VJOURNAL" calendar components to specify the 

1131 organizer of a group-scheduled calendar entity. The property is 

1132 specified within the "VFREEBUSY" calendar component to specify the 

1133 calendar user requesting the free or busy time. When publishing a 

1134 "VFREEBUSY" calendar component, the property is used to specify 

1135 the calendar that the published busy time came from. 

1136 

1137 The property has the property parameters "CN", for specifying the 

1138 common or display name associated with the "Organizer", "DIR", for 

1139 specifying a pointer to the directory information associated with 

1140 the "Organizer", "SENT-BY", for specifying another calendar user 

1141 that is acting on behalf of the "Organizer". The non-standard 

1142 parameters may also be specified on this property. If the 

1143 "LANGUAGE" property parameter is specified, the identified 

1144 language applies to the "CN" parameter value. 

1145 """ 

1146 return self.get("ORGANIZER") 

1147 

1148 

1149def _set_organizer(self: Component, value: vCalAddress | str | None): 

1150 """Set the value.""" 

1151 _del_organizer(self) 

1152 if value is not None: 

1153 self.add("ORGANIZER", value) 

1154 

1155 

1156def _del_organizer(self: Component): 

1157 """Delete the value.""" 

1158 self.pop("ORGANIZER") 

1159 

1160 

1161organizer_property = property(_get_organizer, _set_organizer, _del_organizer) 

1162 

1163 

1164def single_string_enum_property( 

1165 name: str, enum: type[StrEnum], default: StrEnum, docs: str 

1166) -> property: 

1167 """Create a property to access a single string value and convert it to an enum.""" 

1168 prop = single_string_property(name, docs, default=default) 

1169 

1170 def fget(self: Component) -> StrEnum: 

1171 """Get the value.""" 

1172 value = prop.fget(self) 

1173 if value == default: 

1174 return default 

1175 return enum(str(value)) 

1176 

1177 def fset(self: Component, value: str | StrEnum | None) -> None: 

1178 """Set the value.""" 

1179 if value == "": 

1180 value = None 

1181 prop.fset(self, value) 

1182 

1183 return property(fget, fset, prop.fdel, doc=docs) 

1184 

1185 

1186busy_type_property = single_string_enum_property( 

1187 "BUSYTYPE", 

1188 BUSYTYPE, 

1189 BUSYTYPE.BUSY_UNAVAILABLE, 

1190 """BUSYTYPE specifies the default busy time type. 

1191 

1192Returns: 

1193 :class:`icalendar.enums.BUSYTYPE` 

1194 

1195Description: 

1196 This property is used to specify the default busy time 

1197 type. The values correspond to those used by the FBTYPE" 

1198 parameter used on a "FREEBUSY" property, with the exception that 

1199 the "FREE" value is not used in this property. If not specified 

1200 on a component that allows this property, the default is "BUSY- 

1201 UNAVAILABLE". 

1202""", 

1203) 

1204 

1205priority_property = single_int_property( 

1206 "PRIORITY", 

1207 0, 

1208 """ 

1209 

1210Conformance: 

1211 This property can be specified in "VEVENT" and "VTODO" calendar components 

1212 according to :rfc:`5545`. 

1213 :rfc:`7953` adds this property to "VAVAILABILITY". 

1214 

1215Description: 

1216 This priority is specified as an integer in the range 0 

1217 to 9. A value of 0 specifies an undefined priority. A value of 1 

1218 is the highest priority. A value of 2 is the second highest 

1219 priority. Subsequent numbers specify a decreasing ordinal 

1220 priority. A value of 9 is the lowest priority. 

1221 

1222 A CUA with a three-level priority scheme of "HIGH", "MEDIUM", and 

1223 "LOW" is mapped into this property such that a property value in 

1224 the range of 1 to 4 specifies "HIGH" priority. A value of 5 is 

1225 the normal or "MEDIUM" priority. A value in the range of 6 to 9 

1226 is "LOW" priority. 

1227 

1228 A CUA with a priority schema of "A1", "A2", "A3", "B1", "B2", ..., 

1229 "C3" is mapped into this property such that a property value of 1 

1230 specifies "A1", a property value of 2 specifies "A2", a property 

1231 value of 3 specifies "A3", and so forth up to a property value of 

1232 9 specifies "C3". 

1233 

1234 Other integer values are reserved for future use. 

1235 

1236 Within a "VEVENT" calendar component, this property specifies a 

1237 priority for the event. This property may be useful when more 

1238 than one event is scheduled for a given time period. 

1239 

1240 Within a "VTODO" calendar component, this property specified a 

1241 priority for the to-do. This property is useful in prioritizing 

1242 multiple action items for a given time period. 

1243""", 

1244) 

1245 

1246class_property = single_string_enum_property( 

1247 "CLASS", 

1248 CLASS, 

1249 CLASS.PUBLIC, 

1250 """CLASS specifies the class of the calendar component. 

1251 

1252Returns: 

1253 :class:`icalendar.enums.CLASS` 

1254 

1255Description: 

1256 An access classification is only one component of the 

1257 general security system within a calendar application. It 

1258 provides a method of capturing the scope of the access the 

1259 calendar owner intends for information within an individual 

1260 calendar entry. The access classification of an individual 

1261 iCalendar component is useful when measured along with the other 

1262 security components of a calendar system (e.g., calendar user 

1263 authentication, authorization, access rights, access role, etc.). 

1264 Hence, the semantics of the individual access classifications 

1265 cannot be completely defined by this memo alone. Additionally, 

1266 due to the "blind" nature of most exchange processes using this 

1267 memo, these access classifications cannot serve as an enforcement 

1268 statement for a system receiving an iCalendar object. Rather, 

1269 they provide a method for capturing the intention of the calendar 

1270 owner for the access to the calendar component. If not specified 

1271 in a component that allows this property, the default value is 

1272 PUBLIC. Applications MUST treat x-name and iana-token values they 

1273 don't recognize the same way as they would the PRIVATE value. 

1274""", 

1275) 

1276 

1277transparency_property = single_string_enum_property( 

1278 "TRANSP", 

1279 TRANSP, 

1280 TRANSP.OPAQUE, 

1281 """TRANSP defines whether or not an event is transparent to busy time searches. 

1282 

1283Returns: 

1284 :class:`icalendar.enums.TRANSP` 

1285 

1286Description: 

1287 Time Transparency is the characteristic of an event 

1288 that determines whether it appears to consume time on a calendar. 

1289 Events that consume actual time for the individual or resource 

1290 associated with the calendar SHOULD be recorded as OPAQUE, 

1291 allowing them to be detected by free/busy time searches. Other 

1292 events, which do not take up the individual's (or resource's) time 

1293 SHOULD be recorded as TRANSPARENT, making them invisible to free/ 

1294 busy time searches. 

1295""", 

1296) 

1297status_property = single_string_enum_property( 

1298 "STATUS", 

1299 STATUS, 

1300 "", 

1301 """STATUS defines the overall status or confirmation for the calendar component. 

1302 

1303Returns: 

1304 :class:`icalendar.enums.STATUS` 

1305 

1306The default value is ``""``. 

1307 

1308Description: 

1309 In a group-scheduled calendar component, the property 

1310 is used by the "Organizer" to provide a confirmation of the event 

1311 to the "Attendees". For example in a "VEVENT" calendar component, 

1312 the "Organizer" can indicate that a meeting is tentative, 

1313 confirmed, or cancelled. In a "VTODO" calendar component, the 

1314 "Organizer" can indicate that an action item needs action, is 

1315 completed, is in process or being worked on, or has been 

1316 cancelled. In a "VJOURNAL" calendar component, the "Organizer" 

1317 can indicate that a journal entry is draft, final, or has been 

1318 cancelled or removed. 

1319""", 

1320) 

1321 

1322url_property = single_string_property( 

1323 "URL", 

1324 """A Uniform Resource Locator (URL) associated with the iCalendar object. 

1325 

1326Description: 

1327 This property may be used in a calendar component to 

1328 convey a location where a more dynamic rendition of the calendar 

1329 information associated with the calendar component can be found. 

1330 This memo does not attempt to standardize the form of the URI, nor 

1331 the format of the resource pointed to by the property value. If 

1332 the URL property and Content-Location MIME header are both 

1333 specified, they MUST point to the same resource. 

1334 

1335Conformance: 

1336 This property can be specified once in the "VEVENT", 

1337 "VTODO", "VJOURNAL", or "VFREEBUSY" calendar components. 

1338 Since :rfc:`7986`, this property can also be defined on a "VCALENDAR". 

1339 

1340Example: 

1341 The following is an example of this property: 

1342 

1343 .. code-block:: text 

1344 

1345 URL:http://example.com/pub/calendars/jsmith/mytime.ics 

1346 

1347""", 

1348) 

1349 

1350source_property = single_string_property( 

1351 "SOURCE", 

1352 """A URI from where calendar data can be refreshed. 

1353 

1354Description: 

1355 This property identifies a location where a client can 

1356 retrieve updated data for the calendar. Clients SHOULD honor any 

1357 specified "REFRESH-INTERVAL" value when periodically retrieving 

1358 data. Note that this property differs from the "URL" property in 

1359 that "URL" is meant to provide an alternative representation of 

1360 the calendar data rather than the original location of the data. 

1361 

1362Conformance: 

1363 This property can be specified once in an iCalendar object. 

1364 

1365Example: 

1366 The following is an example of this property: 

1367 

1368 .. code-block:: text 

1369 

1370 SOURCE;VALUE=URI:https://example.com/holidays.ics 

1371 

1372""", 

1373) 

1374 

1375location_property = multi_language_text_property( 

1376 "LOCATION", 

1377 None, 

1378 """The intended venue for the activity defined by a calendar component. 

1379 

1380Property Parameters: 

1381 IANA, non-standard, alternate text 

1382 representation, and language property parameters can be specified 

1383 on this property. 

1384 

1385Conformance: 

1386 Since :rfc:`5545`, this property can be specified in "VEVENT" or "VTODO" 

1387 calendar component. 

1388 :rfc:`7953` adds this property to "VAVAILABILITY" and "VAVAILABLE". 

1389 

1390Description: 

1391 Specific venues such as conference or meeting rooms may 

1392 be explicitly specified using this property. An alternate 

1393 representation may be specified that is a URI that points to 

1394 directory information with more structured specification of the 

1395 location. For example, the alternate representation may specify 

1396 either an LDAP URL :rfc:`4516` pointing to an LDAP server entry or a 

1397 CID URL :rfc:`2392` pointing to a MIME body part containing a 

1398 Virtual-Information Card (vCard) :rfc:`2426` for the location. 

1399 

1400""", 

1401) 

1402 

1403contacts_property = multi_text_property( 

1404 "CONTACT", 

1405 """Contact information associated with the calendar component. 

1406 

1407Purpose: 

1408 This property is used to represent contact information or 

1409 alternately a reference to contact information associated with the 

1410 calendar component. 

1411 

1412Property Parameters: 

1413 IANA, non-standard, alternate text 

1414 representation, and language property parameters can be specified 

1415 on this property. 

1416 

1417Conformance: 

1418 In :rfc:`5545`, this property can be specified in a "VEVENT", "VTODO", 

1419 "VJOURNAL", or "VFREEBUSY" calendar component. 

1420 In :rfc:`7953`, this property can be specified in a "VAVAILABILITY" 

1421 amd "VAVAILABLE" calendar component. 

1422 

1423Description: 

1424 The property value consists of textual contact 

1425 information. An alternative representation for the property value 

1426 can also be specified that refers to a URI pointing to an 

1427 alternate form, such as a vCard :rfc:`2426`, for the contact 

1428 information. 

1429 

1430Example: 

1431 The following is an example of this property referencing 

1432 textual contact information: 

1433 

1434 .. code-block:: text 

1435 

1436 CONTACT:Jim Dolittle\\, ABC Industries\\, +1-919-555-1234 

1437 

1438 The following is an example of this property with an alternate 

1439 representation of an LDAP URI to a directory entry containing the 

1440 contact information: 

1441 

1442 .. code-block:: text 

1443 

1444 CONTACT;ALTREP="ldap://example.com:6666/o=ABC%20Industries\\, 

1445 c=US???(cn=Jim%20Dolittle)":Jim Dolittle\\, ABC Industries\\, 

1446 +1-919-555-1234 

1447 

1448 The following is an example of this property with an alternate 

1449 representation of a MIME body part containing the contact 

1450 information, such as a vCard :rfc:`2426` embedded in a text/ 

1451 directory media type :rfc:`2425`: 

1452 

1453 .. code-block:: text 

1454 

1455 CONTACT;ALTREP="CID:part3.msg970930T083000SILVER@example.com": 

1456 Jim Dolittle\\, ABC Industries\\, +1-919-555-1234 

1457 

1458 The following is an example of this property referencing a network 

1459 resource, such as a vCard :rfc:`2426` object containing the contact 

1460 information: 

1461 

1462 .. code-block:: text 

1463 

1464 CONTACT;ALTREP="http://example.com/pdi/jdoe.vcf":Jim 

1465 Dolittle\\, ABC Industries\\, +1-919-555-1234 

1466""", 

1467) 

1468 

1469 

1470def timezone_datetime_property(name: str, docs: str): 

1471 """Create a property to access the values with a proper timezone.""" 

1472 

1473 return single_utc_property(name, docs) 

1474 

1475 

1476rfc_7953_dtstart_property = timezone_datetime_property( 

1477 "DTSTART", 

1478 """Start of the component. 

1479 

1480 This is almost the same as :attr:`Event.DTSTART` with one exception: 

1481 The values MUST have a timezone and DATE is not allowed. 

1482 

1483 Description: 

1484 :rfc:`7953`: If specified, the "DTSTART" and "DTEND" properties in 

1485 "VAVAILABILITY" components and "AVAILABLE" subcomponents MUST be 

1486 "DATE-TIME" values specified as either the date with UTC time or 

1487 the date with local time and a time zone reference. 

1488 

1489 """, 

1490) 

1491 

1492rfc_7953_dtend_property = timezone_datetime_property( 

1493 "DTEND", 

1494 """Start of the component. 

1495 

1496 This is almost the same as :attr:`Event.DTEND` with one exception: 

1497 The values MUST have a timezone and DATE is not allowed. 

1498 

1499 Description: 

1500 :rfc:`7953`: If specified, the "DTSTART" and "DTEND" properties in 

1501 "VAVAILABILITY" components and "AVAILABLE" subcomponents MUST be 

1502 "DATE-TIME" values specified as either the date with UTC time or 

1503 the date with local time and a time zone reference. 

1504 """, 

1505) 

1506 

1507 

1508@property 

1509def rfc_7953_duration_property(self) -> timedelta | None: 

1510 """Compute the duration of this component. 

1511 

1512 If there is no :attr:`DTEND` or :attr:`DURATION` set, this is None. 

1513 Otherwise, the duration is calculated from :attr:`DTSTART` and 

1514 :attr:`DTEND`/:attr:`DURATION`. 

1515 

1516 This is in accordance with :rfc:`7953`: 

1517 If "DTEND" or "DURATION" are not present, then the end time is unbounded. 

1518 """ 

1519 duration = self.DURATION 

1520 if duration: 

1521 return duration 

1522 end = self.DTEND 

1523 if end is None: 

1524 return None 

1525 start = self.DTSTART 

1526 if start is None: 

1527 raise IncompleteComponent("Cannot compute duration without start.") 

1528 return end - start 

1529 

1530 

1531@property 

1532def rfc_7953_end_property(self) -> timedelta | None: 

1533 """Compute the duration of this component. 

1534 

1535 If there is no :attr:`DTEND` or :attr:`DURATION` set, this is None. 

1536 Otherwise, the duration is calculated from :attr:`DTSTART` and 

1537 :attr:`DTEND`/:attr:`DURATION`. 

1538 

1539 This is in accordance with :rfc:`7953`: 

1540 If "DTEND" or "DURATION" are not present, then the end time is unbounded. 

1541 """ 

1542 duration = self.DURATION 

1543 if duration: 

1544 start = self.DTSTART 

1545 if start is None: 

1546 raise IncompleteComponent("Cannot compute end without start.") 

1547 return start + duration 

1548 end = self.DTEND 

1549 if end is None: 

1550 return None 

1551 return end 

1552 

1553 

1554@rfc_7953_end_property.setter 

1555def rfc_7953_end_property(self, value: datetime): 

1556 self.DTEND = value 

1557 

1558 

1559@rfc_7953_end_property.deleter 

1560def rfc_7953_end_property(self): 

1561 del self.DTEND 

1562 

1563 

1564def get_start_end_duration_with_validation( 

1565 component: Component, 

1566 start_property: str, 

1567 end_property: str, 

1568 component_name: str, 

1569) -> tuple[date | datetime | None, date | datetime | None, timedelta | None]: 

1570 """ 

1571 Validate the component and return start, end, and duration. 

1572 

1573 This tests validity according to :rfc:`5545` rules 

1574 for ``Event`` and ``Todo`` components. 

1575 

1576 Parameters: 

1577 component: The component to validate, either ``Event`` or ``Todo``. 

1578 start_property: The start property name, ``DTSTART``. 

1579 end_property: The end property name, either ``DTEND`` for ``Event`` or 

1580 ``DUE`` for ``Todo``. 

1581 component_name: The component name for error messages, 

1582 either ``VEVENT`` or ``VTODO``. 

1583 

1584 Returns: 

1585 tuple: (start, end, duration) values from the component. 

1586 

1587 Raises: 

1588 ~error.InvalidCalendar: If the component violates RFC 5545 constraints. 

1589 

1590 """ 

1591 start = getattr(component, start_property, None) 

1592 end = getattr(component, end_property, None) 

1593 duration = component.DURATION 

1594 

1595 # RFC 5545: Only one of end property and DURATION may be present 

1596 if duration is not None and end is not None: 

1597 end_name = "DTEND" if end_property == "DTEND" else "DUE" 

1598 msg = ( 

1599 f"Only one of {end_name} and DURATION " 

1600 f"may be in a {component_name}, not both." 

1601 ) 

1602 raise InvalidCalendar(msg) 

1603 

1604 # RFC 5545: When DTSTART is a date, DURATION must be of days or weeks 

1605 if ( 

1606 start is not None 

1607 and is_date(start) 

1608 and duration is not None 

1609 and duration.seconds != 0 

1610 ): 

1611 msg = "When DTSTART is a date, DURATION must be of days or weeks." 

1612 raise InvalidCalendar(msg) 

1613 

1614 # RFC 5545: DTSTART and end property must be of the same type 

1615 if start is not None and end is not None and is_date(start) != is_date(end): 

1616 end_name = "DTEND" if end_property == "DTEND" else "DUE" 

1617 msg = ( 

1618 f"DTSTART and {end_name} must be of the same type, either date or datetime." 

1619 ) 

1620 raise InvalidCalendar(msg) 

1621 

1622 return start, end, duration 

1623 

1624 

1625def get_start_property(component: Component) -> date | datetime: 

1626 """ 

1627 Get the start property with validation. 

1628 

1629 Parameters: 

1630 component: The component from which to get its start property. 

1631 

1632 Returns: 

1633 The ``DTSTART`` value. 

1634 

1635 Raises: 

1636 ~error.IncompleteComponent: If no ``DTSTART`` is present. 

1637 

1638 """ 

1639 # Trigger validation by calling _get_start_end_duration 

1640 start, _end, _duration = component._get_start_end_duration() # noqa: SLF001 

1641 if start is None: 

1642 msg = "No DTSTART given." 

1643 raise IncompleteComponent(msg) 

1644 return start 

1645 

1646 

1647def get_end_property(component: Component, end_property: str) -> date | datetime: 

1648 """ 

1649 Get the end property with fallback logic for ``Event`` and ``Todo`` components. 

1650 

1651 Parameters: 

1652 component: The component to get end from 

1653 end_property: The end property name, either ``DTEND`` for ``Event`` or 

1654 ``DUE`` for ``Todo``. 

1655 

1656 Returns: 

1657 The computed end value. 

1658 

1659 Raises: 

1660 ~error.IncompleteComponent: If the provided information is incomplete 

1661 to compute the end property. 

1662 

1663 """ 

1664 # Trigger validation by calling _get_start_end_duration 

1665 start, end, duration = component._get_start_end_duration() # noqa: SLF001 

1666 

1667 if end is None and duration is None: 

1668 if start is None: 

1669 end_name = "DTEND" if end_property == "DTEND" else "DUE" 

1670 msg = f"No {end_name} or DURATION+DTSTART given." 

1671 raise IncompleteComponent(msg) 

1672 

1673 # Default behavior differs for Event vs Todo: 

1674 # Event: date gets +1 day, datetime gets same time 

1675 # Todo: both date and datetime get same time (issue #898) 

1676 if end_property == "DTEND" and is_date(start): 

1677 return start + timedelta(days=1) 

1678 return start 

1679 

1680 if duration is not None: 

1681 if start is not None: 

1682 return start + duration 

1683 end_name = "DTEND" if end_property == "DTEND" else "DUE" 

1684 msg = f"No {end_name} or DURATION+DTSTART given." 

1685 raise IncompleteComponent(msg) 

1686 

1687 return end 

1688 

1689 

1690def get_duration_property(component: Component) -> timedelta: 

1691 """ 

1692 Get the duration property with fallback calculation from start and end. 

1693 

1694 Parameters: 

1695 component: The component from which to get its duration property. 

1696 

1697 Returns: 

1698 The duration as a timedelta. 

1699 

1700 """ 

1701 # First check if DURATION property is explicitly set 

1702 if "DURATION" in component: 

1703 return component["DURATION"].dt 

1704 

1705 # Fall back to calculated duration from start and end 

1706 return component.end - component.start 

1707 

1708 

1709def set_duration_with_locking( 

1710 component: Component, 

1711 duration: timedelta | None, 

1712 locked: Literal["start", "end"], 

1713 end_property: str, 

1714) -> None: 

1715 """ 

1716 Set the duration with explicit locking behavior for ``Event`` and ``Todo``. 

1717 

1718 Parameters: 

1719 component: The component to modify, either ``Event`` or ``Todo``. 

1720 duration: The duration to set, or ``None`` to convert to ``DURATION`` property. 

1721 locked: Which property to keep unchanged, either ``start`` or ``end``. 

1722 end_property: The end property name, either ``DTEND`` for ``Event`` or 

1723 ``DUE`` for ``Todo``. 

1724 

1725 """ 

1726 # Convert to DURATION property if duration is None 

1727 if duration is None: 

1728 if "DURATION" in component: 

1729 return # Already has DURATION property 

1730 current_duration = component.duration 

1731 component.DURATION = current_duration 

1732 return 

1733 

1734 if not isinstance(duration, timedelta): 

1735 msg = f"Use timedelta, not {type(duration).__name__}." 

1736 raise TypeError(msg) 

1737 

1738 # Validate date/duration compatibility 

1739 start = component.DTSTART 

1740 if start is not None and is_date(start) and duration.seconds != 0: 

1741 msg = "When DTSTART is a date, DURATION must be of days or weeks." 

1742 raise InvalidCalendar(msg) 

1743 

1744 if locked == "start": 

1745 # Keep start locked, adjust end 

1746 if start is None: 

1747 msg = "Cannot set duration without DTSTART. Set start time first." 

1748 raise IncompleteComponent(msg) 

1749 component.pop(end_property, None) # Remove end property 

1750 component.DURATION = duration 

1751 elif locked == "end": 

1752 # Keep end locked, adjust start 

1753 current_end = component.end 

1754 component.DTSTART = current_end - duration 

1755 component.pop(end_property, None) # Remove end property 

1756 component.DURATION = duration 

1757 else: 

1758 msg = f"locked must be 'start' or 'end', not {locked!r}" 

1759 raise ValueError(msg) 

1760 

1761 

1762def set_start_with_locking( 

1763 component: Component, 

1764 start: date | datetime, 

1765 locked: Literal["duration", "end"] | None, 

1766 end_property: str, 

1767) -> None: 

1768 """ 

1769 Set the start with explicit locking behavior for ``Event`` and ``Todo`` components. 

1770 

1771 Parameters: 

1772 component: The component to modify, either ``Event`` or ``Todo``. 

1773 start: The start time to set. 

1774 locked: Which property to keep unchanged, either ``duration``, ``end``, 

1775 or ``None`` for auto-detect. 

1776 end_property: The end property name, either ``DTEND`` for ``Event`` or 

1777 ``DUE`` for ``Todo``. 

1778 

1779 """ 

1780 if locked is None: 

1781 # Auto-detect based on existing properties 

1782 if "DURATION" in component: 

1783 locked = "duration" 

1784 elif end_property in component: 

1785 locked = "end" 

1786 else: 

1787 # Default to duration if no existing properties 

1788 locked = "duration" 

1789 

1790 if locked == "duration": 

1791 # Keep duration locked, adjust end 

1792 current_duration = ( 

1793 component.duration 

1794 if "DURATION" in component or end_property in component 

1795 else None 

1796 ) 

1797 component.DTSTART = start 

1798 if current_duration is not None: 

1799 component.pop(end_property, None) # Remove end property 

1800 component.DURATION = current_duration 

1801 elif locked == "end": 

1802 # Keep end locked, adjust duration 

1803 current_end = component.end 

1804 component.DTSTART = start 

1805 component.pop("DURATION", None) # Remove duration property 

1806 setattr(component, end_property, current_end) 

1807 else: 

1808 msg = f"locked must be 'duration', 'end', or None, not {locked!r}" 

1809 raise ValueError(msg) 

1810 

1811 

1812def set_end_with_locking( 

1813 component: Component, 

1814 end: date | datetime, 

1815 locked: Literal["start", "duration"], 

1816 end_property: str, 

1817) -> None: 

1818 """ 

1819 Set the end with explicit locking behavior for Event and Todo components. 

1820 

1821 Parameters: 

1822 component: The component to modify, either ``Event`` or ``Todo``. 

1823 end: The end time to set. 

1824 locked: Which property to keep unchanged, either ``start`` or ``duration``. 

1825 end_property: The end property name, either ``DTEND`` for ``Event`` or ``DUE`` 

1826 for ``Todo``. 

1827 

1828 """ 

1829 if locked == "start": 

1830 # Keep start locked, adjust duration 

1831 component.pop("DURATION", None) # Remove duration property 

1832 setattr(component, end_property, end) 

1833 elif locked == "duration": 

1834 # Keep duration locked, adjust start 

1835 current_duration = component.duration 

1836 component.DTSTART = end - current_duration 

1837 component.pop(end_property, None) # Remove end property 

1838 component.DURATION = current_duration 

1839 else: 

1840 msg = f"locked must be 'start' or 'duration', not {locked!r}" 

1841 raise ValueError(msg) 

1842 

1843 

1844def _get_images(self: Component) -> list[Image]: 

1845 """IMAGE specifies an image associated with the calendar or a calendar component. 

1846 

1847 Description: 

1848 This property specifies an image for an iCalendar 

1849 object or a calendar component via a URI or directly with inline 

1850 data that can be used by calendar user agents when presenting the 

1851 calendar data to a user. Multiple properties MAY be used to 

1852 specify alternative sets of images with, for example, varying 

1853 media subtypes, resolutions, or sizes. When multiple properties 

1854 are present, calendar user agents SHOULD display only one of them, 

1855 picking one that provides the most appropriate image quality, or 

1856 display none. The "DISPLAY" parameter is used to indicate the 

1857 intended display mode for the image. The "ALTREP" parameter, 

1858 defined in :rfc:`5545`, can be used to provide a "clickable" image 

1859 where the URI in the parameter value can be "launched" by a click 

1860 on the image in the calendar user agent. 

1861 

1862 Conformance: 

1863 This property can be specified multiple times in an 

1864 iCalendar object or in "VEVENT", "VTODO", or "VJOURNAL" calendar 

1865 components. 

1866 

1867 .. note:: 

1868 

1869 At the present moment, this property is read-only. If you require a setter, 

1870 please open an issue or a pull request. 

1871 """ 

1872 images = self.get("IMAGE", []) 

1873 if not isinstance(images, SEQUENCE_TYPES): 

1874 images = [images] 

1875 return [Image.from_property_value(img) for img in images] 

1876 

1877 

1878images_property = property(_get_images) 

1879 

1880 

1881def _get_conferences(self: Component) -> list[Conference]: 

1882 """Return the CONFERENCE properties as a list. 

1883 

1884 Purpose: 

1885 This property specifies information for accessing a conferencing system. 

1886 

1887 Conformance: 

1888 This property can be specified multiple times in a 

1889 "VEVENT" or "VTODO" calendar component. 

1890 

1891 Description: 

1892 This property specifies information for accessing a 

1893 conferencing system for attendees of a meeting or task. This 

1894 might be for a telephone-based conference number dial-in with 

1895 access codes included (such as a tel: URI :rfc:`3966` or a sip: or 

1896 sips: URI :rfc:`3261`), for a web-based video chat (such as an http: 

1897 or https: URI :rfc:`7230`), or for an instant messaging group chat 

1898 room (such as an xmpp: URI :rfc:`5122`). If a specific URI for a 

1899 conferencing system is not available, a data: URI :rfc:`2397` 

1900 containing a text description can be used. 

1901 

1902 A conference system can be a bidirectional communication channel 

1903 or a uni-directional "broadcast feed". 

1904 

1905 The "FEATURE" property parameter is used to describe the key 

1906 capabilities of the conference system to allow a client to choose 

1907 the ones that give the required level of interaction from a set of 

1908 multiple properties. 

1909 

1910 The "LABEL" property parameter is used to convey additional 

1911 details on the use of the URI. For example, the URIs or access 

1912 codes for the moderator and attendee of a teleconference system 

1913 could be different, and the "LABEL" property parameter could be 

1914 used to "tag" each "CONFERENCE" property to indicate which is 

1915 which. 

1916 

1917 The "LANGUAGE" property parameter can be used to specify the 

1918 language used for text values used with this property (as per 

1919 Section 3.2.10 of :rfc:`5545`). 

1920 

1921 Example: 

1922 The following are examples of this property: 

1923 

1924 .. code-block:: text 

1925 

1926 CONFERENCE;VALUE=URI;FEATURE=PHONE,MODERATOR; 

1927 LABEL=Moderator dial-in:tel:+1-412-555-0123,,,654321 

1928 CONFERENCE;VALUE=URI;FEATURE=PHONE; 

1929 LABEL=Attendee dial-in:tel:+1-412-555-0123,,,555123 

1930 CONFERENCE;VALUE=URI;FEATURE=PHONE; 

1931 LABEL=Attendee dial-in:tel:+1-888-555-0456,,,555123 

1932 CONFERENCE;VALUE=URI;FEATURE=CHAT; 

1933 LABEL=Chat room:xmpp:chat-123@conference.example.com 

1934 CONFERENCE;VALUE=URI;FEATURE=AUDIO,VIDEO; 

1935 LABEL=Attendee dial-in:https://chat.example.com/audio?id=123456 

1936 

1937 Get all conferences: 

1938 

1939 .. code-block:: pycon 

1940 

1941 >>> from icalendar import Event 

1942 >>> event = Event() 

1943 >>> event.conferences 

1944 [] 

1945 

1946 Set a conference: 

1947 

1948 .. code-block:: pycon 

1949 

1950 >>> from icalendar import Event, Conference 

1951 >>> event = Event() 

1952 >>> event.conferences = [ 

1953 ... Conference( 

1954 ... "tel:+1-412-555-0123,,,654321", 

1955 ... feature="PHONE,MODERATOR", 

1956 ... label="Moderator dial-in", 

1957 ... language="EN", 

1958 ... ) 

1959 ... ] 

1960 >>> print(event.to_ical()) 

1961 BEGIN:VEVENT 

1962 CONFERENCE;FEATURE="PHONE,MODERATOR";LABEL=Moderator dial-in;LANGUAGE=EN;V 

1963 ALUE=URI:tel:+1-412-555-0123,,,654321 

1964 END:VEVENT 

1965 

1966 """ 

1967 conferences = self.get("CONFERENCE", []) 

1968 if not isinstance(conferences, SEQUENCE_TYPES): 

1969 conferences = [conferences] 

1970 return [Conference.from_uri(conference) for conference in conferences] 

1971 

1972 

1973def _set_conferences(self: Component, conferences: list[Conference] | None): 

1974 """Set the conferences.""" 

1975 _del_conferences(self) 

1976 for conference in conferences or []: 

1977 self.add("CONFERENCE", conference.to_uri()) 

1978 

1979 

1980def _del_conferences(self: Component): 

1981 """Delete all conferences.""" 

1982 self.pop("CONFERENCE") 

1983 

1984 

1985conferences_property = property(_get_conferences, _set_conferences, _del_conferences) 

1986 

1987 

1988def _get_links(self: Component) -> list[vUri | vUid | vXmlReference]: 

1989 """LINK properties as a list. 

1990 

1991 Purpose: 

1992 LINK provides a reference to external information related to a component. 

1993 

1994 Property Parameters: 

1995 The VALUE parameter is required. 

1996 Non-standard, link relation type, format type, label, and language parameters 

1997 can also be specified on this property. 

1998 The LABEL parameter is defined in :rfc:`7986`. 

1999 

2000 Conformance: 

2001 This property can be specified zero or more times in any iCalendar component. 

2002 LINK is specified in :rfc:`9253`. 

2003 The LINKREL parameter is required. 

2004 

2005 Description: 

2006 When used in a component, the value of this property points to 

2007 additional information related to the component. 

2008 For example, it may reference the originating web server. 

2009 

2010 This property is a serialization of the model in :rfc:`8288`, 

2011 where the link target is carried in the property value, 

2012 the link context is the containing calendar entity, 

2013 and the link relation type and any target attributes 

2014 are carried in iCalendar property parameters. 

2015 

2016 The LINK property parameters map to :rfc:`8288` attributes as follows: 

2017 

2018 LABEL 

2019 This parameter maps to the "title" 

2020 attribute defined in Section 3.4.1 of :rfc:`8288`. 

2021 LABEL is used to label the destination 

2022 of a link such that it can be used as a human-readable identifier 

2023 (e.g., a menu entry) in the language indicated by the LANGUAGE 

2024 (if present). 

2025 LANGUAGE 

2026 This parameter maps to the "hreflang" attribute defined in Section 3.4.1 

2027 of :rfc:`8288`. See :rfc:`5646`. Example: ``en``, ``de-ch``. 

2028 LINKREL 

2029 This parameter maps to the link relation type defined in Section 2.1 of 

2030 :rfc:`8288`. See `Registered Link Relation Types 

2031 <https://www.iana.org/assignments/link-relations/link-relations.xhtml>`_. 

2032 FMTTYPE 

2033 This parameter maps to the "type" attribute defined in Section 3.4.1 of 

2034 :rfc:`8288`. 

2035 

2036 There is no mapping for "title*", "anchor", "rev", or "media" :rfc:`8288`. 

2037 

2038 Examples: 

2039 The following is an example of this property, 

2040 which provides a reference to the source for the calendar object. 

2041 

2042 .. code-block:: text 

2043 

2044 LINK;LINKREL=SOURCE;LABEL=Venue;VALUE=URI: 

2045 https://example.com/events 

2046 

2047 The following is an example of this property, 

2048 which provides a reference to an entity from which this one was derived. 

2049 The link relation is a vendor-defined value. 

2050 

2051 .. code-block:: text 

2052 

2053 LINK;LINKREL="https://example.com/linkrel/derivedFrom"; 

2054 VALUE=URI: 

2055 https://example.com/tasks/01234567-abcd1234.ics 

2056 

2057 The following is an example of this property, 

2058 which provides a reference to a fragment of an XML document. 

2059 The link relation is a vendor-defined value. 

2060 

2061 .. code-block:: text 

2062 

2063 LINK;LINKREL="https://example.com/linkrel/costStructure"; 

2064 VALUE=XML-REFERENCE: 

2065 https://example.com/xmlDocs/bidFramework.xml 

2066 #xpointer(descendant::CostStruc/range-to( 

2067 following::CostStrucEND[1])) 

2068 

2069 Set a link :class:`icalendar.vUri` to the event page: 

2070 

2071 .. code-block:: pycon 

2072 

2073 >>> from icalendar import Event, vUri 

2074 >>> from datetime import datetime 

2075 >>> link = vUri( 

2076 ... "http://example.com/event-page", 

2077 ... params={"LINKREL":"SOURCE"} 

2078 ... ) 

2079 >>> event = Event.new( 

2080 ... start=datetime(2025, 9, 17, 12, 0), 

2081 ... summary="An Example Event with a page" 

2082 ... ) 

2083 >>> event.links = [link] 

2084 >>> print(event.to_ical()) 

2085 BEGIN:VEVENT 

2086 SUMMARY:An Example Event with a page 

2087 DTSTART:20250917T120000 

2088 DTSTAMP:20250517T080612Z 

2089 UID:d755cef5-2311-46ed-a0e1-6733c9e15c63 

2090 LINK;LINKREL="SOURCE":http://example.com/event-page 

2091 END:VEVENT 

2092 

2093 """ 

2094 links = self.get("LINK", []) 

2095 if not isinstance(links, list): 

2096 links = [links] 

2097 return links 

2098 

2099 

2100LINKS_TYPE_SETTER: TypeAlias = ( 

2101 str | vUri | vUid | vXmlReference | None | list[str | vUri | vUid | vXmlReference] 

2102) 

2103 

2104 

2105def _set_links(self: Component, links: LINKS_TYPE_SETTER) -> None: 

2106 """Set the LINKs.""" 

2107 _del_links(self) 

2108 if links is None: 

2109 return 

2110 if isinstance(links, (str, vUri, vUid, vXmlReference)): 

2111 links = [links] 

2112 for link in links: 

2113 if type(link) is str: 

2114 link = vUri(link, params={"VALUE": "URI"}) # noqa: PLW2901 

2115 self.add("LINK", link) 

2116 

2117 

2118def _del_links(self: Component) -> None: 

2119 """Delete all links.""" 

2120 self.pop("LINK") 

2121 

2122 

2123links_property = property(_get_links, _set_links, _del_links) 

2124 

2125RELATED_TO_TYPE_SETTER: TypeAlias = ( 

2126 None | str | vText | vUri | vUid | list[str | vText | vUri | vUid] 

2127) 

2128 

2129 

2130def _get_related_to(self: Component) -> list[vText | vUri | vUid]: 

2131 """RELATED-TO properties as a list. 

2132 

2133 Purpose: 

2134 This property is used to represent a relationship or reference 

2135 between one calendar component and another. 

2136 :rfc:`9523` allows URI or UID values and a GAP parameter. 

2137 

2138 Value Type: 

2139 :rfc:`5545`: TEXT 

2140 :rfc:`9253`: URI, UID 

2141 

2142 Conformance: 

2143 Since :rfc:`5545`. this property can be specified in the "VEVENT", 

2144 "VTODO", and "VJOURNAL" calendar components. 

2145 Since :rfc:`9523`, this property MAY be specified in any 

2146 iCalendar component. 

2147 

2148 Description (:rfc:`5545`): 

2149 The property value consists of the persistent, globally 

2150 unique identifier of another calendar component. This value would 

2151 be represented in a calendar component by the "UID" property. 

2152 

2153 By default, the property value points to another calendar 

2154 component that has a PARENT relationship to the referencing 

2155 object. The "RELTYPE" property parameter is used to either 

2156 explicitly state the default PARENT relationship type to the 

2157 referenced calendar component or to override the default PARENT 

2158 relationship type and specify either a CHILD or SIBLING 

2159 relationship. The PARENT relationship indicates that the calendar 

2160 component is a subordinate of the referenced calendar component. 

2161 The CHILD relationship indicates that the calendar component is a 

2162 superior of the referenced calendar component. The SIBLING 

2163 relationship indicates that the calendar component is a peer of 

2164 the referenced calendar component. 

2165 

2166 Changes to a calendar component referenced by this property can 

2167 have an implicit impact on the related calendar component. For 

2168 example, if a group event changes its start or end date or time, 

2169 then the related, dependent events will need to have their start 

2170 and end dates changed in a corresponding way. Similarly, if a 

2171 PARENT calendar component is cancelled or deleted, then there is 

2172 an implied impact to the related CHILD calendar components. This 

2173 property is intended only to provide information on the 

2174 relationship of calendar components. It is up to the target 

2175 calendar system to maintain any property implications of this 

2176 relationship. 

2177 

2178 Description (:rfc:`9253`): 

2179 By default or when VALUE=UID is specified, the property value 

2180 consists of the persistent, globally unique identifier of another 

2181 calendar component. This value would be represented in a calendar 

2182 component by the UID property. 

2183 

2184 By default, the property value 

2185 points to another calendar component that has a PARENT relationship 

2186 to the referencing object. The RELTYPE property parameter is used 

2187 to either explicitly state the default PARENT relationship type to 

2188 the referenced calendar component or to override the default 

2189 PARENT relationship type and specify either a CHILD or SIBLING 

2190 relationship or a temporal relationship. 

2191 

2192 The PARENT relationship 

2193 indicates that the calendar component is a subordinate of the 

2194 referenced calendar component. The CHILD relationship indicates 

2195 that the calendar component is a superior of the referenced calendar 

2196 component. The SIBLING relationship indicates that the calendar 

2197 component is a peer of the referenced calendar component. 

2198 

2199 To preserve backwards compatibility, the value type MUST 

2200 be UID when the PARENT, SIBLING, or CHILD relationships 

2201 are specified. 

2202 

2203 The FINISHTOSTART, FINISHTOFINISH, STARTTOFINISH, 

2204 or STARTTOSTART relationships define temporal relationships, as 

2205 specified in the RELTYPE parameter definition. 

2206 

2207 The FIRST and NEXT 

2208 define ordering relationships between calendar components. 

2209 

2210 The DEPENDS-ON relationship indicates that the current calendar 

2211 component depends on the referenced calendar component in some manner. 

2212 For example, a task may be blocked waiting on the other, 

2213 referenced, task. 

2214 

2215 The REFID and CONCEPT relationships establish 

2216 a reference from the current component to the referenced component. 

2217 Changes to a calendar component referenced by this property 

2218 can have an implicit impact on the related calendar component. 

2219 For example, if a group event changes its start or end date or 

2220 time, then the related, dependent events will need to have their 

2221 start and end dates and times changed in a corresponding way. 

2222 Similarly, if a PARENT calendar component is canceled or deleted, 

2223 then there is an implied impact to the related CHILD calendar 

2224 components. This property is intended only to provide information 

2225 on the relationship of calendar components. 

2226 

2227 Deletion of the target component, for example, the target of a 

2228 FIRST, NEXT, or temporal relationship, can result in broken links. 

2229 

2230 It is up to the target calendar system to maintain any property 

2231 implications of these relationships. 

2232 

2233 Examples: 

2234 :rfc:`5545` examples of this property: 

2235 

2236 .. code-block:: text 

2237 

2238 RELATED-TO:jsmith.part7.19960817T083000.xyzMail@example.com 

2239 

2240 .. code-block:: text 

2241 

2242 RELATED-TO:19960401-080045-4000F192713-0052@example.com 

2243 

2244 :rfc:`9253` examples of this property: 

2245 

2246 .. code-block:: text 

2247 

2248 RELATED-TO;VALUE=URI;RELTYPE=STARTTOFINISH: 

2249 https://example.com/caldav/user/jb/cal/ 

2250 19960401-080045-4000F192713.ics 

2251 

2252 See also :class:`icalendar.enum.RELTYPE`. 

2253 

2254 """ 

2255 result = self.get("RELATED-TO", []) 

2256 if not isinstance(result, list): 

2257 return [result] 

2258 return result 

2259 

2260 

2261def _set_related_to(self: Component, values: RELATED_TO_TYPE_SETTER) -> None: 

2262 """Set the RELATED-TO properties.""" 

2263 _del_related_to(self) 

2264 if values is None: 

2265 return 

2266 if not isinstance(values, list): 

2267 values = [values] 

2268 for value in values: 

2269 self.add("RELATED-TO", value) 

2270 

2271 

2272def _del_related_to(self: Component): 

2273 """Delete the RELATED-TO properties.""" 

2274 self.pop("RELATED-TO", None) 

2275 

2276 

2277related_to_property = property(_get_related_to, _set_related_to, _del_related_to) 

2278 

2279 

2280def _get_concepts(self: Component) -> list[vUri]: 

2281 """CONCEPT 

2282 

2283 Purpose: 

2284 CONCEPT defines the formal categories for a calendar component. 

2285 

2286 Conformance: 

2287 Since :rfc:`9253`, 

2288 this property can be specified zero or more times in any iCalendar component. 

2289 

2290 Description: 

2291 This property is used to specify formal categories or classifications of 

2292 the calendar component. The values are useful in searching for a calendar 

2293 component of a particular type and category. 

2294 

2295 This categorization is distinct from the more informal "tagging" of components 

2296 provided by the existing CATEGORIES property. It is expected that the value of 

2297 the CONCEPT property will reference an external resource that provides 

2298 information about the categorization. 

2299 

2300 In addition, a structured URI value allows for hierarchical categorization of 

2301 events. 

2302 

2303 Possible category resources are the various proprietary systems, for example, 

2304 the Library of Congress, or an open source of categorization data. 

2305 

2306 Examples: 

2307 The following is an example of this property. 

2308 It points to a server acting as the source for the calendar object. 

2309 

2310 .. code-block:: text 

2311 

2312 CONCEPT:https://example.com/event-types/arts/music 

2313 

2314 .. seealso:: 

2315 

2316 :attr:`Component.categories` 

2317 """ 

2318 concepts = self.get("CONCEPT", []) 

2319 if not isinstance(concepts, list): 

2320 concepts = [concepts] 

2321 return concepts 

2322 

2323 

2324CONCEPTS_TYPE_SETTER: TypeAlias = list[vUri | str] | str | vUri | None 

2325 

2326 

2327def _set_concepts(self: Component, concepts: CONCEPTS_TYPE_SETTER): 

2328 """Set the concepts.""" 

2329 _del_concepts(self) 

2330 if concepts is None: 

2331 return 

2332 if not isinstance(concepts, list): 

2333 concepts = [concepts] 

2334 for value in concepts: 

2335 self.add("CONCEPT", value) 

2336 

2337 

2338def _del_concepts(self: Component): 

2339 """Delete the concepts.""" 

2340 self.pop("CONCEPT", None) 

2341 

2342 

2343concepts_property = property(_get_concepts, _set_concepts, _del_concepts) 

2344 

2345 

2346def multi_string_property(name: str, doc: str): 

2347 """A property for an iCalendar Property that can occur multiple times.""" 

2348 

2349 def fget(self: Component) -> list[str]: 

2350 """Get the values of a multi-string property.""" 

2351 value = self.get(name, []) 

2352 if not isinstance(value, list): 

2353 value = [value] 

2354 return value 

2355 

2356 def fset(self: Component, value: list[str] | str | None) -> None: 

2357 """Set the values of a multi-string property.""" 

2358 fdel(self) 

2359 if value is None: 

2360 return 

2361 if not isinstance(value, list): 

2362 value = [value] 

2363 for value in value: 

2364 self.add(name, value) 

2365 

2366 def fdel(self: Component): 

2367 """Delete the values of a multi-string property.""" 

2368 self.pop(name, None) 

2369 

2370 return property(fget, fset, fdel, doc=doc) 

2371 

2372 

2373refids_property = multi_string_property( 

2374 "REFID", 

2375 """REFID 

2376 

2377Purpose: 

2378 REFID acts as a key for associated iCalendar entities. 

2379 

2380Conformance: 

2381 Since :rfc:`9253`, 

2382 this property can be specified zero or more times in any iCalendar component. 

2383 

2384Description: 

2385 The value of this property is free-form text that creates an 

2386 identifier for associated components. 

2387 All components that use the same REFID value are associated through 

2388 that value and can be located or retrieved as a group. 

2389 For example, all of the events in a travel itinerary 

2390 would have the same REFID value, so as to be grouped together. 

2391 

2392Examples: 

2393 The following is an example of this property. 

2394 

2395 .. code-block:: text 

2396 

2397 REFID:itinerary-2014-11-17 

2398 

2399 Use a REFID to associate several VTODOs: 

2400 

2401 .. code-block:: pycon 

2402 

2403 >>> from icalendar import Todo 

2404 >>> todo_1 = Todo.new( 

2405 ... summary="turn off stove", 

2406 ... refids=["travel", "alps"] 

2407 ... ) 

2408 >>> todo_2 = Todo.new( 

2409 ... summary="pack backpack", 

2410 ... refids=["travel", "alps"] 

2411 ... ) 

2412 >>> todo_1.refids == todo_2.refids 

2413 True 

2414 

2415.. note:: 

2416 

2417 List modifications do not modify the component. 

2418""", 

2419) 

2420 

2421 

2422__all__ = [ 

2423 "CONCEPTS_TYPE_SETTER", 

2424 "LINKS_TYPE_SETTER", 

2425 "RELATED_TO_TYPE_SETTER", 

2426 "attendees_property", 

2427 "busy_type_property", 

2428 "categories_property", 

2429 "class_property", 

2430 "color_property", 

2431 "comments_property", 

2432 "concepts_property", 

2433 "conferences_property", 

2434 "contacts_property", 

2435 "create_single_property", 

2436 "description_property", 

2437 "descriptions_property", 

2438 "duration_property", 

2439 "exdates_property", 

2440 "get_duration_property", 

2441 "get_end_property", 

2442 "get_start_end_duration_with_validation", 

2443 "get_start_property", 

2444 "images_property", 

2445 "links_property", 

2446 "location_property", 

2447 "multi_language_text_property", 

2448 "multi_string_property", 

2449 "organizer_property", 

2450 "priority_property", 

2451 "property_del_duration", 

2452 "property_doc_duration_template", 

2453 "property_get_duration", 

2454 "property_set_duration", 

2455 "rdates_property", 

2456 "refids_property", 

2457 "related_to_property", 

2458 "rfc_7953_dtend_property", 

2459 "rfc_7953_dtstart_property", 

2460 "rfc_7953_duration_property", 

2461 "rfc_7953_end_property", 

2462 "rrules_property", 

2463 "sequence_property", 

2464 "set_duration_with_locking", 

2465 "set_end_with_locking", 

2466 "set_start_with_locking", 

2467 "single_int_property", 

2468 "single_utc_property", 

2469 "source_property", 

2470 "status_property", 

2471 "summary_property", 

2472 "transparency_property", 

2473 "uid_property", 

2474 "url_property", 

2475]