Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/icalendar/error.py: 36%
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
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
1"""Errors thrown by icalendar."""
3from __future__ import annotations
5import contextlib
8class InvalidCalendar(ValueError):
9 """The calendar given is not valid.
11 This calendar does not conform with RFC 5545 or breaks other RFCs.
12 """
15class IncompleteComponent(ValueError):
16 """The component is missing attributes.
18 The attributes are not required, otherwise this would be
19 an InvalidCalendar. But in order to perform calculations,
20 this attribute is required.
22 This error is not raised in the UPPERCASE properties like .DTSTART,
23 only in the lowercase computations like .start.
24 """
27class IncompleteAlarmInformation(ValueError):
28 """The alarms cannot be calculated yet because information is missing."""
31class LocalTimezoneMissing(IncompleteAlarmInformation):
32 """We are missing the local timezone to compute the value.
34 Use Alarms.set_local_timezone().
35 """
38class ComponentEndMissing(IncompleteAlarmInformation):
39 """We are missing the end of a component that the alarm is for.
41 Use Alarms.set_end().
42 """
45class ComponentStartMissing(IncompleteAlarmInformation):
46 """We are missing the start of a component that the alarm is for.
48 Use Alarms.set_start().
49 """
52class FeatureWillBeRemovedInFutureVersion(DeprecationWarning):
53 """This feature will be removed in a future version."""
56def _repr_index(index: str | int) -> str:
57 """Create a JSON compatible representation for the index."""
58 if isinstance(index, str):
59 return f'"{index}"'
60 return str(index)
63class JCalParsingError(ValueError):
64 """Could not parse a part of the JCal."""
66 _default_value = object()
68 def __init__(
69 self,
70 message: str,
71 parser: str | type = "",
72 path: list[str | int] | None | str | int = None,
73 value: object = _default_value,
74 ):
75 """Create a new JCalParsingError."""
76 self.path = self._get_path(path)
77 if not isinstance(parser, str):
78 parser = parser.__name__
79 self.parser = parser
80 self.message = message
81 self.value = value
82 full_message = message
83 repr_path = ""
84 if self.path:
85 repr_path = "".join([f"[{_repr_index(index)}]" for index in self.path])
86 full_message = f"{repr_path}: {full_message}"
87 repr_path += " "
88 if parser:
89 full_message = f"{repr_path}in {parser}: {message}"
90 if value is not self._default_value:
91 full_message += f" Got value: {value!r}"
92 super().__init__(full_message)
94 @classmethod
95 @contextlib.contextmanager
96 def reraise_with_path_added(cls, *path_components: int | str):
97 """Automatically re-raise the exception with path components added.
99 Raises:
100 ~error.JCalParsingError: If there was an exception in the context.
101 """
102 try:
103 yield
104 except JCalParsingError as e:
105 raise cls(
106 path=list(path_components) + e.path,
107 parser=e.parser,
108 message=e.message,
109 value=e.value,
110 ).with_traceback(e.__traceback__) from e
112 @staticmethod
113 def _get_path(path: list[str | int] | None | str | int) -> list[str | int]:
114 """Return the path as a list."""
115 if path is None:
116 path = []
117 elif not isinstance(path, list):
118 path = [path]
119 return path
121 @classmethod
122 def validate_property(
123 cls,
124 jcal_property,
125 parser: str | type,
126 path: list[str | int] | None | str | int = None,
127 ):
128 """Validate a jCal property.
130 Raises:
131 ~error.JCalParsingError: if the property is not valid.
132 """
133 path = cls._get_path(path)
134 if not isinstance(jcal_property, list) or len(jcal_property) < 4:
135 raise JCalParsingError(
136 "The property must be a list with at least 4 items.",
137 parser,
138 path,
139 value=jcal_property,
140 )
141 if not isinstance(jcal_property[0], str):
142 raise JCalParsingError(
143 "The name must be a string.", parser, path + [0], value=jcal_property[0]
144 )
145 if not isinstance(jcal_property[1], dict):
146 raise JCalParsingError(
147 "The parameters must be a mapping.",
148 parser,
149 path + [1],
150 value=jcal_property[1],
151 )
152 if not isinstance(jcal_property[2], str):
153 raise JCalParsingError(
154 "The VALUE parameter must be a string.",
155 parser,
156 path + [2],
157 value=jcal_property[2],
158 )
160 _type_names = {
161 str: "a string",
162 int: "an integer",
163 float: "a float",
164 bool: "a boolean",
165 }
167 @classmethod
168 def validate_value_type(
169 cls,
170 jcal,
171 expected_type: type[str | int | float | bool]
172 | tuple[type[str | int | float | bool], ...],
173 parser: str | type = "",
174 path: list[str | int] | None | str | int = None,
175 ):
176 """Validate the type of a jCal value."""
177 if not isinstance(jcal, expected_type):
178 type_name = (
179 cls._type_names[expected_type]
180 if isinstance(expected_type, type)
181 else " or ".join(cls._type_names[t] for t in expected_type)
182 )
183 raise cls(
184 f"The value must be {type_name}.",
185 parser=parser,
186 value=jcal,
187 path=path,
188 )
190 @classmethod
191 def validate_list_type(
192 cls,
193 jcal,
194 expected_type: type[str | int | float | bool],
195 parser: str | type = "",
196 path: list[str | int] | None | str | int = None,
197 ):
198 """Validate the type of each item in a jCal list."""
199 path = cls._get_path(path)
200 if not isinstance(jcal, list):
201 raise cls(
202 "The value must be a list.",
203 parser=parser,
204 value=jcal,
205 path=path,
206 )
207 for index, item in enumerate(jcal):
208 if not isinstance(item, expected_type):
209 type_name = cls._type_names[expected_type]
210 raise cls(
211 f"Each item in the list must be {type_name}.",
212 parser=parser,
213 value=item,
214 path=path + [index],
215 )
218__all__ = [
219 "ComponentEndMissing",
220 "ComponentStartMissing",
221 "FeatureWillBeRemovedInFutureVersion",
222 "IncompleteAlarmInformation",
223 "IncompleteComponent",
224 "InvalidCalendar",
225 "JCalParsingError",
226 "LocalTimezoneMissing",
227]