Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/icalendar/attr/property_list.py: 33%

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

79 statements  

1"""A list-like view for :rfc:`5545#section-3.8` component property values.""" 

2 

3from __future__ import annotations 

4 

5import contextlib 

6from collections.abc import MutableSequence, Sequence 

7from typing import TYPE_CHECKING, Any 

8 

9if TYPE_CHECKING: 

10 from collections.abc import Iterator 

11 

12 from icalendar.cal.component import Component 

13 

14 

15class PropertyListView(MutableSequence): 

16 """A list-like view of an :rfc:`5545#section-3.8` property within a component. 

17 

18 This view synchronizes modifications, such as ``append``, ``extend``, ``insert``, 

19 ``clear``, and ``delete``, with the underlying component dictionary. 

20 """ 

21 

22 __hash__ = None 

23 

24 def __init__(self, component: Component, name: str) -> None: 

25 """Initialize the property list view for a given component and property name. 

26 

27 Parameters: 

28 component: The component to view. 

29 name: The name of the property to view. 

30 """ 

31 self._component = component 

32 self._name = name 

33 

34 @contextlib.contextmanager 

35 def _values(self) -> Iterator[list[Any]]: 

36 """Synchronize the value list with the component.""" 

37 raw = self._component.get(self._name, []) 

38 values = [raw] if not isinstance(raw, list) else list(raw) 

39 try: 

40 yield values 

41 finally: 

42 if not values: 

43 self._component.pop(self._name, None) 

44 else: 

45 self._component[self._name] = values 

46 

47 def __getitem__(self, index: Any) -> Any: 

48 """Get an item or slice from the property list.""" 

49 with self._values() as values: 

50 return values[index] 

51 

52 def __setitem__(self, index: Any, value: Any) -> None: 

53 """Set an item or slice in the property list.""" 

54 with self._values() as values: 

55 values[index] = value 

56 

57 def __delitem__(self, index: Any) -> None: 

58 """Delete an item or slice from the property list.""" 

59 with self._values() as values: 

60 del values[index] 

61 

62 def __len__(self) -> int: 

63 """Return the number of items in the property list.""" 

64 raw = self._component.get(self._name, []) 

65 if not raw: 

66 return 0 

67 if not isinstance(raw, list): 

68 return 1 

69 return len(raw) 

70 

71 def insert(self, index: int, value: Any) -> None: 

72 """Insert an item into the property list at a given index. 

73 

74 Parameters: 

75 index: The position at which to insert the item into the property list. 

76 value: The value to insert in the property list at the given index. 

77 """ 

78 with self._values() as values: 

79 values.insert(index, value) 

80 

81 def clear(self) -> None: 

82 """Remove all values and the property from the component.""" 

83 self._component.pop(self._name, None) 

84 

85 def replace_with(self, values: Any) -> None: 

86 """Replace all values in the property with ``values``. 

87 

88 The values are added with 

89 :meth:`Component.add <icalendar.cal.component.Component.add>`, 

90 so they are converted to the correct types. 

91 

92 Parameters: 

93 values: The values to replace in the property list. 

94 ``None`` removes all values from the property, 

95 whereas a single string value or any sequence type 

96 replaces all values in the property and is stored as a list. 

97 """ 

98 if values is None: 

99 new_values = [] 

100 elif isinstance(values, Sequence) and not isinstance(values, (str, bytes)): 

101 # copy first: ``values`` could be this view or a view of this property 

102 new_values = list(values) 

103 else: 

104 new_values = [values] 

105 self.clear() 

106 for value in new_values: 

107 self._component.add(self._name, value) 

108 

109 def __eq__(self, other: object) -> bool: 

110 """Check equality against another sequence.""" 

111 if isinstance(other, PropertyListView): 

112 return list(self) == list(other) 

113 if hasattr(other, "__iter__"): 

114 return list(self) == list(other) 

115 return False 

116 

117 def __repr__(self) -> str: 

118 """Return the string representation of the property list view.""" 

119 return f"PropertyListView({list(self)!r})" 

120 

121 def __add__(self, other: Any) -> list[Any]: 

122 """Concatenate this list with a sequence and return a new list.""" 

123 if isinstance(other, Sequence) and not isinstance(other, (str, bytes)): 

124 return list(self) + list(other) 

125 return NotImplemented 

126 

127 def __radd__(self, other: Any) -> list[Any]: 

128 """Concatenate a sequence with this list and return a new list.""" 

129 if isinstance(other, Sequence) and not isinstance(other, (str, bytes)): 

130 return list(other) + list(self) 

131 return NotImplemented 

132 

133 def __mul__(self, n: int) -> list[Any]: 

134 """Multiply the property list by an integer to return a new repeated list.""" 

135 if isinstance(n, int): 

136 return list(self) * n 

137 return NotImplemented 

138 

139 def __rmul__(self, n: int) -> list[Any]: 

140 """Multiply the property list by an integer to return a new repeated list.""" 

141 if isinstance(n, int): 

142 return list(self) * n 

143 return NotImplemented 

144 

145 def copy(self) -> list[Any]: 

146 """Return a shallow copy of the property list as a regular list.""" 

147 return list(self) 

148 

149 

150__all__ = ["PropertyListView"]