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

70 statements  

1"""ADR property of :rfc:`6350`.""" 

2 

3from typing import Any, ClassVar, NamedTuple 

4 

5from icalendar.compatibility import Self 

6from icalendar.error import JCalParsingError 

7from icalendar.parser import Parameters 

8from icalendar.parser_tools import DEFAULT_ENCODING, to_unicode 

9 

10 

11class AdrFields(NamedTuple): 

12 """Named fields for vCard ADR (Address) property per :rfc:`6350#section-6.3.1`. 

13 

14 Provides named access to the seven address components. 

15 """ 

16 

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).""" 

31 

32 

33class vAdr: 

34 """vCard ADR (Address) structured property per :rfc:`6350#section-6.3.1`. 

35 

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. 

39 

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) 

47 

48 When a component value is missing, the associated component separator 

49 MUST still be specified. 

50 

51 Semicolons are field separators and are NOT escaped. 

52 Commas and backslashes within field values ARE escaped per :rfc:`6350`. 

53 

54 Examples: 

55 .. code-block:: pycon 

56 

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 """ 

64 

65 default_value: ClassVar[str] = "TEXT" 

66 params: Parameters 

67 fields: AdrFields 

68 

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. 

76 

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) 

91 

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 

97 

98 parts = [vText(f).to_ical().decode(DEFAULT_ENCODING) for f in self.fields] 

99 return ";".join(parts).encode(DEFAULT_ENCODING) 

100 

101 @staticmethod 

102 def from_ical(ical: str | bytes) -> AdrFields: 

103 """Parse vCard ADR format into an AdrFields named tuple. 

104 

105 Parameters: 

106 ical: vCard format string with semicolon-separated fields 

107 

108 Returns: 

109 AdrFields named tuple with seven field values. 

110 """ 

111 from icalendar.parser import split_on_unescaped_semicolon 

112 

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) 

120 

121 def __eq__(self, other: object) -> bool: 

122 """self == other""" 

123 return isinstance(other, vAdr) and self.fields == other.fields 

124 

125 def __repr__(self) -> str: 

126 """String representation.""" 

127 return f"{self.__class__.__name__}({self.fields}, params={self.params})" 

128 

129 @property 

130 def ical_value(self) -> AdrFields: 

131 """The address fields as a named tuple.""" 

132 return self.fields 

133 

134 from icalendar.param import VALUE 

135 

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 

141 

142 @classmethod 

143 def from_jcal(cls, jcal_property: list) -> Self: 

144 """Parse jCal from :rfc:`7265`. 

145 

146 Parameters: 

147 jcal_property: The jCal property to parse. 

148 

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 ) 

164 

165 @classmethod 

166 def examples(cls) -> list[Self]: 

167 """Examples of vAdr.""" 

168 return [cls(("", "", "123 Main St", "Springfield", "IL", "62701", "USA"))] 

169 

170 

171__all__ = ["AdrFields", "vAdr"]