1"""UNKNOWN values from :rfc:`7265`."""
2
3from __future__ import annotations
4
5from typing import TYPE_CHECKING, Any, ClassVar
6
7from icalendar.error import JCalParsingError
8from icalendar.parser import Parameters
9from icalendar.parser_tools import DEFAULT_ENCODING, ICAL_TYPE, to_unicode
10
11if TYPE_CHECKING:
12 from icalendar.compatibility import Self
13 from icalendar.parser.content_line import Contentline
14
15
16class vUnknown(str):
17 """A property value of the :rfc:`7265#section-5` reserved UNKNOWN value data type.
18
19 .. versionchanged:: 7.2.0
20
21 Previously ``vUnknown`` inherited from ``vText``, which unescapes values.
22 Now ``vUnknown`` doesn't unescape its values, which is the correct behavior.
23
24 Unlike :class:`~icalendar.prop.text.vText`, the value is preserved verbatim
25 when imported from and exported to iCalendar data, without :rfc:`5545` escaping
26 or unescaping. When the value type of an unrecognized property is not known,
27 then no escaping rules can be applied, and the value must be preserved as is
28 round-trip.
29
30 See also:
31
32 :rfc:`7265#section-5.1`
33 """
34
35 default_value: ClassVar[str] = "UNKNOWN"
36 params: Parameters
37 __slots__ = ("encoding", "params")
38
39 def __new__(
40 cls,
41 value: ICAL_TYPE,
42 encoding: str = DEFAULT_ENCODING,
43 /,
44 params: dict[str, Any] | None = None,
45 ) -> Self:
46 value = to_unicode(value, encoding=encoding)
47 self = super().__new__(cls, value)
48 self.encoding = encoding
49 self.params = Parameters(params)
50 return self
51
52 def __repr__(self) -> str:
53 return f"{self.__class__.__name__}({self.to_ical()!r})"
54
55 def to_ical(self) -> bytes:
56 r"""Return the value verbatim, without :rfc:`5545` escaping.
57
58 This method's implementation is different from that in
59 :class:`~icalendar.prop.text.vText`, whose
60 :meth:`~icalendar.prop.text.vText.to_ical` method escapes ``;``, ``,``,
61 ``\``, and newlines.
62
63 Example:
64
65 The semicolon is kept verbatim for UNKNOWN, unlike a TEXT value
66 which would escape it as ``\\;``.
67
68 .. code-block:: pycon
69
70 >>> from icalendar.prop import vText, vUnknown
71 >>> vUnknown("a;b").to_ical()
72 b'a;b'
73 >>> vText("a;b").to_ical()
74 b'a\\;b'
75
76 See also:
77
78 :rfc:`7265#section-5.2`
79
80 """
81 return self.encode(self.encoding)
82
83 @classmethod
84 def from_ical(cls, ical: ICAL_TYPE) -> Self:
85 """Take the value verbatim, without unescaping."""
86 return cls(ical)
87
88 @classmethod
89 def get_value_from_content_line(cls, line: Contentline) -> str:
90 """Return this type's value from ``line``, taken verbatim.
91
92 Value types that must not have their value unescaped provide this
93 method, and the parser uses it to obtain the value instead of
94 :meth:`~icalendar.parser.content_line.Contentline.parts`. For
95 :rfc:`7265` ``UNKNOWN`` values, the escaping rules of the real value
96 type are not known, so no unescaping can be applied.
97 """
98 return line.raw_parts()[2]
99
100 @property
101 def ical_value(self) -> str:
102 """The string value of the property."""
103 return str(self)
104
105 from icalendar.param import ALTREP, GAP, LANGUAGE, RELTYPE, VALUE
106
107 def to_jcal(self, name: str) -> list:
108 """The jCal representation of this property, according to :rfc:`7265#section-5.1`.
109
110 If the property doesn't include a VALUE property parameter and its value
111 type is not known, then its value type is set to ``"unknown"``. Else the
112 property's value type is converted to lowercase.
113
114 The property's value is the unprocessed value text, aside from standard
115 JSON string escaping.
116 """
117 return [name, self.params.to_jcal(), self.VALUE.lower(), str(self)]
118
119 @classmethod
120 def examples(cls) -> list[Self]:
121 """Examples of vUnknown."""
122 return [cls("Some property text.")]
123
124 @classmethod
125 def from_jcal(cls, jcal_property: list) -> Self:
126 """Parse jCal from :rfc:`7265`, taking the value verbatim.
127
128 Parameters:
129 jcal_property: The jCal property to parse.
130
131 Raises:
132 ~error.JCalParsingError: If the provided jCal is invalid.
133 """
134 JCalParsingError.validate_property(jcal_property, cls)
135 string = jcal_property[3]
136 JCalParsingError.validate_value_type(string, str, cls, 3)
137 return cls(
138 string,
139 params=Parameters.from_jcal_property(jcal_property),
140 )
141
142 @classmethod
143 def parse_jcal_value(cls, jcal_value: Any) -> Self:
144 """Parse a jCal value into a vUnknown."""
145 JCalParsingError.validate_value_type(jcal_value, (str, int, float), cls)
146 return cls(str(jcal_value))
147
148
149__all__ = ["vUnknown"]