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

497 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 

619The categories property is used to specify categories or subtypes of the 

620calendar component. The categories are useful to search for a calendar 

621component of a particular type and category. 

622 

623Within the calendar components, specify categories as a list of strings. 

624You can get, set, and delete categories for a component. 

625 

626This property can be used in icalendar through its Python attributes of: 

627 

628- :attr:`Calendar.categories <icalendar.cal.calendar.Calendar.categories>` 

629- :attr:`Event.categories <icalendar.cal.event.Event.categories>` 

630- :attr:`Journal.categories <icalendar.cal.journal.Journal.categories>` 

631- :attr:`Todo.categories <icalendar.cal.todo.Todo.categories>` 

632 

633The categories property for ``Event``, ``Journal``, and ``Todo`` complies 

634with :rfc:`5545#section-3.8.1.2`, and for ``Calendar`` with :rfc:`7986#section-5.6`. 

635 

636Note: 

637 At present, icalendar doesn't take the LANGUAGE parameter as defined 

638 in :rfc:`5545#section-3.2.10` into account. 

639 

640Parameters: 

641 categories(list[str]): A list of categories as strings. 

642 

643Example: 

644 Create an event, add categories to it, print its ical representation, 

645 append another category, and finally compare the result 

646 against its expected value. 

647 

648 .. code-block:: pycon 

649 

650 >>> from icalendar import Event 

651 >>> event = Event() 

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

653 >>> print(event.to_ical()) 

654 BEGIN:VEVENT 

655 CATEGORIES:Work,Meeting 

656 END:VEVENT 

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

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

659 True 

660 

661See also: 

662 :attr:`Component.concepts <icalendar.cal.component.Component.concepts>` 

663""", 

664) 

665 

666 

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

668 """Get attendees.""" 

669 value = self.get("ATTENDEE") 

670 if value is None: 

671 value = [] 

672 self["ATTENDEE"] = value 

673 return value 

674 if isinstance(value, vCalAddress): 

675 return [value] 

676 return value 

677 

678 

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

680 """Set attendees.""" 

681 _del_attendees(self) 

682 if value is None: 

683 return 

684 if not isinstance(value, list): 

685 value = [value] 

686 self["ATTENDEE"] = value 

687 

688 

689def _del_attendees(self: Component): 

690 """Delete all attendees.""" 

691 self.pop("ATTENDEE", None) 

692 

693 

694attendees_property = property( 

695 _get_attendees, 

696 _set_attendees, 

697 _del_attendees, 

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

699 

700Conformance: 

701 This property MUST be specified in an iCalendar object 

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

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

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

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

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

707 iCalendar object that specifies only a time zone definition or 

708 that defines calendar components that are not group-scheduled 

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

710 

711Description: 

712 This property MUST only be specified within calendar 

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

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

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

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

717 type of iCalendar alarm. 

718 

719Examples: 

720 Add a new attendee to an existing event. 

721 

722 .. code-block:: pycon 

723 

724 >>> from icalendar import Event, vCalAddress 

725 >>> event = Event() 

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

727 >>> print(event.to_ical()) 

728 BEGIN:VEVENT 

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

730 END:VEVENT 

731 

732 Create an email alarm with several attendees: 

733 

734 >>> from icalendar import Alarm, vCalAddress 

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

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

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

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

739 >>> print(alarm.to_ical()) 

740 BEGIN:VALARM 

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

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

743 SUMMARY:Email alarm 

744 END:VALARM 

745""", 

746) 

747 

748uid_property = single_string_property( 

749 "UID", 

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

751 

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

753 

754Returns: 

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

756 

757Description: 

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

759 The generator of the identifier MUST guarantee that the identifier 

760 is unique. 

761 

762 This is the method for correlating scheduling messages with the 

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

764 The full range of calendar components specified by a recurrence 

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

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

767 property allows the reference to an individual instance within the 

768 recurrence set. 

769 

770 This property is an important method for group-scheduling 

771 applications to match requests with later replies, modifications, 

772 or deletion requests. Calendaring and scheduling applications 

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

774 calendar components to assure interoperability with other group- 

775 scheduling applications. This identifier is created by the 

776 calendar system that generates an iCalendar object. 

777 

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

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

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

781 

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

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

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

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

786 to identify the most recent version of a calendar. 

787 

788Conformance: 

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

790 and "VJOURNAL" calendar components. 

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

792 allow it to be defined in an iCalendar object. 

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

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

795 to refer uniquely to the "VALARM" component. 

796 

797 This property can be specified once only. 

798 

799Security: 

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

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

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

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

804 Universally Unique Identifier (UUID) values as defined in 

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

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

807 

808Compatibility: 

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

810 

811Examples: 

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

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

814 

815 Set the UID of a calendar: 

816 

817 .. code-block:: pycon 

818 

819 >>> from icalendar import Calendar 

820 >>> from uuid import uuid4 

821 >>> calendar = Calendar() 

822 >>> calendar.uid = uuid4() 

823 >>> print(calendar.to_ical()) 

824 BEGIN:VCALENDAR 

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

826 END:VCALENDAR 

827 

828""", 

829) 

830 

831summary_property = multi_language_text_property( 

832 "SUMMARY", 

833 None, 

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

835 

836Property Parameters: 

837 IANA, non-standard, alternate text 

838 representation, and language property parameters can be specified 

839 on this property. 

840 

841Conformance: 

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

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

844 

845Description: 

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

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

848 summary about the activity or journal entry. 

849 

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

851 capture the subject of an EMAIL category of alarm. 

852 

853Examples: 

854 The following is an example of this property: 

855 

856 .. code-block:: pycon 

857 

858 SUMMARY:Department Party 

859""", 

860) 

