Source code for icalendar.parser.xcal.component

"""A parser for components."""

from __future__ import annotations

from typing import TYPE_CHECKING

from icalendar.error import InvalidToken, XCalParsingError
from icalendar.parser.xcal.adapter import ElementAdapter
from icalendar.parser.xcal.base import XCalParser
from icalendar.parser.xcal.property import XCalPropertiesParser

if TYPE_CHECKING:
    from xml.etree.ElementTree import Element

    from icalendar.cal.component import Component
    from icalendar.cal.component_factory import ComponentFactory
    from icalendar.prop.factory import TypesFactory


[docs] class XCalComponentParser(XCalParser): """A parser for components. Parameters: component_factory: The component factory to use. element: The element to parse. types_factory: The types factory to use. Examples: The ``element`` can be a an ``icalendar`` stream that contains one or more ``vcalendar`` components. .. code-block:: xml <icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> <vcalendar> ... </vcalendar> </icalendar> Components can contain other components inside the ``components`` tag. The parameter ``element`` can be such a ``components`` tag. .. code-block:: xml <components> <vevent> ... </vevent> </components> """ def __init__( self, element: Element | ElementAdapter, component_factory: ComponentFactory, types_factory: TypesFactory, ) -> None: element = ElementAdapter.with_element(element) if element.tag not in ("icalendar", "components"): # we are not in an icalendar/component stream. We need to wrap it. # This only happens when you pass in a single component. element = element.make_parent("icalendar") super().__init__(element) self._component_factory = component_factory self._types_factory = types_factory
[docs] def parse_component(self) -> Component: """Convert one child into a :class:`~icalendar.cal.component.Component`. Returns: The parsed component. Raises: ~icalendar.error.XCalParsingError: If there is an error in the child. ~icalendar.parser.xcal.base.InvalidParserState: If all components have been parsed or a property value type did not parse anything. Example: The XML below shows the structure of one component element that gets parsed by this method. ``vcalendar`` is the name of the component. Each component can contain a ``properties`` and a ``components`` element with more components. .. code-block:: xml <vcalendar> <properties> ... </properties> <components> <vevent> ... </vevent> </components> </vcalendar> """ component = self.parse_component_from_child() self.done() return component
[docs] def parse_component_from_child(self) -> Component: """Parse a component from a child element. Returns: The parsed component. Raises: ~icalendar.error.XCalParsingError: If there is an error in the child. ~icalendar.parser.xcal.base.InvalidParserState: If all components have been parsed or a property value type did not parse anything. """ try: component_class = self._component_factory.get_component_class( self.child.tag ) except InvalidToken as error: raise XCalParsingError( "The component name must be a valid iCalendar token", self.child.tag, self.child, ) from error component = component_class() properties = self.child.get_child_with_tag("properties") if properties is not None: properties_parser = XCalPropertiesParser( properties, component, self._types_factory ) properties_parser.parse_properties() components = self.child.get_child_with_tag("components") if components is not None: components_parser = XCalComponentParser( components, self._component_factory, self._types_factory ) while not components_parser.is_finished(): component.add_component(components_parser.parse_component()) return component
__all__ = ["XCalComponentParser"]