Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/icalendar/prop/n.py: 74%

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

66 statements  

1"""N property from :rfc:`6350`.""" 

2 

3 

4from typing import Any, ClassVar, NamedTuple 

5 

6from icalendar.compatibility import Self 

7from icalendar.error import JCalParsingError 

8from icalendar.parser import Parameters 

9from icalendar.parser_tools import DEFAULT_ENCODING, to_unicode 

10 

11 

12class NFields(NamedTuple): 

13 """Named fields for vCard N (Name) property per :rfc:`6350#section-6.2.2`. 

14 

15 Provides named access to the five name components. 

16 """ 

17 

18 family: str 

19 """Family names (also known as surnames).""" 

20 given: str 

21 """Given names.""" 

22 additional: str 

23 """Additional names.""" 

24 prefix: str 

25 """Honorific prefixes.""" 

26 suffix: str 

27 """Honorific suffixes.""" 

28 

29 

30class vN: 

31 r"""vCard N (Name) structured property per :rfc:`6350#section-6.2.2`. 

32 

33 The N property represents a person's name. 

34 It consists of a single structured text value. 

35 Each component in the structure may have multiple values, separated by commas. 

36 

37 The structured property value corresponds, in sequence, to the following fields: 

38 

39 - family names (also known as surnames) 

40 - given names 

41 - additional names 

42 - honorific prefixes 

43 - honorific suffixes 

44 

45 Semicolons are field separators and are NOT escaped. 

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

47 

48 Examples: 

49 

50 .. code-block:: pycon 

51 

52 >>> from icalendar.prop import vN 

53 >>> n = vN(("Doe", "John", "M.", "Dr.", "Jr.,M.D.,A.C.P.")) 

54 >>> n.to_ical() 

55 b'Doe;John;M.;Dr.;Jr.\\,M.D.\\,A.C.P.' 

56 >>> vN.from_ical(r"Doe;John;M.;Dr.;Jr.\,M.D.\,A.C.P.") 

57 NFields(family='Doe', given='John', additional='M.', prefix='Dr.', suffix='Jr.,M.D.,A.C.P.') 

58 """ 

59 

60 default_value: ClassVar[str] = "TEXT" 

61 params: Parameters 

62 fields: NFields 

63 

64 def __init__( 

65 self, 

66 fields: tuple[str, ...] | list[str] | str | NFields, 

67 /, 

68 params: dict[str, Any] | None = None, 

69 ): 

70 """Initialize N with five fields or parse from vCard format string. 

71 

72 Parameters: 

73 fields: Either an NFields, tuple, or list of five strings, one per field, 

74 or a vCard format string with semicolon-separated fields 

75 params: Optional property parameters 

76 """ 

77 if isinstance(fields, str): 

78 fields = self.from_ical(fields) 

79 if isinstance(fields, NFields): 

80 self.fields = fields 

81 else: 

82 if len(fields) != 5: 

83 raise ValueError(f"N must have exactly 5 fields, got {len(fields)}") 

84 self.fields = NFields(*(str(f) for f in fields)) 

85 self.params = Parameters(params) 

86 

87 def to_ical(self) -> bytes: 

88 """Generate vCard format with semicolon-separated fields.""" 

89 from icalendar.prop.text import vText 

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

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

92 

93 @staticmethod 

94 def from_ical(ical: str | bytes) -> NFields: 

95 """Parse vCard N format into an NFields named tuple. 

96 

97 Parameters: 

98 ical: vCard format string with semicolon-separated fields 

99 

100 Returns: 

101 NFields named tuple with five field values. 

102 """ 

103 from icalendar.parser import split_on_unescaped_semicolon 

104 

105 ical = to_unicode(ical) 

106 fields = split_on_unescaped_semicolon(ical) 

107 if len(fields) != 5: 

108 raise ValueError(f"N must have exactly 5 fields, got {len(fields)}: {ical}") 

109 return NFields(*fields) 

110 

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

112 """self == other""" 

113 return isinstance(other, vN) and self.fields == other.fields 

114 

115 def __repr__(self) -> str: 

116 """String representation.""" 

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

118 

119 @property 

120 def ical_value(self) -> NFields: 

121 """The name fields as a named tuple.""" 

122 return self.fields 

123 

124 from icalendar.param import VALUE 

125 

126 def to_jcal(self, name: str) -> list: 

127 """The jCal representation of this property according to :rfc:`7265`.""" 

128 result = [name, self.params.to_jcal(), self.VALUE.lower()] 

129 result.extend(self.fields) 

130 return result 

131 

132 @classmethod 

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

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

135 

136 Parameters: 

137 jcal_property: The jCal property to parse. 

138 

139 Raises: 

140 ~error.JCalParsingError: If the provided jCal is invalid. 

141 """ 

142 JCalParsingError.validate_property(jcal_property, cls) 

143 if len(jcal_property) != 8: # name, params, value_type, 5 fields 

144 raise JCalParsingError( 

145 f"N must have 8 elements (name, params, value_type, 5 fields), " 

146 f"got {len(jcal_property)}" 

147 ) 

148 for i, field in enumerate(jcal_property[3:], start=3): 

149 JCalParsingError.validate_value_type(field, str, cls, i) 

150 return cls( 

151 tuple(jcal_property[3:]), 

152 Parameters.from_jcal_property(jcal_property), 

153 ) 

154 

155 @classmethod 

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

157 """Examples of vN.""" 

158 return [cls(("Doe", "John", "M.", "Dr.", "Jr."))] 

159 

160__all__ = ["NFields", "vN"]