861 

862description_property = multi_language_text_property( 

863 "DESCRIPTION", 

864 None, 

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

866 

867Property Parameters: 

868 IANA, non-standard, alternate text 

869 representation, and language property parameters can be specified 

870 on this property. 

871 

872Conformance: 

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

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

875 specified multiple times only within a "VJOURNAL" calendar 

876 component. 

877 

878Description: 

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

880 capture lengthy textual descriptions associated with the activity. 

881 

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

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

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

885 

886Examples: 

887 The following is an example of this property with formatted 

888 line breaks in the property value: 

889 

890 .. code-block:: pycon 

891 

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

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

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

895 

896 """, # noqa: E501 

897) 

898 

899 

900def create_single_property( 

901 prop: str, 

902 value_attr: str | None, 

903 value_type: tuple[type], 

904 type_def: type, 

905 doc: str, 

906 vProp: type = vDDDTypes, # noqa: N803 

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

908): 

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

910 

911 Parameters: 

912 prop: The name of the property. 

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

914 value_type: The type of the value. 

915 type_def: The type of the property. 

916 doc: The docstring of the property. 

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

918 """ 

919 

920 def p_get(self: Component): 

921 default = object() 

922 result = self.get(prop, default) 

923 if result is default: 

924 return None 

925 if isinstance(result, list): 

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

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

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

929 if not isinstance(value, value_type): 

930 raise InvalidCalendar( 

931 f"{prop} must be either a " 

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

933 f" not {value}." 

934 ) 

935 return value 

936 

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

938 if value is None: 

939 p_del(self) 

940 return 

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

942 if not isinstance(value, value_type): 

943 raise TypeError( 

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

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

946 ) 

947 self[prop] = vProp(value) 

948 if prop in self.exclusive: 

949 for other_prop in self.exclusive: 

950 if other_prop != prop: 

951 self.pop(other_prop, None) 

952 

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

954 

955 def p_del(self: Component): 

956 self.pop(prop) 

957 

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

959 

960 {doc} 

961 

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

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

964 If the value is absent, we return None. 

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

966 """ 

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

968 

969 

970X_MOZ_SNOOZE_TIME_property = single_utc_property( 

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

972) 

973X_MOZ_LASTACK_property = single_utc_property( 

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

975) 

976 

977 

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

979 """Getter for property DURATION.""" 

980 default = object() 

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

982 if duration is default: 

983 return None 

984 result = getattr(duration, "td", None) 

985 if result is None: 

986 raise InvalidCalendar( 

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

988 ) 

989 return result 

990 

991 

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

993 """Setter for property DURATION.""" 

994 if value is None: 

995 self.pop("duration", None) 

996 return 

997 if not isinstance(value, timedelta): 

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

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

1000 self.pop("DTEND") 

1001 self.pop("DUE") 

1002 

1003 

1004def property_del_duration(self: Component): 

1005 """Delete property DURATION.""" 

1006 self.pop("DURATION") 

1007 

1008 

1009property_doc_duration_template = """The DURATION property. 

1010 

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

1012start of the {component}. 

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

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

1015of the event. 

1016 

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

1018Instead use the duration property (lower case). 

1019""" 

1020 

1021 

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

1023 """Return the duration property.""" 

1024 return property( 

1025 property_get_duration, 

1026 property_set_duration, 

1027 property_del_duration, 

1028 property_doc_duration_template.format(component=component), 

1029 ) 

1030 

1031 

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

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

1034 

1035 Examples: Journal.descriptions, Event.comments 

1036 """ 

1037 

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

1039 """Get the values.""" 

1040 descriptions = self.get(name) 

1041 if descriptions is None: 

1042 return [] 

1043 if not isinstance(descriptions, SEQUENCE_TYPES): 

1044 return [descriptions] 

1045 return descriptions 

1046 

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

1048 """Set the values.""" 

1049 fdel(self) 

1050 if values is None: 

1051 return 

1052 if isinstance(values, str): 

1053 self.add(name, values) 

1054 else: 

1055 for description in values: 

1056 self.add(name, description) 

1057 

1058 def fdel(self: Component): 

1059 """Delete the values.""" 

1060 self.pop(name) 

1061 

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

1063 

1064 

1065descriptions_property = multi_text_property( 

1066 "DESCRIPTION", 

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

1068 

1069Property Parameters: 

1070 IANA, non-standard, alternate text 

1071 representation, and language property parameters can be specified 

1072 on this property. 

1073 

1074Conformance: 

1075 The property can be 

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

1077 

1078Description: 

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

1080 capture one or more textual journal entries. 

1081 

1082Examples: 

1083 The following is an example of this property with formatted 

1084 line breaks in the property value: 

1085 

1086 .. code-block:: pycon 

1087 

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

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

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

1091 

1092""", # noqa: E501 

1093) 

1094 

1095comments_property = multi_text_property( 

1096 "COMMENT", 

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

1098 

1099Purpose: 

1100 This property specifies non-processing information intended 

1101 to provide a comment to the calendar user. 

1102 

1103Conformance: 

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

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

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

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

1108 "VAVAILABILITY" and "VAVAILABLE". 

1109 

1110Property Parameters: 

1111 IANA, non-standard, alternate text 

1112 representation, and language property parameters can be specified 

1113 on this property. 

1114 

1115""", 

1116) 

1117 

1118RECURRENCE_ID = create_single_property( 

1119 "RECURRENCE-ID", 

1120 "dt", 

1121 (date, datetime), 

1122 date | datetime, 

1123 """ 

1124Identify a specific occurrence of a recurring calendar object. 

1125 

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

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

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

1129 

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

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

1132all-day items instead. 

1133 

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

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

1136""", 

1137 vDDDTypes, 

1138) 

1139 

1140 

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

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

1143 

1144 Property Parameters: 

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

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

1147 specified on this property. 

1148 

1149 Conformance: 

1150 This property MUST be specified in an iCalendar object 

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

1152 MUST be specified in an iCalendar object that specifies the 

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

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

1155 zone definition or that defines calendar components that are not 

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

1157 user's calendar. 

1158 

1159 Description: 

1160 This property is specified within the "VEVENT", 

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

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

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

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

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

1166 the calendar that the published busy time came from. 

1167 

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

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

1170 specifying a pointer to the directory information associated with 

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

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

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

1174 "LANGUAGE" property parameter is specified, the identified 

1175 language applies to the "CN" parameter value. 

1176 """ 

1177 return self.get("ORGANIZER") 

1178 

1179 

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

1181 """Set the value.""" 

1182 _del_organizer(self) 

1183 if value is not None: 

1184 self.add("ORGANIZER", value) 

1185 

1186 

1187def _del_organizer(self: Component): 

1188 """Delete the value.""" 

1189 self.pop("ORGANIZER") 

1190 

1191 

1192organizer_property = property(_get_organizer, _set_organizer, _del_organizer) 

1193 

1194 

1195def single_string_enum_property( 

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

1197) -> property: 

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

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

1200 

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

1202 """Get the value.""" 

1203 value = prop.fget(self) 

1204 if value == default: 

1205 return default 

1206 return enum(str(value)) 

1207 

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

1209 """Set the value.""" 

1210 if value == "": 

1211 value = None 

1212 prop.fset(self, value) 

1213 

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

1215 

1216 

1217busy_type_property = single_string_enum_property( 

1218 "BUSYTYPE", 

1219 BUSYTYPE, 

1220 BUSYTYPE.BUSY_UNAVAILABLE, 

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

1222 

1223Returns: 

1224 :class:`icalendar.enums.BUSYTYPE` 

1225 

1226Description: 

1227 This property is used to specify the default busy time 

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

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

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

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

1232 UNAVAILABLE". 

1233""", 

1234) 

