Repository: collective/icalendar
Pull request #801: Add VCALENDAR properties NAME, DESCRIPTION and COLOR as attributes
Author: login: niccokunzmann
Created: 2025-04-18T17:22:35Z | Merged: 2025-04-21T11:12:56Z

This is a start on #655 and adds these attributes the the calendar component:

- NAME with compatibility to X-WR-CALNAME
- DESCRIPTION with compatibility to X-WR-CALDESC
- COLOR with compatibility to X-APPLE-CALENDAR-COLOR


<!-- readthedocs-preview icalendar start -->
----
📚 Documentation preview 📚: https://icalendar--801.org.readthedocs.build/

<!-- readthedocs-preview icalendar end -->


Diff:
diff --git a/CHANGES.rst b/CHANGES.rst
index 5e563bb55..b18c28bd3 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -15,7 +15,7 @@ Breaking changes:
 
 New features:
 
-- ...
+- Add attributes to the calendar for properties ``NAME``, ``DESCRIPTION``, and ``COLOR``. See `Issue 655 <https://github.com/collective/icalendar/issues/655>`_.
 
 Bug fixes:
 
diff --git a/src/icalendar/attr.py b/src/icalendar/attr.py
new file mode 100644
index 000000000..b6db7ad97
--- /dev/null
+++ b/src/icalendar/attr.py
@@ -0,0 +1,40 @@
+"""Attributes of Components and properties."""
+from __future__ import annotations
+
+from typing import Optional
+
+
+def multi_language_text_property(main_prop:str, compatibility_prop:str, doc:str) -> property:
+    """This creates a text property.
+
+    This property can be defined several times with different ``LANGUAGE`` parameters.
+
+    Args:
+
+        main_prop: The property to set and get, such as ``NAME``
+        compatibility_prop: An old property used before, such as ``X-WR-CALNAME``
+        doc: The documentation string
+    """
+    def fget(self) -> Optional[str]:
+        """Get the property"""
+        result = self.get(main_prop, self.get(compatibility_prop))
+        if isinstance(result, list):
+            for item in result:
+                if "LANGUAGE" not in item.params:
+                    return item
+        return result
+
+    def fset(self, value:str):
+        """Set the property."""
+        fdel(self)
+        self.add(main_prop, value)
+
+    def fdel(self):
+        """Delete the property."""
+        self.pop(main_prop, None)
+        self.pop(compatibility_prop, None)
+
+    return property(fget, fset, fdel, doc)
+
+
+__all__ = ["multi_language_text_property"]
diff --git a/src/icalendar/cal.py b/src/icalendar/cal.py
index 9309b7b12..eebc918c4 100644
--- a/src/icalendar/cal.py
+++ b/src/icalendar/cal.py
@@ -16,6 +16,7 @@
 import dateutil.rrule
 import dateutil.tz
 
+from icalendar.attr import multi_language_text_property
 from icalendar.caselessdict import CaselessDict
 from icalendar.parser import Contentline, Contentlines, Parameters, q_join, q_split
 from icalendar.parser_tools import DEFAULT_ENCODING
@@ -1898,6 +1899,10 @@ class Calendar(Component):
         "PRODID",
         "CALSCALE",
         "METHOD",
