Source code for icalendar.prop.factory

"""Factory class for all the property types."""

from __future__ import annotations

from typing import TYPE_CHECKING, Any, ClassVar

from icalendar.caselessdict import CaselessDict
from icalendar.prop.adr import vAdr
from icalendar.prop.binary import vBinary
from icalendar.prop.boolean import vBoolean
from icalendar.prop.broken import vBroken
from icalendar.prop.cal_address import vCalAddress
from icalendar.prop.categories import vCategory
from icalendar.prop.dt import (
    vDate,
    vDatetime,
    vDDDLists,
    vDDDTypes,
    vDuration,
    vPeriod,
    vTime,
    vUTCOffset,
)
from icalendar.prop.float import vFloat
from icalendar.prop.geo import vGeo
from icalendar.prop.inline import vInline
from icalendar.prop.n import vN
from icalendar.prop.org import vOrg
from icalendar.prop.recur import vFrequency, vRecur, vWeekday
from icalendar.prop.request_status import vRequestStatus
from icalendar.prop.text import vText
from icalendar.prop.uid import vUid
from icalendar.prop.unknown import vUnknown
from icalendar.prop.uri import vUri
from icalendar.prop.xml import vXml
from icalendar.prop.xml_reference import vXmlReference

from .integer import vInt

if TYPE_CHECKING:
    from icalendar.prop import VPROPERTY


