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

498 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 Callable, 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 if compatibility_prop is not None: 

361 self.add(compatibility_prop, value) 

362 

363 def fdel(self: Component): 

364 """Delete the property.""" 

365 self.pop(main_prop, None) 

366 if compatibility_prop is not None: 

367 self.pop(compatibility_prop, None) 

368 

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

370 

371 

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

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

374 

375 Parameters: 

376 prop: The name of the property 

377 default: The default value 

378 doc: The documentation string 

379 """ 

380 

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

382 """Get the property""" 

383 try: 

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

385 except ValueError as e: 

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

387 

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

389 """Set the property.""" 

390 fdel(self) 

391 if value is not None: 

392 self.add(prop, value) 

393 

394 def fdel(self: Component): 

395 """Delete the property.""" 

396 self.pop(prop, None) 

397 

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

399 

400 

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

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

403 

404 Parameters: 

405 name: name of the property 

406 docs: documentation string 

407 """ 

408 docs = ( 

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

410 

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

412 """ 

413 + docs 

414 ) 

415 

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

417 """Get the value.""" 

418 if name not in self: 

419 return None 

420 dt = self.get(name) 

421 if isinstance(dt, vText): 

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

423 value = vDDDTypes.from_ical(dt) 

424 else: 

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

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

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

428 return tzp.localize_utc(value) 

429 

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

431 """Set the value""" 

432 if value is None: 

433 fdel(self) 

434 return 

435 if not isinstance(value, date): 

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

437 fdel(self) 

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

439 

440 def fdel(self: Component): 

441 """Delete the property.""" 

442 self.pop(name, None) 

443 

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

445 

446 

447def single_string_property( 

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

449) -> property: 

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

451 

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

453 """Get the value.""" 

454 result = self.get( 

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

456 ) 

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

458 return default 

459 if isinstance(result, list): 

460 return result[0] 

461 return result 

462 

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

464 """Set the value. 

465 

466 Setting the value to None will delete it. 

467 """ 

468 fdel(self) 

469 if value is not None: 

470 self.add(name, value) 

471 

472 def fdel(self: Component): 

473 """Delete the property.""" 

474 self.pop(name, None) 

475 if other_name is not None: 

476 self.pop(other_name, None) 

477 

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

479 

480 

481color_property = single_string_property( 

482 "COLOR", 

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

484 

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

486 

487 Property Parameters: 

488 IANA and non-standard property parameters can 

489 be specified on this property. 

490 

491 Conformance: 

492 This property can be specified once in an iCalendar 

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

494 

495 Description: 

496 This property specifies a color that clients MAY use 

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

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

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

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

501 

502 Example: 

503 ``"turquoise"``, ``"#ffffff"`` 

504 

505 .. code-block:: pycon 

506 

507 >>> from icalendar import Todo 

508 >>> todo = Todo() 

509 >>> todo.color = "green" 

510 >>> print(todo.to_ical()) 

511 BEGIN:VTODO 

512 COLOR:green 

513 END:VTODO 

514 """, 

515) 

516 

517sequence_property = single_int_property( 

518 "SEQUENCE", 

519 0, 

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

521 

522Value Type: 

523 INTEGER 

524 

525Property Parameters: 

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

527 

528Conformance: 

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

530 "VJOURNAL" calendar component. 

531 

532Description: 

533 When a calendar component is created, its sequence 

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

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

536 calendar component. 

537 

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

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

540 calendar component. 

541 

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

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

544 component to which the "Attendee" is referring. 

545 

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

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

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

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

550 requested. 

551 

552 Recurrence instances of a recurring component MAY have different 

553 sequence numbers. 

554 

555Examples: 

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

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

558 

559 .. code-block:: pycon 

560 

561 >>> from icalendar import Event 

562 >>> event = Event() 

563 >>> event.sequence 

564 0 

565 

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

567 component that has been revised 10 different times by the 

568 "Organizer": 

569 

570 .. code-block:: pycon 

571 

572 >>> from icalendar import Calendar 

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

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

575 >>> event.sequence 

576 10 

577 """, # noqa: E501 

578) 

579 

580 

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

582 """Get all the categories.""" 

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

584 if isinstance(categories, list): 

585 _set_categories( 

586 component, 

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

588 ) 

589 return _get_categories(component) 

590 if categories is None: 

591 categories = vCategory([]) 

592 component.add("CATEGORIES", categories) 

593 return categories.cats 

594 

595 

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

597 """Set the categories.""" 

598 if not cats and cats != []: 

599 _del_categories(component) 

600 return 

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

602 if isinstance(cats, list): 

603 cats.clear() 

604 cats.extend(categories.cats) 

605 categories.cats = cats 

606 

607 

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

609 """Delete the categories.""" 

610 component.pop("CATEGORIES", None) 

611 

612 

613categories_property = property( 

614 _get_categories, 

615 _set_categories, 

616 _del_categories, 

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

618 

619Property Parameters: 

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

621 property. 

622 

623Conformance: 

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

625 components. 

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

627 

628Description: 

629 This property is used to specify categories or subtypes 

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

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

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

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

634 of categories. 

635 

636Example: 

637 Below, we add the categories to an event: 

638 

639 .. code-block:: pycon 

640 

641 >>> from icalendar import Event 

642 >>> event = Event() 

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

644 >>> print(event.to_ical()) 

645 BEGIN:VEVENT 

646 CATEGORIES:Work,Meeting 

647 END:VEVENT 

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

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

650 True 

651 

652.. note:: 

653 

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

655 

656.. seealso:: 

657 

658 :attr:`Component.concepts` 

659""", 

660) 

661 

662 

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

664 """Get attendees.""" 

665 value = self.get("ATTENDEE") 

