ical.calendar_stream

The core, a collection of Calendar and Scheduling objects.

This is an example of parsing an ics file as a stream of calendar objects:

from pathlib import Path
from ical.calendar_stream import IcsCalendarStream

filename = Path("example/calendar.ics")
with filename.open() as ics_file:
    stream = IcsCalendarStream.from_ics(ics_file.read())
    print("File contains %s calendar(s)", len(stream.calendars))

You can encode a calendar stream as ics content calling the ics() method on the IcsCalendarStream:

from pathlib import Path

filename = Path("/tmp/output.ics")
with filename.open(mode="w") as ics_file:
    ics_file.write(stream.ics())
 1"""The core, a collection of Calendar and Scheduling objects.
 2
 3This is an example of parsing an ics file as a stream of calendar objects:
 4```python
 5from pathlib import Path
 6from ical.calendar_stream import IcsCalendarStream
 7
 8filename = Path("example/calendar.ics")
 9with filename.open() as ics_file:
10    stream = IcsCalendarStream.from_ics(ics_file.read())
11    print("File contains %s calendar(s)", len(stream.calendars))
12```
13
14You can encode a calendar stream as ics content calling the `ics()` method on
15the `IcsCalendarStream`:
16
17```python
18from pathlib import Path
19
20filename = Path("/tmp/output.ics")
21with filename.open(mode="w") as ics_file:
22    ics_file.write(stream.ics())
23```
24
25"""
26
27# mypy: allow-any-generics
28
29from __future__ import annotations
30
31import logging
32from pydantic import Field, field_serializer
33
34from .calendar import Calendar
35from .component import ComponentModel
36from .parsing.component import encode_content, parse_content
37from .types.data_types import serialize_field
38from .exceptions import CalendarParseError
39from pydantic import ConfigDict
40
41_LOGGER = logging.getLogger(__name__)
42
43
44class CalendarStream(ComponentModel):
45    """A container that is a collection of calendaring information.
46
47    This object supports parsing an rfc5545 calendar file, but does not
48    support encoding. See `IcsCalendarStream` instead for encoding ics files.
49    """
50
51    calendars: list[Calendar] = Field(alias="vcalendar", default_factory=list)
52
53    @classmethod
54    def from_ics(cls, content: str) -> "CalendarStream":
55        """Factory method to create a new instance from an rfc5545 iCalendar content."""
56        components = parse_content(content)
57        result: dict[str, list] = {"vcalendar": []}
58        for component in components:
59            result.setdefault(component.name, [])
60            result[component.name].append(component.as_dict())
61        _LOGGER.debug("Parsing object %s", result)
62        return cls(**result)
63
64    def ics(self) -> str:
65        """Encode the calendar stream as an rfc5545 iCalendar Stream content."""
66        return encode_content(self.__encode_component_root__().components)
67
68
69class IcsCalendarStream(CalendarStream):
70    """A calendar stream that supports parsing and encoding ICS."""
71
72    @classmethod
73    def calendar_from_ics(cls, content: str) -> Calendar:
74        """Load a single calendar from an ics string."""
75        stream = cls.from_ics(content)
76        if len(stream.calendars) == 1:
77            return stream.calendars[0]
78        if len(stream.calendars) == 0:
79            return Calendar()
80        raise CalendarParseError("Calendar Stream had more than one calendar")
81
82    @classmethod
83    def calendar_to_ics(cls, calendar: Calendar) -> str:
84        """Serialize a calendar as an ICS stream."""
85        stream = cls(vcalendar=[calendar])
86        return stream.ics()
87
88    model_config = ConfigDict(
89        validate_assignment=True,
90        populate_by_name=True,
91    )
92    serialize_fields = field_serializer("*")(serialize_field)  # type: ignore[pydantic-field]
class CalendarStream(ical.component.ComponentModel):
45class CalendarStream(ComponentModel):
46    """A container that is a collection of calendaring information.
47
48    This object supports parsing an rfc5545 calendar file, but does not
49    support encoding. See `IcsCalendarStream` instead for encoding ics files.
50    """
51
52    calendars: list[Calendar] = Field(alias="vcalendar", default_factory=list)
53
54    @classmethod
55    def from_ics(cls, content: str) -> "CalendarStream":
56        """Factory method to create a new instance from an rfc5545 iCalendar content."""
57        components = parse_content(content)
58        result: dict[str, list] = {"vcalendar": []}
59        for component in components:
60            result.setdefault(component.name, [])
61            result[component.name].append(component.as_dict())
62        _LOGGER.debug("Parsing object %s", result)
63        return cls(**result)
64
65    def ics(self) -> str:
66        """Encode the calendar stream as an rfc5545 iCalendar Stream content."""
67        return encode_content(self.__encode_component_root__().components)

