Source code for icalendar.prop.binary

"""BINARY values from :rfc:`5545`."""

import base64
import binascii
from typing import ClassVar

from icalendar.compatibility import Self, deprecate_for_version_8
from icalendar.error import JCalParsingError
from icalendar.parser import Parameters
from icalendar.parser_tools import to_unicode


[docs] class vBinary: """Binary property values are Base64 encoded.""" default_value: ClassVar[str] = "BINARY" params: Parameters bytes: bytes """The raw binary value of the BINARY property. This is the authoritative storage and round-trips losslessly, including for non-UTF-8 data. Use this instead of the deprecated :attr:`obj`. """ def __init__(self, obj: str | bytes, params: dict[str, str] | None = None) -> None: if isinstance(obj, str): self.bytes = obj.encode("utf-8") else: self.bytes = obj self.params = Parameters(encoding="BASE64", value="BINARY") if params: self.params.update(params) def __repr__(self) -> str: return f"vBinary({self.to_ical()})"
[docs] def to_ical(self) -> bytes: return base64.b64encode(self.bytes)
[docs] @staticmethod def from_ical(ical: str | bytes) -> bytes: try: return base64.b64decode(ical, validate=True) except (binascii.Error, ValueError) as e: raise ValueError("Not valid base 64 encoding.") from e
@property def base64data(self) -> str: """The Base64-encoded string view of this value. This is the same string that :meth:`to_ical` produces, exposed as a plain :class:`str` instead of :class:`bytes` so you don't need to call :func:`base64.b64encode` yourself. See :issue:`1550`. Returns: The Base64-encoded representation of the stored value. """ return self.to_ical().decode("ascii") @base64data.setter def base64data(self, value: str) -> None: """Set this value from a Base64-encoded string. The decoded raw bytes are stored in :attr:`bytes`, so non-UTF-8 payloads round-trip losslessly. Parameters: value: A Base64-encoded string. Raises: ValueError: If ``value`` isn't valid Base64. """ self.bytes = self.from_ical(value) @property @deprecate_for_version_8 def obj(self) -> str: """Deprecated string view of the value. .. deprecated:: 7.1.3 Use :attr:`bytes` for the raw binary value. ``obj`` decodes :attr:`bytes` as text and is lossy for non-UTF-8 data. It will be removed in icalendar 8. """ return to_unicode(self.bytes) @obj.setter @deprecate_for_version_8 def obj(self, value: str | bytes) -> None: self.bytes = value.encode("utf-8") if isinstance(value, str) else value def __eq__(self, other: object) -> bool: """self == other""" return isinstance(other, vBinary) and self.bytes == other.bytes def __hash__(self) -> int: """Hash of the vBinary object.""" return hash(self.bytes)
[docs] @classmethod def examples(cls) -> list[Self]: """Examples of vBinary.""" return [cls("VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4")]
from icalendar.param import VALUE
[docs] def to_jcal(self, name: str) -> list: """The jCal representation of this property according to :rfc:`7265`.""" params = self.params.to_jcal() if params.get("encoding") == "BASE64": # BASE64 is the only allowed encoding del params["encoding"] return [name, params, self.VALUE.lower(), self.base64data]
@property def ical_value(self) -> bytes: """The raw ``bytes`` value of the BINARY property. .. versionadded:: 7.1.0 .. versionchanged:: 7.1.3 Returns the raw stored bytes. Previously the stored value was Base64-decoded, which raised :class:`ValueError` for non-Base64 input. See :pr:`1356`. """ return self.bytes
[docs] @classmethod def from_jcal(cls, jcal_property: list) -> Self: """Parse jCal from :rfc:`7265` to a vBinary. Parameters: jcal_property: The jCal property to parse. Raises: ~error.JCalParsingError: If the provided jCal is invalid. """ JCalParsingError.validate_property(jcal_property, cls) JCalParsingError.validate_value_type(jcal_property[3], str, cls, 3) return cls( cls.from_ical(jcal_property[3]), params=Parameters.from_jcal_property(jcal_property), )
__all__ = ["vBinary"]