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

522 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 vUnknown, 

21 vUri, 

22 vXmlReference, 

23) 

24from icalendar.prop.conference import Conference 

25from icalendar.prop.image import Image 

26from icalendar.timezone import tzp 

27from icalendar.tools import is_date 

28 

29if TYPE_CHECKING: 

30 from collections.abc import Callable, Sequence 

31 

32 from icalendar.cal import Component 

33 

34 

35def _get_rdates( 

36 self: Component, 

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

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

39 

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

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

42 

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

44 with and without timezone. 

45 

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

47 if the end is specified. 

48 

49 Value Type: 

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

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

52 

53 Property Parameters: 

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

55 zone identifier property parameters can be specified on this 

56 property. 

57 

58 Conformance: 

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

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

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

62 calendar component. 

63 

64 Description: 

65 This property can appear along with the "RRULE" 

66 property to define an aggregate set of repeating occurrences. 

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

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

69 the "RDATE" and "RRULE". 

70 

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

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

73 recurrence instances for a calendar component. The recurrence set 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

89 Duplicate instances are ignored. 

90 

91 Example: 

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

93 

94 .. code-block:: pycon 

95 

96 >>> from icalendar import Event 

97 >>> from datetime import datetime 

98 >>> event = Event() 

99 

100 # Add a list of recurrence dates 

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

102 >>> event.rdates 

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

104 

105 .. note:: 

106 

107 Modifying the returned list does not change the RDATE value. Assign to 

108 :attr:`rdates` or use :func:`icalendar.cal.Component.add` instead. 

109 

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

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

112 

113 """ 

114 result = [] 

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

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

117 for dts in rdates.dts: 

118 rdate = dts.dt 

119 if isinstance(rdate, tuple): 

120 # we have a period as rdate 

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

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

123 else: 

124 result.append(rdate) 

125 else: 

126 # we have a date/datetime 

127 result.append((rdate, None)) 

128 return result 

129 

130 

131def _set_rdates(self: Component, value) -> None: 

132 """Set the RDATE values, replacing any existing ones. 

133 

134 ``value`` is a list as returned by :attr:`rdates` (each item a date, a 

135 datetime, or a ``(start, end)`` period tuple). Setting an empty list or 

136 :obj:`None` removes the RDATE property. 

137 """ 

138 _del_rdates(self) 

139 if value: 

140 self.add("RDATE", value) 

141 

142 

143def _del_rdates(self: Component) -> None: 

144 """Delete all RDATE values.""" 

145 self.pop("RDATE", None) 

146 

147 

148rdates_property = property(_get_rdates, _set_rdates, _del_rdates) 

149 

150 

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

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

153 

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

155 

156 Value Type: 

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

158 The value type can be set to DATE. 

159 

160 Property Parameters: 

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

162 zone identifier property parameters can be specified on this 

163 property. 

164 

165 Conformance: 

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

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

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

169 calendar component. 

170 

171 Description: 

172 The exception dates, if specified, are used in 

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

174 set of recurrence instances for a calendar component. The 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

190 Duplicate instances are ignored. 

191 

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

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

194 MUST still be maintained by the calendaring and scheduling system 

195 because the original "DTSTART" value has inherent usage 

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

197 

198 Example: 

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

200 

201 .. code-block:: pycon 

202 

203 >>> from icalendar import Event 

204 >>> from datetime import datetime 

205 >>> event = Event() 

206 

207 # Add a list of excluded dates 

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

209 >>> event.exdates 

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

211 

212 .. note:: 

213 

214 Modifying the returned list does not change the EXDATE value. Assign to 

215 :attr:`exdates` or use :func:`icalendar.cal.Component.add` instead. 

216 

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

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

219 

220 """ 

221 result = [] 

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

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

224 for dts in exdates.dts: 

225 exdate = dts.dt 

226 # we have a date/datetime 

227 result.append(exdate) 

228 return result 

229 

230 

231def _set_exdates(self: Component, value) -> None: 

232 """Set the EXDATE values, replacing any existing ones. 

233 

234 ``value`` is a list as returned by :attr:`exdates` (each item a date or a 

235 datetime). Setting an empty list or :obj:`None` removes the EXDATE property. 

236 """ 

237 _del_exdates(self) 

238 if value: 

239 self.add("EXDATE", value) 

240 

241 

242def _del_exdates(self: Component) -> None: 

243 """Delete all EXDATE values.""" 

244 self.pop("EXDATE", None) 

245 

246 

247exdates_property = property(_get_exdates, _set_exdates, _del_exdates) 

248 

249 

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

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

252 

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

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

255 

256 Property Parameters: 

257 IANA and non-standard property parameters can 

258 be specified on this property. 

259 

260 Conformance: 

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

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

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

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

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

266 undefined. 

267 

268 Description: 

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

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

271 recurrence instances for a calendar component. The recurrence set 

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

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

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

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

276 value SHOULD be synchronized with the recurrence rule, if 

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

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

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

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

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

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

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

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

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

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

287 Duplicate instances are ignored. 

288 

289 The "DTSTART" property specified within the iCalendar object 

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

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

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

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

294 same local time regardless of time zone changes. 

295 

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

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

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

299 duration of the recurring component is specified with the 

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

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

302 duration of each recurrence instance will depend on its specific 

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

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

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

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

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

308 type. 

309 

310 Examples: 

311 Daily for 10 occurrences: 

312 

313 .. code-block:: pycon 

314 

315 >>> from icalendar import Event 

316 >>> from datetime import datetime 

317 >>> from zoneinfo import ZoneInfo 

318 >>> event = Event() 

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

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

321 >>> print(event.to_ical()) 

322 BEGIN:VEVENT 

323 DTSTART;TZID=America/New_York:19970902T090000 

324 RRULE:FREQ=DAILY;COUNT=10 

325 END:VEVENT 

326 >>> event.rrules 

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

328 

329 Daily until December 24, 1997: 

330 

331 .. code-block:: pycon 

332 

333 >>> from icalendar import Event, vRecur 

334 >>> from datetime import datetime 

335 >>> from zoneinfo import ZoneInfo 

336 >>> event = Event() 

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

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

339 >>> print(event.to_ical()) 

340 BEGIN:VEVENT 

341 DTSTART;TZID=America/New_York:19970902T090000 

342 RRULE:FREQ=DAILY;UNTIL=19971224T000000Z 

343 END:VEVENT 

344 >>> event.rrules 

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

346 

347 .. note:: 

348 

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

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

351 

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

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

354 

355 """ # noqa: E501 

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

357 if not isinstance(rrules, list): 

358 return [rrules] 

359 return rrules 

360 

361 

362rrules_property = property(_get_rrules) 

363 

364 

365def multi_language_text_property( 

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

367) -> property: 

368 """This creates a text property. 

369 

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

371 

372 Parameters: 

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

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

375 doc (str): The documentation string 

376 """ 

377 

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

379 """Get the property""" 

380 result = self.get(main_prop) 

381 if result is None and compatibility_prop is not None: 

382 result = self.get(compatibility_prop) 

383 if isinstance(result, list): 

384 for item in result: 

385 if "LANGUAGE" not in item.params: 

386 return item 

387 return result 

388 

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

390 """Set the property.""" 

391 fdel(self) 

392 if value is not None: 

393 self.add(main_prop, value) 

394 if compatibility_prop is not None: 

395 self.add(compatibility_prop, value) 

396 

397 def fdel(self: Component): 

398 """Delete the property.""" 

399 self.pop(main_prop, None) 

400 if compatibility_prop is not None: 

401 self.pop(compatibility_prop, None) 

402 

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

404 

405 

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

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

408 

409 Parameters: 

410 prop: The name of the property 

411 default: The default value 

412 doc: The documentation string 

413 """ 

414 

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

416 """Get the property""" 

417 try: 

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

419 except ValueError as e: 

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

421 

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

423 """Set the property.""" 

424 fdel(self) 

425 if value is not None: 

426 self.add(prop, value) 

427 

428 def fdel(self: Component): 

429 """Delete the property.""" 

430 self.pop(prop, None) 

431 

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

433 

434 

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

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

437 

438 Parameters: 

439 name: name of the property 

440 docs: documentation string 

441 """ 

442 docs = ( 

443 f"""The {name} property with all values converted to a 

444 :class:`~datetime.datetime` in UTC. 

445 

446 """ 

447 + docs 

448 ) 

449 

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

451 """Get the value.""" 

452 if name not in self: 

453 return None 

454 dt = self.get(name) 

455 if isinstance(dt, (vText, vUnknown)): 

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

457 value = vDDDTypes.from_ical(dt) 

458 else: 

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

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

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

462 return tzp.localize_utc(value) 

463 

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

465 """Set the value""" 

466 if value is None: 

467 fdel(self) 

468 return 

469 if not isinstance(value, date): 

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

471 fdel(self) 

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

473 

474 def fdel(self: Component): 

475 """Delete the property.""" 

476 self.pop(name, None) 

477 

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

479 

480 

481def single_string_property( 

482 name: str, docs: str, other_name: str | list[str] | None = None, default: str = "" 

483) -> property: 

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

485 other_names = ( 

486 [] 

487 if other_name is None 

488 else [other_name] 

489 if isinstance(other_name, str) 

490 else list(other_name) 

491 ) 

492 

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

494 """Get the value.""" 

495 result = self.get(name, None) 

496 if result is None: 

497 for alias in other_names: 

498 result = self.get(alias, None) 

499 if result is not None: 

500 break 

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

502 return default 

503 if isinstance(result, list): 

504 return result[0] 

505 return result 

506 

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

508 """Set the value. 

509 

510 Setting the value to None will delete it. 

511 """ 

512 fdel(self) 

513 if value is not None: 

514 self.add(name, value) 

515 

516 def fdel(self: Component): 

517 """Delete the property.""" 

518 self.pop(name, None) 

519 for alias in other_names: 

520 self.pop(alias, None) 

521 

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

523 

524 

525color_property = single_string_property( 

526 "COLOR", 

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

528 

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

530 

531 Property Parameters: 

532 IANA and non-standard property parameters can 

533 be specified on this property. 

534 

535 Conformance: 

536 This property can be specified once in an iCalendar 

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

538 

539 Description: 

540 This property specifies a color that clients MAY use 

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

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

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

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

545 

546 Example: 

547 ``"turquoise"``, ``"#ffffff"`` 

548 

549 .. code-block:: pycon 

550 

551 >>> from icalendar import Todo 

552 >>> todo = Todo() 

553 >>> todo.color = "green" 

554 >>> print(todo.to_ical()) 

555 BEGIN:VTODO 

556 COLOR:green 

557 END:VTODO 

558 """, 

559) 

