Source code for icalendar.attr.property_list
"""A list-like view for :rfc:`5545#section-3.8` component property values."""
from __future__ import annotations
import contextlib
from collections.abc import MutableSequence, Sequence
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from collections.abc import Iterator
from icalendar.cal.component import Component
[docs]
class PropertyListView(MutableSequence):
"""A list-like view of an :rfc:`5545#section-3.8` property within a component.
This view synchronizes modifications, such as ``append``, ``extend``, ``insert``,
``clear``, and ``delete``, with the underlying component dictionary.
"""
__hash__ = None
def __init__(self, component: Component, name: str) -> None:
"""Initialize the property list view for a given component and property name.
Parameters:
component: The component to view.
name: The name of the property to view.
"""
self._component = component
self._name = name
@contextlib.contextmanager
def _values(self) -> Iterator[list[Any]]:
"""Synchronize the value list with the component."""
raw = self._component.get(self._name, [])
values = [raw] if not isinstance(raw, list) else list(raw)
try:
yield values
finally:
if not values:
self._component.pop(self._name, None)
else:
self._component[self._name] = values
def __getitem__(self, index: Any) -> Any:
"""Get an item or slice from the property list."""
with self._values() as values:
return values[index]
def __setitem__(self, index: Any, value: Any) -> None:
"""Set an item or slice in the property list."""
with self._values() as values:
values[index] = value
def __delitem__(self, index: Any) -> None:
"""Delete an item or slice from the property list."""
with self._values() as values:
del values[index]
def __len__(self) -> int:
"""Return the number of items in the property list."""
raw = self._component.get(self._name, [])
if not raw:
return 0
if not isinstance(raw, list):
return 1
return len(raw)
[docs]
def insert(self, index: int, value: Any) -> None:
"""Insert an item into the property list at a given index.
Parameters:
index: The position at which to insert the item into the property list.
value: The value to insert in the property list at the given index.
"""
with self._values() as values:
values.insert(index, value)
[docs]
def clear(self) -> None:
"""Remove all values and the property from the component."""
self._component.pop(self._name, None)
[docs]
def replace_with(self, values: Any) -> None:
"""Replace all values in the property with ``values``.
The values are added with
:meth:`Component.add <icalendar.cal.component.Component.add>`,
so they are converted to the correct types.
Parameters:
values: The values to replace in the property list.
``None`` removes all values from the property,
whereas a single string value or any sequence type
replaces all values in the property and is stored as a list.
"""
if values is None:
new_values = []
elif isinstance(values, Sequence) and not isinstance(values, (str, bytes)):
# copy first: ``values`` could be this view or a view of this property
new_values = list(values)
else:
new_values = [values]
self.clear()
for value in new_values:
self._component.add(self._name, value)
def __eq__(self, other: object) -> bool:
"""Check equality against another sequence."""
if isinstance(other, PropertyListView):
return list(self) == list(other)
if hasattr(other, "__iter__"):
return list(self) == list(other)
return False
def __repr__(self) -> str:
"""Return the string representation of the property list view."""
return f"PropertyListView({list(self)!r})"
def __add__(self, other: Any) -> list[Any]:
"""Concatenate this list with a sequence and return a new list."""
if isinstance(other, Sequence) and not isinstance(other, (str, bytes)):
return list(self) + list(other)
return NotImplemented
def __radd__(self, other: Any) -> list[Any]:
"""Concatenate a sequence with this list and return a new list."""
if isinstance(other, Sequence) and not isinstance(other, (str, bytes)):
return list(other) + list(self)
return NotImplemented
def __mul__(self, n: int) -> list[Any]:
"""Multiply the property list by an integer to return a new repeated list."""
if isinstance(n, int):
return list(self) * n
return NotImplemented
def __rmul__(self, n: int) -> list[Any]:
"""Multiply the property list by an integer to return a new repeated list."""
if isinstance(n, int):
return list(self) * n
return NotImplemented
[docs]
def copy(self) -> list[Any]:
"""Return a shallow copy of the property list as a regular list."""
return list(self)
__all__ = ["PropertyListView"]