1235 

1236priority_property = single_int_property( 

1237 "PRIORITY", 

1238 0, 

1239 """ 

1240 

1241Conformance: 

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

1243 according to :rfc:`5545`. 

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

1245 

1246Description: 

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

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

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

1250 priority. Subsequent numbers specify a decreasing ordinal 

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

1252 

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

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

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

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

1257 is "LOW" priority. 

1258 

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

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

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

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

1263 9 specifies "C3". 

1264 

1265 Other integer values are reserved for future use. 

1266 

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

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

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

1270 

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

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

1273 multiple action items for a given time period. 

1274""", 

1275) 

1276 

1277class_property = single_string_enum_property( 

1278 "CLASS", 

1279 CLASS, 

1280 CLASS.PUBLIC, 

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

1282 

1283Returns: 

1284 :class:`icalendar.enums.CLASS` 

1285 

1286Description: 

1287 An access classification is only one component of the 

1288 general security system within a calendar application. It 

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

1290 calendar owner intends for information within an individual 

1291 calendar entry. The access classification of an individual 

1292 iCalendar component is useful when measured along with the other 

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

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

1295 Hence, the semantics of the individual access classifications 

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

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

1298 memo, these access classifications cannot serve as an enforcement 

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

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

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

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

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

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

1305""", 

1306) 

1307 

1308transparency_property = single_string_enum_property( 

1309 "TRANSP", 

1310 TRANSP, 

1311 TRANSP.OPAQUE, 

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

1313 

1314Returns: 

1315 :class:`icalendar.enums.TRANSP` 

1316 

1317Description: 

1318 Time Transparency is the characteristic of an event 

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

1320 Events that consume actual time for the individual or resource 

1321 associated with the calendar SHOULD be recorded as OPAQUE, 

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

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

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

1325 busy time searches. 

1326""", 

1327) 