560 

561sequence_property = single_int_property( 

562 "SEQUENCE", 

563 0, 

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

565 

566Value Type: 

567 INTEGER 

568 

569Property Parameters: 

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

571 

572Conformance: 

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

574 "VJOURNAL" calendar component. 

575 

576Description: 

577 When a calendar component is created, its sequence 

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

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

580 calendar component. 

581 

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

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

584 calendar component. 

585 

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

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

588 component to which the "Attendee" is referring. 

589 

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

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

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

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

594 requested. 

595 

596 Recurrence instances of a recurring component MAY have different 

597 sequence numbers. 

598 

599Examples: 

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

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

602 

603 .. code-block:: pycon 

604 

605 >>> from icalendar import Event 

606 >>> event = Event() 

607 >>> event.sequence 

608 0 

609 

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

611 component that has been revised 10 different times by the 

612 "Organizer": 

613 

614 .. code-block:: pycon 

615 

616 >>> from icalendar import Calendar 

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

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

619 >>> event.sequence 

620 10 

621 """, # noqa: E501 

622) 

623 

624 

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

626 """Get all the categories.""" 

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

628 if isinstance(categories, list): 

629 _set_categories( 

630 component, 

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

632 ) 

633 return _get_categories(component) 

634 if categories is None: 

635 categories = vCategory([]) 

636 component.add("CATEGORIES", categories) 

637 return categories.cats 

638 

639 

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

641 """Set the categories.""" 

642 if not cats and cats != []: 

643 _del_categories(component) 

644 return 

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

646 if isinstance(cats, list): 

647 cats.clear() 

648 cats.extend(categories.cats) 

649 categories.cats = cats 

650 

651 

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

653 """Delete the categories.""" 

654 component.pop("CATEGORIES", None) 

655 

656 

657categories_property = property( 

658 _get_categories, 

659 _set_categories, 

660 _del_categories, 

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

662 

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

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

665component of a particular type and category. 

666 

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

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

669 

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

671 

672- :attr:`Available.categories <icalendar.cal.available.Available.categories>` 

673- :attr:`Availability.categories <icalendar.cal.availability.Availability.categories>` 

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

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

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

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

678 

679The categories property for ``Available`` and ``Availability`` complies with 

680:rfc:`7953#section-3.1`, for ``Event``, ``Journal``, and ``Todo`` with 

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

682 

683Note: 

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

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

686 

687Parameters: 

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

689 

690Example: 

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

692 append another category, and finally compare the result 

693 against its expected value. 

694 

695 .. code-block:: pycon 

696 

697 >>> from icalendar import Event 

698 >>> event = Event() 

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

700 >>> print(event.to_ical()) 

701 BEGIN:VEVENT 

702 CATEGORIES:Work,Meeting 

703 END:VEVENT 

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

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

706 True 

707 

708See also: 

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

710""", 

711) 

712 

713 

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

715 """Get attendees.""" 

716 value = self.get("ATTENDEE") 

717 if value is None: 

718 value = [] 

719 self["ATTENDEE"] = value 

720 return value 

721 if isinstance(value, vCalAddress): 

722 return [value] 

723 return value 

724 

725 

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

727 """Set attendees.""" 

728 _del_attendees(self) 

729 if value is None: 

730 return 

731 if not isinstance(value, list): 

