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`."""
2from typing import Any, ClassVar, NamedTuple
4from icalendar.compatibility import Self
5from icalendar.error import JCalParsingError
6from icalendar.parser import Parameters
7from icalendar.parser_tools import DEFAULT_ENCODING, to_unicode
10class AdrFields(NamedTuple):
11 """Named fields for vCard ADR (Address) property per :rfc:`6350#section-6.3.1`.
13 Provides named access to the seven address components.
14 """
16 po_box: str
17 """Post office box."""
18 extended: str
19 """Extended address (e.g., apartment or suite number)."""
20 street: str
21 """Street address."""
22 locality: str
23 """Locality (e.g., city)."""
24 region: str
25 """Region (e.g., state or province)."""
26 postal_code: str
27 """Postal code."""
28 country: str
29 """Country name (full name)."""
32class vAdr:
33 """vCard ADR (Address) structured property per :rfc:`6350#section-6.3.1`.
35 The ADR property represents a delivery address as a single text value.
36 The structured type value consists of a sequence of seven address components.
37 The component values must be specified in their corresponding position.
39 - post office box
40 - extended address (e.g., apartment or suite number)
41 - street address
42 - locality (e.g., city)
43 - region (e.g., state or province)
44 - postal code
45 - country name (full name)
47 When a component value is missing, the associated component separator MUST still be specified.
49 Semicolons are field separators and are NOT escaped.
50 Commas and backslashes within field values ARE escaped per :rfc:`6350`.
52 Examples:
53 .. code-block:: pycon
55 >>> from icalendar.prop import vAdr
56 >>> adr = vAdr(("", "", "123 Main St", "Springfield", "IL", "62701", "USA"))
57 >>> adr.to_ical()
58 b';;123 Main St;Springfield;IL;62701;USA'
59 >>> vAdr.from_ical(";;123 Main St;Springfield;IL;62701;USA")
60 AdrFields(po_box='', extended='', street='123 Main St', locality='Springfield', region='IL', postal_code='62701', country='USA')
61 """
63 default_value: ClassVar[str] = "TEXT"
64 params: Parameters
65 fields: AdrFields
67 def __init__(
68 self,
69 fields: tuple[str, ...] | list[str] | str | AdrFields,
70 /,
71 params: dict[str, Any] | None = None,
72 ):
73 """Initialize ADR with seven fields or parse from vCard format string.
75 Parameters:
76 fields: Either an AdrFields, tuple, or list of seven strings, one per field,
77 or a vCard format string with semicolon-separated fields
78 params: Optional property parameters
79 """
80 if isinstance(fields, str):
81 fields = self.from_ical(fields)
82 if isinstance(fields, AdrFields):
83 self.fields = fields
84 else:
85 if len(fields) != 7:
86 raise ValueError(f"ADR must have exactly 7 fields, got {len(fields)}")
87 self.fields = AdrFields(*(str(f) for f in fields))
88 self.params = Parameters(params)
90 def to_ical(self) -> bytes:
91 """Generate vCard format with semicolon-separated fields."""
92 # Each field is vText (handles comma/backslash escaping)
93 # but we join with unescaped semicolons (field separators)
94 from icalendar.prop.text import vText
95 parts = [vText(f).to_ical().decode(DEFAULT_ENCODING) for f in self.fields]
96 return ";".join(parts).encode(DEFAULT_ENCODING)
98 @staticmethod
99 def from_ical(ical: str | bytes) -> AdrFields:
100 """Parse vCard ADR format into an AdrFields named tuple.
102 Parameters:
103 ical: vCard format string with semicolon-separated fields
105 Returns:
106 AdrFields named tuple with seven field values.
107 """
108 from icalendar.parser import split_on_unescaped_semicolon
110 ical = to_unicode(ical)
111 fields = split_on_unescaped_semicolon(ical)
112 if len(fields) != 7:
113 raise ValueError(
114 f"ADR must have exactly 7 fields, got {len(fields)}: {ical}"
115 )
116 return AdrFields(*fields)
118 def __eq__(self, other: object) -> bool:
119 """self == other"""
120 return isinstance(other, vAdr) and self.fields == other.fields
122 def __repr__(self) -> str:
123 """String representation."""
124 return f"{self.__class__.__name__}({self.fields}, params={self.params})"
126 @property
127 def ical_value(self) -> AdrFields:
128 """The address fields as a named tuple."""
129 return self.fields
131 from icalendar.param import VALUE
133 def to_jcal(self, name: str) -> list:
134 """The jCal representation of this property according to :rfc:`7265`."""
135 result = [name, self.params.to_jcal(), self.VALUE.lower()]
136 result.extend(self.fields)
137 return result
139 @classmethod
140 def from_jcal(cls, jcal_property: list) -> Self:
141 """Parse jCal from :rfc:`7265`.
143 Parameters:
144 jcal_property: The jCal property to parse.
146 Raises:
147 ~error.JCalParsingError: If the provided jCal is invalid.
148 """
149 JCalParsingError.validate_property(jcal_property, cls)
150 if len(jcal_property) != 10: # name, params, value_type, 7 fields
151 raise JCalParsingError(
152 f"ADR must have 10 elements (name, params, value_type, 7 fields), "
153 f"got {len(jcal_property)}"
154 )
155 for i, field in enumerate(jcal_property[3:], start=3):
156 JCalParsingError.validate_value_type(field, str, cls, i)
157 return cls(
158 tuple(jcal_property[3:]),
159 Parameters.from_jcal_property(jcal_property),
160 )
162 @classmethod
163 def examples(cls) -> list[Self]:
164 """Examples of vAdr."""
165 return [cls(("", "", "123 Main St", "Springfield", "IL", "62701", "USA"))]
169__all__ = ["AdrFields", "vAdr"]