1328status_property = single_string_enum_property( 

1329 "STATUS", 

1330 STATUS, 

1331 "", 

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

1333 

1334Returns: 

1335 :class:`icalendar.enums.STATUS` 

1336 

1337The default value is ``""``. 

1338 

1339Description: 

1340 In a group-scheduled calendar component, the property 

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

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

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

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

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

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

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

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

1349 cancelled or removed. 

1350""", 

1351) 

1352 

1353url_property = single_string_property( 

1354 "URL", 

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

1356 

1357Description: 

1358 This property may be used in a calendar component to 

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

1360 information associated with the calendar component can be found. 

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

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

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

1364 specified, they MUST point to the same resource. 

1365 

1366Conformance: 

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

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

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

1370 

1371Example: 

1372 The following is an example of this property: 

1373 

1374 .. code-block:: ics 

1375 

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

1377 

1378""", 

1379) 

1380 

1381source_property = single_string_property( 

1382 "SOURCE", 

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

1384 

1385Description: 

1386 This property identifies a location where a client can 

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

1388 specified "REFRESH-INTERVAL" value when periodically retrieving 

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

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

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

1392 

1393Conformance: 

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

1395 

1396Example: 

1397 The following is an example of this property: 

1398 

1399 .. code-block:: ics 

1400 

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

1402 

1403""", 

1404) 

1405 

1406location_property = multi_language_text_property( 

1407 "LOCATION", 

1408 None, 

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

1410 

1411Property Parameters: 

1412 IANA, non-standard, alternate text 

1413 representation, and language property parameters can be specified 

1414 on this property. 

1415 

1416Conformance: 

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

1418 calendar component. 

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

1420 

1421Description: 

1422 Specific venues such as conference or meeting rooms may 

1423 be explicitly specified using this property. An alternate 

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

1425 directory information with more structured specification of the 

1426 location. For example, the alternate representation may specify 

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

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

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

1430 

1431""", 

1432) 

1433 

