Skip to content

Commit adbd467

Browse files
committed
feat(stopwords): add async support for stopwords operations
- add AsyncStopwords class for async stopwords collection operations - add AsyncStopwordsSet class for async individual stopwords set operations - add async tests for stopwords and stopwords set functionality - add async fixtures for testing async stopwords operations
1 parent 0743752 commit adbd467

File tree

5 files changed

+381
-4
lines changed

5 files changed

+381
-4
lines changed

src/typesense/async_stopwords.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
"""
2+
This module provides async functionality for managing stopwords in Typesense.
3+
4+
Classes:
5+
- AsyncStopwords: Handles async operations related to stopwords and stopword sets.
6+
7+
Methods:
8+
- __init__: Initializes the AsyncStopwords object.
9+
- __getitem__: Retrieves or creates an AsyncStopwordsSet object for a given stopwords_set_id.
10+
- upsert: Creates or updates a stopwords set.
11+
- retrieve: Retrieves all stopwords sets.
12+
13+
Attributes:
14+
- RESOURCE_PATH: The API resource path for stopwords operations.
15+
16+
The AsyncStopwords class interacts with the Typesense API to manage stopwords operations.
17+
It provides methods to create, update, and retrieve stopwords sets, as well as access
18+
individual AsyncStopwordsSet objects.
19+
20+
This module uses type hinting and is compatible with Python 3.11+ as well as earlier
21+
versions through the use of the typing_extensions library.
22+
"""
23+
24+
import sys
25+
26+
if sys.version_info >= (3, 11):
27+
import typing
28+
else:
29+
import typing_extensions as typing
30+
31+
from typesense.async_api_call import AsyncApiCall
32+
from typesense.async_stopwords_set import AsyncStopwordsSet
33+
from typesense.types.stopword import (
34+
StopwordCreateSchema,
35+
StopwordSchema,
36+
StopwordsRetrieveSchema,
37+
)
38+
39+
40+
class AsyncStopwords:
41+
"""
42+
Class for managing stopwords in Typesense (async).
43+
44+
This class provides methods to interact with stopwords and stopwords sets, including
45+
creating, updating, retrieving, and accessing individual stopwords sets.
46+
47+
Attributes:
48+
RESOURCE_PATH (str): The API resource path for stopwords operations.
49+
api_call (AsyncApiCall): The API call object for making requests.
50+
stopwords_sets (Dict[str, AsyncStopwordsSet]): A dictionary of AsyncStopwordsSet objects.
51+
"""
52+
53+
resource_path: typing.Final[str] = "/stopwords"
54+
55+
def __init__(self, api_call: AsyncApiCall):
56+
"""
57+
Initialize the AsyncStopwords object.
58+
59+
Args:
60+
api_call (AsyncApiCall): The API call object for making requests.
61+
"""
62+
self.api_call = api_call
63+
self.stopwords_sets: typing.Dict[str, AsyncStopwordsSet] = {}
64+
65+
def __getitem__(self, stopwords_set_id: str) -> AsyncStopwordsSet:
66+
"""
67+
Get or create an AsyncStopwordsSet object for a given stopwords_set_id.
68+
69+
Args:
70+
stopwords_set_id (str): The ID of the stopwords set.
71+
72+
Returns:
73+
AsyncStopwordsSet: The AsyncStopwordsSet object for the given ID.
74+
"""
75+
if not self.stopwords_sets.get(stopwords_set_id):
76+
self.stopwords_sets[stopwords_set_id] = AsyncStopwordsSet(
77+
self.api_call,
78+
stopwords_set_id,
79+
)
80+
return self.stopwords_sets[stopwords_set_id]
81+
82+
async def upsert(
83+
self,
84+
stopwords_set_id: str,
85+
stopwords_set: StopwordCreateSchema,
86+
) -> StopwordSchema:
87+
"""
88+
Create or update a stopwords set.
89+
90+
Args:
91+
stopwords_set_id (str): The ID of the stopwords set to upsert.
92+
stopwords_set (StopwordCreateSchema):
93+
The schema for creating or updating the stopwords set.
94+
95+
Returns:
96+
StopwordSchema: The created or updated stopwords set.
97+
"""
98+
response: StopwordSchema = await self.api_call.put(
99+
"/".join([AsyncStopwords.resource_path, stopwords_set_id]),
100+
body=stopwords_set,
101+
entity_type=StopwordSchema,
102+
)
103+
return response
104+
105+
async def retrieve(self) -> StopwordsRetrieveSchema:
106+
"""
107+
Retrieve all stopwords sets.
108+
109+
Returns:
110+
StopwordsRetrieveSchema: The schema containing all stopwords sets.
111+
"""
112+
response: StopwordsRetrieveSchema = await self.api_call.get(
113+
AsyncStopwords.resource_path,
114+
as_json=True,
115+
entity_type=StopwordsRetrieveSchema,
116+
)
117+
return response
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
"""
2+
This module provides async functionality for managing individual stopwords sets in Typesense.
3+
4+
Classes:
5+
- AsyncStopwordsSet: Handles async operations related to a specific stopwords set.
6+
7+
Methods:
8+
- __init__: Initializes the AsyncStopwordsSet object.
9+
- retrieve: Retrieves the details of this specific stopwords set.
10+
- delete: Deletes this specific stopwords set.
11+
- _endpoint_path: Constructs the API endpoint path for this specific stopwords set.
12+
13+
The AsyncStopwordsSet class interacts with the Typesense API to manage operations on a
14+
specific stopwords set. It provides methods to retrieve and delete individual stopwords sets.
15+
16+
For more information regarding Stopwords, refer to the Stopwords [documentation]
17+
(https://typesense.org/docs/27.0/api/stopwords.html).
18+
19+
This module uses type hinting and is compatible with Python 3.11+ as well as earlier
20+
versions through the use of the typing_extensions library.
21+
"""
22+
23+
from typesense.async_api_call import AsyncApiCall
24+
from typesense.types.stopword import StopwordDeleteSchema, StopwordsSingleRetrieveSchema
25+
26+
27+
class AsyncStopwordsSet:
28+
"""
29+
Class for managing individual stopwords sets in Typesense (async).
30+
31+
This class provides methods to interact with a specific stopwords set,
32+
including retrieving and deleting it.
33+
34+
Attributes:
35+
stopwords_set_id (str): The ID of the stopwords set.
36+
api_call (AsyncApiCall): The API call object for making requests.
37+
"""
38+
39+
def __init__(self, api_call: AsyncApiCall, stopwords_set_id: str) -> None:
40+
"""
41+
Initialize the AsyncStopwordsSet object.
42+
43+
Args:
44+
api_call (AsyncApiCall): The API call object for making requests.
45+
stopwords_set_id (str): The ID of the stopwords set.
46+
"""
47+
self.stopwords_set_id = stopwords_set_id
48+
self.api_call = api_call
49+
50+
async def retrieve(self) -> StopwordsSingleRetrieveSchema:
51+
"""
52+
Retrieve this specific stopwords set.
53+
54+
Returns:
55+
StopwordsSingleRetrieveSchema: The schema containing the stopwords set details.
56+
"""
57+
response: StopwordsSingleRetrieveSchema = await self.api_call.get(
58+
self._endpoint_path,
59+
entity_type=StopwordsSingleRetrieveSchema,
60+
as_json=True,
61+
)
62+
return response
63+
64+
async def delete(self) -> StopwordDeleteSchema:
65+
"""
66+
Delete this specific stopwords set.
67+
68+
Returns:
69+
StopwordDeleteSchema: The schema containing the deletion response.
70+
"""
71+
response: StopwordDeleteSchema = await self.api_call.delete(
72+
self._endpoint_path,
73+
entity_type=StopwordDeleteSchema,
74+
)
75+
return response
76+
77+
@property
78+
def _endpoint_path(self) -> str:
79+
"""
80+
Construct the API endpoint path for this specific stopwords set.
81+
82+
Returns:
83+
str: The constructed endpoint path.
84+
"""
85+
from typesense.async_stopwords import AsyncStopwords
86+
87+
return "/".join([AsyncStopwords.resource_path, self.stopwords_set_id])

