Coverage for flogin/conditions.py: 100%

55 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-03 22:51 +0000

1from __future__ import annotations 

2 

3from typing import TYPE_CHECKING 

4 

5if TYPE_CHECKING: 

6 import re 

7 from collections.abc import Iterable 

8 

9 from ._types.search_handlers import SearchHandlerCondition 

10 from .query import Query 

11 

12__all__ = ( 

13 "AllCondition", 

14 "AnyCondition", 

15 "KeywordCondition", 

16 "PlainTextCondition", 

17 "RegexCondition", 

18) 

19 

20 

21class PlainTextCondition: 

22 r"""A builtin search condition to check plain text. 

23 

24 This condition will only run if the query's text is the same as the text given to this condition. 

25 See the :ref:`search handler section <search_handlers>` for more information about using search handlers and conditions. 

26 

27 Attributes 

28 ---------- 

29 text: :class:`str` 

30 The text to compare the query to 

31 """ 

32 

33 __slots__ = ("text",) 

34 

35 def __init__(self, text: str) -> None: 

36 self.text = text 

37 

38 def __call__(self, query: Query) -> bool: 

39 return query.text == self.text 

40 

41 

42class RegexCondition: 

43 r"""A builtin search condition to check a regex pattern. 

44 

45 This condition will only run if the query's text is a match to the regex pattern given to this condition. 

46 See the :ref:`search handler section <search_handlers>` for more information about using search handlers and conditions. 

47 

48 This condition will set the query's :attr:`~flogin.query.Query.condition_data` attribute to the :class:`re.Match` object. 

49 

50 Attributes 

51 ---------- 

52 pattern: :class:`re.Pattern` 

53 The pattern to check the queries against. 

54 """ 

55 

56 __slots__ = ("pattern",) 

57 

58 def __init__(self, pattern: re.Pattern[str]) -> None: 

59 self.pattern = pattern 

60 

61 def __call__(self, query: Query[re.Match[str]]) -> bool: 

62 match = self.pattern.match(query.text) 

63 if match: 

64 query.condition_data = match 

65 return True 

66 return False 

67 

68 

69class _MultiCondition: 

70 __slots__ = ("conditions",) 

71 

72 def __init__(self, *conditions: SearchHandlerCondition) -> None: 

73 self.conditions = conditions 

74 

75 def __repr__(self) -> str: 

76 return f"{self.__class__.__name__} {self.conditions=}" 

77 

78 

79class AllCondition(_MultiCondition): 

80 r"""This builtin search condition acts similiarly to the builtin ``all`` function. It only returns ``True`` if all of the given conditions also return ``True``. 

81 

82 This condition will set :attr:`flogin.query.Query.condition_data` to a dictionary containing the conditions, where the keys are the conditions, and the values are the condition data that they gave. 

83 

84 Attributes 

85 ---------- 

86 conditions: list[:ref:`condition <condition_example>`] 

87 A list that contains all the conditions that should be used with this condition. 

88 """ 

89 

90 def __call__(self, query: Query) -> bool: 

91 condition_data = {} 

92 for condition in self.conditions: 

93 if condition(query) is False: 

94 return False 

95 condition_data[condition] = query.condition_data 

96 query.condition_data = None 

97 

98 query.condition_data = condition_data 

99 return True 

100 

101 

102class AnyCondition(_MultiCondition): 

103 r"""This builtin search condition acts similiarly to the builtin ``any`` function. It only returns ``True`` if any one of the given conditions return ``True``. 

104 

105 This condition will set :attr:`flogin.query.Query.condition_data` to a tuple containing two values. The first value will be the condition that returned true, and the second will be the condition data that the condition gave. :: 

106 

107 (condition, query.condition_data) 

108 

109 Attributes 

110 ----------- 

111 conditions: list[:ref:`condition <condition_example>`] 

112 A list that contains all the conditions that should be used with this condition. 

113 """ 

114 

115 def __call__(self, query: Query) -> bool: 

116 for condition in self.conditions: 

117 if condition(query) is True: 

118 query.condition_data = (condition, query.condition_data) 

119 return True 

120 return False 

121 

122 

123class KeywordCondition: 

124 r"""A builtin search condition to check what keyword was used with the query. 

125 

126 If the :attr:`~flogin.conditions.KeywordCondition.allowed_keywords` attribute is given, the handler will only run if the query's keyword is in the list of allowed keywords. 

127 If the :attr:`~flogin.conditions.KeywordCondition.disallowed_keywords` attribute is given, the handler will only run if the query's keyword is not in the list of allowed keywords. 

128 

129 See the :ref:`search handler section <search_handlers>` for more information about using search handlers and conditions. 

130 

131 Attributes 

132 ---------- 

133 allowed_keywords: Optional[Iterable[:class:`str`]] 

134 The allowed keywords 

135 disallowed_keywords: Optional[Iterable[:class:`str`]] 

136 The disallowed keywords 

137 """ 

138 

139 __slots__ = "allowed_keywords", "disallowed_keywords" 

140 

141 def __init__( 

142 self, 

143 *, 

144 allowed_keywords: Iterable[str] | None = None, 

145 disallowed_keywords: Iterable[str] | None = None, 

146 ) -> None: 

147 if allowed_keywords is None and disallowed_keywords is None: 

148 raise TypeError( 

149 "Either the 'allowed_keywords' arg or the 'disallowed_keywords' arg must be given" 

150 ) 

151 if allowed_keywords is not None and disallowed_keywords is not None: 

152 raise TypeError( 

153 "'allowed_keywords' and 'disallowed_keywords' can not be passed together. Use `MultiCondition` if you would like to achieve it." 

154 ) 

155 

156 self.allowed_keywords: Iterable[str] | None = allowed_keywords or None 

157 self.disallowed_keywords: Iterable[str] | None = disallowed_keywords or None 

158 

159 def __call__(self, query: Query) -> bool: 

160 if self.allowed_keywords is None and self.disallowed_keywords is not None: 

161 return query.keyword not in self.disallowed_keywords 

162 if self.allowed_keywords is not None and self.disallowed_keywords is None: 

163 return query.keyword in self.allowed_keywords 

164 

165 raise RuntimeError( 

166 "'allowed_keywords' and 'disallowed_keywords' have been modified to be invalid" 

167 )