1434contacts_property = multi_text_property( 

1435 "CONTACT", 

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

1437 

1438Purpose: 

1439 This property is used to represent contact information or 

1440 alternately a reference to contact information associated with the 

1441 calendar component. 

1442 

1443Property Parameters: 

1444 IANA, non-standard, alternate text 

1445 representation, and language property parameters can be specified 

1446 on this property. 

1447 

1448Conformance: 

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

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

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

1452 amd "VAVAILABLE" calendar component. 

1453 

1454Description: 

1455 The property value consists of textual contact 

1456 information. An alternative representation for the property value 

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

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

1459 information. 

1460 

1461Example: 

1462 The following is an example of this property referencing 

1463 textual contact information: 

1464 

1465 .. code-block:: ics 

1466 

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

1468 

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

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

1471 contact information: 

1472 

1473 .. code-block:: ics 

1474 

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

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

1477 +1-919-555-1234 

1478 

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

1480 representation of a MIME body part containing the contact 

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

1482 directory media type :rfc:`2425`: 

1483 

1484 .. code-block:: ics 

1485 

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

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

1488 

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

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

1491 information: 

1492 

1493 .. code-block:: ics 

1494 

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

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

1497""", 

1498) 

1499 

1500 

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

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

1503 

1504 return single_utc_property(name, docs) 

1505 

1506 

1507rfc_7953_dtstart_property = timezone_datetime_property( 

1508 "DTSTART", 

1509 """Start of the component. 

1510 

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

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

1513 

1514 Description: 

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

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

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

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

1519 

1520 """, 

1521) 

1522 

1523rfc_7953_dtend_property = timezone_datetime_property( 

1524 "DTEND", 

1525 """Start of the component. 

1526 

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

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

1529 

1530 Description: 

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

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

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

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

1535 """, 

1536) 

1537 

1538 

1539@property 

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

1541 """Compute the duration of this component. 

1542 

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

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

1545 :attr:`DTEND`/:attr:`DURATION`. 

1546 

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

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

1549 """ 

1550 duration = self.DURATION 

1551 if duration: 

1552 return duration 

1553 end = self.DTEND 

1554 if end is None: 

1555 return None 

1556 start = self.DTSTART 

1557 if start is None: 

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

1559 return end - start 

1560 

1561 

1562@property 

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

1564 """Compute the duration of this component. 

1565 

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

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

1568 :attr:`DTEND`/:attr:`DURATION`. 

1569 

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

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

1572 """ 

1573 duration = self.DURATION 

1574 if duration: 

1575 start = self.DTSTART 

1576 if start is None: 

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

1578 return start + duration 

1579 end = self.DTEND 

1580 if end is None: 

1581 return None 

1582 return end 

1583 

1584 

1585@rfc_7953_end_property.setter 

1586def rfc_7953_end_property(self, value: datetime): 

1587 self.DTEND = value 

1588 

1589 

1590@rfc_7953_end_property.deleter 

1591def rfc_7953_end_property(self): 

1592 del self.DTEND 

1593 

1594 

1595def get_start_end_duration_with_validation( 

1596 component: Component, 

1597 start_property: str, 

1598 end_property: str, 

1599 component_name: str, 

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

1601 """ 

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

1603 

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

1605 for ``Event`` and ``Todo`` components. 

1606 

1607 Parameters: 

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

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

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

1611 ``DUE`` for ``Todo``. 

1612 component_name: The component name for error messages, 

1613 either ``VEVENT`` or ``VTODO``. 

1614 

1615 Returns: 

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

1617 

1618 Raises: 

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

1620 

1621 """ 

1622 start = getattr(component, start_property, None) 

1623 end = getattr(component, end_property, None) 

1624 duration = component.DURATION 

1625 

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

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

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

1629 msg = ( 

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

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

1632 ) 

1633 raise InvalidCalendar(msg) 

1634 

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

1636 if ( 

1637 start is not None 

1638 and is_date(start) 

1639 and duration is not None 

1640 and duration.seconds != 0 

1641 ): 

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

1643 raise InvalidCalendar(msg) 

1644 

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

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

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

1648 msg = ( 

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

1650 ) 

1651 raise InvalidCalendar(msg) 

1652 

1653 return start, end, duration 

1654 

1655 

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

1657 """ 

1658 Get the start property with validation. 

1659 

1660 Parameters: 

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

1662 

1663 Returns: 

1664 The ``DTSTART`` value. 

1665 

1666 Raises: 

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

1668 

1669 """ 

1670 # Trigger validation by calling _get_start_end_duration 

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

1672 if start is None: 

1673 msg = "No DTSTART given." 

1674 raise IncompleteComponent(msg) 

1675 return start 

1676 

1677 

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

1679 """ 

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

1681 

1682 Parameters: 

1683 component: The component to get end from 

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

1685 ``DUE`` for ``Todo``. 

1686 

1687 Returns: 

1688 The computed end value. 

1689 

1690 Raises: 

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

1692 to compute the end property. 

1693 

1694 """ 

1695 # Trigger validation by calling _get_start_end_duration 

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

1697 

1698 if end is None and duration is None: 

1699 if start is None: 

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

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

1702 raise IncompleteComponent(msg) 

1703 

1704 # Default behavior differs for Event vs Todo: 

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

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

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

1708 return start + timedelta(days=1) 

1709 return start 

1710 

1711 if duration is not None: 

1712 if start is not None: 

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

1714 return start 

1715 return start + duration 

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

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

1718 raise IncompleteComponent(msg) 

1719 

1720 return end 

1721 

1722 

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

1724 """ 

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

1726 

1727 Parameters: 

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

1729 

1730 Returns: 

1731 The duration as a timedelta. 

1732 

1733 """ 

1734 # First check if DURATION property is explicitly set 

1735 if "DURATION" in component: 

1736 return component["DURATION"].dt 

1737 

1738 # Fall back to calculated duration from start and end 

1739 return component.end - component.start 

1740 

1741 

1742def set_duration_with_locking( 

1743 component: Component, 

1744 duration: timedelta | None, 

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

1746 end_property: str, 

1747) -> None: 

1748 """ 

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

1750 

1751 Parameters: 

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

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

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

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

1756 ``DUE`` for ``Todo``. 

1757 

1758 """ 

1759 # Convert to DURATION property if duration is None 

1760 if duration is None: 

1761 if "DURATION" in component: 

1762 return # Already has DURATION property 

1763 current_duration = component.duration 

1764 component.DURATION = current_duration 

1765 return 

1766 

1767 if not isinstance(duration, timedelta): 

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

1769 raise TypeError(msg) 

1770 

1771 # Validate date/duration compatibility 

1772 start = component.DTSTART 

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

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

1775 raise InvalidCalendar(msg) 

1776 

1777 if locked == "start": 

1778 # Keep start locked, adjust end 

1779 if start is None: 

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

1781 raise IncompleteComponent(msg) 

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

1783 component.DURATION = duration 

1784 elif locked == "end": 

1785 # Keep end locked, adjust start 

1786 current_end = component.end 

1787 component.DTSTART = current_end - duration 

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

1789 component.DURATION = duration 

1790 else: 

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

1792 raise ValueError(msg) 

1793 

1794 

1795def set_start_with_locking( 

1796 component: Component, 

1797 start: date | datetime, 

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

1799 end_property: str, 

1800) -> None: 

1801 """ 

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

1803 

1804 Parameters: 

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

1806 start: The start time to set. 

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

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

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

1810 ``DUE`` for ``Todo``. 

1811 

1812 """ 

1813 if locked is None: 

1814 # Auto-detect based on existing properties 

1815 if "DURATION" in component: 

1816 locked = "duration" 

1817 elif end_property in component: 

1818 locked = "end" 

1819 else: 

1820 # Default to duration if no existing properties 

1821 locked = "duration" 

1822 

1823 if locked == "duration": 

1824 # Keep duration locked, adjust end 

1825 current_duration = ( 

1826 component.duration 

1827 if "DURATION" in component or end_property in component 

1828 else None 

1829 ) 

1830 component.DTSTART = start 

1831 if current_duration is not None: 

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

1833 component.DURATION = current_duration 

1834 elif locked == "end": 

1835 # Keep end locked, adjust duration 

1836 current_end = component.end 

1837 component.DTSTART = start 

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

1839 setattr(component, end_property, current_end) 

1840 else: 

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

1842 raise ValueError(msg) 

1843 

1844 

1845def set_end_with_locking( 

1846 component: Component, 

1847 end: date | datetime, 

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

1849 end_property: str, 

1850) -> None: 

1851 """ 

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

1853 

1854 Parameters: 

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

1856 end: The end time to set. 

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

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

1859 for ``Todo``. 

1860 

1861 """ 

1862 if locked == "start": 

1863 # Keep start locked, adjust duration 

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

1865 setattr(component, end_property, end) 

1866 elif locked == "duration": 

1867 # Keep duration locked, adjust start 

1868 current_duration = component.duration 

1869 component.DTSTART = end - current_duration 

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

1871 component.DURATION = current_duration 

1872 else: 

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

1874 raise ValueError(msg) 

1875 

1876 

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

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

1879 

1880 Description: 

1881 This property specifies an image for an iCalendar 

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

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

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

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

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

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

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

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

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

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

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

1893 on the image in the calendar user agent. 

1894 

1895 Conformance: 

1896 This property can be specified multiple times in an 

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

1898 components. 

1899 

1900 .. note:: 

1901 

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

1903 please open an issue or a pull request. 

1904 """ 

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

1906 if not isinstance(images, SEQUENCE_TYPES): 

1907 images = [images] 

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

1909 

1910 

1911images_property = property(_get_images) 

1912 

1913 

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

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

1916 

1917 Purpose: 

1918 This property specifies information for accessing a conferencing system. 

1919 

1920 Conformance: 

1921 This property can be specified multiple times in a 

1922 "VEVENT" or "VTODO" calendar component. 

1923 

1924 Description: 

1925 This property specifies information for accessing a 

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

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

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

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

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

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

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

1933 containing a text description can be used. 

1934 

1935 A conference system can be a bidirectional communication channel 

1936 or a uni-directional "broadcast feed". 

1937 

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

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

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

1941 multiple properties. 

1942 

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

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

1945 codes for the moderator and attendee of a teleconference system 

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

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

1948 which. 

1949 

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

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

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

1953 

1954 Example: 

1955 The following are examples of this property: 

1956 

1957 .. code-block:: ics 

1958 

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

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

1961 CONFERENCE;VALUE=URI;FEATURE=PHONE; 

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

1963 CONFERENCE;VALUE=URI;FEATURE=PHONE; 

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

1965 CONFERENCE;VALUE=URI;FEATURE=CHAT; 

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

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

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

1969 

1970 Get all conferences: 

1971 

1972 .. code-block:: pycon 

1973 

1974 >>> from icalendar import Event 

1975 >>> event = Event() 

1976 >>> event.conferences 

1977 [] 

1978 

1979 Set a conference: 

1980 

1981 .. code-block:: pycon 

1982 

1983 >>> from icalendar import Event, Conference 

1984 >>> event = Event() 

1985 >>> event.conferences = [ 

1986 ... Conference( 

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

1988 ... feature="PHONE,MODERATOR", 

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

1990 ... language="EN", 

1991 ... ) 

1992 ... ] 

1993 >>> print(event.to_ical()) 

1994 BEGIN:VEVENT 

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

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

1997 END:VEVENT 

1998 

1999 """ 

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

2001 if not isinstance(conferences, SEQUENCE_TYPES): 

2002 conferences = [conferences] 

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

2004 

2005 

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

2007 """Set the conferences.""" 

2008 _del_conferences(self) 

2009 for conference in conferences or []: 

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

2011 

2012 

2013def _del_conferences(self: Component): 

2014 """Delete all conferences.""" 

2015 self.pop("CONFERENCE") 

2016 

2017 

2018conferences_property = property(_get_conferences, _set_conferences, _del_conferences) 

2019 

2020 

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

2022 """LINK properties as a list. 

2023 

2024 Purpose: 

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

2026 

2027 Property Parameters: 

2028 The VALUE parameter is required. 

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

2030 can also be specified on this property. 

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

2032 

2033 Conformance: 

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

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

2036 The LINKREL parameter is required. 

2037 

2038 Description: 

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

2040 additional information related to the component. 

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

2042 

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

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

2045 the link context is the containing calendar entity, 

2046 and the link relation type and any target attributes 

2047 are carried in iCalendar property parameters. 

2048 

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

2050 

2051 LABEL 

2052 This parameter maps to the "title" 

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

2054 LABEL is used to label the destination 

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

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

2057 (if present). 

2058 LANGUAGE 

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

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

2061 LINKREL 

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

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

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

2065 FMTTYPE 

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

2067 :rfc:`8288`. 

2068 

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

2070 

2071 Examples: 

2072 The following is an example of this property, 

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

2074 

2075 .. code-block:: ics 

2076 

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

2078 https://example.com/events 

2079 

2080 The following is an example of this property, 

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

2082 The link relation is a vendor-defined value. 

2083 

2084 .. code-block:: ics 

2085 

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

2087 VALUE=URI: 

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

2089 

2090 The following is an example of this property, 

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

2092 The link relation is a vendor-defined value. 

2093 

2094 .. code-block:: ics 

2095 

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

2097 VALUE=XML-REFERENCE: 

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

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

2100 following::CostStrucEND[1])) 