666 if value is None: 

667 value = [] 

668 self["ATTENDEE"] = value 

669 return value 

670 if isinstance(value, vCalAddress): 

671 return [value] 

672 return value 

673 

674 

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

676 """Set attendees.""" 

677 _del_attendees(self) 

678 if value is None: 

679 return 

680 if not isinstance(value, list): 

681 value = [value] 

682 self["ATTENDEE"] = value 

683 

684 

685def _del_attendees(self: Component): 

686 """Delete all attendees.""" 

687 self.pop("ATTENDEE", None) 

688 

689 

690attendees_property = property( 

691 _get_attendees, 

692 _set_attendees, 

693 _del_attendees, 

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

695 

696Conformance: 

697 This property MUST be specified in an iCalendar object 

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

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

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

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

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

703 iCalendar object that specifies only a time zone definition or 

704 that defines calendar components that are not group-scheduled 

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

706 

707Description: 

708 This property MUST only be specified within calendar 

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

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

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

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

713 type of iCalendar alarm. 

714 

715Examples: 

716 Add a new attendee to an existing event. 

717 

718 .. code-block:: pycon 

719 

720 >>> from icalendar import Event, vCalAddress 

721 >>> event = Event() 

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

723 >>> print(event.to_ical()) 

724 BEGIN:VEVENT 

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

726 END:VEVENT 

727 

728 Create an email alarm with several attendees: 

729 

730 >>> from icalendar import Alarm, vCalAddress 

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

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

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

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

735 >>> print(alarm.to_ical()) 

736 BEGIN:VALARM 

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

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

739 SUMMARY:Email alarm 

740 END:VALARM 

741""", 

742) 

743 

744uid_property = single_string_property( 

745 "UID", 

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

747 

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

749 

750Returns: 

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

752 

753Description: 

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

755 The generator of the identifier MUST guarantee that the identifier 

756 is unique. 

757 

758 This is the method for correlating scheduling messages with the 

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

760 The full range of calendar components specified by a recurrence 

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

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

763 property allows the reference to an individual instance within the 

764 recurrence set. 

765 

766 This property is an important method for group-scheduling 

767 applications to match requests with later replies, modifications, 

768 or deletion requests. Calendaring and scheduling applications 

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

770 calendar components to assure interoperability with other group- 

771 scheduling applications. This identifier is created by the 

772 calendar system that generates an iCalendar object. 

773 

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

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

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

777 

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

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

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

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

782 to identify the most recent version of a calendar. 

783 

784Conformance: 

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

786 and "VJOURNAL" calendar components. 

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

788 allow it to be defined in an iCalendar object. 

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

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

791 to refer uniquely to the "VALARM" component. 

792 

793 This property can be specified once only. 

794 

795Security: 

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

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

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

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

800 Universally Unique Identifier (UUID) values as defined in 

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

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

803 

804Compatibility: 

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

806 

807Examples: 

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

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

810 

811 Set the UID of a calendar: 

812 

813 .. code-block:: pycon 

814 

815 >>> from icalendar import Calendar 

816 >>> from uuid import uuid4 

817 >>> calendar = Calendar() 

818 >>> calendar.uid = uuid4() 

819 >>> print(calendar.to_ical()) 

820 BEGIN:VCALENDAR 

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

822 END:VCALENDAR 

823 

824""", 

825) 

826 

827summary_property = multi_language_text_property( 

828 "SUMMARY", 

829 None, 

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

831 

832Property Parameters: 

833 IANA, non-standard, alternate text 

834 representation, and language property parameters can be specified 

835 on this property. 

836 

837Conformance: 

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

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

840 

841Description: 

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

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

844 summary about the activity or journal entry. 

845 

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

847 capture the subject of an EMAIL category of alarm. 

848 

849Examples: 

850 The following is an example of this property: 

851 

852 .. code-block:: pycon 

853 

854 SUMMARY:Department Party 

855""", 

856) 

857 

858description_property = multi_language_text_property( 

859 "DESCRIPTION", 

860 None, 

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

862 

863Property Parameters: 

864 IANA, non-standard, alternate text 

865 representation, and language property parameters can be specified 

866 on this property. 

867 

868Conformance: 

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

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

871 specified multiple times only within a "VJOURNAL" calendar 

872 component. 

873 

874Description: 

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

876 capture lengthy textual descriptions associated with the activity. 

877 

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

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

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

881 

882Examples: 

883 The following is an example of this property with formatted 

884 line breaks in the property value: 

885 

886 .. code-block:: pycon 

887 

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

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

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

891 

892 """, # noqa: E501 

893) 

894 

895 

896def create_single_property( 

897 prop: str, 

898 value_attr: str | None, 

899 value_type: tuple[type], 

900 type_def: type, 

901 doc: str, 

902 vProp: type = vDDDTypes, # noqa: N803 

903 convert: Callable[[object], object] | None = None, 

904): 

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

906 

907 Parameters: 

908 prop: The name of the property. 

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

910 value_type: The type of the value. 

911 type_def: The type of the property. 

912 doc: The docstring of the property. 

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

914 """ 

915 

916 def p_get(self: Component): 

917 default = object() 

918 result = self.get(prop, default) 

919 if result is default: 

920 return None 

921 if isinstance(result, list): 

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

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

924 value = value if convert is None else convert(value) 

925 if not isinstance(value, value_type): 

926 raise InvalidCalendar( 

927 f"{prop} must be either a " 

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

929 f" not {value}." 

930 ) 

931 return value 

932 

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

934 if value is None: 

935 p_del(self) 

936 return 

937 value = convert(value) if convert is not None else value 

938 if not isinstance(value, value_type): 

939 raise TypeError( 

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

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

942 ) 

943 self[prop] = vProp(value) 

944 if prop in self.exclusive: 

945 for other_prop in self.exclusive: 

946 if other_prop != prop: 

947 self.pop(other_prop, None) 

948 

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

950 

951 def p_del(self: Component): 

952 self.pop(prop) 

953 

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

955 

956 {doc} 

957 

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

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

960 If the value is absent, we return None. 

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

962 """ 

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