732 value = [value] 

733 self["ATTENDEE"] = value 

734 

735 

736def _del_attendees(self: Component): 

737 """Delete all attendees.""" 

738 self.pop("ATTENDEE", None) 

739 

740 

741attendees_property = property( 

742 _get_attendees, 

743 _set_attendees, 

744 _del_attendees, 

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

746 

747Conformance: 

748 This property MUST be specified in an iCalendar object 

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

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

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

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

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

754 iCalendar object that specifies only a time zone definition or 

755 that defines calendar components that are not group-scheduled 

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

757 

758Description: 

759 This property MUST only be specified within calendar 

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

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

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

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

764 type of iCalendar alarm. 

765 

766Examples: 

767 Add a new attendee to an existing event. 

768 

769 .. code-block:: pycon 

770 

771 >>> from icalendar import Event, vCalAddress 

772 >>> event = Event() 

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

774 >>> print(event.to_ical()) 

775 BEGIN:VEVENT 

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

777 END:VEVENT 

778 

779 Create an email alarm with several attendees: 

780 

781 >>> from icalendar import Alarm, vCalAddress 

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

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

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

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

786 >>> print(alarm.to_ical()) 

787 BEGIN:VALARM 

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

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

790 SUMMARY:Email alarm 

791 END:VALARM 

792""", 

793) 

794 

795uid_property = single_string_property( 

796 "UID", 

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

798 

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

800 

801Returns: 

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

803 

804Description: 

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

806 The generator of the identifier MUST guarantee that the identifier 

807 is unique. 

808 

809 This is the method for correlating scheduling messages with the 

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

811 The full range of calendar components specified by a recurrence 

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

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

814 property allows the reference to an individual instance within the 

815 recurrence set. 

816 

817 This property is an important method for group-scheduling 

818 applications to match requests with later replies, modifications, 

819 or deletion requests. Calendaring and scheduling applications 

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

821 calendar components to assure interoperability with other group- 

822 scheduling applications. This identifier is created by the 

823 calendar system that generates an iCalendar object. 

824 

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

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

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

828 

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

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

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

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

833 to identify the most recent version of a calendar. 

834 

835Conformance: 

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

837 and "VJOURNAL" calendar components. 

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

839 allow it to be defined in an iCalendar object. 

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

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

842 to refer uniquely to the "VALARM" component. 

843 

844 This property can be specified once only. 

845 

846Security: 

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

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

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

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

851 Universally Unique Identifier (UUID) values as defined in 

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

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

854 

855Compatibility: 

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

857 

858Examples: 

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

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

861 

862 Set the UID of a calendar: 

863 

864 .. code-block:: pycon 

865 

866 >>> from icalendar import Calendar 

867 >>> from uuid import uuid4 

868 >>> calendar = Calendar() 

869 >>> calendar.uid = uuid4() 

870 >>> print(calendar.to_ical()) 

871 BEGIN:VCALENDAR 

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

873 END:VCALENDAR 

874 

875""", 

876) 

877 

878summary_property = multi_language_text_property( 

879 "SUMMARY", 

880 None, 

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

882 

883Property Parameters: 

884 IANA, non-standard, alternate text 

885 representation, and language property parameters can be specified 

886 on this property. 

887 

888Conformance: 

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

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

891 

892Description: 

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

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

895 summary about the activity or journal entry. 

896 

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

898 capture the subject of an EMAIL category of alarm. 

899 

900Examples: 

901 The following is an example of this property: 

902 

903 .. code-block:: pycon 

904 

905 SUMMARY:Department Party 

906""", 

907) 

908 

909description_property = multi_language_text_property( 

910 "DESCRIPTION", 

911 None, 

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

913 

914Property Parameters: 

915 IANA, non-standard, alternate text 

916 representation, and language property parameters can be specified 

917 on this property. 

918 

919Conformance: 

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

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

922 specified multiple times only within a "VJOURNAL" calendar 

923 component. 

924 

925Description: 

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

927 capture lengthy textual descriptions associated with the activity. 

928 

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

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

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

932 

933Examples: 

934 The following is an example of this property with formatted 

935 line breaks in the property value: 

936 

937 .. code-block:: pycon 

938 

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

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

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

942 

943 """, # noqa: E501 

944) 

945 

946 

947def create_single_property( 

948 prop: str, 

949 value_attr: str | None, 

950 value_type: tuple[type], 

951 type_def: type, 

952 doc: str, 

953 vProp: type = vDDDTypes, # noqa: N803 

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

955): 

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

957 

958 Parameters: 

959 prop: The name of the property. 

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

961 value_type: The type of the value. 

962 type_def: The type of the property. 

963 doc: The docstring of the property. 

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

965 """ 

966 

967 def p_get(self: Component): 

968 default = object() 

969 result = self.get(prop, default) 

970 if result is default: 

971 return None 

972 if isinstance(result, list): 

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

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

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

976 if not isinstance(value, value_type): 

977 raise InvalidCalendar( 

978 f"{prop} must be either a " 

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

980 f" not {value}." 

981 ) 

982 return value 

983 

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

985 if value is None: 

986 p_del(self) 

987 return 

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

989 if not isinstance(value, value_type): 

990 raise TypeError( 

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

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

993 ) 

994 self[prop] = vProp(value) 

995 if prop in self.exclusive: 

996 for other_prop in self.exclusive: 

997 if other_prop != prop: 

998 self.pop(other_prop, None) 

999 

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

1001 

1002 def p_del(self: Component): 

1003 self.pop(prop) 

1004 

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

1006 

1007 {doc} 

1008 

1009 To delete the value, either use ``del`` or set it to ``None``. 

1010 

1011 Raises: 

1012 InvalidCalendar: if the attribute has invalid values. 

1013 """ 

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

1015 

1016 

1017X_MOZ_SNOOZE_TIME_property = single_utc_property( 

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

1019) 

1020X_MOZ_LASTACK_property = single_utc_property( 

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

1022) 

1023 

1024 

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

1026 """Getter for property DURATION.""" 

1027 default = object() 

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

1029 if duration is default: 

1030 return None 

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

1032 if result is None: 

1033 raise InvalidCalendar( 

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

1035 ) 

1036 return result 

1037 

1038 

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

1040 """Setter for property DURATION.""" 

1041 if value is None: 

1042 self.pop("duration", None) 

1043 return 

1044 if not isinstance(value, timedelta): 

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

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

1047 self.pop("DTEND") 

1048 self.pop("DUE") 

1049 

1050 

1051def property_del_duration(self: Component): 

1052 """Delete property DURATION.""" 

1053 self.pop("DURATION") 

1054 

1055 

1056property_doc_duration_template = """The DURATION property. 

1057 

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

1059start of the {component}. 

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

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

1062of the event. 

1063 

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

1065Instead use the duration property (lower case). 

1066""" 

1067 

1068 

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

1070 """Return the duration property.""" 

1071 return property( 

1072 property_get_duration, 

1073 property_set_duration, 

1074 property_del_duration, 

1075 property_doc_duration_template.format(component=component), 

1076 ) 

