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 

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 NFields(NamedTuple): 

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

13 

14 Provides named access to the five name components. 

15 """ 

16 

17 family: str 

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

19 given: str 

20 """Given names.""" 

21 additional: str 

22 """Additional names.""" 

23 prefix: str 

24 """Honorific prefixes.""" 

25 suffix: str 

26 """Honorific suffixes.""" 

27 

28 

29class vN: 

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

31 

32 The N property represents a person's name. 

33 It consists of a single structured text value. 

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

35 

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

37 

38 - family names (also known as surnames) 

39 - given names 

40 - additional names 

41 - honorific prefixes 

42 - honorific suffixes 

43 

44 Semicolons are field separators and are NOT escaped. 

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

46 

47 Examples: 

48 

49 .. code-block:: pycon 

50 

51 >>> from icalendar.prop import vN 

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

53 >>> n.to_ical() 

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

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

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

57 """ 

58 

59 default_value: ClassVar[str] = "TEXT" 

60 params: Parameters 

61 fields: NFields 

62 

63 def __init__( 

64 self, 

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

66 /, 

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

68 ): 

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

70 

71 Parameters: 

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

73 or a vCard format string with semicolon-separated fields 

74 params: Optional property parameters 

75 """ 

76 if isinstance(fields, str): 

77 fields = self.from_ical(fields) 

78 if isinstance(fields, NFields): 

79 self.fields = fields 

80 else: 

81 if len(fields) != 5: 

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

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

84 self.params = Parameters(params) 

85 

86 def to_ical(self) -> bytes: 

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

88 from icalendar.prop.text import vText 

89 

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 

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