Source code for icalendar.prop.categories
"""CATEGORIES property values from :rfc:`5545`."""
from collections.abc import Iterator
from typing import Any, ClassVar
from xml.etree.ElementTree import Element
from icalendar.compatibility import Self
from icalendar.error import JCalParsingError
from icalendar.parser import Parameters
from icalendar.parser.xcal.base import XCalParser
from icalendar.parser.xcal.wrapper import from_xcal_wrapper
from icalendar.parser_tools import to_unicode
from icalendar.prop.text import vText
[docs]
class vCategory:
default_value: ClassVar[str] = "TEXT"
params: Parameters
def __init__(
self, c_list: list[str] | str, /, params: dict[str, Any] | None = None
) -> None:
if not hasattr(c_list, "__iter__") or isinstance(c_list, str):
c_list = [c_list]
self.cats: list[vText | str] = [vText(c) for c in c_list]
self.params = Parameters(params)
def __iter__(self) -> Iterator[vText | str]:
return iter(self.cats)
[docs]
def to_ical(self) -> bytes:
return b",".join(
[
c.to_ical() if hasattr(c, "to_ical") else vText(c).to_ical()
for c in self.cats
]
)
[docs]
@staticmethod
def from_ical(ical: list[str] | str) -> list[str]:
"""Parse a CATEGORIES value from iCalendar format.
This helper is normally called by :meth:`Component.from_ical <icalendar.cal.component.Component.from_ical>`, which
already splits the CATEGORIES property into a list of unescaped
category strings. New code should therefore pass a list of strings.
Passing a single comma-separated string is supported only for
backwards compatibility with older parsing code and is considered
legacy behavior.
Parameters:
ical: A list of category strings (preferred, as provided by
:meth:`Component.from_ical <icalendar.cal.component.Component.from_ical>`), or a single comma-separated
string from a legacy caller.
Returns:
A list of category strings.
"""
if isinstance(ical, list):
# Already split by Component.from_ical()
return ical
# Legacy: simple comma split (no escaping handled)
ical = to_unicode(ical)
return ical.split(",")
def __eq__(self, other: object) -> bool:
"""self == other"""
if isinstance(other, vCategory):
return self.cats == other.cats
if isinstance(other, list):
return self.cats == other
return NotImplemented
def __hash__(self) -> int:
"""Hash of the vCategory object."""
return hash(tuple(self.cats))
def __repr__(self) -> str:
"""String representation."""
return f"{self.__class__.__name__}({self.cats}, params={self.params})"
[docs]
def to_jcal(self, name: str) -> list:
"""The jCal representation for categories."""
result = [name, self.params.to_jcal(), self.VALUE.lower()]
result.extend(map(str, self.cats))
if not self.cats:
result.append("")
return result
[docs]
@classmethod
def examples(cls) -> list[Self]:
"""Examples of vCategory."""
return [cls(["HOME", "COSY"])]
from icalendar.param import VALUE
[docs]
@classmethod
def from_jcal(cls, jcal_property: list) -> Self:
"""Parse jCal from :rfc:`7265`.
Parameters:
jcal_property: The jCal property to parse.
Raises:
~icalendar.error.JCalParsingError: If the provided jCal is invalid.
"""
JCalParsingError.validate_property(jcal_property, cls)
for i, category in enumerate(jcal_property[3:], start=3):
JCalParsingError.validate_value_type(category, str, cls, i)
return cls(
jcal_property[3:],
Parameters.from_jcal_property(jcal_property),
)
@property
def ical_value(self) -> list[str]:
"""The list of categories as strings."""
return [str(cat) for cat in self.cats]
[docs]
def to_xcal(self, element: Element) -> None:
"""Add the xCal representation of this property according to :rfc:`6321`."""
self.params.to_xcal(element)
for cat in self.cats:
if not isinstance(cat, vText):
cat = vText(cat)
cat.to_xcal(element)
[docs]
@classmethod
@from_xcal_wrapper
def from_xcal(cls, parser: XCalParser, params: Parameters) -> Self:
"""Parse xCal from :rfc:`6321`.
Parameters:
xml: The XML to parse or a parser.
Raises:
~error.XCalParsingError: If the provided xCal is invalid.
"""
elements = parser.parse_tags(cls.default_value)
cats = [element.get_xsd_string() for element in elements]
return cls(cats, params=params)
__all__ = ["vCategory"]