1077 

1078 

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

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

1081 

1082 Examples: Journal.descriptions, Event.comments 

1083 """ 

1084 

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

1086 """Get the values.""" 

1087 descriptions = self.get(name) 

1088 if descriptions is None: 

1089 return [] 

1090 if not isinstance(descriptions, SEQUENCE_TYPES): 

1091 return [descriptions] 

1092 return descriptions 

1093 

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

1095 """Set the values.""" 

1096 fdel(self) 

1097 if values is None: 

1098 return 

1099 if isinstance(values, str): 

1100 self.add(name, values) 

1101 else: 

1102 for description in values: 

1103 self.add(name, description) 

1104 

1105 def fdel(self: Component): 

1106 """Delete the values.""" 

1107 self.pop(name) 

1108 

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

1110 

1111 

1112descriptions_property = multi_text_property( 

1113 "DESCRIPTION", 

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

1115 

1116Property Parameters: 

1117 IANA, non-standard, alternate text 

1118 representation, and language property parameters can be specified 

1119 on this property. 

1120 

1121Conformance: 

1122 The property can be 

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

1124 

1125Description: 

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

1127 capture one or more textual journal entries. 

1128 

1129Examples: 

1130 The following is an example of this property with formatted 

1131 line breaks in the property value: 

1132 

1133 .. code-block:: pycon 

1134 

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

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

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

1138 

1139""", # noqa: E501 

1140) 

1141 

