Coverage for flogin/conditions.py: 100%
55 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-03 22:51 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-03 22:51 +0000
1from __future__ import annotations
3from typing import TYPE_CHECKING
5if TYPE_CHECKING:
6 import re
7 from collections.abc import Iterable
9 from ._types.search_handlers import SearchHandlerCondition
10 from .query import Query
12__all__ = (
13 "AllCondition",
14 "AnyCondition",
15 "KeywordCondition",
16 "PlainTextCondition",
17 "RegexCondition",
18)
21class PlainTextCondition:
22 r"""A builtin search condition to check plain text.
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.
27 Attributes
28 ----------
29 text: :class:`str`
30 The text to compare the query to
31 """
33 __slots__ = ("text",)
35 def __init__(self, text: str) -> None:
36 self.text = text
38 def __call__(self, query: Query) -> bool:
39 return query.text == self.text
42class RegexCondition:
43 r"""A builtin search condition to check a regex pattern.
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.
48 This condition will set the query's :attr:`~flogin.query.Query.condition_data` attribute to the :class:`re.Match` object.
50 Attributes
51 ----------
52 pattern: :class:`re.Pattern`
53 The pattern to check the queries against.
54 """
56 __slots__ = ("pattern",)
58 def __init__(self, pattern: re.Pattern[str]) -> None:
59 self.pattern = pattern
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
69class _MultiCondition:
70 __slots__ = ("conditions",)
72 def __init__(self, *conditions: SearchHandlerCondition) -> None:
73 self.conditions = conditions
75 def __repr__(self) -> str:
76 return f"{self.__class__.__name__} {self.conditions=}"
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``.
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.
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 """
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
98 query.condition_data = condition_data
99 return True
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``.
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. ::
107 (condition, query.condition_data)
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 """
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
123class KeywordCondition:
124 r"""A builtin search condition to check what keyword was used with the query.
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.
129 See the :ref:`search handler section <search_handlers>` for more information about using search handlers and conditions.
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 """
139 __slots__ = "allowed_keywords", "disallowed_keywords"
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 )
156 self.allowed_keywords: Iterable[str] | None = allowed_keywords or None
157 self.disallowed_keywords: Iterable[str] | None = disallowed_keywords or None
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
165 raise RuntimeError(
166 "'allowed_keywords' and 'disallowed_keywords' have been modified to be invalid"
167 )