tests/fixtures/stopword_fixtures.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import requests
55

66
from typesense.api_call import ApiCall
7+
from typesense.async_api_call import AsyncApiCall
8+
from typesense.async_stopwords import AsyncStopwords
9+
from typesense.async_stopwords_set import AsyncStopwordsSet
710
from typesense.stopwords import Stopwords
811
from typesense.stopwords_set import StopwordsSet
912

@@ -67,3 +70,27 @@ def fake_stopwords_fixture(fake_api_call: ApiCall) -> Stopwords:
6770
def fake_stopwords_set_fixture(fake_api_call: ApiCall) -> StopwordsSet:
6871
"""Return a Collection object with test values."""
6972
return StopwordsSet(fake_api_call, "company_stopwords")
73+
74+
75+
@pytest.fixture(scope="function", name="actual_async_stopwords")
76+
def actual_async_stopwords_fixture(actual_async_api_call: AsyncApiCall) -> AsyncStopwords:
77+
"""Return a AsyncStopwords object using a real API."""
78+
return AsyncStopwords(actual_async_api_call)
79+
80+
81+
@pytest.fixture(scope="function", name="actual_async_stopwords_set")
82+
def actual_async_stopwords_set_fixture(actual_async_api_call: AsyncApiCall) -> AsyncStopwordsSet:
83+
"""Return a AsyncStopwordsSet object using a real API."""
84+
return AsyncStopwordsSet(actual_async_api_call, "company_stopwords")
85+
86+
87+
@pytest.fixture(scope="function", name="fake_async_stopwords")
88+
def fake_async_stopwords_fixture(fake_async_api_call: AsyncApiCall) -> AsyncStopwords:
89+
"""Return a AsyncStopwords object with test values."""
90+
return AsyncStopwords(fake_async_api_call)
91+
92+
93+
@pytest.fixture(scope="function", name="fake_async_stopwords_set")
94+
def fake_async_stopwords_set_fixture(fake_async_api_call: AsyncApiCall) -> AsyncStopwordsSet:
95+
"""Return a AsyncStopwordsSet object with test values."""
96+
return AsyncStopwordsSet(fake_async_api_call, "company_stopwords")

