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

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

50 statements  

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

2 

3import re 

4from typing import Any 

5 

6from icalendar.caselessdict import CaselessDict 

7from icalendar.compatibility import Self 

8from icalendar.error import JCalParsingError 

9from icalendar.parser import Parameters 

10from icalendar.parser_tools import DEFAULT_ENCODING, to_unicode 

11 

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

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

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

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

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

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

18# of ``DIGIT``. 

19WEEKDAY_RULE = re.compile( 

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

21) 

22 

23 

24class vWeekday(str): 

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

26 

27 .. code-block:: pycon 

28 

29 >>> from icalendar import vWeekday 

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

31 'MO' 

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

33 2 

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

35 'FR' 

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

37 -1 

38 

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

40 

41 .. code-block:: text 

42 

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

44 plus = "+" 

45 minus = "-" 

46 ordwk = 1*2DIGIT ;1 to 53 

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

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

49 ;FRIDAY, and SATURDAY days of the week. 

50 

51 """ 

52 

53 params: Parameters 

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

55 

56 week_days = CaselessDict( 

57 { 

58 "SU": 0, 

59 "MO": 1, 

60 "TU": 2, 

61 "WE": 3, 

62 "TH": 4, 

63 "FR": 5, 

64 "SA": 6, 

65 } 

66 ) 

67 

68 def __new__( 

69 cls, 

70 value, 

71 encoding=DEFAULT_ENCODING, 

72 /, 

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

74 ): 

75 value = to_unicode(value, encoding=encoding) 

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

77 match = WEEKDAY_RULE.match(self) 

78 if match is None: 

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

80 match = match.groupdict() 

81 sign = match["signal"] 

82 weekday = match["weekday"] 

83 relative = match["relative"] 

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

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

86 self.weekday = weekday or None 

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

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

89 self.relative *= -1 

90 self.params = Parameters(params) 

91 return self 

92 

93 def to_ical(self): 

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

95 

96 @classmethod 

97 def from_ical(cls, ical): 

98 try: 

99 return cls(ical.upper()) 

100 except Exception as e: 

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

102 

103 @property 

104 def ical_value(self) -> str: 

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

106 

107 See Also: 

108 

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

110 """ 

111 return str(self) 

112 

113 @classmethod 

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

115 """Parse a jCal value for vWeekday. 

116 

117 Raises: 

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

119 """ 

120 JCalParsingError.validate_value_type(value, str, cls) 

121 try: 

122 return cls(value) 

123 except ValueError as e: 

124 raise JCalParsingError( 

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

126 ) from e 

127 

128 

129__all__ = ["vWeekday"]