Skip to content

Commit 9699c16

Browse files
authored
Merge branch 'master' into flow_api-to-flow
2 parents bae4c30 + 6509e19 commit 9699c16

File tree

6 files changed

+136
-35
lines changed

6 files changed

+136
-35
lines changed

docs/whats_new.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Breaking Changes
1414
- Remove ``flogin.errors.SettingNotFound``
1515
- Rewrite the CLI commands
1616
- Remove ``flogin.conditions.MultiCondition`` in favor of :class:`flogin.conditions.AnyCondition` and :class:`flogin.conditions.AllCondition`
17+
- Remove `Query.from_json`
18+
- For :func:`flogin.testing.plugin_tester.PluginTester.test_query`, switch from receiving a query object to taking kwargs that will be used to make a query object
1719
- Rename the ``flogin.flow_api`` directory to ``flogin.flow``
1820
- Rename ``flogin.flow_api.client.py`` to ``flogin.flow.api.py``
1921

@@ -33,6 +35,8 @@ New Features
3335
- Make :attr:`flogin.jsonrpc.results.Result.title` optional
3436
- Add :class:`flogin.conditions.AnyCondition`
3537
- Add :class:`flogin.conditions.AllCondition`
38+
- Add :func:`flogin.query.Query.update_results`
39+
- Add :func:`flogin.query.Query.update`
3640
- Add ``flogin.flow.settings.py``
3741
- Add :class:`flogin.flow.settings.CustomFileManager`
3842
- Add :class:`flogin.flow.settings.CustomBrowser`