2101 

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

2103 

2104 .. code-block:: pycon 

2105 

2106 >>> from icalendar import Event, vUri 

2107 >>> from datetime import datetime 

2108 >>> link = vUri( 

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

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

2111 ... ) 

2112 >>> event = Event.new( 

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

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

2115 ... ) 

2116 >>> event.links = [link] 

2117 >>> print(event.to_ical()) 

2118 BEGIN:VEVENT 

2119 SUMMARY:An Example Event with a page 

2120 DTSTART:20250917T120000 

2121 DTSTAMP:20250517T080612Z 

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

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

2124 END:VEVENT 

2125 

2126 """ 

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

2128 if not isinstance(links, list): 

2129 links = [links] 

2130 return links 

2131 

2132 

2133LINKS_TYPE_SETTER: TypeAlias = ( 

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

2135) 

2136 

2137 

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

2139 """Set the LINKs.""" 

2140 _del_links(self) 

2141 if links is None: 

2142 return 

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

2144 links = [links] 

2145 for link in links: 

2146 if type(link) is str: 

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

2148 self.add("LINK", link) 

2149 

2150 

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

2152 """Delete all links.""" 

2153 self.pop("LINK") 

2154 

2155 

2156links_property = property(_get_links, _set_links, _del_links) 

2157 

2158RELATED_TO_TYPE_SETTER: TypeAlias = ( 

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

2160) 

2161 

2162 

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

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

2165 

2166 Purpose: 

2167 This property is used to represent a relationship or reference 

2168 between one calendar component and another. 

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

2170 

2171 Value Type: 

2172 :rfc:`5545`: TEXT 

2173 :rfc:`9253`: URI, UID 

2174 

2175 Conformance: 

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

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

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

2179 iCalendar component. 

2180 

2181 Description (:rfc:`5545`): 

2182 The property value consists of the persistent, globally 

2183 unique identifier of another calendar component. This value would 

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

2185 

2186 By default, the property value points to another calendar 

2187 component that has a PARENT relationship to the referencing 

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

2189 explicitly state the default PARENT relationship type to the 

2190 referenced calendar component or to override the default PARENT 

2191 relationship type and specify either a CHILD or SIBLING 

2192 relationship. The PARENT relationship indicates that the calendar 

2193 component is a subordinate of the referenced calendar component. 

2194 The CHILD relationship indicates that the calendar component is a 

2195 superior of the referenced calendar component. The SIBLING 

2196 relationship indicates that the calendar component is a peer of 

2197 the referenced calendar component. 

2198 

2199 Changes to a calendar component referenced by this property can 

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

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

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

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

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

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

2206 property is intended only to provide information on the 

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

2208 calendar system to maintain any property implications of this 

2209 relationship. 

2210 

2211 Description (:rfc:`9253`): 

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

2213 consists of the persistent, globally unique identifier of another 

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

2215 component by the UID property. 

2216 

2217 By default, the property value 

2218 points to another calendar component that has a PARENT relationship 

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

2220 to either explicitly state the default PARENT relationship type to 

2221 the referenced calendar component or to override the default 

2222 PARENT relationship type and specify either a CHILD or SIBLING 

2223 relationship or a temporal relationship. 

2224 

2225 The PARENT relationship 

2226 indicates that the calendar component is a subordinate of the 

2227 referenced calendar component. The CHILD relationship indicates 

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

2229 component. The SIBLING relationship indicates that the calendar 

2230 component is a peer of the referenced calendar component. 

2231 

2232 To preserve backwards compatibility, the value type MUST 

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

2234 are specified. 

2235 

2236 The FINISHTOSTART, FINISHTOFINISH, STARTTOFINISH, 

2237 or STARTTOSTART relationships define temporal relationships, as 

2238 specified in the RELTYPE parameter definition. 

2239 

2240 The FIRST and NEXT 

2241 define ordering relationships between calendar components. 

2242 

2243 The DEPENDS-ON relationship indicates that the current calendar 

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

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

2246 referenced, task. 

2247 

2248 The REFID and CONCEPT relationships establish 

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

2250 Changes to a calendar component referenced by this property 

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

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

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

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

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

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

2257 components. This property is intended only to provide information 

2258 on the relationship of calendar components. 

2259 

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

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

2262 

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

2264 implications of these relationships. 

2265 

2266 Examples: 

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

2268 

2269 .. code-block:: ics 

2270 

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

2272 

2273 .. code-block:: ics 

2274 

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

2276 

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

2278 

2279 .. code-block:: ics 

2280 

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

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

2283 19960401-080045-4000F192713.ics 

2284 

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

2286 

2287 """ 

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