1142comments_property = multi_text_property( 

1143 "COMMENT", 

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

1145 

1146Purpose: 

1147 This property specifies non-processing information intended 

1148 to provide a comment to the calendar user. 

1149 

1150Conformance: 

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

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

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

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

1155 "VAVAILABILITY" and "VAVAILABLE". 

1156 

1157Property Parameters: 

1158 IANA, non-standard, alternate text 

1159 representation, and language property parameters can be specified 

1160 on this property. 

1161 

1162""", 

1163) 

1164 

1165RECURRENCE_ID = create_single_property( 

1166 "RECURRENCE-ID", 

1167 "dt", 

1168 (date, datetime), 

1169 date | datetime, 

1170 """ 

1171Identify a specific occurrence of a recurring calendar object. 

1172 

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

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

1175date or datetime of that instance, not the rescheduled time. 

1176 

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

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

1179all-day items instead. 

1180 

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

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

1183""", 

1184 vDDDTypes, 

1185) 

1186 

1187 

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

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

1190 

1191 Property Parameters: 

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

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

1194 specified on this property. 

1195 

1196 Conformance: 

1197 This property MUST be specified in an iCalendar object 

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

1199 MUST be specified in an iCalendar object that specifies the 

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

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

1202 zone definition or that defines calendar components that are not 

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

1204 user's calendar. 

1205 

1206 Description: 

1207 This property is specified within the "VEVENT", 

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

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

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

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

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

1213 the calendar that the published busy time came from. 

1214 

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

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

1217 specifying a pointer to the directory information associated with 

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

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

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

1221 "LANGUAGE" property parameter is specified, the identified 

1222 language applies to the "CN" parameter value. 

1223 """ 

1224 return self.get("ORGANIZER") 

1225 

1226 

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

1228 """Set the value.""" 

1229 _del_organizer(self) 

1230 if value is not None: 

1231 self.add("ORGANIZER", value) 

1232 

1233 

1234def _del_organizer(self: Component): 

1235 """Delete the value.""" 

1236 self.pop("ORGANIZER") 

1237 

1238 

1239organizer_property = property(_get_organizer, _set_organizer, _del_organizer) 

1240 

1241 

1242def single_string_enum_property( 

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

1244) -> property: 

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

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

1247 

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

1249 """Get the value.""" 

1250 value = prop.fget(self) 

1251 if value == default: 

1252 return default 

1253 return enum(str(value)) 

1254 

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

1256 """Set the value.""" 

1257 if value == "": 

1258 value = None 

1259 prop.fset(self, value) 

1260 

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

1262 

1263 

1264busy_type_property = single_string_enum_property( 

1265 "BUSYTYPE", 

1266 BUSYTYPE, 

1267 BUSYTYPE.BUSY_UNAVAILABLE, 

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

1269 

1270Returns: 

1271 :class:`icalendar.enums.BUSYTYPE` 

1272 

1273Description: 

1274 This property is used to specify the default busy time 

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

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

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

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

1279 UNAVAILABLE". 

1280""", 

1281) 

1282 

1283 

1284def _make_repeat_property() -> property: 

1285 from icalendar.config import _clamp_repeat 

1286 

1287 _base = single_int_property( 

1288 "REPEAT", 

1289 0, 

1290 """The number of additional times the alarm is triggered after the 

1291initial trigger. 

1292 

1293Defaults to ``0``, meaning the alarm fires once. Must be paired with 

1294:attr:`~icalendar.cal.alarm.Alarm.DURATION`. Conforms with :rfc:`5545#section-3.8.6.2`. 

1295The value is capped at :data:`icalendar.config.MAX_ALARM_REPEAT` on read. 

1296""", 

1297 ) 

1298 

1299 def fget(self): 

1300 return _clamp_repeat(_base.fget(self)) 

1301 

1302 return property(fget, _base.fset, _base.fdel, _base.__doc__) 

1303 

1304 

1305repeat_property = _make_repeat_property() 

1306 

1307priority_property = single_int_property( 

1308 "PRIORITY", 

1309 0, 

1310 """ 

1311 

1312Conformance: 

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

1314 according to :rfc:`5545`. 

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

1316 

1317Description: 

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

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

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

1321 priority. Subsequent numbers specify a decreasing ordinal 

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

1323 

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

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

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

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

1328 is "LOW" priority. 

1329 

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

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

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

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

1334 9 specifies "C3". 

1335 

1336 Other integer values are reserved for future use. 

1337 

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

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

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

1341 

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

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

1344 multiple action items for a given time period. 

1345""", 

1346) 

1347 

1348class_property = single_string_enum_property( 

1349 "CLASS", 

1350 CLASS, 

1351 CLASS.PUBLIC, 

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

1353 

1354Returns: 

1355 :class:`icalendar.enums.CLASS` 

1356 

1357Description: 

1358 An access classification is only one component of the 

1359 general security system within a calendar application. It 

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

1361 calendar owner intends for information within an individual 

1362 calendar entry. The access classification of an individual 

1363 iCalendar component is useful when measured along with the other 

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

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

1366 Hence, the semantics of the individual access classifications 

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

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

1369 memo, these access classifications cannot serve as an enforcement 

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

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

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

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

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

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

1376""", 

1377) 

1378 

1379transparency_property = single_string_enum_property( 

1380 "TRANSP", 

1381 TRANSP, 

1382 TRANSP.OPAQUE, 

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

1384 

1385Returns: 

1386 :class:`icalendar.enums.TRANSP` 

1387 

1388Description: 

1389 Time Transparency is the characteristic of an event 

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

1391 Events that consume actual time for the individual or resource 

1392 associated with the calendar SHOULD be recorded as OPAQUE, 

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

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

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

1396 busy time searches. 

1397""", 

1398) 

1399status_property = single_string_enum_property( 

1400 "STATUS", 

1401 STATUS, 

1402 "", 

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

1404 

1405Returns: 

1406 :class:`icalendar.enums.STATUS` 

1407 

1408The default value is ``""``. 

1409 

1410Description: 

1411 In a group-scheduled calendar component, the property 

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

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

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

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

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

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

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

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

1420 cancelled or removed. 

1421""", 

1422) 

1423 

1424url_property = single_string_property( 

1425 "URL", 

1426 """A Uniform Resource Locator (URL) associated with a calendar component. 

1427 

1428This property specifies a URI where a more dynamic rendition of the calendar 

1429information can be found. It is commonly used to reference related resources 

1430or provide additional information about the component. 

1431 

1432According to :rfc:`5545#section-3.8.4.6`, this property can be specified 

1433once in "VEVENT", "VTODO", "VJOURNAL", or "VFREEBUSY" calendar components. 

1434Since :rfc:`7986#section-5.5`, this property can also be defined on a 

1435"VCALENDAR". :rfc:`7953#section-3.1` allows this property in "VAVAILABILITY" components. 

1436 

1437This property may be used in a calendar component to convey a location 

1438where a more dynamic rendition of the calendar information can be found. 

1439If both the URL property and Content-Location MIME header are specified, 

1440they MUST point to the same resource. 

1441 

1442This differs from the SOURCE property, which identifies where calendar 

1443data can be refreshed from, whereas URL provides an alternative 

1444representation of the current calendar data. 

1445 

1446Examples: 

1447 

1448 Set a URL for an event that references additional information: 

1449 

1450 .. code-block:: pycon 

1451 

1452 >>> from icalendar import Event 

1453 >>> event = Event() 

1454 >>> event.add('url', 'http://example.com/events/meeting-2025') 

1455 >>> print(event.to_ical().decode('utf-8')) 

1456 BEGIN:VEVENT 

1457 URL:http://example.com/events/meeting-2025 

1458 END:VEVENT 

1459 

1460 Set a URL for a calendar: 

1461 

1462 .. code-block:: pycon 

1463 

1464 >>> from icalendar import Calendar 

1465 >>> calendar = Calendar() 

1466 >>> calendar.add('url', 'http://example.com/pub/calendars/jsmith/mytime.ics') 

1467 >>> print(calendar.to_ical().decode('utf-8')) 

1468 BEGIN:VCALENDAR 

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

1470 END:VCALENDAR 

1471 

1472See also: 

1473 :attr:`~icalendar.cal.calendar.Calendar.source` for specifying from where 

1474 calendar data can be refreshed. 

1475 

1476 icalendar implementations: 

1477 

1478 - :attr:`Availability.url <icalendar.cal.availability.Availability.url>` 

1479 - :attr:`Calendar.url <icalendar.cal.calendar.Calendar.url>` 

1480 - :attr:`Event.url <icalendar.cal.event.Event.url>` 

1481 - :attr:`FreeBusy.url <icalendar.cal.free_busy.FreeBusy.url>` 

1482 - :attr:`Journal.url <icalendar.cal.journal.Journal.url>` 

1483 - :attr:`Todo.url <icalendar.cal.todo.Todo.url>` 

1484 

1485 

1486""", 

1487) 

1488 

1489source_property = single_string_property( 

1490 "SOURCE", 

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

1492 

1493Description: 

1494 This property identifies a location where a client can 

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

1496 specified "REFRESH-INTERVAL" value when periodically retrieving 

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

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

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

1500 

1501Conformance: 

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

1503 

1504Example: 

1505 The following is an example of this property: 

1506 

1507 .. code-block:: ics 

1508 

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

1510 

1511""", 

1512) 

1513 

1514location_property = multi_language_text_property( 

1515 "LOCATION", 

1516 None, 

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

1518 

1519Property Parameters: 

1520 IANA, non-standard, alternate text 

1521 representation, and language property parameters can be specified 

1522 on this property. 

1523 

1524Conformance: 

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

1526 calendar component. 

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

1528 

1529Description: 

1530 Specific venues such as conference or meeting rooms may 

1531 be explicitly specified using this property. An alternate 

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

1533 directory information with more structured specification of the 

1534 location. For example, the alternate representation may specify 

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

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

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

1538 

1539""", 

1540) 

1541 

1542contacts_property = multi_text_property( 

1543 "CONTACT", 

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

1545 

1546Purpose: 

1547 This property is used to represent contact information or 

1548 alternately a reference to contact information associated with the 

1549 calendar component. 

1550 

1551Property Parameters: 

1552 IANA, non-standard, alternate text 

1553 representation, and language property parameters can be specified 

1554 on this property. 

1555 

1556Conformance: 

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

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

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

1560 amd "VAVAILABLE" calendar component. 

1561 

1562Description: 

1563 The property value consists of textual contact 

1564 information. An alternative representation for the property value 

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

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

1567 information. 

1568 

1569Example: 

1570 The following is an example of this property referencing 

1571 textual contact information: 

1572 

1573 .. code-block:: ics 

1574 

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

1576 

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

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

1579 contact information: 

1580 

1581 .. code-block:: ics 

1582 

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

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

1585 +1-919-555-1234 

1586 

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

1588 representation of a MIME body part containing the contact 

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

1590 directory media type :rfc:`2425`: 

1591 

1592 .. code-block:: ics 

1593 

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

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

1596 

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

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

1599 information: 

1600 

1601 .. code-block:: ics 

1602 

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

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

1605""", 

1606) 

1607 

1608 

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

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

1611 

1612 return single_utc_property(name, docs) 

1613 

1614 

1615rfc_7953_dtstart_property = timezone_datetime_property( 

1616 "DTSTART", 

1617 """Start of the component. 

1618 

1619 This is almost the same as 

1620 :attr:`Event.DTSTART <icalendar.cal.event.Event.DTSTART>` with one exception: 

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

1622 

1623 Description: 

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

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

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

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

1628 

1629 """, 

1630) 

1631 

1632rfc_7953_dtend_property = timezone_datetime_property( 

1633 "DTEND", 

1634 """Start of the component. 

1635 

1636 This is almost the same as 

1637 :attr:`Event.DTEND <icalendar.cal.event.Event.DTEND>` with one exception: 

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

1639 

1640 Description: 

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

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

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

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

1645 """, 

1646) 

1647 

1648 

1649@property 

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

1651 """Compute the duration of this component. 

1652 

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

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

1655 :attr:`DTEND`/:attr:`DURATION`. 

1656 

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

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

1659 """ 

1660 duration = self.DURATION 

1661 if duration: 

1662 return duration 

1663 end = self.DTEND 

1664 if end is None: 

1665 return None 

1666 start = self.DTSTART 

1667 if start is None: 

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

1669 return end - start 

1670 

1671 

1672@property 

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

1674 """Compute the duration of this component. 

1675 

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

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

1678 :attr:`DTEND`/:attr:`DURATION`. 

1679 

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

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

1682 """ 

1683 duration = self.DURATION 

1684 if duration: 

1685 start = self.DTSTART 

1686 if start is None: 

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

1688 return start + duration 

1689 end = self.DTEND 

1690 if end is None: 

1691 return None 

1692 return end 

1693 

1694 

1695@rfc_7953_end_property.setter 

1696def rfc_7953_end_property(self, value: datetime): 

1697 self.DTEND = value 

1698 

1699 

1700@rfc_7953_end_property.deleter 

1701def rfc_7953_end_property(self): 

1702 del self.DTEND 

1703 

1704 

1705def get_start_end_duration_with_validation( 

1706 component: Component, 

1707 start_property: str, 

1708 end_property: str, 

1709 component_name: str, 

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

1711 """ 

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

1713 

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

1715 for ``Event`` and ``Todo`` components. 

1716 

1717 Parameters: 

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

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

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

1721 ``DUE`` for ``Todo``. 

1722 component_name: The component name for error messages, 

1723 either ``VEVENT`` or ``VTODO``. 

1724 

1725 Returns: 

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

1727 

1728 Raises: 

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

1730 

1731 """ 

1732 start = getattr(component, start_property, None) 

1733 end = getattr(component, end_property, None) 

1734 duration = component.DURATION 

1735 

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

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

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

1739 msg = ( 

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

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

1742 ) 

1743 raise InvalidCalendar(msg) 

1744 

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

1746 if ( 

1747 start is not None 

1748 and is_date(start) 

1749 and duration is not None 

1750 and duration.seconds != 0 

1751 ): 

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

1753 raise InvalidCalendar(msg) 

1754 

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

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

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

1758 msg = ( 

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

1760 ) 

1761 raise InvalidCalendar(msg) 

1762 

1763 return start, end, duration 

1764 

1765 

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

1767 """ 

1768 Get the start property with validation. 

1769 

1770 Parameters: 

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

1772 

1773 Returns: 

1774 The ``DTSTART`` value. 

1775 

1776 Raises: 

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

1778 

1779 """ 

1780 # Trigger validation by calling _get_start_end_duration 

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

1782 if start is None: 

1783 msg = "No DTSTART given." 

1784 raise IncompleteComponent(msg) 

1785 return start 

1786 

1787 

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

1789 """ 

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

1791 

1792 Parameters: 

1793 component: The component to get end from 

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

1795 ``DUE`` for ``Todo``. 

1796 

1797 Returns: 

1798 The computed end value. 

1799 

1800 Raises: 

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

1802 to compute the end property. 

1803 

1804 """ 

1805 # Trigger validation by calling _get_start_end_duration 

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

1807 

1808 if end is None and duration is None: 

1809 if start is None: 

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

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

1812 raise IncompleteComponent(msg) 

1813 

1814 # Default behavior differs for Event vs Todo: 

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

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

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

1818 return start + timedelta(days=1) 

1819 return start 

1820 

1821 if duration is not None: 

1822 if start is not None: 

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

1824 return start 

1825 return start + duration 

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

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

1828 raise IncompleteComponent(msg) 

1829 

1830 return end 

1831 

1832 

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

1834 """ 

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

1836 

1837 Parameters: 

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

1839 

1840 Returns: 

1841 The duration as a timedelta. 

1842 

1843 """ 

1844 # First check if DURATION property is explicitly set 

1845 if "DURATION" in component: 

1846 return component["DURATION"].dt 

1847 

1848 # Fall back to calculated duration from start and end 

1849 return component.end - component.start 

1850 

1851 

1852def set_duration_with_locking( 

1853 component: Component, 

1854 duration: timedelta | None, 

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

1856 end_property: str, 

1857) -> None: 

1858 """ 

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

1860 

1861 Parameters: 

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

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

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

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

1866 ``DUE`` for ``Todo``. 

1867 

1868 """ 

1869 # Convert to DURATION property if duration is None 

1870 if duration is None: 

1871 if "DURATION" in component: 

1872 return # Already has DURATION property 

1873 current_duration = component.duration 

1874 component.DURATION = current_duration 

1875 return 

1876 

1877 if not isinstance(duration, timedelta): 

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

1879 raise TypeError(msg) 

1880 

1881 # Validate date/duration compatibility 

1882 start = component.DTSTART 

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

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

1885 raise InvalidCalendar(msg) 

1886 

1887 if locked == "start": 

1888 # Keep start locked, adjust end 

1889 if start is None: 

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

1891 raise IncompleteComponent(msg) 

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

1893 component.DURATION = duration 

1894 elif locked == "end": 

1895 # Keep end locked, adjust start 

1896 current_end = component.end 

1897 component.DTSTART = current_end - duration 

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

1899 component.DURATION = duration 

1900 else: 

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

1902 raise ValueError(msg) 

1903 

1904 

1905def set_start_with_locking( 

1906 component: Component, 

1907 start: date | datetime, 

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

1909 end_property: str, 

1910) -> None: 

1911 """ 

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

1913 

1914 Parameters: 

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

1916 start: The start time to set. 

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

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

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

1920 ``DUE`` for ``Todo``. 

1921 

1922 """ 

1923 if locked is None: 

1924 # Auto-detect based on existing properties 

1925 if "DURATION" in component: 

1926 locked = "duration" 

1927 elif end_property in component: 

1928 locked = "end" 

1929 else: 

1930 # Default to duration if no existing properties 

1931 locked = "duration" 

1932 

1933 if locked == "duration": 

1934 # Keep duration locked, adjust end 

1935 current_duration = ( 

1936 component.duration 

1937 if "DURATION" in component or end_property in component 

1938 else None 

1939 ) 

1940 component.DTSTART = start 

1941 if current_duration is not None: 

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

1943 component.DURATION = current_duration 

1944 elif locked == "end": 

1945 # Keep end locked, adjust duration 

1946 current_end = component.end 

1947 component.DTSTART = start 

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

1949 setattr(component, end_property, current_end) 

1950 else: 

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

1952 raise ValueError(msg) 

1953 

1954 

1955def set_end_with_locking( 

1956 component: Component, 

1957 end: date | datetime, 

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

1959 end_property: str, 

1960) -> None: 

1961 """ 

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

1963 

1964 Parameters: 

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

1966 end: The end time to set. 

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

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

1969 for ``Todo``. 

1970 

1971 """ 

1972 if locked == "start": 

1973 # Keep start locked, adjust duration 

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

1975 setattr(component, end_property, end) 

1976 elif locked == "duration": 

1977 # Keep duration locked, adjust start 

1978 current_duration = component.duration 

1979 component.DTSTART = end - current_duration 

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

1981 component.DURATION = current_duration 

1982 else: 

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

1984 raise ValueError(msg) 

1985 

1986 

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

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

1989 

1990 Description: 

1991 This property specifies an image for an iCalendar 

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

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

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

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

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

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

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

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

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

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

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

2003 on the image in the calendar user agent. 

2004 

2005 Conformance: 

2006 This property can be specified multiple times in an 

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

2008 components. 

2009 

2010 .. note:: 

2011 

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

2013 please open an issue or a pull request. 

2014 """ 

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

2016 if not isinstance(images, SEQUENCE_TYPES): 

2017 images = [images] 

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

2019 

2020 

2021images_property = property(_get_images) 

2022 

2023 

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

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

2026 

2027 Purpose: 

2028 This property specifies information for accessing a conferencing system. 

2029 

2030 Conformance: 

2031 This property can be specified multiple times in a 

2032 "VEVENT" or "VTODO" calendar component. 

2033 

2034 Description: 

2035 This property specifies information for accessing a 

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

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

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

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

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

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

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

2043 containing a text description can be used. 

2044 

2045 A conference system can be a bidirectional communication channel 

2046 or a uni-directional "broadcast feed". 

2047 

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

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

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

2051 multiple properties. 

2052 

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

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

2055 codes for the moderator and attendee of a teleconference system 

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

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

2058 which. 

2059 

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

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

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

2063 

2064 Example: 

2065 The following are examples of this property: 

2066 

2067 .. code-block:: ics 

2068 

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

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

2071 CONFERENCE;VALUE=URI;FEATURE=PHONE; 

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

2073 CONFERENCE;VALUE=URI;FEATURE=PHONE; 

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

2075 CONFERENCE;VALUE=URI;FEATURE=CHAT; 

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

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

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

2079 

2080 Get all conferences: 

2081 

2082 .. code-block:: pycon 

2083 

2084 >>> from icalendar import Event 

2085 >>> event = Event() 

2086 >>> event.conferences 

2087 [] 

2088 

2089 Set a conference: 

2090 

2091 .. code-block:: pycon 

2092 

2093 >>> from icalendar import Event, Conference 

2094 >>> event = Event() 

2095 >>> event.conferences = [ 

2096 ... Conference( 

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

2098 ... feature="PHONE,MODERATOR", 

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

2100 ... language="EN", 

2101 ... ) 

2102 ... ] 

2103 >>> print(event.to_ical()) 

2104 BEGIN:VEVENT 

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

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

2107 END:VEVENT 

2108 

2109 """ 

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

2111 if not isinstance(conferences, SEQUENCE_TYPES): 

2112 conferences = [conferences] 

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

2114 

2115 

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

2117 """Set the conferences.""" 

2118 _del_conferences(self) 

2119 for conference in conferences or []: 

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

2121 

2122 

2123def _del_conferences(self: Component): 

2124 """Delete all conferences.""" 

2125 self.pop("CONFERENCE") 

2126 

2127 

2128conferences_property = property(_get_conferences, _set_conferences, _del_conferences) 

2129 

2130 

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

2132 """LINK properties as a list. 

2133 

2134 Purpose: 

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

2136 

2137 Property Parameters: 

2138 The VALUE parameter is required. 

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

2140 can also be specified on this property. 

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

2142 

2143 Conformance: 

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

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

2146 The LINKREL parameter is required. 

2147 

2148 Description: 

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

2150 additional information related to the component. 

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

2152 

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

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

2155 the link context is the containing calendar entity, 

2156 and the link relation type and any target attributes 

2157 are carried in iCalendar property parameters. 

2158 

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

2160 

2161 LABEL 

2162 This parameter maps to the "title" 

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

2164 LABEL is used to label the destination 

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

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

2167 (if present). 

2168 LANGUAGE 

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

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

2171 LINKREL 

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

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

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

2175 FMTTYPE 

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

2177 :rfc:`8288`. 

2178 

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

2180 

2181 Examples: 

2182 The following is an example of this property, 

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

2184 

2185 .. code-block:: ics 

2186 

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

2188 https://example.com/events 

2189 

2190 The following is an example of this property, 

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

2192 The link relation is a vendor-defined value. 

2193 

2194 .. code-block:: ics 

2195 

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

2197 VALUE=URI: 

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

2199 

2200 The following is an example of this property, 

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

2202 The link relation is a vendor-defined value. 

2203 

2204 .. code-block:: ics 

2205 

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

2207 VALUE=XML-REFERENCE: 

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

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

2210 following::CostStrucEND[1])) 