tests/stopwords_set_test.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
"""Tests for the StopwordsSet class."""
22

3-
4-
53
from tests.utils.object_assertions import assert_match_object, assert_object_lists_match
64
from typesense.api_call import ApiCall
5+
from typesense.async_api_call import AsyncApiCall
6+
from typesense.async_stopwords import AsyncStopwords
77
from typesense.stopwords import Stopwords
88
from typesense.stopwords_set import StopwordsSet
99
from typesense.types.stopword import StopwordDeleteSchema, StopwordSchema
@@ -51,3 +51,49 @@ def test_actual_delete(
5151
response = actual_stopwords["company_stopwords"].delete()
5252

5353
assert response == {"id": "company_stopwords"}
54+
55+
56+
def test_init_async(fake_async_api_call: AsyncApiCall) -> None:
57+
"""Test that the AsyncStopwordsSet object is initialized correctly."""
58+
from typesense.async_stopwords_set import AsyncStopwordsSet
59+
60+
stopword_set = AsyncStopwordsSet(fake_async_api_call, "company_stopwords")
61+
62+
assert stopword_set.stopwords_set_id == "company_stopwords"
63+
assert_match_object(stopword_set.api_call, fake_async_api_call)
64+
assert_object_lists_match(
65+
stopword_set.api_call.node_manager.nodes,
66+
fake_async_api_call.node_manager.nodes,
67+
)
68+
assert_match_object(
69+
stopword_set.api_call.config.nearest_node,
70+
fake_async_api_call.config.nearest_node,
71+
)
72+
assert stopword_set._endpoint_path == "/stopwords/company_stopwords" # noqa: WPS437
73+
74+
75+
async def test_actual_retrieve_async(
76+
actual_async_stopwords: AsyncStopwords,
77+
delete_all_stopwords: None,
78+
delete_all: None,
79+
create_stopword: None,
80+
) -> None:
81+
"""Test that the AsyncStopwordsSet object can retrieve an stopword_set from Typesense Server."""
82+
response = await actual_async_stopwords["company_stopwords"].retrieve()
83+
84+
assert response == {
85+
"stopwords": {
86+
"id": "company_stopwords",
87+
"stopwords": ["and", "is", "the"],
88+
},
89+
}
90+
91+
92+
async def test_actual_delete_async(
93+
actual_async_stopwords: AsyncStopwords,
94+
create_stopword: None,
95+
) -> None:
96+
"""Test that the AsyncStopwordsSet object can delete an stopword_set from Typesense Server."""
97+
response = await actual_async_stopwords["company_stopwords"].delete()
98+
99+
assert response == {"id": "company_stopwords"}

0 commit comments

Comments
 (0)