+        "DESCRIPTION",
+        "X-WR-CALDESC",
+        "NAME",
+        "X-WR-CALNAME",
     )
     required = (
         "PRODID",
@@ -2055,6 +2060,107 @@ def add_missing_timezones(
                 continue
             self.add_component(timezone)
 
+    calendar_name = multi_language_text_property(
+        "NAME", "X-WR-CALNAME",
+        """This property specifies the name of the calendar.
+
+    This implements :rfc:`7986` ``NAME`` and ``X-WR-CALNAME``.
+
+    Property Parameters:
+
+        IANA, non-standard, alternate text
+        representation, and language property parameters can be specified
+        on this property.
+
+    Conformance:
+
+        This property can be specified multiple times in an
+        iCalendar object.  However, each property MUST represent the name
+        of the calendar in a different language.
+
+    Description:
+
+        This property is used to specify a name of the
+        iCalendar object that can be used by calendar user agents when
+        presenting the calendar data to a user.  Whilst a calendar only
+        has a single name, multiple language variants can be specified by
+        including this property multiple times with different "LANGUAGE"
+        parameter values on each.
+
+    >>> from icalendar import Calendar
+    >>> calendar = Calendar()
+    >>> calendar.calendar_name = "My Calendar"
+    >>> print(calendar.to_ical())
+    BEGIN:VCALENDAR
+    NAME:My Calendar
+    END:VCALENDAR
+    """)
+
+    description = multi_language_text_property(
+        "DESCRIPTION", "X-WR-CALDESC",
+        """This property specifies the description of the calendar.
+
+    This implements :rfc:`7986` ``DESCRIPTION`` and ``X-WR-CALDESC``.
+
+    Conformance:
+
+        This property can be specified multiple times in an
+        iCalendar object.  However, each property MUST represent the
+        description of the calendar in a different language.
+
+    Description:
+
+        This property is used to specify a lengthy textual
+        description of the iCalendar object that can be used by calendar
+        user agents when describing the nature of the calendar data to a
+        user.  Whilst a calendar only has a single description, multiple
+        language variants can be specified by including this property
+        multiple times with different "LANGUAGE" parameter values on each.
+
+    >>> from icalendar import Calendar
+    >>> calendar = Calendar()
+    >>> calendar.description = "This is a calendar"
+    >>> print(calendar.to_ical())
+    BEGIN:VCALENDAR
+    DESCRIPTION:This is a calendar
+    END:VCALENDAR
+    """)
+
+    color = multi_language_text_property(
+        "COLOR", "X-APPLE-CALENDAR-COLOR",
+        """This property specifies a color used for displaying the calendar.
+
+    This implements :rfc:`7986` ``COLOR`` and ``X-APPLE-CALENDAR-COLOR``.
+
+    Property Parameters:
+
+        IANA and non-standard property parameters can
+        be specified on this property.
+
+    Conformance:
+
+        This property can be specified once in an iCalendar
+        object or in ``VEVENT``, ``VTODO``, or ``VJOURNAL`` calendar components.
+
+    Description:
+
+        This property specifies a color that clients MAY use
+        when presenting the relevant data to a user.  Typically, this
+        would appear as the "background" color of events or tasks.  The
+        value is a case-insensitive color name taken from the CSS3 set of
+        names, defined in Section 4.3 of `W3C.REC-css3-color-20110607 <https://www.w3.org/TR/css-color-3/>`_.
+
+    Example: ``"turquoise"``, ``"#ffffff"``
+
+    >>> from icalendar import Calendar
+    >>> calendar = Calendar()
+    >>> calendar.color = "black"
+    >>> print(calendar.to_ical())
+    BEGIN:VCALENDAR
+    COLOR:black
+    END:VCALENDAR
+    """
+    )
 
 # These are read only singleton, so one instance is enough for the module
 types_factory = TypesFactory()
diff --git a/src/icalendar/tests/test_rfc_7986.py b/src/icalendar/tests/test_rfc_7986.py
new file mode 100644
index 000000000..23ae26653
--- /dev/null
+++ b/src/icalendar/tests/test_rfc_7986.py
@@ -0,0 +1,114 @@
+"""This tests additional attributes from :rfc:`7986`.
+
+Some attributes are also available as ``X-*`` attributes.
+They are also considered.
+"""
+
+import pytest
+
+from icalendar import Calendar
+from icalendar.prop import vText
+
+
+@pytest.fixture()
+def calendar() -> Calendar:
+    """Empty calendar"""
+    return Calendar()
+
+
+param_name = pytest.mark.parametrize("name", ["Company Vacation Days", "Calendar Name"])
+param_prop = pytest.mark.parametrize("prop", ["NAME", "X-WR-CALNAME"])
+
+
+@param_prop
+@param_name
+def test_get_calendar_name(prop, name, calendar):
+    """Get the name of the calendar."""
+    calendar.add(prop, name)
+    assert calendar.calendar_name == name
+
+
+@param_name
+def test_set_calendar_name(name, calendar):
+    """Setting the name overrides the old attributes."""
+    calendar.calendar_name = name
+    assert calendar.calendar_name == name
+    assert calendar["NAME"] == name
+
+
+@param_name
+@param_prop
+def test_replace_name(name, prop, calendar):
+    """Setting the name overrides the old attributes."""
+    calendar[prop] = "Other Name"
+    calendar.calendar_name = name
+    assert calendar.calendar_name == name
+
+
+@param_name
+@param_prop
+def test_del_name(name, calendar, prop):
+    """Delete the name."""
+    calendar.add(prop, name)
+    del calendar.calendar_name
+    assert calendar.calendar_name is None
+
+
+def test_default_name(calendar):
+    """We have no name by default."""
+    assert calendar.calendar_name is None
+
+
+@param_name
+def test_setting_the_name_deletes_the_non_standard_attribute(calendar, name):
+    """The default_attr is deleted when setting the name."""
+    calendar["X-WR-CALNAME"] = name
+    assert "X-WR-CALNAME" in calendar
+    calendar.calendar_name = "other name"
+    assert "X-WR-CALNAME" not in calendar
+
+
+@param_name
+@pytest.mark.parametrize("order", [1, 2])
+def test_multiple_names_use_the_one_without_a_language(calendar, name, order):
+    """Add several names and use the one without a language param."""
+    if order == 1:
+        calendar.add("NAME", name)
+    calendar.add("NAME", vText("Kalendername", params={"LANGUAGE":"de"}))
+    if order == 2:
+        calendar.add("NAME", name)
+    assert calendar.calendar_name == name
+
+
+@param_name
+def test_name_is_preferred(calendar, name):
+    """NAME is more important that X-WR-CALNAME"""
+    calendar.add("NAME", name)
+    calendar.add("X-WR-CALNAME", "asd")
+    assert calendar.calendar_name == name
+
+
+
+# For description, we would use the same tests as name, but we also use the
+# same code, so it is all right.
+
+param_color = pytest.mark.parametrize("desc", ["DESCRIPTION", "X-WR-CALDESC"])
+
+@param_color
+@param_name
+def test_description(calendar, desc, name):
+    """Get the value"""
+    calendar.add(desc, name)
+    assert calendar.description == name
+
+# For color, we would use the same tests as name, but we also use the
+# same code, so it is all right.
+
+param_color = pytest.mark.parametrize("color", ["COLOR", "X-APPLE-CALENDAR-COLOR"])
+
+@param_color
+@param_name
+def test_color(calendar, color, name):
+    """Get the value"""
+    calendar.add(color, name)
+    assert calendar.color == name


Reviews:
user: login: stevepiercy
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-04-18T18:57:02Z

Review comments:
diff_hunk: @@ -15,7 +15,7 @@ Breaking changes:
 
 New features:
 
-- ...
+- Add attributes to the calendar for properties NAME, DESCRIPTION and COLOR, see `Issue 655 <https://github.com/collective/icalendar/issues/655>`_.
path: CHANGES.rst
user: login: stevepiercy
body: ```suggestion
- Add attributes to the calendar for properties ``NAME``, ``DESCRIPTION``, and ``COLOR``. See `Issue 655 <https://github.com/collective/icalendar/issues/655>`_.
```
created_at: 2025-04-18T18:37:03Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,40 @@
+"""Attributes of Components and properties."""
+from __future__ import annotations
+
+from typing import Optional
+
+
+def multi_language_text_property(main_prop:str, compatibility_prop:str, doc:str) -> property:
+    """This creates a text property.
+
+    This property can be defined several times with different LANGAUGE parameters.
path: src/icalendar/attr.py
user: login: stevepiercy
body: ```suggestion
    This property can be defined several times with different ``LANGUAGE`` parameters.
```
created_at: 2025-04-18T18:38:50Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,40 @@
+"""Attributes of Components and properties."""
+from __future__ import annotations
+
+from typing import Optional
+
+
+def multi_language_text_property(main_prop:str, compatibility_prop:str, doc:str) -> property:
+    """This creates a text property.
+
+    This property can be defined several times with different LANGAUGE parameters.
+
+    Args:
+
+        main_prop: The property to set and get, e.g. NAME
+        compatibility_prop: An old property used before, e.g. X-WR-CALNAME
path: src/icalendar/attr.py
user: login: stevepiercy
body: ```suggestion
        main_prop: The property to set and get, such as ``NAME``
        compatibility_prop: An old property used before, such as ``X-WR-CALNAME``
```
created_at: 2025-04-18T18:40:01Z
author_association: MEMBER
diff_hunk: @@ -2055,6 +2060,107 @@ def add_missing_timezones(
                 continue
             self.add_component(timezone)
 
+    calendar_name = multi_language_text_property(
+        "NAME", "X-WR-CALNAME",
+        """This property specifies the name of the calendar
path: src/icalendar/cal.py
user: login: stevepiercy
body: ```suggestion
        """This property specifies the name of the calendar.
```
created_at: 2025-04-18T18:45:19Z
author_association: MEMBER
diff_hunk: @@ -2055,6 +2060,107 @@ def add_missing_timezones(
                 continue
             self.add_component(timezone)
 
+    calendar_name = multi_language_text_property(
+        "NAME", "X-WR-CALNAME",
+        """This property specifies the name of the calendar
+
+    This takes care of :rfc:`7986` ``NAME`` and ``X-WR-CALNAME``.
+
+    Property Parameters:
+
+        IANA, non-standard, alternate text
+        representation, and language property parameters can be specified
+        on this property.
+
+    Conformance:
+
+        This property can be specified multiple times in an
+        iCalendar object.  However, each property MUST represent the name
+        of the calendar in a different language.
+
+    Description:
+
+        This property is used to specify a name of the
+        iCalendar object that can be used by calendar user agents when
+        presenting the calendar data to a user.  Whilst a calendar only
+        has a single name, multiple language variants can be specified by
+        including this property multiple times with different "LANGUAGE"
+        parameter values on each.
+
+    >>> from icalendar import Calendar
+    >>> calendar = Calendar()
+    >>> calendar.calendar_name = "My Calendar"
+    >>> print(calendar.to_ical())
+    BEGIN:VCALENDAR
+    NAME:My Calendar
+    END:VCALENDAR
+    """)
+
+    description = calendar_description = multi_language_text_property(
+        "DESCRIPTION", "X-WR-CALDESC",
+        """This property specifies the description of the calendar.
+
+    This takes care of :rfc:`7986` ``DESCRIPTION`` and ``X-WR-CALDESC``.
+
+    Conformance:
+
+        This property can be specified multiple times in an
+        iCalendar object.  However, each property MUST represent the
+        description of the calendar in a different language.
+
+    Description:
+
+        This property is used to specify a lengthy textual
+        description of the iCalendar object that can be used by calendar
+        user agents when describing the nature of the calendar data to a
+        user.  Whilst a calendar only has a single description, multiple
+        language variants can be specified by including this property
+        multiple times with different "LANGUAGE" parameter values on each.
+
+    >>> from icalendar import Calendar
+    >>> calendar = Calendar()
+    >>> calendar.description = "This is a calendar"
+    >>> print(calendar.to_ical())
+    BEGIN:VCALENDAR
+    DESCRIPTION:This is a calendar
+    END:VCALENDAR
+    """)
+
+    color = calendar_color = multi_language_text_property(
+        "COLOR", "X-APPLE-CALENDAR-COLOR",
+        """This property specifies a color used for displaying the calendar.
+
+    This takes care of :rfc:`7986` ``COLOR`` and ``X-APPLE-CALENDAR-COLOR``.
path: src/icalendar/cal.py
user: login: stevepiercy
body: ```suggestion
    This implements :rfc:`7986` ``COLOR`` and ``X-APPLE-CALENDAR-COLOR``.
```
created_at: 2025-04-18T18:47:03Z
author_association: MEMBER
diff_hunk: @@ -2055,6 +2060,107 @@ def add_missing_timezones(
                 continue
             self.add_component(timezone)
 
+    calendar_name = multi_language_text_property(
+        "NAME", "X-WR-CALNAME",
+        """This property specifies the name of the calendar
+
+    This takes care of :rfc:`7986` ``NAME`` and ``X-WR-CALNAME``.
+
+    Property Parameters:
+
+        IANA, non-standard, alternate text
+        representation, and language property parameters can be specified
+        on this property.
+
+    Conformance:
+
+        This property can be specified multiple times in an
+        iCalendar object.  However, each property MUST represent the name
+        of the calendar in a different language.
+
+    Description:
+
+        This property is used to specify a name of the
+        iCalendar object that can be used by calendar user agents when
+        presenting the calendar data to a user.  Whilst a calendar only
+        has a single name, multiple language variants can be specified by
+        including this property multiple times with different "LANGUAGE"
+        parameter values on each.
+
+    >>> from icalendar import Calendar
+    >>> calendar = Calendar()
+    >>> calendar.calendar_name = "My Calendar"
+    >>> print(calendar.to_ical())
+    BEGIN:VCALENDAR
+    NAME:My Calendar
+    END:VCALENDAR
+    """)
+
+    description = calendar_description = multi_language_text_property(
+        "DESCRIPTION", "X-WR-CALDESC",
+        """This property specifies the description of the calendar.
+
+    This takes care of :rfc:`7986` ``DESCRIPTION`` and ``X-WR-CALDESC``.
+
+    Conformance:
+
+        This property can be specified multiple times in an
+        iCalendar object.  However, each property MUST represent the
+        description of the calendar in a different language.
+
+    Description:
+
+        This property is used to specify a lengthy textual
+        description of the iCalendar object that can be used by calendar
+        user agents when describing the nature of the calendar data to a
+        user.  Whilst a calendar only has a single description, multiple
+        language variants can be specified by including this property
+        multiple times with different "LANGUAGE" parameter values on each.
+
+    >>> from icalendar import Calendar
+    >>> calendar = Calendar()
+    >>> calendar.description = "This is a calendar"
+    >>> print(calendar.to_ical())
+    BEGIN:VCALENDAR
+    DESCRIPTION:This is a calendar
+    END:VCALENDAR
+    """)
+
+    color = calendar_color = multi_language_text_property(
+        "COLOR", "X-APPLE-CALENDAR-COLOR",
+        """This property specifies a color used for displaying the calendar.
+
+    This takes care of :rfc:`7986` ``COLOR`` and ``X-APPLE-CALENDAR-COLOR``.
+
+    Property Parameters:
+
+        IANA and non-standard property parameters can
+        be specified on this property.
+
+    Conformance:
+
+        This property can be specified once in an iCalendar
+        object or in "VEVENT", "VTODO", or "VJOURNAL" calendar components.
path: src/icalendar/cal.py
user: login: stevepiercy
body: ```suggestion
        object or in ``VEVENT``, ``VTODO``, or ``VJOURNAL`` calendar components.
```
created_at: 2025-04-18T18:47:41Z
author_association: MEMBER
diff_hunk: @@ -2055,6 +2060,107 @@ def add_missing_timezones(
                 continue
             self.add_component(timezone)
 
+    calendar_name = multi_language_text_property(
+        "NAME", "X-WR-CALNAME",
+        """This property specifies the name of the calendar
+
+    This takes care of :rfc:`7986` ``NAME`` and ``X-WR-CALNAME``.
+
+    Property Parameters:
+
+        IANA, non-standard, alternate text
+        representation, and language property parameters can be specified
+        on this property.
+
+    Conformance:
+
+        This property can be specified multiple times in an
+        iCalendar object.  However, each property MUST represent the name
+        of the calendar in a different language.
+
+    Description:
+
+        This property is used to specify a name of the
+        iCalendar object that can be used by calendar user agents when
+        presenting the calendar data to a user.  Whilst a calendar only
+        has a single name, multiple language variants can be specified by
+        including this property multiple times with different "LANGUAGE"
+        parameter values on each.
+
+    >>> from icalendar import Calendar
+    >>> calendar = Calendar()
+    >>> calendar.calendar_name = "My Calendar"
+    >>> print(calendar.to_ical())
+    BEGIN:VCALENDAR
+    NAME:My Calendar
+    END:VCALENDAR
+    """)
+
+    description = calendar_description = multi_language_text_property(
+        "DESCRIPTION", "X-WR-CALDESC",
+        """This property specifies the description of the calendar.
+
+    This takes care of :rfc:`7986` ``DESCRIPTION`` and ``X-WR-CALDESC``.
+
+    Conformance:
+
+        This property can be specified multiple times in an
+        iCalendar object.  However, each property MUST represent the
+        description of the calendar in a different language.
+
+    Description:
+
+        This property is used to specify a lengthy textual
+        description of the iCalendar object that can be used by calendar
+        user agents when describing the nature of the calendar data to a
+        user.  Whilst a calendar only has a single description, multiple
+        language variants can be specified by including this property
+        multiple times with different "LANGUAGE" parameter values on each.
+
+    >>> from icalendar import Calendar
+    >>> calendar = Calendar()
+    >>> calendar.description = "This is a calendar"
+    >>> print(calendar.to_ical())
+    BEGIN:VCALENDAR
+    DESCRIPTION:This is a calendar
+    END:VCALENDAR
+    """)
+
+    color = calendar_color = multi_language_text_property(
+        "COLOR", "X-APPLE-CALENDAR-COLOR",
+        """This property specifies a color used for displaying the calendar.
+
+    This takes care of :rfc:`7986` ``COLOR`` and ``X-APPLE-CALENDAR-COLOR``.
+
+    Property Parameters:
+
+        IANA and non-standard property parameters can
+        be specified on this property.
+
+    Conformance:
+
+        This property can be specified once in an iCalendar
+        object or in "VEVENT", "VTODO", or "VJOURNAL" calendar components.
+
+    Description:
+
+        This property specifies a color that clients MAY use
+        when presenting the relevant data to a user.  Typically, this
+        would appear as the "background" color of events or tasks.  The
+        value is a case-insensitive color name taken from the CSS3 set of
+        names, defined in Section 4.3 of [W3C.REC-css3-color-20110607].
path: src/icalendar/cal.py
user: login: stevepiercy
body: ```suggestion
        names, defined in Section 4.3 of `W3C.REC-css3-color-20110607 <https://www.w3.org/TR/css-color-3/>`_.
```
created_at: 2025-04-18T18:48:51Z
author_association: MEMBER
diff_hunk: @@ -2055,6 +2060,107 @@ def add_missing_timezones(
                 continue
             self.add_component(timezone)
 
+    calendar_name = multi_language_text_property(
+        "NAME", "X-WR-CALNAME",
+        """This property specifies the name of the calendar
+
+    This takes care of :rfc:`7986` ``NAME`` and ``X-WR-CALNAME``.
path: src/icalendar/cal.py
user: login: stevepiercy
body: ```suggestion
    This implements :rfc:`7986` ``NAME`` and ``X-WR-CALNAME``.
```
created_at: 2025-04-18T18:49:21Z
author_association: MEMBER
diff_hunk: @@ -2055,6 +2060,107 @@ def add_missing_timezones(
                 continue
             self.add_component(timezone)
 
+    calendar_name = multi_language_text_property(
+        "NAME", "X-WR-CALNAME",
+        """This property specifies the name of the calendar
+
+    This takes care of :rfc:`7986` ``NAME`` and ``X-WR-CALNAME``.
+
+    Property Parameters:
+
+        IANA, non-standard, alternate text
+        representation, and language property parameters can be specified
+        on this property.
+
+    Conformance:
+
+        This property can be specified multiple times in an
+        iCalendar object.  However, each property MUST represent the name
+        of the calendar in a different language.
+
+    Description:
+
+        This property is used to specify a name of the
+        iCalendar object that can be used by calendar user agents when
+        presenting the calendar data to a user.  Whilst a calendar only
+        has a single name, multiple language variants can be specified by
+        including this property multiple times with different "LANGUAGE"
+        parameter values on each.
+
+    >>> from icalendar import Calendar
+    >>> calendar = Calendar()
+    >>> calendar.calendar_name = "My Calendar"
+    >>> print(calendar.to_ical())
+    BEGIN:VCALENDAR
+    NAME:My Calendar
+    END:VCALENDAR
+    """)
+
+    description = calendar_description = multi_language_text_property(
+        "DESCRIPTION", "X-WR-CALDESC",
+        """This property specifies the description of the calendar.
+
+    This takes care of :rfc:`7986` ``DESCRIPTION`` and ``X-WR-CALDESC``.
path: src/icalendar/cal.py
user: login: stevepiercy
body: ```suggestion
    This implements :rfc:`7986` ``DESCRIPTION`` and ``X-WR-CALDESC``.
```
created_at: 2025-04-18T18:49:31Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,114 @@
+"""This tests additional attributes from RFC 7986.
path: src/icalendar/tests/test_rfc_7986.py
user: login: stevepiercy
body: ```suggestion
"""This tests additional attributes from :rfc:`7986`.
```
created_at: 2025-04-18T18:50:08Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,114 @@
+"""This tests additional attributes from RFC 7986.
+
+Some attributes are also available as X-... attributes.
path: src/icalendar/tests/test_rfc_7986.py
user: login: stevepiercy
body: ```suggestion
Some attributes are also available as ``X-*`` attributes.
```
created_at: 2025-04-18T18:50:22Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,114 @@
+"""This tests additional attributes from RFC 7986.
+
+Some attributes are also available as X-... attributes.
+They are also considered.
+"""
+
+import pytest
+
+from icalendar import Calendar
+from icalendar.prop import vText
+
+
+@pytest.fixture()
+def calendar() -> Calendar:
+    """Empty calendar"""
+    return Calendar()
+
+
+param_name = pytest.mark.parametrize("name", ["Company Vacation Days", "Calendar Name"])
+param_prop = pytest.mark.parametrize("prop", ["NAME", "X-WR-CALNAME"])
+
+
+@param_prop
+@param_name
+def test_get_calendar_name(prop, name, calendar):
+    """Get the name of the calendar."""
+    calendar.add(prop, name)
+    assert calendar.calendar_name == name
+
+
+@param_name
+def test_set_calendar_name(name, calendar):
+    """Setting the name overrides the old attributes."""
+    calendar.calendar_name = name
+    assert calendar.calendar_name == name
+    assert calendar["NAME"] == name
+
+
+@param_name
+@param_prop
+def test_replace_name(name, prop, calendar):
+    """Setting the name overrides the old attributes."""
+    calendar[prop] = "Other Name"
+    calendar.calendar_name = name
+    assert calendar.calendar_name == name
+
+
+@param_name
+@param_prop
+def test_del_name(name, calendar, prop):
+    """Delete the name."""
+    calendar.add(prop, name)
+    del calendar.calendar_name
+    assert calendar.calendar_name is None
+
+
+def test_default_name(calendar):
+    """We have no name by default."""
+    assert calendar.calendar_name is None
+
+
+@param_name
+def test_setting_the_name_deletes_the_non_standard_attribute(calendar, name):
+    """The default_attr is deleted when setting the name."""
+    calendar["X-WR-CALNAME"] = name
+    assert "X-WR-CALNAME" in calendar
+    calendar.calendar_name = "other name"
+    assert "X-WR-CALNAME" not in calendar
+
+
+@param_name
+@pytest.mark.parametrize("order", [1, 2])
+def test_multiple_names_use_the_one_without_a_language(calendar, name, order):
+    """Add several names and use the one without a language param."""
+    if order == 1:
+        calendar.add("NAME", name)
+    calendar.add("NAME", vText("Kalendername", params={"LANGUAGE":"de"}))
+    if order == 2:
+        calendar.add("NAME", name)
+    assert calendar.calendar_name == name
+
+
+@param_name
+def test_name_is_preferred(calendar, name):
+    """NAME is more important that X-WR-CALNAME"""
+    calendar.add("NAME", name)
+    calendar.add("X-WR-CALNAME", "asd")
+    assert calendar.calendar_name == name
+
+
+
+# For description, we would use the same tests as name but we also use the
+# same code, so it is alright.
path: src/icalendar/tests/test_rfc_7986.py
user: login: stevepiercy
body: ```suggestion
# For description, we would use the same tests as name, but we also use the
# same code, so it is all right.
```
created_at: 2025-04-18T18:56:32Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,114 @@
+"""This tests additional attributes from RFC 7986.
+
+Some attributes are also available as X-... attributes.
+They are also considered.
+"""
+
+import pytest
+
+from icalendar import Calendar
+from icalendar.prop import vText
+
+
+@pytest.fixture()
+def calendar() -> Calendar:
+    """Empty calendar"""
+    return Calendar()
+
+
+param_name = pytest.mark.parametrize("name", ["Company Vacation Days", "Calendar Name"])
+param_prop = pytest.mark.parametrize("prop", ["NAME", "X-WR-CALNAME"])
+
+
+@param_prop
+@param_name
+def test_get_calendar_name(prop, name, calendar):
+    """Get the name of the calendar."""
+    calendar.add(prop, name)
+    assert calendar.calendar_name == name
+
+
+@param_name
+def test_set_calendar_name(name, calendar):
+    """Setting the name overrides the old attributes."""
+    calendar.calendar_name = name
+    assert calendar.calendar_name == name
+    assert calendar["NAME"] == name
+
+
+@param_name
+@param_prop
+def test_replace_name(name, prop, calendar):
+    """Setting the name overrides the old attributes."""
+    calendar[prop] = "Other Name"
+    calendar.calendar_name = name
+    assert calendar.calendar_name == name
+
+
+@param_name
+@param_prop
+def test_del_name(name, calendar, prop):
+    """Delete the name."""
+    calendar.add(prop, name)
+    del calendar.calendar_name
+    assert calendar.calendar_name is None
+
+
+def test_default_name(calendar):
+    """We have no name by default."""
+    assert calendar.calendar_name is None
+
+
+@param_name
+def test_setting_the_name_deletes_the_non_standard_attribute(calendar, name):
+    """The default_attr is deleted when setting the name."""
+    calendar["X-WR-CALNAME"] = name
+    assert "X-WR-CALNAME" in calendar
+    calendar.calendar_name = "other name"
+    assert "X-WR-CALNAME" not in calendar
+
+
+@param_name
+@pytest.mark.parametrize("order", [1, 2])
+def test_multiple_names_use_the_one_without_a_language(calendar, name, order):
+    """Add several names and use the one without a language param."""
+    if order == 1:
+        calendar.add("NAME", name)
+    calendar.add("NAME", vText("Kalendername", params={"LANGUAGE":"de"}))
+    if order == 2:
+        calendar.add("NAME", name)
+    assert calendar.calendar_name == name
+
+
+@param_name
+def test_name_is_preferred(calendar, name):
+    """NAME is more important that X-WR-CALNAME"""
+    calendar.add("NAME", name)
+    calendar.add("X-WR-CALNAME", "asd")
+    assert calendar.calendar_name == name
+
+
+
+# For description, we would use the same tests as name but we also use the
+# same code, so it is alright.
+
+param_color = pytest.mark.parametrize("desc", ["DESCRIPTION", "X-WR-CALDESC"])
+
+@param_color
+@param_name
+def test_description(calendar, desc, name):
+    """Get the value"""
+    calendar.add(desc, name)
+    assert calendar.calendar_description == name
+
+# For color, we would use the same tests as name but we also use the
+# same code, so it is alright.
path: src/icalendar/tests/test_rfc_7986.py
user: login: stevepiercy
body: ```suggestion
# For color, we would use the same tests as name, but we also use the
# same code, so it is all right.
```
created_at: 2025-04-18T18:56:49Z
author_association: MEMBER

PR comments:
user: login: niccokunzmann
created_at: 2025-04-18T17:26:27Z
body: @stevepiercy what I am wondering about is this:

- the `name` attribute is kind-of misleading, so I use `calendar_name`
- the other attributes are doubled with and without `calendar_`. I wonder: What do you prefer?
author_association: MEMBER
user: login: coveralls
created_at: 2025-04-18T17:40:32Z
body: ## Pull Request Test Coverage Report for [Build 14570627573](https://coveralls.io/builds/73308676)


### Details

* **86** of **86**   **(100.0%)**  changed or added relevant lines in **3** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**0.06%**) to **96.518%**

---



|  Totals | [![Coverage Status](https://coveralls.io/builds/73308676/badge)](https://coveralls.io/builds/73308676) |
| :-- | --: |
| Change from base [Build 14557247183](https://coveralls.io/builds/73301550): |  0.06% |
| Covered Lines: | 4831 |
| Relevant Lines: | 4998 |

---
##### 💛  - [Coveralls](https://coveralls.io)

author_association: NONE
user: login: stevepiercy
created_at: 2025-04-18T18:55:16Z
body: > @stevepiercy what I am wondering about is this:
> 
>     * the `name` attribute is kind-of misleading, so I use `calendar_name`
> 
>     * the other attributes are doubled with and without `calendar_`. I wonder: What do you prefer?

When the context of the property is obvious, such as a calendar, then I don't see the need to prefix it with `calendar_`. That would be consistent with the RFCs.

For example, the following test, before and after refactoring.

```python
def test_get_calendar_name(prop, name, calendar):
    """Get the name of the calendar."""
    calendar.add(prop, name)
    assert calendar.calendar_name == name
```

```python
def test_get_calendar_name(prop, name, calendar):
    """Get the name of the calendar."""
    calendar.add(prop, name)
    assert calendar.name == name
```

The test name is clear and should remain the same. The `name` attribute clearly belongs to the `calendar` object.
author_association: MEMBER
user: login: niccokunzmann
created_at: 2025-04-19T17:54:15Z
body: > The test name is clear and should remain the same. The name attribute clearly belongs to the calendar object.

There is this problem that does not apply to any other attributes than `name`

```
class Calendar(...):
    name = "VCALENDAR" # used to identify the component
```

So, I would then choose `description` and `color` and maybe use `calendar_name`. I am thinking of this a bit more as I do not want to change the API but also be consistent with the future attributes that get added. There is likely no `event_name` but `Event` has a description:

> Purpose:  This property specifies a color used for displaying the
      calendar, event, todo, or journal data.

vs.

>    Purpose:  This property specifies the name of the calendar.

That would be my proposal:

- `calendar_name` (Calendar only + name conflict otherwise)
- `description` (Calendar + others)
- `color` (Calendar + others)
author_association: MEMBER
user: login: stevepiercy
created_at: 2025-04-19T21:16:37Z
body: I'm sorry, I don't follow you. Where is the conflict with `name`? It would be a method of `Calendar`, if refactored from `calendar_name` to `name`.

Usage:

```pycon
    >>> from icalendar import Calendar
    >>> calendar = Calendar()
    >>> calendar.name = "My Calendar"
    >>> print(calendar.to_ical())
    BEGIN:VCALENDAR
    NAME:My Calendar
    END:VCALENDAR
```
author_association: MEMBER
user: login: niccokunzmann
created_at: 2025-04-21T08:44:17Z
body: This is the line:

https://github.com/collective/icalendar/blob/8add6bfe64132555e9da5d5b376ae1e4d03e8008/src/icalendar/cal.py#L1895

And this interface is so old that we cannot change it without breaking numerous downstream packages.
author_association: MEMBER
user: login: stevepiercy
created_at: 2025-04-21T09:14:02Z
body: Ah, OK, now I see. meh. I guess that `calendar_name` will have to do.
author_association: MEMBER
user: login: niccokunzmann
created_at: 2025-04-21T10:19:33Z
body: Would you merge it? Should I merge it?
author_association: MEMBER
user: login: stevepiercy
created_at: 2025-04-21T10:41:17Z
body: Yes.
author_association: MEMBER