Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/icalendar/prop/recur/weekday.py: 75%

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

68 statements  

1"""BYWEEKDAY, BYDAY, and WKST value type of RECUR from :rfc:`5545`.""" 

2 

3from __future__ import annotations 

4 

5import re 

6from typing import TYPE_CHECKING, Any 

7from xml.etree.ElementTree import Element, SubElement 

8 

9from icalendar.caselessdict import CaselessDict 

10from icalendar.error import JCalParsingError, XCalParsingError 

11from icalendar.parser import Parameters 

12from icalendar.parser.xcal.wrapper import from_xcal_wrapper 

13from icalendar.parser_tools import DEFAULT_ENCODING, to_unicode 

14 

15if TYPE_CHECKING: 

16 from icalendar.compatibility import Self 

17 from icalendar.parser.xcal.base import XCalParser 

18 

19# Use ``[0-9]`` to explicitly specify that only ASCII digits should be 

20# matched, instead of ``\d``. In Python, ``\d`` matches a digit zero through 

21# nine in any script except ideographic scripts, and is equivalent to 

22# ``\p{Nd}``. For example, the Arabic-Indic "١٢" would match. Here, a 

23# malformed ``ordwk`` would get parsed into a valid ``relative`` number. 

24# RFC 5545, section 3.3.10, allows only ASCII characters per its definition 

25# of ``DIGIT``. 

26WEEKDAY_RULE = re.compile( 

27 r"(?P<signal>[+-]?)(?P<relative>[0-9]{0,2})(?P<weekday>[\w]{2})\Z" 

28) 

29 

30 

31class vWeekday(str): 

32 """Either a ``weekday`` or a ``weekdaynum``. 

33 

34 .. code-block:: pycon 

35 

36 >>> from icalendar import vWeekday 

37 >>> vWeekday("MO") # Simple weekday 

38 'MO' 

39 >>> vWeekday("2FR").relative # Second friday 

40 2 

41 >>> vWeekday("2FR").weekday 

42 'FR' 

43 >>> vWeekday("-1SU").relative # Last Sunday 

44 -1 

45 

46 Definition from :rfc:`5545#section-3.3.10`: 

47 

48 .. code-block:: text 

49 

50 weekdaynum = [[plus / minus] ordwk] weekday 

51 plus = "+" 

52 minus = "-" 

53 ordwk = 1*2DIGIT ;1 to 53 

54 weekday = "SU" / "MO" / "TU" / "WE" / "TH" / "FR" / "SA" 

55 ;Corresponding to SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, 

56 ;FRIDAY, and SATURDAY days of the week. 

57 

58 """ 

59 

60 params: Parameters 

61 __slots__ = ("params", "relative", "weekday") 

62 

63 week_days = CaselessDict( 

64 { 

65 "SU": 0, 

66 "MO": 1, 

67 "TU": 2, 

68 "WE": 3, 

69 "TH": 4, 

70 "FR": 5, 

71 "SA": 6, 

72 } 

73 ) 

74 

75 def __new__( 

76 cls, 

77 value, 

78 encoding=DEFAULT_ENCODING, 

79 /, 

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

81 ): 

82 value = to_unicode(value, encoding=encoding) 

83 self = super().__new__(cls, value) 

84 match = WEEKDAY_RULE.match(self) 

85 if match is None: 

86 raise ValueError(f"Expected weekday abbreviation, got: {self}") 

87 match = match.groupdict() 

88 sign = match["signal"] 

89 weekday = match["weekday"] 

90 relative = match["relative"] 

91 if weekday not in vWeekday.week_days or sign not in "+-": 

92 raise ValueError(f"Expected weekday abbreviation, got: {self}") 

93 self.weekday = weekday or None 

94 self.relative = (relative and int(relative)) or None 

95 if sign == "-" and self.relative: 

96 self.relative *= -1 

97 self.params = Parameters(params) 

98 return self 

99 

100 def to_ical(self): 

101 return self.encode(DEFAULT_ENCODING).upper() 

102 

103 @classmethod 

104 def from_ical(cls, ical): 

105 try: 

106 return cls(ical.upper()) 

107 except Exception as e: 

108 raise ValueError(f"Expected weekday abbreviation, got: {ical}") from e 

109 

110 @property 

111 def ical_value(self) -> str: 

112 """Returns the weekday value as a string, for example, ``MO``, ``+2TH``, or ``-1SU``. 

113 

114 See Also: 

115 

116 :rfc:`5545#section-3.3.10` for the ``BYDAY`` rule grammar. 

117 """ 

118 return str(self) 

119 

120 @classmethod 

121 def parse_jcal_value(cls, value: Any) -> Self: 

122 """Parse a jCal value for vWeekday. 

123 

124 Raises: 

125 ~error.JCalParsingError: If the value is not a valid weekday. 

126 """ 

127 JCalParsingError.validate_value_type(value, str, cls) 

128 try: 

129 return cls(value) 

130 except ValueError as e: 

131 raise JCalParsingError( 

132 "The value must be a valid weekday.", cls, value=value 

133 ) from e 

134 

135 @classmethod 

136 @from_xcal_wrapper 

137 def from_xcal(cls, parser: XCalParser, params: Parameters) -> Self: 

138 """Parse xCal from :rfc:`6321`. 

139 

140 Parameters: 

141 xml: The XML to parse or a parser. 

142 

143 Raises: 

144 ~error.XCalParsingError: If the provided xCal is invalid. 

145 """ 

146 child = parser.parse_tag() 

147 text = child.get_xsd_token() 

148 try: 

149 return cls(text.upper(), params=params) 

150 except ValueError as e: 

151 raise XCalParsingError( 

152 "Expected weekday https://datatracker.ietf.org/doc/html/rfc5545#section-3.3.10", 

153 text, 

154 child, 

155 ) from e 

156 

157 def to_xcal(self, element: Element) -> None: 

158 """Add the xCal representation of this property according to :rfc:`6321`.""" 

159 self.params.to_xcal(element) 

160 weekday_element = SubElement(element, "byday") 

161 weekday_element.text = self.ical_value 

162 

163 

164__all__ = ["vWeekday"]