2289 if not isinstance(result, list): 

2290 return [result] 

2291 return result 

2292 

2293 

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

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

2296 _del_related_to(self) 

2297 if values is None: 

2298 return 

2299 if not isinstance(values, list): 

2300 values = [values] 

2301 for value in values: 

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

2303 

2304 

2305def _del_related_to(self: Component): 

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

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

2308 

2309 

2310related_to_property = property(_get_related_to, _set_related_to, _del_related_to) 

2311 

2312 

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

2314 """CONCEPT 

2315 

2316 Purpose: 

2317 CONCEPT defines the formal categories for a calendar component. 

2318 

2319 Conformance: 

2320 Since :rfc:`9253`, 

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

2322 

2323 Description: 

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

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

2326 component of a particular type and category. 

2327 

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

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

2330 the CONCEPT property will reference an external resource that provides 

2331 information about the categorization. 

2332 

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

2334 events. 

2335 

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

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

2338 

2339 Examples: 

2340 The following is an example of this property. 

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

2342 

2343 .. code-block:: ics 

2344 

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

2346 

2347 .. seealso:: 

2348 

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

2350 """ 

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

2352 if not isinstance(concepts, list): 

2353 concepts = [concepts] 

2354 return concepts 

2355 

2356 

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

2358 

2359 

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