2211 

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

2213 

2214 .. code-block:: pycon 

2215 

2216 >>> from icalendar import Event, vUri 

2217 >>> from datetime import datetime 

2218 >>> link = vUri( 

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

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

2221 ... ) 

2222 >>> event = Event.new( 

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

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

2225 ... ) 

2226 >>> event.links = [link] 

2227 >>> print(event.to_ical()) 

2228 BEGIN:VEVENT 

2229 SUMMARY:An Example Event with a page 

2230 DTSTART:20250917T120000 

2231 DTSTAMP:20250517T080612Z 

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

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

2234 END:VEVENT 

2235 

2236 """ 

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

2238 if not isinstance(links, list): 

2239 links = [links] 

2240 return links 

2241 

2242 

2243LINKS_TYPE_SETTER: TypeAlias = ( 

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

2245) 

2246 

2247 

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

2249 """Set the LINKs.""" 

2250 _del_links(self) 

2251 if links is None: 

2252 return 

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

2254 links = [links] 

2255 for link in links: 

2256 if type(link) is str: 

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

2258 self.add("LINK", link) 

2259 

2260 

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

2262 """Delete all links.""" 

2263 self.pop("LINK") 

2264 

2265 

2266links_property = property(_get_links, _set_links, _del_links) 

2267 

2268RELATED_TO_TYPE_SETTER: TypeAlias = ( 

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

2270) 

2271 

2272 

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

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

2275 

2276 Purpose: 

2277 This property is used to represent a relationship or reference 

2278 between one calendar component and another. 

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

2280 

2281 Value Type: 

2282 :rfc:`5545`: TEXT 

2283 :rfc:`9253`: URI, UID 

2284 

2285 Conformance: 

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

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

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

2289 iCalendar component. 

2290 

2291 Description (:rfc:`5545`): 

2292 The property value consists of the persistent, globally 

2293 unique identifier of another calendar component. This value would 

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

2295 

2296 By default, the property value points to another calendar 

2297 component that has a PARENT relationship to the referencing 

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

2299 explicitly state the default PARENT relationship type to the 

2300 referenced calendar component or to override the default PARENT 

2301 relationship type and specify either a CHILD or SIBLING 

2302 relationship. The PARENT relationship indicates that the calendar 

2303 component is a subordinate of the referenced calendar component. 

2304 The CHILD relationship indicates that the calendar component is a 

2305 superior of the referenced calendar component. The SIBLING 

2306 relationship indicates that the calendar component is a peer of 

2307 the referenced calendar component. 

2308 

2309 Changes to a calendar component referenced by this property can 

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

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

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

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

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

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

2316 property is intended only to provide information on the 

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

2318 calendar system to maintain any property implications of this 

2319 relationship. 

2320 

2321 Description (:rfc:`9253`): 

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

2323 consists of the persistent, globally unique identifier of another 

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

2325 component by the UID property. 

2326 

2327 By default, the property value 

2328 points to another calendar component that has a PARENT relationship 

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

2330 to either explicitly state the default PARENT relationship type to 

2331 the referenced calendar component or to override the default 

2332 PARENT relationship type and specify either a CHILD or SIBLING 

2333 relationship or a temporal relationship. 

2334 

2335 The PARENT relationship 

2336 indicates that the calendar component is a subordinate of the 

2337 referenced calendar component. The CHILD relationship indicates 

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

2339 component. The SIBLING relationship indicates that the calendar 

2340 component is a peer of the referenced calendar component. 

2341 

2342 To preserve backwards compatibility, the value type MUST 

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

2344 are specified. 

2345 

2346 The FINISHTOSTART, FINISHTOFINISH, STARTTOFINISH, 

2347 or STARTTOSTART relationships define temporal relationships, as 

2348 specified in the RELTYPE parameter definition. 

2349 

2350 The FIRST and NEXT 

2351 define ordering relationships between calendar components. 

2352 

2353 The DEPENDS-ON relationship indicates that the current calendar 

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

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

2356 referenced, task. 

2357 

2358 The REFID and CONCEPT relationships establish 

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

2360 Changes to a calendar component referenced by this property 

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

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

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

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

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

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

2367 components. This property is intended only to provide information 

2368 on the relationship of calendar components. 

2369 

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

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

2372 

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

2374 implications of these relationships. 

2375 

2376 Examples: 

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

2378 

2379 .. code-block:: ics 

2380 

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

2382 

2383 .. code-block:: ics 

2384 

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

2386 

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

2388 

2389 .. code-block:: ics 

2390 

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

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

2393 19960401-080045-4000F192713.ics 

2394 

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

2396 

2397 """ 

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