964 

965 

966X_MOZ_SNOOZE_TIME_property = single_utc_property( 

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

968) 

969X_MOZ_LASTACK_property = single_utc_property( 

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

971) 

972 

973 

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

975 """Getter for property DURATION.""" 

976 default = object() 

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

978 if isinstance(duration, vDDDTypes): 

979 return duration.dt 

980 if isinstance(duration, vDuration): 

981 return duration.td 

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

983 raise InvalidCalendar( 

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

985 ) 

986 return None 

987 

988 

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

990 """Setter for property DURATION.""" 

991 if value is None: 

992 self.pop("duration", None) 

993 return 

994 if not isinstance(value, timedelta): 

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

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

997 self.pop("DTEND") 

998 self.pop("DUE") 

999 

1000 

1001def property_del_duration(self: Component): 

1002 """Delete property DURATION.""" 

1003 self.pop("DURATION") 

1004 

1005 

1006property_doc_duration_template = """The DURATION property. 

1007 

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

1009start of the {component}. 

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

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

1012of the event. 

1013 

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

1015Instead use the duration property (lower case). 

1016""" 

1017 

1018 

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

1020 """Return the duration property.""" 

1021 return property( 

1022 property_get_duration, 

1023 property_set_duration, 

1024 property_del_duration, 

1025 property_doc_duration_template.format(component=component), 

1026 ) 

1027 

1028 

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

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

1031 

1032 Examples: Journal.descriptions, Event.comments 

1033 """ 

1034 

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

1036 """Get the values.""" 

1037 descriptions = self.get(name) 

1038 if descriptions is None: 

1039 return [] 

1040 if not isinstance(descriptions, SEQUENCE_TYPES): 

1041 return [descriptions] 

1042 return descriptions 

1043 

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

1045 """Set the values.""" 

1046 fdel(self) 

1047 if values is None: 

1048 return 

1049 if isinstance(values, str): 

1050 self.add(name, values) 

1051 else: 

1052 for description in values: 

1053 self.add(name, description) 

1054 

1055 def fdel(self: Component): 

1056 """Delete the values.""" 

1057 self.pop(name) 

1058 

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

1060 

1061 

1062descriptions_property = multi_text_property( 

1063 "DESCRIPTION", 

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

1065 

1066Property Parameters: 

1067 IANA, non-standard, alternate text 

1068 representation, and language property parameters can be specified 

1069 on this property. 

1070 

1071Conformance: 

1072 The property can be 

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

1074 

1075Description: 

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

1077 capture one or more textual journal entries. 

1078 

1079Examples: 

1080 The following is an example of this property with formatted 

1081 line breaks in the property value: 

1082 

1083 .. code-block:: pycon 

1084 

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

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

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

1088 

1089""", # noqa: E501 

1090) 

1091 