A container that is a collection of calendaring information.

This object supports parsing an rfc5545 calendar file, but does not support encoding. See IcsCalendarStream instead for encoding ics files.

calendars: list[ical.calendar.Calendar]
@classmethod
def from_ics(cls, content: str) -> CalendarStream:
54    @classmethod
55    def from_ics(cls, content: str) -> "CalendarStream":
56        """Factory method to create a new instance from an rfc5545 iCalendar content."""
57        components = parse_content(content)
58        result: dict[str, list] = {"vcalendar": []}
59        for component in components:
60            result.setdefault(component.name, [])
61            result[component.name].append(component.as_dict())
62        _LOGGER.debug("Parsing object %s", result)
63        return cls(**result)

Factory method to create a new instance from an rfc5545 iCalendar content.

def ics(self) -> str:
65    def ics(self) -> str:
66        """Encode the calendar stream as an rfc5545 iCalendar Stream content."""
67        return encode_content(self.__encode_component_root__().components)

Encode the calendar stream as an rfc5545 iCalendar Stream content.

model_config = {'validate_assignment': True, 'populate_by_name': True, 'arbitrary_types_allowed': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class IcsCalendarStream(CalendarStream):
70class IcsCalendarStream(CalendarStream):
71    """A calendar stream that supports parsing and encoding ICS."""
72
73    @classmethod
74    def calendar_from_ics(cls, content: str) -> Calendar:
75        """Load a single calendar from an ics string."""
76        stream = cls.from_ics(content)
77        if len(stream.calendars) == 1:
78            return stream.calendars[0]
79        if len(stream.calendars) == 0:
80            return Calendar()
81        raise CalendarParseError("Calendar Stream had more than one calendar")
82
83    @classmethod
84    def calendar_to_ics(cls, calendar: Calendar) -> str:
85        """Serialize a calendar as an ICS stream."""
86        stream = cls(vcalendar=[calendar])
87        return stream.ics()
88
89    model_config = ConfigDict(
90        validate_assignment=True,
91        populate_by_name=True,
92    )
93    serialize_fields = field_serializer("*")(serialize_field)  # type: ignore[pydantic-field]

A calendar stream that supports parsing and encoding ICS.

@classmethod
def calendar_from_ics(cls, content: str) -> ical.calendar.Calendar:
73    @classmethod
74    def calendar_from_ics(cls, content: str) -> Calendar:
75        """Load a single calendar from an ics string."""
76        stream = cls.from_ics(content)
77        if len(stream.calendars) == 1:
78            return stream.calendars[0]
79        if len(stream.calendars) == 0:
80            return Calendar()
81        raise CalendarParseError("Calendar Stream had more than one calendar")

Load a single calendar from an ics string.

@classmethod
def calendar_to_ics(cls, calendar: ical.calendar.Calendar) -> str:
83    @classmethod
84    def calendar_to_ics(cls, calendar: Calendar) -> str:
85        """Serialize a calendar as an ICS stream."""
86        stream = cls(vcalendar=[calendar])
87        return stream.ics()

Serialize a calendar as an ICS stream.

model_config = {'validate_assignment': True, 'populate_by_name': True, 'arbitrary_types_allowed': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

def serialize_fields( self: pydantic.main.BaseModel, value: Any, info: pydantic_core.core_schema.SerializationInfo) -> Any:
171def serialize_field(self: BaseModel, value: Any, info: SerializationInfo) -> Any:
172    if not info.context or not info.context.get("ics"):
173        return value
174    if isinstance(value, list):
175        res = []
176        for val in value:
177            for base in val.__class__.__mro__[:-1]:
178                if (func := DATA_TYPE.encode_property_json.get(base)) is not None:
179                    res.append(func(val))
180                    break
181            else:
182                res.append(val)
183        return res
184
185    for base in value.__class__.__mro__[:-1]:
186        if (func := DATA_TYPE.encode_property_json.get(base)) is not None:
187            return func(value)
188    return value
Inherited Members
CalendarStream
calendars
from_ics
ics