Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/icalendar/prop/adr.py: 76%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""ADR property of :rfc:`6350`."""
3from typing import Any, ClassVar, NamedTuple
5from icalendar.compatibility import Self
6from icalendar.error import JCalParsingError
7from icalendar.parser import Parameters
8from icalendar.parser_tools import DEFAULT_ENCODING, to_unicode
11class AdrFields(NamedTuple):
12 """Named fields for vCard ADR (Address) property per :rfc:`6350#section-6.3.1`.
14 Provides named access to the seven address components.
15 """
17 po_box: str
18 """Post office box."""
19 extended: str
20 """Extended address (e.g., apartment or suite number)."""
21 street: str
22 """Street address."""
23 locality: str
24 """Locality (e.g., city)."""
25 region: str
26 """Region (e.g., state or province)."""
27 postal_code: str
28 """Postal code."""
29 country: str
30 """Country name (full name)."""
33class vAdr:
34 """vCard ADR (Address) structured property per :rfc:`6350#section-6.3.1`.
36 The ADR property represents a delivery address as a single text value.
37 The structured type value consists of a sequence of seven address components.
38 The component values must be specified in their corresponding position.
40 - post office box
41 - extended address (e.g., apartment or suite number)
42 - street address
43 - locality (e.g., city)
44 - region (e.g., state or province)
45 - postal code
46 - country name (full name)
48 When a component value is missing, the associated component separator
49 MUST still be specified.
51 Semicolons are field separators and are NOT escaped.
52 Commas and backslashes within field values ARE escaped per :rfc:`6350`.
54 Examples:
55 .. code-block:: pycon
57 >>> from icalendar.prop import vAdr
58 >>> adr = vAdr(("", "", "123 Main St", "Springfield", "IL", "62701", "USA"))
59 >>> adr.to_ical()
60 b';;123 Main St;Springfield;IL;62701;USA'
61 >>> vAdr.from_ical(";;123 Main St;Springfield;IL;62701;USA")
62 AdrFields(po_box='', extended='', street='123 Main St', locality='Springfield', region='IL', postal_code='62701', country='USA')
63 """
65 default_value: ClassVar[str] = "TEXT"
66 params: Parameters
67 fields: AdrFields
69 def __init__(
70 self,
71 fields: tuple[str, ...] | list[str] | str | AdrFields,
72 /,
73 params: dict[str, Any] | None = None,
74 ):
75 """Initialize ADR with seven fields or parse from vCard format string.
77 Parameters:
78 fields: Either an AdrFields, tuple, or list of seven strings, one per field,
79 or a vCard format string with semicolon-separated fields
80 params: Optional property parameters
81 """
82 if isinstance(fields, str):
83 fields = self.from_ical(fields)
84 if isinstance(fields, AdrFields):
85 self.fields = fields
86 else:
87 if len(fields) != 7:
88 raise ValueError(f"ADR must have exactly 7 fields, got {len(fields)}")
89 self.fields = AdrFields(*(str(f) for f in fields))
90 self.params = Parameters(params)
92 def to_ical(self) -> bytes:
93 """Generate vCard format with semicolon-separated fields."""
94 # Each field is vText (handles comma/backslash escaping)
95 # but we join with unescaped semicolons (field separators)
96 from icalendar.prop.text import vText
98 parts = [vText(f).to_ical().decode(DEFAULT_ENCODING) for f in self.fields]
99 return ";".join(parts).encode(DEFAULT_ENCODING)
101 @staticmethod
102 def from_ical(ical: str | bytes) -> AdrFields:
103 """Parse vCard ADR format into an AdrFields named tuple.
105 Parameters:
106 ical: vCard format string with semicolon-separated fields
108 Returns:
109 AdrFields named tuple with seven field values.
110 """
111 from icalendar.parser import split_on_unescaped_semicolon
113 ical = to_unicode(ical)
114 fields = split_on_unescaped_semicolon(ical)
115 if len(fields) != 7:
116 raise ValueError(
117 f"ADR must have exactly 7 fields, got {len(fields)}: {ical}"
118 )
119 return AdrFields(*fields)
121 def __eq__(self, other: object) -> bool:
122 """self == other"""
123 return isinstance(other, vAdr) and self.fields == other.fields
125 def __repr__(self) -> str:
126 """String representation."""
127 return f"{self.__class__.__name__}({self.fields}, params={self.params})"
129 @property
130 def ical_value(self) -> AdrFields:
131 """The address fields as a named tuple."""
132 return self.fields
134 from icalendar.param import VALUE
136 def to_jcal(self, name: str) -> list:
137 """The jCal representation of this property according to :rfc:`7265`."""
138 result = [name, self.params.to_jcal(), self.VALUE.lower()]
139 result.extend(self.fields)
140 return result
142 @classmethod
143 def from_jcal(cls, jcal_property: list) -> Self:
144 """Parse jCal from :rfc:`7265`.
146 Parameters:
147 jcal_property: The jCal property to parse.
149 Raises:
150 ~error.JCalParsingError: If the provided jCal is invalid.
151 """
152 JCalParsingError.validate_property(jcal_property, cls)
153 if len(jcal_property) != 10: # name, params, value_type, 7 fields
154 raise JCalParsingError(
155 f"ADR must have 10 elements (name, params, value_type, 7 fields), "
156 f"got {len(jcal_property)}"
157 )
158 for i, field in enumerate(jcal_property[3:], start=3):
159 JCalParsingError.validate_value_type(field, str, cls, i)
160 return cls(
161 tuple(jcal_property[3:]),
162 Parameters.from_jcal_property(jcal_property),
163 )
165 @classmethod
166 def examples(cls) -> list[Self]:
167 """Examples of vAdr."""
168 return [cls(("", "", "123 Main St", "Springfield", "IL", "62701", "USA"))]
171__all__ = ["AdrFields", "vAdr"]