2399 if not isinstance(result, list): 

2400 return [result] 

2401 return result 

2402 

2403 

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

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

2406 _del_related_to(self) 

2407 if values is None: 

2408 return 

2409 if not isinstance(values, list): 

2410 values = [values] 

2411 for value in values: 

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

2413 

2414 

2415def _del_related_to(self: Component): 

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

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

2418 

2419 

2420related_to_property = property(_get_related_to, _set_related_to, _del_related_to) 

2421 

2422 

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

2424 """CONCEPT 

2425 

2426 Purpose: 

2427 CONCEPT defines the formal categories for a calendar component. 

2428 

2429 Conformance: 

2430 Since :rfc:`9253`, 

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

2432 

2433 Description: 

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

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

2436 component of a particular type and category. 

2437 

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

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

2440 the CONCEPT property will reference an external resource that provides 

2441 information about the categorization. 

2442 

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

2444 events. 

2445 

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

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

2448 

2449 Examples: 

2450 The following is an example of this property. 

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

2452 

2453 .. code-block:: ics 

2454 

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

2456 

2457 .. seealso:: 

2458 

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

2460 """ 

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

2462 if not isinstance(concepts, list): 

2463 concepts = [concepts] 

2464 return concepts 

2465 

2466 

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

2468 

2469 

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