2361 """Set the concepts.""" 

2362 _del_concepts(self) 

2363 if concepts is None: 

2364 return 

2365 if not isinstance(concepts, list): 

2366 concepts = [concepts] 

2367 for value in concepts: 

2368 self.add("CONCEPT", value) 

2369 

2370 

2371def _del_concepts(self: Component): 

2372 """Delete the concepts.""" 

2373 self.pop("CONCEPT", None) 

2374 

2375 

2376concepts_property = property(_get_concepts, _set_concepts, _del_concepts) 

2377 

2378 

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

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

2381 

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

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

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

2385 if not isinstance(value, list): 

2386 value = [value] 

2387 return value 

2388 

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

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

2391 fdel(self) 

2392 if value is None: 

2393 return 

2394 if not isinstance(value, list): 

2395 value = [value] 

2396 for value in value: 

2397 self.add(name, value) 

2398 

2399 def fdel(self: Component): 

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

2401 self.pop(name, None) 

2402 

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

2404 

2405 

2406refids_property = multi_string_property( 

2407 "REFID", 

2408 """REFID 

2409 

2410Purpose: 

2411 REFID acts as a key for associated iCalendar entities. 

2412 

2413Conformance: 

2414 Since :rfc:`9253`, 

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

2416 

2417Description: 

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

2419 identifier for associated components. 

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

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

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

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

2424 

2425Examples: 

2426 The following is an example of this property. 

2427 

2428 .. code-block:: ics 

2429 

2430 REFID:itinerary-2014-11-17 

2431 

2432 Use a REFID to associate several VTODOs: 

2433 

2434 .. code-block:: pycon 

2435 

2436 >>> from icalendar import Todo 

2437 >>> todo_1 = Todo.new( 

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

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

2440 ... ) 

2441 >>> todo_2 = Todo.new( 

2442 ... summary="pack backpack", 

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

2444 ... ) 

2445 >>> todo_1.refids == todo_2.refids 

2446 True 

2447 

2448.. note:: 

2449 

2450 List modifications do not modify the component. 

2451""", 

2452) 

2453 

2454 

2455__all__ = [ 

2456 "CONCEPTS_TYPE_SETTER", 

2457 "LINKS_TYPE_SETTER", 

2458 "RECURRENCE_ID", 

2459 "RELATED_TO_TYPE_SETTER", 

2460 "attendees_property", 

2461 "busy_type_property", 

2462 "categories_property", 

2463 "class_property", 

2464 "color_property", 

2465 "comments_property", 

2466 "concepts_property", 

2467 "conferences_property", 

2468 "contacts_property", 

2469 "create_single_property", 

2470 "description_property", 

2471 "descriptions_property", 

2472 "duration_property", 

2473 "exdates_property", 

2474 "get_duration_property", 

2475 "get_end_property", 

2476 "get_start_end_duration_with_validation", 

2477 "get_start_property", 

2478 "images_property", 

2479 "links_property", 

2480 "location_property", 

2481 "multi_language_text_property", 

2482 "multi_string_property", 

2483 "organizer_property", 

2484 "priority_property", 

2485 "property_del_duration", 

2486 "property_doc_duration_template", 

2487 "property_get_duration", 

2488 "property_set_duration", 

2489 "rdates_property", 

2490 "refids_property", 

2491 "related_to_property", 

2492 "rfc_7953_dtend_property", 

2493 "rfc_7953_dtstart_property", 

2494 "rfc_7953_duration_property", 

2495 "rfc_7953_end_property", 

2496 "rrules_property", 

2497 "sequence_property", 

2498 "set_duration_with_locking", 

2499 "set_end_with_locking", 

2500 "set_start_with_locking", 

2501 "single_int_property", 

2502 "single_utc_property", 

2503 "source_property", 

2504 "status_property", 

2505 "summary_property", 

2506 "transparency_property", 

2507 "uid_property", 

2508 "url_property", 

2509]