Raw outline of the components. Vdirsyncer's operations on iCalendar and VCard objects are limited to retrieving the UID and splitting larger files into items. Consequently this parser is very lazy, with the downside that manipulation of item properties are extremely costly.
| 236 | |
| 237 | |
| 238 | class _Component: |
| 239 | ''' |
| 240 | Raw outline of the components. |
| 241 | |
| 242 | Vdirsyncer's operations on iCalendar and VCard objects are limited to |
| 243 | retrieving the UID and splitting larger files into items. Consequently this |
| 244 | parser is very lazy, with the downside that manipulation of item properties |
| 245 | are extremely costly. |
| 246 | |
| 247 | Other features: |
| 248 | |
| 249 | - Preserve the original property order and wrapping. |
| 250 | - Don't choke on irrelevant details like invalid datetime formats. |
| 251 | |
| 252 | Original version from https://github.com/collective/icalendar/, but apart |
| 253 | from the similar API, very few parts have been reused. |
| 254 | ''' |
| 255 | |
| 256 | def __init__(self, name, lines, subcomponents): |
| 257 | ''' |
| 258 | :param name: The component name. |
| 259 | :param lines: The component's own properties, as list of lines |
| 260 | (strings). |
| 261 | :param subcomponents: List of components. |
| 262 | ''' |
| 263 | self.name = name |
| 264 | self.props = lines |
| 265 | self.subcomponents = subcomponents |
| 266 | |
| 267 | @classmethod |
| 268 | def parse(cls, lines, multiple=False): |
| 269 | if isinstance(lines, bytes): |
| 270 | lines = lines.decode('utf-8') |
| 271 | if isinstance(lines, str): |
| 272 | lines = lines.splitlines() |
| 273 | |
| 274 | stack = [] |
| 275 | rv = [] |
| 276 | try: |
| 277 | for _i, line in enumerate(lines): |
| 278 | if line.startswith('BEGIN:'): |
| 279 | c_name = line[len('BEGIN:'):].strip().upper() |
| 280 | stack.append(cls(c_name, [], [])) |
| 281 | elif line.startswith('END:'): |
| 282 | component = stack.pop() |
| 283 | if stack: |
| 284 | stack[-1].subcomponents.append(component) |
| 285 | else: |
| 286 | rv.append(component) |
| 287 | else: |
| 288 | if line.strip(): |
| 289 | stack[-1].props.append(line) |
| 290 | except IndexError: |
| 291 | raise ValueError('Parsing error at line {}'.format(_i + 1)) |
| 292 | |
| 293 | if multiple: |
| 294 | return rv |
| 295 | elif len(rv) != 1: |
no outgoing calls
no test coverage detected