2471 """Set the concepts.""" 

2472 _del_concepts(self) 

2473 if concepts is None: 

2474 return 

2475 if not isinstance(concepts, list): 

2476 concepts = [concepts] 

2477 for value in concepts: 

2478 self.add("CONCEPT", value) 

2479 

2480 

2481def _del_concepts(self: Component): 

2482 """Delete the concepts.""" 

2483 self.pop("CONCEPT", None) 

2484 

2485 

2486concepts_property = property(_get_concepts, _set_concepts, _del_concepts) 

2487 

2488 

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

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

2491 

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

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

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

2495 if not isinstance(value, list): 

2496 value = [value] 

2497 return value 

2498 

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

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

2501 fdel(self) 

2502 if value is None: 

2503 return 

2504 if not isinstance(value, list): 

2505 value = [value] 

2506 for value in value: 

2507 self.add(name, value) 

2508 

2509 def fdel(self: Component): 

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

2511 self.pop(name, None) 

2512 

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

2514 

2515 

2516refids_property = multi_string_property( 

2517 "REFID", 

2518 """REFID 

2519 

2520Purpose: 

2521 REFID acts as a key for associated iCalendar entities. 

2522 

2523Conformance: 

2524 Since :rfc:`9253`, 

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

2526 

2527Description: 

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

2529 identifier for associated components. 

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

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

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

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

2534 

2535Examples: 

2536 The following is an example of this property. 

2537 

2538 .. code-block:: ics 

2539 

2540 REFID:itinerary-2014-11-17 

2541 

2542 Use a REFID to associate several VTODOs: 

2543 

2544 .. code-block:: pycon 

2545 

2546 >>> from icalendar import Todo 

2547 >>> todo_1 = Todo.new( 

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

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

2550 ... ) 

2551 >>> todo_2 = Todo.new( 

2552 ... summary="pack backpack", 

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

2554 ... ) 

2555 >>> todo_1.refids == todo_2.refids 

2556 True 

2557 

2558.. note:: 

2559 

2560 List modifications do not modify the component. 

2561""", 

2562) 

2563 

2564 

2565__all__ = [ 

2566 "CONCEPTS_TYPE_SETTER", 

2567 "LINKS_TYPE_SETTER", 

2568 "RECURRENCE_ID", 

2569 "RELATED_TO_TYPE_SETTER", 

2570 "attendees_property", 

2571 "busy_type_property", 

2572 "categories_property", 

2573 "class_property", 

2574 "color_property", 

2575 "comments_property", 

2576 "concepts_property", 

2577 "conferences_property", 

2578 "contacts_property", 

2579 "create_single_property", 

2580 "description_property", 

2581 "descriptions_property", 

2582 "duration_property", 

2583 "exdates_property", 

2584 "get_duration_property", 

2585 "get_end_property", 

2586 "get_start_end_duration_with_validation", 

2587 "get_start_property", 

2588 "images_property", 

2589 "links_property", 

2590 "location_property", 

2591 "multi_language_text_property", 

2592 "multi_string_property", 

2593 "organizer_property", 

2594 "priority_property", 

2595 "property_del_duration", 

2596 "property_doc_duration_template", 

2597 "property_get_duration", 

2598 "property_set_duration", 

2599 "rdates_property", 

2600 "refids_property", 

2601 "related_to_property", 

2602 "repeat_property", 

2603 "rfc_7953_dtend_property", 

2604 "rfc_7953_dtstart_property", 

2605 "rfc_7953_duration_property", 

2606 "rfc_7953_end_property", 

2607 "rrules_property", 

2608 "sequence_property", 

2609 "set_duration_with_locking", 

2610 "set_end_with_locking", 

2611 "set_start_with_locking", 

2612 "single_int_property", 

2613 "single_utc_property", 

2614 "source_property", 

2615 "status_property", 

2616 "summary_property", 

2617 "transparency_property", 

2618 "uid_property", 

2619 "url_property", 

2620]