1092comments_property = multi_text_property( 

1093 "COMMENT", 

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

1095 

1096Purpose: 

1097 This property specifies non-processing information intended 

1098 to provide a comment to the calendar user. 

1099 

1100Conformance: 

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

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

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

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

1105 "VAVAILABILITY" and "VAVAILABLE". 

1106 

1107Property Parameters: 

1108 IANA, non-standard, alternate text 

1109 representation, and language property parameters can be specified 

1110 on this property. 

1111 

1112""", 

1113) 

1114 

1115RECURRENCE_ID = create_single_property( 

1116 "RECURRENCE-ID", 

1117 "dt", 

1118 (date, datetime), 

1119 date | datetime, 

1120 """ 

1121Identify a specific occurrence of a recurring calendar object. 

1122 

1123This property is used together with ``UID`` and ``SEQUENCE`` to refer to one 

1124particular instance in a recurrence set. The value is the original start 

1125date or date-time of that instance, not the rescheduled time. 

1126 

1127The value is usually a DATE-TIME and must use the same value type as the 

1128``DTSTART`` property in the same component. A DATE value may be used for 

1129all-day items instead. 

1130 

1131This property corresponds to ``RECURRENCE-ID`` as defined in RFC 5545 and 

1132may appear in recurring ``VEVENT``, ``VTODO``, and ``VJOURNAL`` components. 

1133""", 

1134 vDDDTypes, 

1135) 

1136 

1137 

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

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

1140 

1141 Property Parameters: 

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

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

1144 specified on this property. 

1145 

1146 Conformance: 

1147 This property MUST be specified in an iCalendar object 

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

1149 MUST be specified in an iCalendar object that specifies the 

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

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

1152 zone definition or that defines calendar components that are not 

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

1154 user's calendar. 

1155 

1156 Description: 

1157 This property is specified within the "VEVENT", 

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

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

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

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

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

1163 the calendar that the published busy time came from. 

1164 

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

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

1167 specifying a pointer to the directory information associated with 

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

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

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

1171 "LANGUAGE" property parameter is specified, the identified 

1172 language applies to the "CN" parameter value. 

1173 """ 

1174 return self.get("ORGANIZER") 

1175 

1176 

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

1178 """Set the value.""" 

1179 _del_organizer(self) 

1180 if value is not None: 

1181 self.add("ORGANIZER", value) 

1182 

1183 

1184def _del_organizer(self: Component): 

1185 """Delete the value.""" 

1186 self.pop("ORGANIZER") 

1187 

1188 

1189organizer_property = property(_get_organizer, _set_organizer, _del_organizer) 

1190 

1191 

1192def single_string_enum_property( 

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

1194) -> property: 

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

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

1197 

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

1199 """Get the value.""" 

1200 value = prop.fget(self) 

1201 if value == default: 

1202 return default 

1203 return enum(str(value)) 

1204 

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

1206 """Set the value.""" 

1207 if value == "": 

1208 value = None 

1209 prop.fset(self, value) 

1210 

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

1212 

1213 

1214busy_type_property = single_string_enum_property( 

1215 "BUSYTYPE", 

1216 BUSYTYPE, 

1217 BUSYTYPE.BUSY_UNAVAILABLE, 

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

1219 

1220Returns: 

1221 :class:`icalendar.enums.BUSYTYPE` 

1222 

1223Description: 

1224 This property is used to specify the default busy time 

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

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

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

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

1229 UNAVAILABLE". 

1230""", 

1231) 

1232 

1233priority_property = single_int_property( 

1234 "PRIORITY", 

1235 0, 

1236 """ 

1237 

1238Conformance: 

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

1240 according to :rfc:`5545`. 

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

1242 

1243Description: 

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

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

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

1247 priority. Subsequent numbers specify a decreasing ordinal 

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

1249 

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

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

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

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

1254 is "LOW" priority. 

1255 

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

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

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

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

1260 9 specifies "C3". 

1261 

1262 Other integer values are reserved for future use. 

1263 

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

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

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

1267 

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

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

1270 multiple action items for a given time period. 

1271""", 

1272) 

1273 

1274class_property = single_string_enum_property( 

1275 "CLASS", 

1276 CLASS, 

1277 CLASS.PUBLIC, 

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

1279 

1280Returns: 

1281 :class:`icalendar.enums.CLASS` 

1282 

1283Description: 

1284 An access classification is only one component of the 

1285 general security system within a calendar application. It 

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

1287 calendar owner intends for information within an individual 

1288 calendar entry. The access classification of an individual 

1289 iCalendar component is useful when measured along with the other 

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

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

1292 Hence, the semantics of the individual access classifications 

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

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

1295 memo, these access classifications cannot serve as an enforcement 

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

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

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

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

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

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

1302""", 

1303) 

1304 

1305transparency_property = single_string_enum_property( 

1306 "TRANSP", 

1307 TRANSP, 

1308 TRANSP.OPAQUE, 

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

1310 

1311Returns: 

1312 :class:`icalendar.enums.TRANSP` 

1313 

1314Description: 

1315 Time Transparency is the characteristic of an event 

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

1317 Events that consume actual time for the individual or resource 

1318 associated with the calendar SHOULD be recorded as OPAQUE, 

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

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

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

1322 busy time searches. 

1323""", 

1324) 

1325status_property = single_string_enum_property( 

1326 "STATUS", 

1327 STATUS, 

1328 "", 

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

1330 

1331Returns: 

1332 :class:`icalendar.enums.STATUS` 

1333 

1334The default value is ``""``. 

1335 

1336Description: 

1337 In a group-scheduled calendar component, the property 

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

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

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

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

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

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

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

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

1346 cancelled or removed. 

1347""", 

1348) 

1349 

1350url_property = single_string_property( 

1351 "URL", 

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

1353 

1354Description: 

1355 This property may be used in a calendar component to 

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

1357 information associated with the calendar component can be found. 

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

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

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

1361 specified, they MUST point to the same resource. 

1362 

1363Conformance: 

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

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

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

1367 

1368Example: 

1369 The following is an example of this property: 

1370 

1371 .. code-block:: ics 

1372 

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

1374 

1375""", 

1376) 

1377 

1378source_property = single_string_property( 

1379 "SOURCE", 

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

1381 

1382Description: 

1383 This property identifies a location where a client can 

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

1385 specified "REFRESH-INTERVAL" value when periodically retrieving 

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

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

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

1389 

1390Conformance: 

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

1392 

1393Example: 

1394 The following is an example of this property: 

1395 

1396 .. code-block:: ics 

1397 

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

1399 

1400""", 

1401) 

1402 

1403location_property = multi_language_text_property( 

1404 "LOCATION", 

1405 None, 

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

1407 

1408Property Parameters: 

1409 IANA, non-standard, alternate text 

1410 representation, and language property parameters can be specified 

1411 on this property. 

1412 

1413Conformance: 

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

1415 calendar component. 

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

1417 

1418Description: 

1419 Specific venues such as conference or meeting rooms may 

1420 be explicitly specified using this property. An alternate 

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

1422 directory information with more structured specification of the 

1423 location. For example, the alternate representation may specify 

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

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

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

1427 

1428""", 

1429) 

1430 

1431contacts_property = multi_text_property( 

1432 "CONTACT", 

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

1434 

1435Purpose: 

1436 This property is used to represent contact information or 

1437 alternately a reference to contact information associated with the 

1438 calendar component. 

1439 

1440Property Parameters: 

1441 IANA, non-standard, alternate text 

1442 representation, and language property parameters can be specified 

1443 on this property. 

1444 

1445Conformance: 

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

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

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

1449 amd "VAVAILABLE" calendar component. 

1450 

1451Description: 

1452 The property value consists of textual contact 

1453 information. An alternative representation for the property value 

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

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

1456 information. 

1457 

1458Example: 

1459 The following is an example of this property referencing 

1460 textual contact information: 

1461 

1462 .. code-block:: ics 

1463 

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

1465 

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

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

1468 contact information: 

1469 

1470 .. code-block:: ics 

1471 

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

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

1474 +1-919-555-1234 

1475 

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

1477 representation of a MIME body part containing the contact 

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

1479 directory media type :rfc:`2425`: 

1480 

1481 .. code-block:: ics 

1482 

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

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

1485 

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

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

1488 information: 

1489 

1490 .. code-block:: ics 

1491 

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

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

1494""", 

1495) 

1496 

1497 

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

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

1500 

1501 return single_utc_property(name, docs) 

1502 

1503 

1504rfc_7953_dtstart_property = timezone_datetime_property( 

1505 "DTSTART", 

1506 """Start of the component. 

1507 

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

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

1510 

1511 Description: 

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

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

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

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

1516 

1517 """, 

1518) 

1519 

1520rfc_7953_dtend_property = timezone_datetime_property( 

1521 "DTEND", 

1522 """Start of the component. 

1523 

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

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

1526 

1527 Description: 

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

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

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

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

1532 """, 

1533) 

1534 

1535 

1536@property 

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

1538 """Compute the duration of this component. 

1539 

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

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

1542 :attr:`DTEND`/:attr:`DURATION`. 

1543 

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

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

1546 """ 

1547 duration = self.DURATION 

1548 if duration: 

1549 return duration 

1550 end = self.DTEND 

1551 if end is None: 

1552 return None 

1553 start = self.DTSTART 

1554 if start is None: 

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

1556 return end - start 

1557 

1558 

1559@property 

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

1561 """Compute the duration of this component. 

1562 

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

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

1565 :attr:`DTEND`/:attr:`DURATION`. 

1566 

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

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

1569 """ 

1570 duration = self.DURATION 

1571 if duration: 

1572 start = self.DTSTART 

1573 if start is None: 

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

1575 return start + duration 

1576 end = self.DTEND 

1577 if end is None: 

1578 return None 

1579 return end 

1580 

1581 

1582@rfc_7953_end_property.setter 

1583def rfc_7953_end_property(self, value: datetime): 

1584 self.DTEND = value 

1585 

1586 

1587@rfc_7953_end_property.deleter 

1588def rfc_7953_end_property(self): 

1589 del self.DTEND 

1590 

1591 

1592def get_start_end_duration_with_validation( 

1593 component: Component, 

1594 start_property: str, 

1595 end_property: str, 

1596 component_name: str, 

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

1598 """ 

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

1600 

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

1602 for ``Event`` and ``Todo`` components. 

1603 

1604 Parameters: 

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

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

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

1608 ``DUE`` for ``Todo``. 

1609 component_name: The component name for error messages, 

1610 either ``VEVENT`` or ``VTODO``. 

1611 

1612 Returns: 

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

1614 

1615 Raises: 

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

1617 

1618 """ 

1619 start = getattr(component, start_property, None) 

1620 end = getattr(component, end_property, None) 

1621 duration = component.DURATION 

1622 

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

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

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

1626 msg = ( 

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

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

1629 ) 

1630 raise InvalidCalendar(msg) 

1631 

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

1633 if ( 

1634 start is not None 

1635 and is_date(start) 

1636 and duration is not None 

1637 and duration.seconds != 0 

1638 ): 

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

1640 raise InvalidCalendar(msg) 

1641 

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

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

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

1645 msg = ( 

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

1647 ) 

1648 raise InvalidCalendar(msg) 

1649 

1650 return start, end, duration 

1651 

1652 

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

1654 """ 

1655 Get the start property with validation. 

1656 

1657 Parameters: 

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

1659 

1660 Returns: 

1661 The ``DTSTART`` value. 

1662 

1663 Raises: 

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

1665 

1666 """ 

1667 # Trigger validation by calling _get_start_end_duration 

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

1669 if start is None: 

1670 msg = "No DTSTART given." 

1671 raise IncompleteComponent(msg) 

1672 return start 

1673 

1674 

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

1676 """ 

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

1678 

1679 Parameters: 

1680 component: The component to get end from 

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

1682 ``DUE`` for ``Todo``. 

1683 

1684 Returns: 

1685 The computed end value. 

1686 

1687 Raises: 

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

1689 to compute the end property. 

1690 

1691 """ 

1692 # Trigger validation by calling _get_start_end_duration 

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

1694 

1695 if end is None and duration is None: 

1696 if start is None: 

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

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

1699 raise IncompleteComponent(msg) 

1700 

1701 # Default behavior differs for Event vs Todo: 

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

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

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

1705 return start + timedelta(days=1) 

1706 return start 

1707 

1708 if duration is not None: 

1709 if start is not None: 

1710 if component.name == "VEVENT" and duration.total_seconds() <= 0: 

1711 return start 

1712 return start + duration 

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

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

1715 raise IncompleteComponent(msg) 

1716 

1717 return end 

1718 

1719 

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

1721 """ 

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

1723 

1724 Parameters: 

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

1726 

1727 Returns: 

1728 The duration as a timedelta. 

1729 

1730 """ 

1731 # First check if DURATION property is explicitly set 

1732 if "DURATION" in component: 

1733 return component["DURATION"].dt 

1734 

1735 # Fall back to calculated duration from start and end 

1736 return component.end - component.start 

1737 

1738 

1739def set_duration_with_locking( 

1740 component: Component, 

1741 duration: timedelta | None, 

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

1743 end_property: str, 

1744) -> None: 

1745 """ 

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

1747 

1748 Parameters: 

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

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

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

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

1753 ``DUE`` for ``Todo``. 

1754 

1755 """ 

1756 # Convert to DURATION property if duration is None 

1757 if duration is None: 

1758 if "DURATION" in component: 

1759 return # Already has DURATION property 

1760 current_duration = component.duration 

1761 component.DURATION = current_duration 

1762 return 

1763 

1764 if not isinstance(duration, timedelta): 

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

1766 raise TypeError(msg) 

1767 

1768 # Validate date/duration compatibility 

1769 start = component.DTSTART 

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

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

1772 raise InvalidCalendar(msg) 

1773 

1774 if locked == "start": 

1775 # Keep start locked, adjust end 

1776 if start is None: 

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

1778 raise IncompleteComponent(msg) 

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

1780 component.DURATION = duration 

1781 elif locked == "end": 

1782 # Keep end locked, adjust start 

1783 current_end = component.end 

1784 component.DTSTART = current_end - duration 

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

1786 component.DURATION = duration 

1787 else: 

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

1789 raise ValueError(msg) 

1790 

1791 

1792def set_start_with_locking( 

1793 component: Component, 

1794 start: date | datetime, 

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

1796 end_property: str, 

1797) -> None: 

1798 """ 

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

1800 

1801 Parameters: 

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

1803 start: The start time to set. 

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

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

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

1807 ``DUE`` for ``Todo``. 

1808 

1809 """ 

1810 if locked is None: 

1811 # Auto-detect based on existing properties 

1812 if "DURATION" in component: 

1813 locked = "duration" 

1814 elif end_property in component: 

1815 locked = "end" 

1816 else: 

1817 # Default to duration if no existing properties 

1818 locked = "duration" 

1819 

1820 if locked == "duration": 

1821 # Keep duration locked, adjust end 

1822 current_duration = ( 

1823 component.duration 

1824 if "DURATION" in component or end_property in component 

1825 else None 

1826 ) 

1827 component.DTSTART = start 

1828 if current_duration is not None: 

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

1830 component.DURATION = current_duration 

1831 elif locked == "end": 

1832 # Keep end locked, adjust duration 

1833 current_end = component.end 

1834 component.DTSTART = start 

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

1836 setattr(component, end_property, current_end) 

1837 else: 

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

1839 raise ValueError(msg) 

1840 

1841 

1842def set_end_with_locking( 

1843 component: Component, 

1844 end: date | datetime, 

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

1846 end_property: str, 

1847) -> None: 

1848 """ 

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

1850 

1851 Parameters: 

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

1853 end: The end time to set. 

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

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

1856 for ``Todo``. 

1857 

1858 """ 

1859 if locked == "start": 

1860 # Keep start locked, adjust duration 

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

1862 setattr(component, end_property, end) 

1863 elif locked == "duration": 

1864 # Keep duration locked, adjust start 

1865 current_duration = component.duration 

1866 component.DTSTART = end - current_duration 

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

1868 component.DURATION = current_duration 

1869 else: 

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

1871 raise ValueError(msg) 

1872 

1873 

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

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

1876 

1877 Description: 

1878 This property specifies an image for an iCalendar 

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

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

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

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

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

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

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

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

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

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

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

1890 on the image in the calendar user agent. 

1891 

1892 Conformance: 

1893 This property can be specified multiple times in an 

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

1895 components. 

1896 

1897 .. note:: 

1898 

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

1900 please open an issue or a pull request. 

1901 """ 

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

1903 if not isinstance(images, SEQUENCE_TYPES): 

1904 images = [images] 

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

1906 

1907 

1908images_property = property(_get_images) 

1909 

1910 

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

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

1913 

1914 Purpose: 

1915 This property specifies information for accessing a conferencing system. 

1916 

1917 Conformance: 

1918 This property can be specified multiple times in a 

1919 "VEVENT" or "VTODO" calendar component. 

1920 

1921 Description: 

1922 This property specifies information for accessing a 

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

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

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

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

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

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

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

1930 containing a text description can be used. 

1931 

1932 A conference system can be a bidirectional communication channel 

1933 or a uni-directional "broadcast feed". 

1934 

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

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

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

1938 multiple properties. 

1939 

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

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

1942 codes for the moderator and attendee of a teleconference system 

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

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

1945 which. 

1946 

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

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

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

1950 

1951 Example: 

1952 The following are examples of this property: 

1953 

1954 .. code-block:: ics 

1955 

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

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

1958 CONFERENCE;VALUE=URI;FEATURE=PHONE; 

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

1960 CONFERENCE;VALUE=URI;FEATURE=PHONE; 

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

1962 CONFERENCE;VALUE=URI;FEATURE=CHAT; 

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

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

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

1966 

1967 Get all conferences: 

1968 

1969 .. code-block:: pycon 

1970 

1971 >>> from icalendar import Event 

1972 >>> event = Event() 

1973 >>> event.conferences 

1974 [] 

1975 

1976 Set a conference: 

1977 

1978 .. code-block:: pycon 

1979 

1980 >>> from icalendar import Event, Conference 

1981 >>> event = Event() 

1982 >>> event.conferences = [ 

1983 ... Conference( 

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

1985 ... feature="PHONE,MODERATOR", 

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

1987 ... language="EN", 

1988 ... ) 

1989 ... ] 

1990 >>> print(event.to_ical()) 

1991 BEGIN:VEVENT 

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

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

1994 END:VEVENT 

1995 

1996 """ 

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

1998 if not isinstance(conferences, SEQUENCE_TYPES): 

1999 conferences = [conferences] 

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

2001 

2002 

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

2004 """Set the conferences.""" 

2005 _del_conferences(self) 

2006 for conference in conferences or []: 

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

2008 

2009 

2010def _del_conferences(self: Component): 

2011 """Delete all conferences.""" 

2012 self.pop("CONFERENCE") 

2013 

2014 

2015conferences_property = property(_get_conferences, _set_conferences, _del_conferences) 

2016 

2017 

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

2019 """LINK properties as a list. 

2020 

2021 Purpose: 

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

2023 

2024 Property Parameters: 

2025 The VALUE parameter is required. 

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

2027 can also be specified on this property. 

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

2029 

2030 Conformance: 

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

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

2033 The LINKREL parameter is required. 

2034 

2035 Description: 

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

2037 additional information related to the component. 

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

2039 

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

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

2042 the link context is the containing calendar entity, 

2043 and the link relation type and any target attributes 

2044 are carried in iCalendar property parameters. 

2045 

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

2047 

2048 LABEL 

2049 This parameter maps to the "title" 

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

2051 LABEL is used to label the destination 

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

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

2054 (if present). 

2055 LANGUAGE 

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

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

2058 LINKREL 

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

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

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

2062 FMTTYPE 

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

2064 :rfc:`8288`. 

2065 

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

2067 

2068 Examples: 

2069 The following is an example of this property, 

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

2071 

2072 .. code-block:: ics 

2073 

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

2075 https://example.com/events 

2076 

2077 The following is an example of this property, 

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

2079 The link relation is a vendor-defined value. 

2080 

2081 .. code-block:: ics 

2082 

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

2084 VALUE=URI: 

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

2086 

2087 The following is an example of this property, 

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

2089 The link relation is a vendor-defined value. 

2090 

2091 .. code-block:: ics 

2092 

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

2094 VALUE=XML-REFERENCE: 

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

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

2097 following::CostStrucEND[1])) 

2098 

2099 Set a link :class:`icalendar.prop.uri.vUri` to the event page: 

2100 

2101 .. code-block:: pycon 

2102 

2103 >>> from icalendar import Event, vUri 

2104 >>> from datetime import datetime 

2105 >>> link = vUri( 

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

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

2108 ... ) 

2109 >>> event = Event.new( 

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

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

2112 ... ) 

2113 >>> event.links = [link] 

2114 >>> print(event.to_ical()) 

2115 BEGIN:VEVENT 

2116 SUMMARY:An Example Event with a page 

2117 DTSTART:20250917T120000 

2118 DTSTAMP:20250517T080612Z 

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

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

2121 END:VEVENT 

2122 

2123 """ 

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

2125 if not isinstance(links, list): 

2126 links = [links] 

2127 return links 

2128 

2129 

2130LINKS_TYPE_SETTER: TypeAlias = ( 

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

2132) 

2133 

2134 

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

2136 """Set the LINKs.""" 

2137 _del_links(self) 

2138 if links is None: 

2139 return 

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

2141 links = [links] 

2142 for link in links: 

2143 if type(link) is str: 

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

2145 self.add("LINK", link) 

2146 

2147 

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

2149 """Delete all links.""" 

2150 self.pop("LINK") 

2151 

2152 

2153links_property = property(_get_links, _set_links, _del_links) 

2154 

2155RELATED_TO_TYPE_SETTER: TypeAlias = ( 

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

2157) 

2158 

2159 

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

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

2162 

2163 Purpose: 

2164 This property is used to represent a relationship or reference 

2165 between one calendar component and another. 

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

2167 

2168 Value Type: 

2169 :rfc:`5545`: TEXT 

2170 :rfc:`9253`: URI, UID 

2171 

2172 Conformance: 

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

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

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

2176 iCalendar component. 

2177 

2178 Description (:rfc:`5545`): 

2179 The property value consists of the persistent, globally 

2180 unique identifier of another calendar component. This value would 

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

2182 

2183 By default, the property value points to another calendar 

2184 component that has a PARENT relationship to the referencing 

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

2186 explicitly state the default PARENT relationship type to the 

2187 referenced calendar component or to override the default PARENT 

2188 relationship type and specify either a CHILD or SIBLING 

2189 relationship. The PARENT relationship indicates that the calendar 

2190 component is a subordinate of the referenced calendar component. 

2191 The CHILD relationship indicates that the calendar component is a 

2192 superior of the referenced calendar component. The SIBLING 

2193 relationship indicates that the calendar component is a peer of 

2194 the referenced calendar component. 

2195 

2196 Changes to a calendar component referenced by this property can 

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

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

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

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

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

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

2203 property is intended only to provide information on the 

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

2205 calendar system to maintain any property implications of this 

2206 relationship. 

2207 

2208 Description (:rfc:`9253`): 

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

2210 consists of the persistent, globally unique identifier of another 

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

2212 component by the UID property. 

2213 

2214 By default, the property value 

2215 points to another calendar component that has a PARENT relationship 

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

2217 to either explicitly state the default PARENT relationship type to 

2218 the referenced calendar component or to override the default 

2219 PARENT relationship type and specify either a CHILD or SIBLING 

2220 relationship or a temporal relationship. 

2221 

2222 The PARENT relationship 

2223 indicates that the calendar component is a subordinate of the 

2224 referenced calendar component. The CHILD relationship indicates 

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

2226 component. The SIBLING relationship indicates that the calendar 

2227 component is a peer of the referenced calendar component. 

2228 

2229 To preserve backwards compatibility, the value type MUST 

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

2231 are specified. 

2232 

2233 The FINISHTOSTART, FINISHTOFINISH, STARTTOFINISH, 

2234 or STARTTOSTART relationships define temporal relationships, as 

2235 specified in the RELTYPE parameter definition. 

2236 

2237 The FIRST and NEXT 

2238 define ordering relationships between calendar components. 

2239 

2240 The DEPENDS-ON relationship indicates that the current calendar 

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

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

2243 referenced, task. 

2244 

2245 The REFID and CONCEPT relationships establish 

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

2247 Changes to a calendar component referenced by this property 

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

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

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

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

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

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

2254 components. This property is intended only to provide information 

2255 on the relationship of calendar components. 

2256 

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

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

2259 

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

2261 implications of these relationships. 

2262 

2263 Examples: 

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

2265 

2266 .. code-block:: ics 

2267 

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

2269 

2270 .. code-block:: ics 

2271 

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

2273 

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

2275 

2276 .. code-block:: ics 

2277 

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

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

2280 19960401-080045-4000F192713.ics 

2281 

2282 See also :class:`icalendar.enums.RELTYPE`. 

2283 

2284 """ 

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

2286 if not isinstance(result, list): 

2287 return [result] 

2288 return result 

2289 

2290 

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

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

2293 _del_related_to(self) 

2294 if values is None: 

2295 return 

2296 if not isinstance(values, list): 

2297 values = [values] 

2298 for value in values: 

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

2300 

2301 

2302def _del_related_to(self: Component): 

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

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

2305 

2306 

2307related_to_property = property(_get_related_to, _set_related_to, _del_related_to) 

2308 

2309 

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

2311 """CONCEPT 

2312 

2313 Purpose: 

2314 CONCEPT defines the formal categories for a calendar component. 

2315 

2316 Conformance: 

2317 Since :rfc:`9253`, 

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

2319 

2320 Description: 

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

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

2323 component of a particular type and category. 

2324 

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

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

2327 the CONCEPT property will reference an external resource that provides 

2328 information about the categorization. 

2329 

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

2331 events. 

2332 

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

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

2335 

2336 Examples: 

2337 The following is an example of this property. 

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

2339 

2340 .. code-block:: ics 

2341 

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

2343 

2344 .. seealso:: 

2345 

2346 :attr:`icalendar.prop.categories.vCategory` 

2347 """ 

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

2349 if not isinstance(concepts, list): 

2350 concepts = [concepts] 

2351 return concepts 

2352 

2353 

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

2355 

2356 

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

2358 """Set the concepts.""" 

2359 _del_concepts(self) 

2360 if concepts is None: 

2361 return 

2362 if not isinstance(concepts, list): 

2363 concepts = [concepts] 

2364 for value in concepts: 

2365 self.add("CONCEPT", value) 

2366 

2367 

2368def _del_concepts(self: Component): 

2369 """Delete the concepts.""" 

2370 self.pop("CONCEPT", None) 

2371 

2372 

2373concepts_property = property(_get_concepts, _set_concepts, _del_concepts) 

2374 

2375 

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

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

2378 

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

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

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

2382 if not isinstance(value, list): 

2383 value = [value] 

2384 return value 

2385 

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

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

2388 fdel(self) 

2389 if value is None: 

2390 return 

2391 if not isinstance(value, list): 

2392 value = [value] 

2393 for value in value: 

2394 self.add(name, value) 

2395 

2396 def fdel(self: Component): 

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

2398 self.pop(name, None) 

2399 

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

2401 

2402 

2403refids_property = multi_string_property( 

2404 "REFID", 

2405 """REFID 

2406 

2407Purpose: 

2408 REFID acts as a key for associated iCalendar entities. 

2409 

2410Conformance: 

2411 Since :rfc:`9253`, 

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

2413 

2414Description: 

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

2416 identifier for associated components. 

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

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

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

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

2421 

2422Examples: 

2423 The following is an example of this property. 

2424 

2425 .. code-block:: ics 

2426 

2427 REFID:itinerary-2014-11-17 

2428 

2429 Use a REFID to associate several VTODOs: 

2430 

2431 .. code-block:: pycon 

2432 

2433 >>> from icalendar import Todo 

2434 >>> todo_1 = Todo.new( 

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

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

2437 ... ) 

2438 >>> todo_2 = Todo.new( 

2439 ... summary="pack backpack", 

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

2441 ... ) 

2442 >>> todo_1.refids == todo_2.refids 

2443 True 

2444 

2445.. note:: 

2446 

2447 List modifications do not modify the component. 

2448""", 

2449) 

2450 

2451 

2452__all__ = [ 

2453 "CONCEPTS_TYPE_SETTER", 

2454 "LINKS_TYPE_SETTER", 

2455 "RECURRENCE_ID", 

2456 "RELATED_TO_TYPE_SETTER", 

2457 "attendees_property", 

2458 "busy_type_property", 

2459 "categories_property", 

2460 "class_property", 

2461 "color_property", 

2462 "comments_property", 

2463 "concepts_property", 

2464 "conferences_property", 

2465 "contacts_property", 

2466 "create_single_property", 

2467 "description_property", 

2468 "descriptions_property", 

2469 "duration_property", 

2470 "exdates_property", 

2471 "get_duration_property", 

2472 "get_end_property", 

2473 "get_start_end_duration_with_validation", 

2474 "get_start_property", 

2475 "images_property", 

2476 "links_property", 

2477 "location_property", 

2478 "multi_language_text_property", 

2479 "multi_string_property", 

2480 "organizer_property", 

2481 "priority_property", 

2482 "property_del_duration", 

2483 "property_doc_duration_template", 

2484 "property_get_duration", 

2485 "property_set_duration", 

2486 "rdates_property", 

2487 "refids_property", 

2488 "related_to_property", 

2489 "rfc_7953_dtend_property", 

2490 "rfc_7953_dtstart_property", 

2491 "rfc_7953_duration_property", 

2492 "rfc_7953_end_property", 

2493 "rrules_property", 

2494 "sequence_property", 

2495 "set_duration_with_locking", 

2496 "set_end_with_locking", 

2497 "set_start_with_locking", 

2498 "single_int_property", 

2499 "single_utc_property", 

2500 "source_property", 

2501 "status_property", 

2502 "summary_property", 

2503 "transparency_property", 

2504 "uid_property", 

2505 "url_property", 

2506]