flogin/default_events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ async def on_error(
2626

2727
def get_default_events(plugin: Plugin[Any]) -> dict[str, Callable[..., Awaitable[Any]]]:
2828
def on_query(data: dict[str, Any], raw_settings: dict[str, Any]):
29-
query = Query.from_json(data)
29+
query = Query(data, plugin)
3030
if plugin._settings_are_populated is False:
3131
LOG.info(f"Settings have not been populated yet, creating a new instance")
3232
plugin._settings_are_populated = True

flogin/query.py

Lines changed: 91 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
from __future__ import annotations
22

3-
from typing import Any, Generic, TypeVar
3+
from typing import TYPE_CHECKING, Any, Generic, TypedDict, TypeVar
4+
5+
from ._types import PluginT
6+
from .utils import MISSING
7+
8+
if TYPE_CHECKING:
9+
from .jsonrpc.results import Result
410

511
T = TypeVar("T")
612

713
__all__ = ("Query",)
814

915

16+
class RawQuery(TypedDict):
17+
search: str
18+
rawQuery: str
19+
isReQuery: bool
20+
actionKeyword: str
21+
22+
1023
class Query(Generic[T]):
1124
r"""This class represents the query data sent from flow launcher
1225
@@ -34,24 +47,10 @@ class Query(Generic[T]):
3447
The keyword used to initiate the query
3548
"""
3649

37-
def __init__(
38-
self, *, raw_text: str, is_requery: bool = False, text: str, keyword: str
39-
) -> None:
50+
def __init__(self, data: RawQuery, plugin: PluginT) -> None:
4051
self.__search_condition_data: T | None = None
41-
42-
self.raw_text = raw_text
43-
self.is_requery = is_requery
44-
self.text = text
45-
self.keyword = keyword
46-
47-
@classmethod
48-
def from_json(cls: type[Query], data: dict[str, Any]) -> Query:
49-
return cls(
50-
raw_text=data["rawQuery"],
51-
is_requery=data["isReQuery"],
52-
text=data["search"],
53-
keyword=data["actionKeyword"],
54-
)
52+
self._data = data
53+
self.plugin = plugin
5554

5655
@property
5756
def condition_data(self) -> T | None:
@@ -62,6 +61,22 @@ def condition_data(self) -> T | None:
6261
def condition_data(self, value: T) -> None:
6362
self.__search_condition_data = value
6463

64+
@property
65+
def is_requery(self) -> bool:
66+
return self._data["isReQuery"]
67+
68+
@property
69+
def keyword(self) -> str:
70+
return self._data["actionKeyword"]
71+
72+
@property
73+
def raw_text(self) -> str:
74+
return self._data["rawQuery"]
75+
76+
@property
77+
def text(self) -> str:
78+
return self._data["search"]
79+
6580
def __eq__(self, other: Any) -> bool:
6681
return (
6782
isinstance(other, Query)
@@ -74,3 +89,61 @@ def __hash__(self) -> int:
7489

7590
def __repr__(self) -> str:
7691
return f"<Query {self.raw_text=} {self.text=} {self.keyword=} {self.is_requery=} {self.condition_data=}>"
92+
93+
async def update_results(self, results: list[Result]) -> None:
94+
r"""|coro|
95+
96+
Tells flow to change the results shown to the user, using the query from this query object.
97+
98+
This method provides quick acess to :func:`flogin.flow_api.client.FlowLauncherAPI.update_results`. Because of that, this method will only take affect if the user has not changed the query.
99+
100+
Parameters
101+
----------
102+
results: list[:class:`~flogin.jsonrpc.results.Result`]
103+
The new results
104+
105+
Returns
106+
-------
107+
None
108+
"""
109+
110+
return await self.plugin.api.update_results(self.raw_text, results)
111+
112+
async def update(
113+
self, *, text: str, keyword: str | None = MISSING, requery: bool = False
114+
) -> None:
115+
r"""|coro|
116+
117+
Applies updates to the query with flow, and to this object.
118+
119+
This method provides quick acess to :func:`flogin.flow_api.client.FlowLauncherAPI.change_query`
120+
121+
Parameters
122+
----------
123+
text: :class:`str`
124+
The text that will be used with the query.
125+
keyword: :class:`str`
126+
The keyword that will be used with the query. Defaults to the pre-existing value of :attr:`Query.keyword`. Set this to ``None`` or `"*"` for no keyword to be used.
127+
requery: :class:`bool`
128+
Whether or not to re-send a query request in the event that the new query is the same as the current query
129+
130+
Returns
131+
--------
132+
None
133+
"""
134+
135+
if keyword is MISSING:
136+
keyword = self.keyword
137+
138+
if keyword is None or keyword == "*":
139+
raw_text = text
140+
self._data["actionKeyword"] = "*"
141+
else:
142+
raw_text = f"{keyword} {text}"
143+
self._data["actionKeyword"] = keyword
144+
145+
self._data["rawQuery"] = raw_text
146+
self._data["search"] = text
147+
self._data["isReQuery"] = requery
148+
149+
return await self.plugin.api.change_query(raw_text, requery=requery)

flogin/testing/plugin_tester.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99

1010
from .._types import PluginT, RawSettings
1111
from ..flow.plugin_metadata import PluginMetadata
12+
from ..query import Query
1213
from ..settings import Settings
1314
from ..utils import MISSING
1415
from .filler import FillerObject
1516

1617
if TYPE_CHECKING:
1718
from ..jsonrpc.responses import QueryResponse
1819
from ..jsonrpc.results import Result
19-
from ..query import Query
2020

2121
API_FILLER_TEXT = "FlowLauncherAPI is unavailable during testing. Consider passing the 'flow_api_client' arg into PluginTester to impliment your own flow api client."
2222
CHARACTERS = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLLZXCVBNM1234567890"
@@ -88,7 +88,12 @@ def set_flow_api_client(self, flow_api_client: Any = MISSING) -> None:
8888
self.plugin.metadata._flow_api = flow_api_client
8989

9090
async def test_query(
91-
self, query: Query, *, settings: Settings | RawSettings | None = MISSING
91+
self,
92+
text: str,
93+
*,
94+
keyword: str = "*",
95+
is_requery: bool = False,
96+
settings: Settings | RawSettings | None = MISSING,
9297
) -> QueryResponse:
9398
r"""|coro|
9499
@@ -116,6 +121,16 @@ async def test_query(
116121
self.plugin.settings = settings
117122
self.plugin._settings_are_populated = True
118123

124+
query = Query(
125+
{
126+
"rawQuery": f"{keyword} {text}",
127+
"search": text,
128+
"actionKeyword": keyword,
129+
"isReQuery": is_requery,
130+
},
131+
self.plugin,
132+
)
133+
119134
coro = self.plugin.process_search_handlers(query)
120135

121136
if coro is None:

tests/test_conditions.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,33 @@
1010
Query,
1111
RegexCondition,
1212
)
13+
from flogin.testing.filler import FillerObject
14+
15+
16+
def _create_query(text: str, plugin, keyword: str = "*", is_requery: bool = False):
17+
return Query(
18+
{
19+
"rawQuery": f"{keyword} {text}",
20+
"search": text,
21+
"actionKeyword": keyword,
22+
},
23+
plugin,
24+
)
25+
26+
27+
@pytest.fixture
28+
def plugin():
29+
return FillerObject(f"Fake plugin object provided")
1330

1431

1532
@pytest.fixture
16-
def yes_query():
17-
return Query(raw_text="bar foo", text="foo", keyword="bar")
33+
def yes_query(plugin):
34+
return _create_query(text="foo", keyword="bar", plugin=plugin)
1835

1936

2037
@pytest.fixture
21-
def no_query():
22-
return Query(raw_text="car foo", text="apple", keyword="car")
38+
def no_query(plugin):
39+
return _create_query(text="apple", keyword="car", plugin=plugin)
2340

2441

2542
@pytest.fixture(params=[0, 1])

tests/test_search_handlers.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,6 @@ def tester(plugin, metadata):
1919
return PluginTester(plugin, metadata=metadata)
2020

2121

22-
@pytest.fixture
23-
def query():
24-
txt = "bar"
25-
keyword = "foo"
26-
27-
return Query(raw_text=f"{txt} {keyword}", text=txt, keyword=keyword)
28-
29-
3022
class ReturnSingleResultHandler(SearchHandler):
3123
async def callback(self, query: Query):
3224
return Result("Title")
@@ -75,7 +67,7 @@ def handler(plugin: Plugin, request: pytest.FixtureRequest):
7567

7668

7769
@pytest.mark.asyncio
78-
async def test_handler_result(tester: PluginTester, query: Query):
79-
response = await tester.test_query(query)
70+
async def test_handler_result(tester: PluginTester):
71+
response = await tester.test_query("bar", keyword="foo")
8072
result = response.results[0]
8173
assert result.title == "Title"

0 commit comments

Comments
 (0)