[docs] class TypesFactory(CaselessDict): """Factory for all value types defined in :rfc:`5545` and subsequent. The value and parameter names don't overlap. So one factory is enough for both kinds. """ _instance: ClassVar[TypesFactory | None] = None
[docs] @staticmethod def instance() -> TypesFactory: """Return a singleton instance of this class.""" if TypesFactory._instance is None: TypesFactory._instance = TypesFactory() return TypesFactory._instance
def __init__(self, *args: Any, **kwargs: Any) -> None: """Set keys to upper for initial dict""" super().__init__(*args, **kwargs) self.all_types = ( vBinary, vBoolean, vCalAddress, vDDDLists, vDDDTypes, vDate, vDatetime, vDuration, vFloat, vFrequency, vGeo, vInline, vInt, vPeriod, vRecur, vText, vTime, vUTCOffset, vUri, vWeekday, vCategory, vAdr, vN, vOrg, vUid, vXmlReference, vXml, vUnknown, vRequestStatus, vBroken, ) self["binary"] = vBinary self["boolean"] = vBoolean self["cal-address"] = vCalAddress self["date"] = vDDDTypes self["date-time"] = vDDDTypes self["duration"] = vDDDTypes self["float"] = vFloat self["integer"] = vInt self["period"] = vPeriod self["recur"] = vRecur self["text"] = vText self["time"] = vTime self["uri"] = vUri self["utc-offset"] = vUTCOffset self["geo"] = vGeo self["inline"] = vInline self["date-time-list"] = vDDDLists self["categories"] = vCategory self["request-status-text"] = vRequestStatus # RFC 5545 and 6321 self["adr"] = vAdr # RFC 6350 vCard self["n"] = vN # RFC 6350 vCard self["org"] = vOrg # RFC 6350 vCard self["unknown"] = vUnknown # RFC 7265 self["uid"] = vUid # RFC 9253 self["xml-reference"] = vXmlReference # RFC 9253 self["xml-text"] = vXml # RFC 6321 self["x-broken"] = vBroken ################################################# # Property types # These are the default types types_map = CaselessDict( { #################################### # Property value types # Calendar Properties "calscale": "text", "method": "text", "prodid": "text", "version": "text", # Descriptive Component Properties "attach": "uri", "categories": "categories", "class": "text", # vCard Properties (RFC 6350) "adr": "adr", "n": "n", "org": "org", "comment": "text", "description": "text", "geo": "geo", "location": "text", "percent-complete": "integer", "priority": "integer", "resources": "text", "status": "text", "summary": "text", # RFC 9253 # link should be uri, xml-reference or uid # uri is likely most helpful if people forget to set VALUE "link": "uri", "concept": "uri", "refid": "text", # Date and Time Component Properties "completed": "date-time", "dtend": "date-time", "due": "date-time", "dtstart": "date-time", "duration": "duration", "freebusy": "period", "transp": "text", "refresh-interval": "duration", # RFC 7986 # Time Zone Component Properties "tzid": "text", "tzname": "text", "tzoffsetfrom": "utc-offset", "tzoffsetto": "utc-offset", "tzurl": "uri", # Relationship Component Properties "attendee": "cal-address", "contact": "text", "organizer": "cal-address", "recurrence-id": "date-time", "related-to": "text", "url": "uri", "conference": "uri", # RFC 7986 "image": "unknown", # RFC 7986 has no default value type "source": "uri", "uid": "text", # Recurrence Component Properties "exdate": "date-time-list", "exrule": "recur", "rdate": "date-time-list", "rrule": "recur", # Alarm Component Properties "action": "text", "repeat": "integer", "trigger": "duration", "acknowledged": "date-time", "proximity": "text", # RFC 9074 # Change Management Component Properties "created": "date-time", "dtstamp": "date-time", "last-modified": "date-time", "sequence": "integer", # XML property (RFC 6321) "xml": "xml-text", # Miscellaneous Component Properties "request-status": "request-status-text", #################################### # parameter types (luckily there is no name overlap) "altrep": "uri", "cn": "text", "cutype": "text", "delegated-from": "cal-address", "delegated-to": "cal-address", "dir": "uri", "encoding": "text", "fmttype": "text", "fbtype": "text", "language": "text", "member": "cal-address", "partstat": "text", "range": "text", "related": "text", "reltype": "text", "role": "text", "rsvp": "boolean", "sent-by": "cal-address", "value": "text", # rfc 9253 parameters "label": "text", "linkrel": "text", "gap": "duration", } )
[docs] def for_property(self, name, value_param: str | None = None) -> type[VPROPERTY]: """Returns the type class for a property or parameter. Parameters: name: Property or parameter name value_param: Optional ``VALUE`` parameter, for example, "DATE", "DATE-TIME", or other string. Returns: The appropriate value type class. """ name = name.upper() value_param = value_param.upper() if value_param else None # RFC 7265's UNKNOWN type is always represented verbatim, even for # properties such as RDATE/EXDATE that normally use list parsing. if value_param and value_param == "UNKNOWN": return self["unknown"] # Special case: RDATE and EXDATE always use vDDDLists to support list values # regardless of the VALUE parameter if name in ("RDATE", "EXDATE") and ( value_param is None or value_param in vDDDTypes.VALUE_MAP ): return self["date-time-list"] # Only use VALUE parameter for known properties that support multiple value # types (like DTSTART, DTEND, etc. which can be DATE or DATE-TIME) # For unknown/custom properties, always use the default type from types_map if value_param and name in self.types_map and value_param in self: # vCategory also has TEXT as VALUE. # It should be preferred over TEXT # This checks the case that the value type actually matches. value_type: VPROPERTY = self[self.types_map[name]] if value_type.default_value == value_param: return value_type return self[value_param] if value_param and (value_param in self) and value_param != "IMAGE": return self[value_param] return self[self.types_map.get(name, "unknown")]
#: jCal value types defined by :rfc:`7265` that are used verbatim as the #: type field. Internal type keys outside of this set (for example #: ``"categories"`` or ``"date-time-list"``) are mapped to their concrete #: jCal type via the value class. _jcal_value_types = frozenset( { "binary", "boolean", "cal-address", "date", "date-time", "duration", "float", "integer", "period", "recur", "text", "time", "uri", "utc-offset", "unknown", } )
[docs] def default_value_type(self, name: str) -> str: """Return the default jCal value type for a property. The result is a lowercase :rfc:`7265` value type, for example ``"date-time"`` for ``DTSTART`` or ``"duration"`` for ``TRIGGER``. This is the value type a property uses when no explicit ``VALUE`` parameter is given, so it tells :meth:`Component.from_jcal <icalendar.cal.component.Component.from_jcal>` whether a ``VALUE`` parameter has to be restored from the jCal type field. Parameters: name: Property name. Returns: The default jCal value type as a lowercase string. """ internal = self.types_map.get(name, "unknown").lower() if internal in self._jcal_value_types: return internal # Internal keys such as ``categories`` or ``date-time-list`` are not # jCal value types themselves; ask the value class for the type it # actually serialises to. return self[internal].default_value.lower()
[docs] def to_ical(self, name, value): """Encodes a named value from a primitive python type to an icalendar encoded string. """ type_class = self.for_property(name) return type_class(value).to_ical()
[docs] def from_ical(self, name, value): """Decodes a named property or parameter value from an icalendar encoded string to a primitive python type. """ type_class = self.for_property(name) return type_class.from_ical(value)
__all__ = ["TypesFactory"]